Current OutPut:
PM_DIG_OUTPUT_1_CLOSED
PM_DIG_OUTPUT_10_CLOSED
PM_DIG_OUTPUT_14_CLOSED
PM_DIG_OUTPUT_15_CLOSED
PM_DIG_OUTPUT_16_CLOSED
PM_DIG_OUTPUT_2_CLOSED
PM_DIG_OUTPUT_3_CLOSED
Expected Output:
PM_DIG_OUTPUT_1_CLOSED
PM_DIG_OUTPUT_2_CLOSED
PM_DIG_OUTPUT_3_CLOSED
PM_DIG_OUTPUT_10_CLOSED
PM_DIG_OUTPUT_14_CLOSED
PM_DIG_OUTPUT_15_CLOSED
PM_DIG_OUTPUT_16_CLOSED
Index of Number is not fixed
What is the best way to achieve this order?
EDIT:
Some records also contain following data
PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO1
PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO2
PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO3
PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO4
Query:
SQLFIDDLEExample
SELECT *
FROM Table1 t1
ORDER BY CAST(REPLACE(REPLACE(col, 'PM_DIG_OUTPUT_', ''),'_CLOSED', '') AS int)
Result:
| COL |
|-------------------------|
| PM_DIG_OUTPUT_1_CLOSED |
| PM_DIG_OUTPUT_2_CLOSED |
| PM_DIG_OUTPUT_3_CLOSED |
| PM_DIG_OUTPUT_10_CLOSED |
| PM_DIG_OUTPUT_14_CLOSED |
| PM_DIG_OUTPUT_15_CLOSED |
| PM_DIG_OUTPUT_16_CLOSED |
EDITED ANSWER
You could use query:
SQLFIDDLEExample
SELECT *
FROM Table1 t1
ORDER BY CASE WHEN LEFT(col, 2) = 'PM'
THEN CAST(REPLACE(REPLACE(col, 'PM_DIG_OUTPUT_', ''),'_CLOSED', '') AS int)
ELSE RIGHT(col,1) END
Result:
| COL |
|-----------------------------------------------|
| PM_DIG_OUTPUT_1_CLOSED |
| PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO1 |
| PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO2 |
| PM_DIG_OUTPUT_2_CLOSED |
| PM_DIG_OUTPUT_3_CLOSED |
| PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO3 |
| PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO4 |
| PM_DIG_OUTPUT_10_CLOSED |
| PM_DIG_OUTPUT_14_CLOSED |
| PM_DIG_OUTPUT_15_CLOSED |
| PM_DIG_OUTPUT_16_CLOSED |
Assuming that after the number there's only one underscore and some text (then no matter what is before the number, if it's at least one underscore and some text).
Edit for new values. It will sort by the numbers if it can find them and won't break if no numbers were found by matching the pattern:
select
*,
substring(y,len(y)-charindex('_',reverse(y))+2,100) as num
from (
select
*,
substring(x,1,len(x)-charindex('_',reverse(x))) as y
from (
select 'PM_DIG_OUTPUT_1_CLOSED' as x union all
select 'PM_DIG_OUTPUT_10_CLOSED' union all
select 'PM_DIG_OUTPUT_14_CLOSED' union all
select 'PM_DIG_OUTPUT_15_CLOSED' union all
select 'PM_DIG_OUTPUT_16_CLOSED' union all
select 'PM_DIG_OUTPUT_2_CLOSED' union all
select 'PM_DIG_OUTPUT_3_CLOSED' union all
select 'PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO1' union all
select 'PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO2' union all
select 'PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO3' union all
select 'PRM_CODE_MIO_DIGITALOUT_WRITE_LOGIC_CARD2_DO4'
) x
) y
order by
case when isnumeric(substring(y,len(y)-charindex('_',reverse(y))+2,100))=1 then cast(substring(y,len(y)-charindex('_',reverse(y))+2,100) as int) end
Related
I have a table with two columns: ColumnA, ColumnB, with rows:
| A | 1 |
| B | 1 |
| B | 2 |
| C | 1 |
| C | 1 |
| C | 1 |
| A | 2 |
| B | 1 |
| A | 2 |
| A | 1 |
I would like to write a query that would return all unique values for ColumnB, for each unique value of ColumnA, where ColumnA has more than 1 value in ColumnB i.e.
| A | 1 |
| A | 2 |
| B | 1 |
| B | 2 |
C 1 should be omitted because there is only one distinct value for ColumnA = 'C'
There might be a simpler approach but this works:
SELECT t.ColumnA, t2.ColumnB
FROM ( select ColumnA
from dbo.TableName t
group by t.ColumnA
having count(distinct t.ColumnB) > 1) t
CROSS APPLY ( select distinct t2.ColumnB
from dbo.TableName t2
where t.ColumnA=t2.ColumnA ) t2
The first subquery returns all unique ColumnA values that have multiple (different) ColumnB values. The 2nd subquery returns all distinct ColumnB values of those ColumnA-values with CROSS APPLY.
SELECT DISTINCT * FROM x WHERE ColumnA IN(
SELECT xd.ColumnA
FROM (
SELECT DISTINCT ColumnA, ColumnB FROM x
) xd
GROUP BY xd.ColumnA HAVING COUNT(*) > 1
)
SELECT y.ColumnA, y.ColumnB
FROM (
SELECT ColumnA, ColumnB, COUNT(*) OVER (PARTITION BY ColumnA) m
FROM x
GROUP BY ColumnA, ColumnB
) y
WHERE m > 1
I have a table with the following structure ...
+--------+------+------+------+------+------+
| ID | colA | colB | colC | colD | colE | [...] etc.
+--------+------+------+------+------+------+
| 100100 | 15 | 100 | 90 | 80 | 10 |
+--------+------+------+------+------+------+
| 100200 | 10 | 80 | 90 | 100 | 10 |
+--------+------+------+------+------+------+
| 100300 | 100 | 90 | 10 | 10 | 80 |
+--------+------+------+------+------+------+
I need to return a concatenated value of column names which hold the maximum 3 values per row ...
+--------+----------------------------------+
| ID | maxCols |
+--------+----------------------------------+
| 100100 | colB,colC,colD |
+--------+------+------+------+------+------+
| 100200 | colD,colC,colB |
+--------+------+------+------+------+------+
| 100300 | colA,colB,colE |
+--------+------+------+------+------+------+
It's okay to not concatenate the column names, and have maxCol1 | maxCol2 | maxCol3 if that's simpler
The order of the columns is important when concatenating them
The number of columns is limited and not dynamic
The number of rows is many
You could use UNPIVOT and get TOP 3 for each ID
;with temp AS
(
SELECT ID, ColValue, ColName
FROM #SampleData sd
UNPIVOT
(
ColValue For ColName in ([colA], [colB], [colC], [colD], [colE])
) unp
)
SELECT sd.ID, ca.ColMax
FROM #SampleData sd
CROSS APPLY
(
SELECT STUFF(
(
SELECT TOP 3 WITH TIES
',' + t.ColName
FROM temp t
WHERE t.ID = sd.ID
ORDER BY t.ColValue DESC
FOR XML PATH('')
)
,1,1,'') AS ColMax
) ca
See demo here: http://rextester.com/CZCPU51785
Here is one trick to do it using Cross Apply and Table Valued Constructor
SELECT Id,
maxCols= Stuff(cs.maxCols, 1, 1, '')
FROM Yourtable
CROSS apply(SELECT(SELECT TOP 3 ',' + NAME
FROM (VALUES (colA,'colA'),(colB,'colB'),(colC,'colC'),
(colD,'colD'),(colE,'colE')) tc (val, NAME)
ORDER BY val DESC
FOR xml path, type).value('.[1]', 'nvarchar(max)')) cs (maxCols)
If needed it can be made dynamic using Information_schema.Columns
table:
+-----------+--------------+------------+------------+
| RequestID | RequestStaus | StartDate | EndDate |
+-----------+--------------+------------+------------+
| 1 | pending | 9/1/2015 | 10/2/2015 |
| 1 | in progress | 10/2/2015 | 10/20/2015 |
| 1 | completed | 10/20/2015 | 11/3/2015 |
| 1 | reopened | 11/3/2015 | null |
| 2 | pending | 9/5/2015 | 9/7/2015 |
| 2 | in progress | 9/7/2015 | 9/25/2015 |
| 2 | completed | 9/25/2015 | 10/7/2015 |
| 2 | reopened | 10/10/2015 | 10/16/2015 |
| 2 | completed | 10/16/2015 | null |
+-----------+--------------+------------+------------+
I would like to calculate the days opened but exclude the days between completed and reopened. For example, RequestID 1, the days opened will be (11/3/2015 - 9/1/2015) + (GetDate() - 11/3/2015), for request 2, the total days will be (10/7/2015 - 9/5/2015) + ( 10/16/2015 - 10/10/2015).
The result I want will be something like:
+-----------+-------------------------------+
| RequestID | DaysOpened |
+-----------+-------------------------------+
| 1 | 63 + (getdate() - 11/3/2015) |
| 2 | 38 |
+-----------+-------------------------------+
How do I approach this problem? thank you!
Tested. Works well. :)
Note:
1) I suppose the required result = (FirstCompleteEndDate - PendingStartDate)+(Sum of all the Reopen duration)
2) So I used the self joins. Table b provides the exact completed record which immediately follows the in process record for each RequestID. Table c provides Sum of all the Reopen duration.
--create tbl structure
create table #test (RequestID int, RequestStatus varchar(20), StartDate date, EndDate date)
go
--insert sample data
insert #test
select 1,'pending','09/01/2015','10/2/2015'
union all
select 1,'in progress','10/2/2015','10/20/2015'
union all
select 1,'completed','10/20/2015','11/3/2015'
union all
select 1,'reopened','11/3/2015',null
union all
select 2,'pending','09/05/2015','9/7/2015'
union all
select 2,'in progress','09/07/2015','9/25/2015'
union all
select 2,'completed','9/25/2015','10/7/2015'
union all
select 2,'reopened','10/10/2015','10/16/2015'
union all
select 2, 'completed','10/16/2015','11/12/2015'
union all
select 2,'reopened','11/20/2015',null
select * from #test
--below is solution
select a.RequestID, a.Startdate as [PendingStartDate], b.enddate as [FirstCompleteEndDate], c.startdate as [LatestReopenStartDate],
datediff(day,a.startdate,b.enddate)+c.ReopenDays as [days] from #test a
join (
select *, row_number()over(partition by RequestID,RequestStatus order by StartDate) as rid from #test
) as b
on a.RequestID = b.RequestID
join (
select distinct RequestID, RequestStatus, max(StartDate)over(partition by RequestID,RequestStatus) as StartDate,
Sum(Case when enddate is null then datediff(day,startdate,getdate())
when enddate is not null then datediff(day,startdate,enddate)
end)over(partition by RequestID,RequestStatus) as [ReopenDays]
from #test
where RequestStatus = 'reopened'
) as c
on b.RequestID = c.RequestID
where a.RequestStatus ='pending' and b.RequestStatus = 'completed' and b.rid = 1
Result:
I have table like
Tabel data
+-------+-------+
| name1 | name2 |
+-------+-------+
| AA | XX |
| BB | YY |
| CC | ZZ |
+-------+-------+
and I want the output like
Edit
+-------+
| name1 |
+-------+
| AA |
| XX |
| BB |
| YY |
| CC |
| ZZ |
+-------+
can u please guide me to query to obtain this task
You can use UNION ALL or UNION . UNION will create a DISTINCT result Set. If you don't want, you can use UNION ALL, which will simply concat the data
SELECT NAME1 as NAME FROM YOUR_TABLE
UNION
SELECT NAME2 FROM YOUR_TABLE
ORDER BY NAME
EDIT: For your updated requirements
with my_data(name1,name2) as
(
select 'AA','XX' from dual
union
select 'BB','YY' from dual
union
select 'CC','ZZ' from dual
),
indexed_my_data(name1,name2,row_sequence) as
(
select name1,name2, row_number() over (order by null) from my_data
)
select name1,row_sequence,'A' as col_sequence from indexed_my_data
union all
select name2,row_sequence,'B' from indexed_my_data
ORDER BY row_sequence,col_sequence;
I have following data:
+----------------+--------------+-----+
| StgDescription | ID | Amt |
+----------------+--------------+-----+
| A | OA17 | 11 |
| A | OA17 | 11 |
| A | OA17 | 11 |
| A | OA17 | 11 |
| B | ZA47/ A | 12 |
| B | ZA47/ A | 12 |
| B | ZA47/ B | 10 |
| B | ZA47/ B | 10 |
| B | ZA48/ A | 14 |
| B | ZA48/ F | 10 |
| B | ZA48 /G | 13 |
| B | ZA48 /H | 10 |
| B | ZA48/ I | 15 |
| B | ZA48/ J | 10 |
| B | ZA48/ K | 16 |
| B | ZA48/ L | 10 |
| c | FA01LM100340 | 10 |
| c | PA53 AE | 10 |
+----------------+--------------+-----+
I want to generate report in following format. The amount should be sum for ID for same StgDescription.
+----------------+-----+
| StgDescription | Amt |
+----------------+-----+
| a | 11 |
| b | 120 |
| c | 20 |
+----------------+-----+
I've written following query to get this result:
WITH CTE AS(
SELECT
distinct
s.StgDescription
,p.ID
,Amt
FROM [DinDb].[dbo].[tblTvlTransaction] t
JOIN tblstgmaster s on t.StgId=s.StgId
JOIN tblProjDocSt p on t.TDocID=p.DocId
JOIN [PdasDb].[dbo].[tblIDmaster] f ON p.ID=f.ID
where OptAuthoDateTime between '2015-07-27 00:00:00' and '2015-09-01 00:00:00')
select StgDescription,sum(AMT) from cte group by StgDescription
Is there any other efficient alternative to do this?
First in cte remove duplicates, then GROUP BY like:
WITH cte AS (
SELECT DISTINCT StgDescription, ID, Amt
FROM your_tab
)
SELECT
StgDescription,
Amt = SUM(Amt)
FROM cte
GROUP BY StgDescription;
OR:
WITH cte AS (
SELECT StgDescription, ID, Amt
FROM your_tab
GROUP BY StgDescription, ID, Amt
)
SELECT
StgDescription,
Amt = SUM(Amt)
FROM cte
GROUP BY StgDescription;
I hope that you get the data from a query, not from a table. It would not be good to store data thus redundantly. And it would not be gould to name a column ID which is not the unique identifier for a row in a table.
Your problem with the data is that you have duplicates, which prevents you from getting the sum directly. So use DISTINCT to make your data unique first.
If this data is from a query then simply add DISTINCT after the SELECT keyword. If not, use a derived table (i.e. a subquery) where you select distinct records from the table.
select stgdescription, sum(amt)
from
(
select distinct stgdescription, id, amt
from mydata
) distinct_data
group by stgdescription;
You may want to replace stgdescription with lower(stgdescription), though, if stgdescription can be 'A' or 'a' and you want to treat them the same.
I'd keep it as simple as possible, like this:
select StgDescription, sum(Amt) from
(
select distinct StgDescription, ID, Amt from tablename
) a
group by StgDescription
Hope it helps!
I suspect your duplicates are coming from [tblTvlTransaction], therefore, I would remove this table as a JOIN and use EXISTS to just check a record is there. So essentially the only tables in the FROM clause are those you actually need data from:
SELECT s.StgDescription, p.ID, s.Amt
FROM tblstgmaster AS s
INNER JOIN tblProjDocSt p on
t.TDocID = p.DocId
INNER JOIN [PdasDb].[dbo].[tblIDmaster] AS f
ON p.ID = f.ID
WHERE EXISTS
( SELECT 1
FROM [DinDb].[dbo].[tblTvlTransaction] AS t
WHERE t.OptAuthoDateTime BETWEEN '2015-07-27 00:00:00' AND '2015-09-01 00:00:00'
AND t.StgId = s.StgId
);
The advantage of EXISTS is that it can use a semi-join, which essentially means rather than pulling back all the rows from the transaction table, it will stop the seek/scan as soon as it finds one matching record. This should leave you without duplicates so you can do the SUM directly:
SELECT s.StgDescription, Amount = SUM(s.Amt)
FROM tblstgmaster AS s
INNER JOIN tblProjDocSt p on
t.TDocID = p.DocId
INNER JOIN [PdasDb].[dbo].[tblIDmaster] AS f
ON p.ID = f.ID
WHERE EXISTS
( SELECT 1
FROM [DinDb].[dbo].[tblTvlTransaction] AS t
WHERE t.OptAuthoDateTime BETWEEN '2015-07-27 00:00:00' AND '2015-09-01 00:00:00'
AND t.StgId = s.StgId
)
GROUP BY s.StgDescription;