I'm a newbie in SQL Server; I have trouble to write this query.
This is my sample data:
| Id | date | time |
+----+-----------+------------+
| 1 | 2020-11-1 | 07:30:00 |
| 1 | 2020-11-1 | 15:50:00 |
| 2 | 2020-11-1 | 07:30:00 |
| 2 | 2020-11-1 | 15:54:00 |
| 1 | 2020-11-2 | 07:31:00 |
| 1 | 2020-11-2 | 15:46:00 |
This is the result I need:
| id |in_2020_11_01 | out_2020_11_01 | in_2020_11_02 | out_2020_11_02 |
+----+--------------+-----------------+----------------+----------------+
| 1 | 07:30:00 | 15:50:00 | 07:31:00 | 15:46:00 |
| 2 | 07:30:00 | 15:54:00 | 00:00:00 | 00:00:00 |
How can I build the result? Is it possible to get a result like that?
Thanks in advance
You need to use dynamic T-SQL and build a PIVOT statement. Here is full working example:
DROP TABLE IF EXISTS #DataSource;
CREATE TABLE #DataSource
(
[Id] INT
,[date] DATE
,[time] TIME
);
INSERT INTO #DataSource ([Id], [date], [time])
VALUES (1, '2020-11-1', '07:30:00')
,(1, '2020-11-1', '15:50:00')
,(2, '2020-11-1', '07:30:00')
,(2, '2020-11-1', '15:54:00')
,(1, '2020-11-2', '07:31:00')
,(1, '2020-11-2', '15:46:00');
DECLARE #ColumnsPIVOT VARCHAR(MAX)
,#ColumnsSELECT VARCHAR(MAX)
,#DyanmicTSQLSTatement NVARCHAR(MAX);
WITH DataSource ([date], [prefix], [column_name]) AS
(
SELECT DISTINCT [date]
,[prefix]
,[prefix] + '_' + REPLACE([date], '-', '_')
FROM #DataSource
CROSS APPLY
(
VALUES ('in')
,('out')
) DS ([prefix])
)
SELECT #ColumnsPIVOT = STUFF
(
(
SELECT ',' + QUOTENAME([column_name])
FROM DataSource
ORDER BY [date]
,[prefix]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
,#ColumnsSELECT = STUFF
(
(
SELECT ', ISNULL(' + QUOTENAME([column_name]) + ', ''00:00:00'') AS ' + QUOTENAME([column_name])
FROM DataSource
ORDER BY [date]
,[prefix]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
);
-- SQL Server 2017+
/*
WITH DataSource ([date], [prefix], [column_name]) AS
(
SELECT DISTINCT [date]
,[prefix]
,[prefix] + '_' + REPLACE([date], '-', '_')
FROM #DataSource
CROSS APPLY
(
VALUES ('in')
,('out')
) DS ([prefix])
)
SELECT #ColumnsPIVOT = STRING_AGG(QUOTENAME([column_name]), ',') WITHIN GROUP(ORDER BY [date], [prefix])
,#ColumnsSELECT = STRING_AGG('ISNULL(' + QUOTENAME([column_name]) + ', ''00:00:00'') AS ' + QUOTENAME([column_name]), ',') WITHIN GROUP(ORDER BY [date], [prefix])
FROM DataSource;
*/
SET #DyanmicTSQLSTatement= 'SELECT [ID], ' + #ColumnsSELECT + '
FROM
(
SELECT [id]
,CASE ROW_NUMBER() OVER (PARTITION BY [ID], [Date] ORDER BY [Time])
WHEN 1 THEN ''in''
WHEN 2 THEN ''out''
END + ''_'' + REPLACE([date], ''-'', ''_'')
,[time]
FROM #DataSource
) DS ([id], [date], [time])
PIVOT
(
MAX([time]) FOR [date] IN (' + #ColumnsPIVOT +')
) PVT';
EXEC sp_executesql #DyanmicTSQLSTatement;
Related
I have the below record and would like to create 1 row record.
I tried STUFF, FOR XML PATH and did not work
+-----------+-------+---------+
| CLIENT_ID | Event | DX_Code |
+-----------+-------+---------+
| 54 | 5 | F45.72 |
| 54 | 5 | X45.34 |
| 54 | 5 | M98.32 |
+-----------+-------+---------+
Output = 54, 5, F45.72 X45.34 M98.32
You can do that by using STUFF() with FOR XML PATH('') as
CREATE TABLE T
([CLIENT_ID] int, [Event] int, [DX_Code] varchar(6))
;
INSERT INTO T
([CLIENT_ID], [Event], [DX_Code])
VALUES
(54, 5, 'F45.72'),
(54, 5, 'X45.34'),
(54, 5, 'M98.32')
;
SELECT DISTINCT T1.[CLIENT_ID],
T1.[Event],
STUFF(
(
SELECT ',' + T2.[DX_Code]
FROM T T2
WHERE T2.[CLIENT_ID] = T1.[CLIENT_ID]
AND T2.[Event] = T1.[Event]
FOR XML PATH ('')
) , 1, 1, ''
) Result
FROM T T1;
This should give you the expected result
SELECT CAST(t1.CLIENT_ID AS VARCHAR) + ','+ CAST(t1.Event AS VARCHAR)+ ','+
STUFF(( SELECT ' ' + t2.DX_Code AS [text()]
FROM #temp t2
WHERE
t2.CLIENT_ID = t1.CLIENT_ID
and t2.Event = t1.Event
FOR XML PATH('')
), 1, 1, '' )
AS OutputText
FROM #temp t1
GROUP BY t1.CLIENT_ID,t1.Event
Output:
54,5,F45.72 X45.34 M98.32
I am trying to use a pivot to get information in a diff format.
Here is my table:
CREATE TABLE yourtable
([case] int, [category] varchar(4))
;
INSERT INTO yourtable
([case], [category])
VALUES
(1, 'xx'),
(1, 'xyx'),
(1, 'abc'),
(2, 'ghj'),
(2, 'asdf'),
(3, 'dfgh')
;
Here is my pivot command courtesy of bluefeet:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME('cat'+cast(seq as
varchar(10)))
from
(
select row_number() over(partition by [case]
order by category) seq
from yourtable
) d
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [case],' + #cols + '
from
(
SELECT [case], category,
''cat''+
cast(row_number() over(partition by [case]
order by category) as varchar(10)) seq
FROM yourTable
) x
pivot
(
max(category)
for seq in (' + #cols + ')
) p '
execute sp_executesql #query;
The output is good, it is in the format I need.
CASE CAT1 CAT2 CAT3
1 abc xx xyx
2 asdf ghj (null)
3 dfgh (null) (null)
However, I also need to add additional columns to the table. The modified table would be as follows, but I'm not sure how to add this to the QUOTENAME.
CREATE TABLE yourtable
([case] int, [category] varchar(4), [status] varchar(4))
;
INSERT INTO yourtable
([case], [category], [status])
VALUES
(1, 'xx', '00'),
(1, 'xyx', '01'),
(1, 'abc', '00'),
(2, 'ghj', '01'),
(2, 'asdf', '00'),
(3, 'dfgh', '01')
;
How can this be done? Should I add an additional QUOTENAME command? Results should be:
CASE CAT1 status1 CAT2 status2 CAT3 status3
1 abc 00 xx 00 xyx 01
2 asdf 00 ghj 01 (null) (null)
3 dfgh 01 (null) (null) (null) (null)
Since you now have two columns that you want to PIVOT, you can first unpivot the category and status columns into a single column with multiple rows.
There are a few different ways you can unpivot the data, you can use UNPIVOT or CROSS APPLY. The basic syntax will be:
select [case],
col+cast(seq as varchar(10)) seq,
value
from
(
SELECT [case], status, category,
row_number() over(partition by [case]
order by status) seq
FROM yourTable
) d
cross apply
(
select 'cat', category union all
select 'status', status
) c (col, value)
See SQL Fiddle with Demo This will convert your multiple columns of data into something that looks like this:
| CASE | SEQ | VALUE |
|------|---------|-------|
| 1 | cat1 | xx |
| 1 | status1 | 00 |
| 1 | cat2 | abc |
| 1 | status2 | 00 |
| 1 | cat3 | xyx |
| 1 | status3 | 01 |
| 2 | cat1 | asdf |
| 2 | status1 | 00 |
Once the data is in this format, then you can apply the PIVOT function to it.
SELECT [case], cat1, status1, cat2, status2, cat3, status3
FROM
(
select [case],
col+cast(seq as varchar(10)) seq,
value
from
(
SELECT [case], status, category,
row_number() over(partition by [case]
order by status) seq
FROM yourTable
) d
cross apply
(
select 'cat', category union all
select 'status', status
) c (col, value)
) x
PIVOT
(
max(value)
for seq in (cat1, status1, cat2, status2, cat3, status3)
)p;
See SQL Fiddle with Demo
Then you can convert it to dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+cast(seq as varchar(10)))
from
(
select row_number() over(partition by [case]
order by category) seq
from yourtable
) d
cross apply
(
select 'cat', 1 union all
select 'status', 2
) c (col, so)
group by seq, col, so
order by seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [case],' + #cols + '
from
(
select [case],
col+cast(seq as varchar(10)) seq,
value
from
(
SELECT [case], status, category,
row_number() over(partition by [case]
order by status) seq
FROM yourTable
) d
cross apply
(
select ''cat'', category union all
select ''status'', status
) c (col, value)
) x
pivot
(
max(value)
for seq in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo The final result will be:
| CASE | CAT1 | STATUS1 | CAT2 | STATUS2 | CAT3 | STATUS3 |
|------|------|---------|--------|---------|--------|---------|
| 1 | xx | 00 | abc | 00 | xyx | 01 |
| 2 | asdf | 00 | ghj | 01 | (null) | (null) |
| 3 | dfgh | 01 | (null) | (null) | (null) | (null) |
I have data that looks as follows:
I would like to pivot this data so that there is only a single row for the SubId. The columns would be SubId, 802Lineage, 802ReadTime, 1000Lineage, 1000ReadTime etc.
If it wasn't for the requirement to have the Lineage included, this would be pretty straightforward, as follows:
Select SubId, [800] as [800Time], [1190] as [1190Time], [1605] as [1605Time]
From
(Select SubId, ProcessNumber, ReadTime From MyTable) as SourceTable
PIVOT
(
Max(ReadTime)
For ProcessNumber IN ([800],[802],[1190],[1605])
) as PivotTable;
I'm just not sure how to do this with the Lineage included. This is for SQL Server 2012
you can pivot manually:
select
SubId,
max(case when ProcessNumber = 802 then ReadTime end) as [802Time],
max(case when ProcessNumber = 802 then Lineage end) as [802Lineage],
....
from SourceTable
group by SubId
Example of joining two pivot tables, as requested in comments.
CREATE TABLE #MyTable (SubId int, ProcessNumber int, Lineage varchar(16), ReadTime datetime)
INSERT INTO #MyTable
(SubID, ProcessNumber, Lineage, ReadTime)
VALUES
(1, 9, 'A', GETDATE()),
(1, 8, 'A', GETDATE()),
(1, 7, 'B', GETDATE()),
(2, 9, 'C', GETDATE()),
(2, 8, 'C', GETDATE())
SELECT *
FROM (
Select SubId, [9] as [9Time], [8] as [8Time], [7] as [7Time]
From
(Select SubId, ProcessNumber, ReadTime From #MyTable) as SourceTable
PIVOT(Max(ReadTime) For ProcessNumber IN ([9],[8],[7],[6])) as PivotTable1
) AS T1
INNER JOIN (
Select SubId, [9] as [9Lineage], [8] as [8Lineage], [7] as [7Lineage]
From
(Select SubId, ProcessNumber, Lineage From #MyTable) as SourceTable
PIVOT(Max(Lineage) For ProcessNumber IN ([9],[8],[7],[6])) as PivotTable1
) AS T2
ON T1.SubId = T2.SubId
GO
You can use the PIVOT function to get the result but you will have to unpivot the Lineage and ReadTime columns from the multiple columns into multiple rows.
Since you are using SQL Server 2012 you can unpivot the data using CROSS APPLY with VALUES:
select subid,
colname = cast(processNumber as varchar(10)) + colname,
value
from mytable
cross apply
(
values
('Lineage', Lineage),
('ReadTime', convert(varchar(20), readtime, 120))
) c (colname, value)
See SQL Fiddle with Demo. This will convert your current data into the format:
| SUBID | COLNAME | VALUE |
|-------------|--------------|---------------------|
| 12010231146 | 802Lineage | PBG12A |
| 12010231146 | 802ReadTime | 2012-01-02 21:44:00 |
| 12010231146 | 1000Lineage | PBG12A |
| 12010231146 | 1000ReadTime | 2012-01-02 21:43:00 |
| 12010231146 | 1190Lineage | PBG11B |
| 12010231146 | 1190ReadTime | 2012-01-03 14:36:00 |
Once the data is in this format, then you can easily apply the PIVOT function to get your final result:
select *
from
(
select subid,
colname = cast(processNumber as varchar(10)) + colname,
value
from mytable
cross apply
(
values
('Lineage', Lineage),
('ReadTime', convert(varchar(20), readtime, 120))
) c (colname, value)
) d
pivot
(
max(value)
for colname in ([802Lineage], [802ReadTime],
[1000Lineage], [1000ReadTime],
[1190Lineage], [1190ReadTime])
) piv;
See SQL Fiddle with Demo.
The above works great if you have a limited number of rows that you want to convert, but if you have an unknown number then you can use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cast(processnumber as varchar(10))+col)
from mytable
cross apply
(
select 'Lineage', 0 union all
select 'ReadTime', 1
) c (col, so)
group by processnumber, col, so
order by processnumber, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT subid, ' + #cols + '
from
(
select subid,
colname = cast(processNumber as varchar(10)) + colname,
value
from mytable
cross apply
(
values
(''Lineage'', Lineage),
(''ReadTime'', convert(varchar(20), readtime, 120))
) c (colname, value)
) x
pivot
(
max(value)
for colname in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. This gives a result:
| SUBID | 802LINEAGE | 802READTIME | 1000LINEAGE | 1000READTIME | 1190LINEAGE | 1190READTIME | 1605LINEAGE | 1605READTIME | 1745LINEAGE | 1745READTIME | 1790LINEAGE | 1790READTIME | 1990LINEAGE | 1990READTIME | 2690LINEAGE | 2690READTIME | 2795LINEAGE | 2795READTIME | 2990LINEAGE | 2990READTIME | 3090LINEAGE | 3090READTIME | 3290LINEAGE | 3290READTIME |
|-------------|------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|
| 12010231146 | PBG12A | 2012-01-02 21:44:00 | PBG12A | 2012-01-02 21:43:00 | PBG11B | 2012-01-03 14:36:00 | PBG11B | 2012-01-03 15:15:00 | PBG11A | 2012-01-03 15:16:00 | PBG11A | 2012-01-03 15:19:00 | PBG11A | 2012-01-03 15:23:00 | PBG11A | 2012-01-03 15:32:00 | PBG11A | 2012-01-03 15:39:00 | PBG11A | 2012-01-03 15:41:00 | PBG11A | 2012-01-03 15:46:00 | PBG11A | 2012-01-03 15:47:00 |
I have the following tables:
tblengineeringlookupcolumnmaster
elccolumnid | elclookupcode | elccolumnname | elcisrequired
1 | 64 | FirstName | 1
2 | 64 | LastName | 1
3 | 65 | abc | 1
4 | 65 | xyz | 1
tblengineeringlookupdetail
eldrecordId | eldlookupcode |eldlookupsequence |eldlookupvalue | eldlookupvaluedescription
245 | 64 | 0 | Red | Aravinth,Arumugam
246 | 64 | 1 | Blue | Santhosh,Chandran
247 | 64 | 2 | Green | Karthik,Balasubramanian
When I pass '64' as the parameter to the procedure.
I got the output as:
FirstName | LastName | eldRecordId
-------------------------------------
Aravinth | Arumugam | 245
Santhosh | Chandran | 246
Karthik | Balasubramanian| 247
The Store procedure used is
//SP
-- Select the columns to be used
DECLARE #tcol TABLE
(
ID INT IDENTITY(1,1)
, elclookupcode INT
, elccolumnname VARCHAR(100)
)
-- Insert the records into the table
INSERT INTO #tcol (elclookupcode, elccolumnname)
SELECT elclookupcode,elccolumnname FROM tblEngineeringLookupColumnMaster WHERE elclookupcode=#LookupCode
-- Select the columns which should be as output as a table
DECLARE #temp TABLE
(
elcLookupCode INT
, RecordId INT
, txt VARCHAR(8000)
)
-- Select the records from the table and insert
INSERT INTO #temp (elcLookupCode, RecordId, txt)
SELECT eldLookupCode,eldRecordId, eldLookupValueDescription FROM tblEngineeringLookupDetail WHERE eldLookupCode=#LookupCode
DECLARE #SQL NVARCHAR(MAX)
-- Have a table for the selected values
;WITH cte AS
(
SELECT
token = ', [' + d2.elccolumnname + '] = ''' + d.token + ''''
, d.RecordId
FROM (
SELECT
token = t.c.value('.', 'VARCHAR(50)')
, a.RecordId
, a.elcLookupCode
, rn = ROW_NUMBER() OVER (PARTITION BY a.RecordId ORDER BY a.RecordId)
FROM (
SELECT
RecordId
, elcLookupCode
, txml = CAST('<t>' + REPLACE(txt, ',', '</t><t>') + '</t>' AS XML)
FROM #temp
) a
CROSS APPLY txml.nodes('/t') t(c)
) d
-- Select the columns to be mapped
JOIN (
SELECT
elclookupcode
, elccolumnname
, rn = ROW_NUMBER() OVER (PARTITION BY elclookupcode ORDER BY elclookupcode)
FROM #tcol
) d2 ON d.elcLookupCode = d2.elclookupcode AND d2.rn = d.rn
)
-- Join all the records taken
SELECT #SQL = STUFF((
SELECT CHAR(13) + 'UNION ALL SELECT '+ STUFF((
SELECT t2.token
FROM cte t2
WHERE t2.RecordId = t.RecordId
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, ''), ',[RecordId] = ' + CAST(RecordId AS VARCHAR(10))
FROM #temp t
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 11, '')
PRINT #SQL
EXEC sp_executesql #SQL
END
The question now is:
If I pass '65' as parameter to the procedure,Where there are values in the first table(tblengineeringlookupcolumnmaster) and no corresponding values in the second table(tblengineeringlookupdetail) I'm getting no results( A message showing 3rows affected).
But I have to get the columns headers alone.
'Sample:
xyz | abc | eldrecordId
NULL |NULL | NULL
Why i'm not getting this? Where i'm wrong?
Try this one -
DECLARE #tcol TABLE
(
ID INT IDENTITY(1,1)
, elclookupcode INT
, elccolumnname VARCHAR(20)
)
INSERT INTO #tcol (elclookupcode, elccolumnname)
VALUES
(65, 'FirstName'),
(65, 'LastName')
DECLARE #temp TABLE
(
elcLookupCode INT
, eldRecordId INT
, txt VARCHAR(100)
)
INSERT INTO #temp (elcLookupCode, eldRecordId, txt)
VALUES
(64, 245, 'Aravinth,Arumugam'),
(64, 246, 'Santhosh,Chandran'),
(64, 247, 'Karthik,Balasubramanian')
DECLARE #SQL NVARCHAR(MAX)
;WITH cte AS
(
SELECT
token = ', [' + d2.elccolumnname + '] = ''' + d.token + ''''
, d.eldRecordId
FROM (
SELECT
token = t.c.value('.', 'VARCHAR(50)')
, a.eldRecordId
, a.elcLookupCode
, rn = ROW_NUMBER() OVER (PARTITION BY a.eldRecordId ORDER BY a.eldRecordId)
FROM (
SELECT
eldRecordId
, elcLookupCode
, txml = CAST('<t>' + REPLACE(txt, ',', '</t><t>') + '</t>' AS XML)
FROM #temp
) a
CROSS APPLY txml.nodes('/t') t(c)
) d
LEFT JOIN (
SELECT
elclookupcode
, elccolumnname
, rn = ROW_NUMBER() OVER (PARTITION BY elclookupcode ORDER BY elclookupcode)
FROM #tcol
) d2 ON d.elcLookupCode = d2.elclookupcode AND d2.rn = d.rn
)
SELECT #SQL = STUFF((
SELECT CHAR(13) + 'UNION ALL SELECT [eldRecordId] = ' + CAST(eldRecordId AS VARCHAR(10)) + ', ' +
'[elcLookupCode] = ' + CAST(elcLookupCode AS VARCHAR(10)) + ISNULL(STUFF((
SELECT t2.token
FROM cte t2
WHERE t2.eldRecordId = t.eldRecordId
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, ', '), '')
FROM #temp t
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 11, '')
PRINT #SQL
EXEC sys.sp_executesql #SQL
sample datas
date | num | name | value
01012012 | 1 | A | 20
01012012 | 1 | B | 30
01012012 | 2 | C | 40
wish to like this
date | num | A | B | C
01012012 | 1 | 20 | 30 | --
01012012 | 2 | -- | -- | 40
name is not fixed can allow many names
How about something like
DECLARE #Table TABLE(
date DATETIME,
num INT,
name VARCHAR(20),
value FLOAT
)
INSERT INTO #Table SELECT '20121201',1,'A',20
INSERT INTO #Table SELECT '20121201',1,'B',30
INSERT INTO #Table SELECT '20121201',2,'C',40
SELECT *
FROM (
SELECT date,
num,
name,
value
FROM #Table
) t
PIVOT (
SUM(Value) FOR name IN ([A],[B],[C])
) p
SQL Fiddle DEMO
For dynamic columns you need to use dynamic SQL
SQL FIDDLE EXAMPLE
declare
#cols nvarchar(max),
#stmt nvarchar(max)
select #cols = isnull(#cols + ', ', '') + '[' + Name + ']' from table1
select #stmt = '
select *
from table1 as T
pivot
(
max(T.value)
for name in (' + #cols + ')
) as P'
exec sp_executesql #stmt = #stmt
If you don't need a dynamic number of columns, you can use plain SQL like
select *
from table1 as T
pivot
(
max(T.value)
for name in ([A],[B],[C])
) as P