SQL Server - Multiple rows to columns by unique identifier - sql-server

I have the following database table:
| Article | Material |
|------------|-------------|
| article001 | material001 |
| article001 | material002 |
| article002 | material002 |
| article002 | material003 |
| article002 | material004 |
| article003 | material002 |
| article004 | material003 |
| article004 | material004 |
| article004 | material005 |
| article004 | material006 |
I want to achive a result like this:
| Article | Material1 | Material2 | Material3 | Material4 |
|------------|-------------|-------------|-------------|-------------|
| article001 | material001 | material002 | | |
| article002 | material002 | material003 | material004 | |
| article003 | material002 | | | |
| article004 | material003 | material004 | material005 | material006 |
I have already tried various possibilities (Pivot, CTE, Temp Tables). Unfortunately I do not get it. I'm pretty new to SQL.

A simple solution is a conditional aggregation (if you have a max number of materials). Let me know if you need to dynamic.
Select Article
,Material1 = max(case when RN=1 then Material else '' end)
,Material2 = max(case when RN=2 then Material else '' end)
,Material3 = max(case when RN=3 then Material else '' end)
,Material4 = max(case when RN=4 then Material else '' end)
From (
Select *
,RN = Row_Number() over (Partition By Article Order by Material)
From YourTable
) A
Group By Article
Returns
EDIT - Dynamic Pivot
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName('Material'+cast(RN as varchar(25))) From (Select Distinct RN = Row_Number() over (Partition By Article Order by Material) From Yourtable) A Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [Article],' + #SQL + '
From (
Select Article,Material
,Item = ''Material''+cast(Row_Number() over (Partition By Article Order by Material) as varchar(25))
From YourTable
) A
Pivot (max(Material) For [Item] in (' + #SQL + ') ) p'
Exec(#SQL);

Related

Pivot concatenated string

My Input Table is
+--------+----------+----------+-------+
| userid | teamname | task1 | task2 |
+--------+----------+----------+-------+
| 101 | T1 | 101task1 | desc1 |
| 101 | T1 | 101task2 | desc2 |
| 101 | T2 | 101task1 | desc3 |
| 101 | T2 | 101task2 | desc4 |
| 101 | T2 | 101task2 | desc5 |
| 102 | T3 | 102task1 | desc6 |
| 102 | T3 | 102task2 | desc7 |
| 102 | T3 | 102task3 | desc8 |
| 102 | T3 | 102task4 | desc9 |
+--------+----------+----------+-------+
I want the output as follow, to pivot(dynamic) task1+task2
+--------+----------+----------------+----------------+----------------+----------------+
| userid | teamname | 1 | 2 | 3 | 4 |
+--------+----------+----------------+----------------+----------------+----------------+
| 101 | T1 | 101task1 desc1 | 101task2 desc2 | | |
| 101 | T2 | 101task1 desc3 | 101task2 desc4 | 101task2 desc5 | |
| 102 | T3 | 102task1 desc6 | 102task2 desc7 | 102task3 desc8 | 102task4 desc9 |
+--------+----------+----------------+----------------+----------------+----------------+
I tried to pivot task1 and it worked like sample code SampleCode,
but when I pivot max(task1+task2) I get error
You need to combine them in source of pivot table and use them by giving them an alias.
set #query = 'SELECT userid,teamname,' + #cols + ' from
(
select userid,
teamname,
concat(task1,task2) as col, --Combine here
ROW_NUMBER() OVER(PARTITION BY userid,teamname order by task1 asc) AS Row#
from #yt
) x
pivot
(
max(col) --Alias used here
for row# in (' + #cols + ')
) p '
execute(#query);
Edit : To add a space in between fields
set #query = 'SELECT userid,teamname,' + #cols + ' from
(
select userid,
teamname,
CONCAT(task1,SPACE(1),task2) col,
ROW_NUMBER()OVER(PARTITION BY userid,teamname order by task1 asc) AS Row#
from #yt
) x
pivot
(
max(col)
for row# in (' + #cols + ')
) p '
execute(#query);

Compare historical rows (LAG rows) and combine changed values to single column

Compare historical rows (LAG rows based on ResultChngDt) and combine changed column values to single column. Looking for help in writing elegant/efficient SQL Server 2016 TSQL Code(without cursors).
I have a table with the structure and data like this:
+----+-------+--------------+---------------+--------+--------+--------------+
| ID | RepID | CollctedDate | CompletedDate | Result | Tcode | ResultChngDt |
+----+-------+--------------+---------------+--------+--------+--------------+
| 1 | 101 | 11/20/2017 | 12/13/2017 | | L-2190 | 12/13/2017 |
| 1 | 101 | 11/22/2017 | 12/15/2017 | POS | L-Afb | 1/5/2018 |
| 1 | 102 | 11/22/2017 | 12/15/2017 | | L-2191 | 12/15/2017 |
| 1 | 102 | 11/22/2017 | 12/15/2017 | POS | L-2192 | 12/31/2017 |
+----+-------+--------------+---------------+--------+--------+--------------+
I need to generate a report/result as follows:
+----+-------+---------------------------+--------------------------+--+
| ID | RepID | Previous | Current | |
+----+-------+---------------------------+--------------------------+--+
| 1 | 101 | CollctedDate:11/20/2017 | CollctedDate:11/22/2017 | |
| | | CompletedDate:12/13/2017 | CompletedDate:12/15/2017 | |
| | | Result: | Result:POS | |
| | | Tcode:L-2190 | Tcode:L-Afb | |
| 1 | 102 | CollctedDate:11/22/2017 | CollctedDate:11/22/2017 | |
| | | CompletedDate:12/15/2017 | CompletedDate:12/15/2017 | |
| | | Result: | Result:POS | |
| | | Tcode:L-2191 | Tcode:L-2192 | |
+----+-------+---------------------------+--------------------------+--+
CREATE TABLE [dbo].[Table1]
(
[ID] INT NULL,
[RepID] INT NULL,
[CollctedDate] DATETIME NULL,
[CompletedDate] DATETIME NULL,
[Result] VARCHAR(3) NULL,
[Tcode] VARCHAR(10) NULL,
[ResultChngDt] DATETIME NULL
) ON [PRIMARY];
GO
INSERT INTO [dbo].[Table1] ([ID], [RepID], [CollctedDate], [CompletedDate], [Result], [Tcode], [ResultChngDt])
VALUES (1, 101, N'11/20/2017', N'12/13/2017', N'', N'L-2190', N'12/13/2017')
, (1, 101, N'11/22/2017', N'12/15/2017', N'POS', N'L-Afb', N'1/5/2018')
, (1, 102, N'11/22/2017', N'12/15/2017', N'', N'L-2191', N'12/15/2017')
, (1, 102, N'11/22/2017', N'12/15/2017', N'POS', N'L-2192', N'12/31/2017')
Here's my query for your question:
WITH cte_LEADLAG AS(
SELECT ID,
RepID,
CollctedDate,
CompletedDate,
Result,
Tcode,
ResultChngDt,
CONCAT('CollectedDate:',CAST(CollctedDate AS DATETIME2), ' CompletedDate:', CAST(CompletedDate AS DATETIME2), ' Result:', Result, ' Tcode', Tcode) AS dates,
LAG(CollctedDate) OVER(PARTITION BY RepID ORDER BY CollctedDate) AS 'LAGCollectedDate' ,
lead(CollctedDate) OVER(PARTITION BY RepID ORDER BY CollctedDate) AS 'LEADCollectedDate',
LAG(CompletedDate) OVER(PARTITION BY RepID ORDER BY CompletedDate) AS 'LAGCompDate' ,
lead(CompletedDate) OVER(PARTITION BY RepID ORDER BY CompletedDate) AS 'LEADcompDate' ,
LEAD(Result) OVER(PARTITION BY RepID ORDER BY CompletedDate) AS 'LEADResult' ,
LEAD(Tcode) OVER(PARTITION BY RepID ORDER BY CompletedDate) AS 'LEADTcode'
FROM #temp
),
cte_FINAL AS(
SELECT distinct ID,
RepID,
CASE WHEN cte.LAGCollectedDate IS NULL THEN CONCAT('CollectedDate:',CAST(CollctedDate AS DATETIME2), ' CompletedDate:', CAST(CompletedDate AS DATETIME2), ' Result:', Result, ' Tcode', Tcode) end AS 'Previous',
CASE WHEN cte.LEADCollectedDate IS not NULL THEN CONCAT('CollectedDate:',CAST(cte.LEADCollectedDate AS DATETIME2), ' CompletedDate:', CAST(LEADcompDate AS DATETIME2), ' Result:', cte.LEADResult, ' Tcode', cte.LEADTcode) end AS 'Current'
FROM cte_LEADLAG AS cte
WHERE cte.LEADCollectedDate IN (SELECT MAX(LEADCollectedDate) FROM cte_LEADLAG WHERE cte_LEADLAG.RepID = cte.RepID))
)
SELECT *
FROM cte_FINAL;
Result:
with data as (
select *, row_number() over (partition by RepID order by ResultChgDt desc) as rn
from dbo.Table1
)
select
from data as d1 left outer join data as d2 on d2.rn = d1.rn + 1
where d1.rn = 1 -- I suppose you only want the two most recent??
This gives you all the data you need in a single row. You can handle report formatting to suit whatever requirements you have in whatever tool you're using for that.

How to convert table columns to rows [duplicate]

This question already has answers here:
Convert Rows to columns using 'Pivot' in SQL Server
(9 answers)
Closed 6 years ago.
I have a table like below:
+-----------+------------+--------+
| Col1 | Col2 | Col3 |
+-----------+------------+--------+
| 12345678 | FirstName | John |
| 12345678 | LastName | Smith |
| 987456321 | LastName | Clancy |
| 987456321 | MiddleName | T |
| 987456321 | Height | 176cm |
| 654125878 | FirstName | Tan |
| 654125878 | Weight | 150lb |
+-----------+------------+--------+
How to convert that to:
+-----------+-----------+------------+----------+--------+--------+
| ID | FirstName | MiddleName | LastName | Height | Weight |
+-----------+-----------+------------+----------+--------+--------+
| 12345678 | John | null | Smith | null | null |
| 987456321 | null | T | Clancy | 176cm | null |
| 654125878 | Tan | null | null | null | 150lb |
+-----------+-----------+------------+----------+--------+--------+
I think a conditional aggregation would do the trick here.
Assuming you don't need dynamic
Select ID = Col1
,FirstName = max(case when Col2='FirstName' then Col3 else null end)
,MiddleName = max(case when Col2='MiddleName ' then Col3 else null end)
,LastName = max(case when Col2='LastName ' then Col3 else null end)
,Height = max(case when Col2='Height' then Col3 else null end)
,Weight = max(case when Col2='Weight' then Col3 else null end)
From YourTable
Group By Col1
If you need dynamic
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName([Col2]) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [Col1] as ID ,' + #SQL + '
From YourTable
Pivot (max(Col3) For [Col2] in (' + #SQL + ') ) p'
Exec(#SQL);
If it helps, the generated SQL for the dynamic Pivot is as follows:
Select [Col1] as ID ,[FirstName],[Height],[LastName],[MiddleName],[Weight]
From YourTable
Pivot (max(Col3) For [Col2] in ([FirstName],[Height],[LastName],[MiddleName],[Weight]) ) p

calculate days with gaps

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:

T-SQL Transposing column headers into Rows

I want to transpose my table.
I have simple 'Person' table as shown below.
+---+----------+------------+------------+----------------------+
|ID | Person | BirthDate | Phone | Email |
+---+----------+------------+------------+----------------------+
| 1 | Tom | 1985-11-08 | 1111111111 | tom#somedomain.com |
+---+----------+------------+------------+----------------------+
| 2 | Dick | 1982-02-24 | 2222222222 | dick#otherdomain.com |
+---+----------+------------+------------+----------------------+
| 3 | Harry | 1986-04-17 | 3333333333 | harry#thatdomain.com |
+---+----------+------------+------------+----------------------+
And I want this table to be transposed like below.
+-----------+--------------------+----------------------+----------------------+
| Key | Value1 | Value2 | Value3 |
+-----------+--------------------+----------------------+----------------------+
| ID | 1 | 2 | 3 |
+-----------+--------------------+----------------------+----------------------+
| Person | Tom | Dick | Harry |
+-----------+--------------------+----------------------+----------------------+
| BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+-----------+--------------------+----------------------+----------------------+
| Phone | 1111111111 | 2222222222 | 3333333333 |
+-----------+--------------------+----------------------+----------------------+
| Email | tom#somedomain.com | dick#otherdomain.com | harry#thatdomain.com |
+-----------+--------------------+----------------------+----------------------+
I am using MS SQL server 2008 R2.
Try this.. First u need to unpivot the columns using Cross apply to get the data in single row. Then pivot that row to get the result.
CREATE TABLE #tt
(ID INT,Person VARCHAR(50),BirthDate DATE,
Phone BIGINT,Email VARCHAR(50)
)
INSERT INTO #tt
VALUES (1,'Tom','1985-11-08',1111111111,'tom#somedomain.com' ),
( 2,'Dick','1982-02-24',2222222222,'dick#otherdomain.com'),
( 3,'Harry ','1986-04-17',3333333333,'harry#thatdomain.com' )
SELECT [key],
Max([value1]) [value1],
Max([value2]) [value2],
Max([value3]) [value3]
FROM (SELECT 'value' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES ('ID',
CONVERT(VARCHAR(50), ID)),
('Person',Person),
('BirthDate',CONVERT(VARCHAR(50), BirthDate)),
('Phone',CONVERT(VARCHAR(50), Phone)),
('Email',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN([value1],[value2],[value3])) piv
GROUP BY [key]
DYNAMIC VERSION
Declare #cols varchar(max)='',#aggcols varchar(max)='',#sql nvarchar(max)
SELECT #cols+= ',value' + CONVERT(VARCHAR(30), id)
FROM #tt
SELECT #aggcols+= ',max([value' + CONVERT(VARCHAR(30), id) +']) value' + CONVERT(VARCHAR(30), id)
FROM #tt
select #cols= right(#cols,LEN(#cols)-1)
select #aggcols =right(#aggcols,LEN(#aggcols)-1)
set #sql = 'SELECT [key],
'+#aggcols+'
FROM (SELECT ''value'' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES (''ID'',CONVERT(VARCHAR(50), ID)),
(''Person'',Person),
(''BirthDate'',CONVERT(VARCHAR(50), BirthDate)),
(''Phone'',CONVERT(VARCHAR(50), Phone)),
(''Email'',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN('+#cols+')) piv
GROUP BY [key] '
execute sp_executesql #sql
OUTPUT
+----------+--------------------+---------------------+----------------------+
|key | value1 | value2 | value3 |
+----------+--------------------+---------------------+----------------------+
|BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+----------+--------------------+---------------------+----------------------+
|Email | tom#somedomain.com |dick#otherdomain.com | harry#thatdomain.com |
+----------+--------------------+---------------------+----------------------+
|ID | 1 | 2 | 3 |
+----------+--------------------+---------------------+----------------------+
|Person | Tom | Dick | Harry |
+----------+--------------------+---------------------+----------------------+
|Phone | 1111111111 | 2222222222 | 3333333333 |
+----------+--------------------+---------------------+----------------------+

Resources