I am using SQL Server. I created a query:
SELECT
p.[accountNumber],
pf.fundid
FROM
[dbo].[Property] p
LEFT JOIN
dbo.propertyfundassessment pf ON p.id = pf.propertyid
LEFT JOIN
dbo.fund f ON pf.fundid = f.id
WHERE
p.accountnumber = '238492348' AND p.taxyear = 2018
This shows the data as:
accountNumber fundid
--------------------------
1 238492348 1004
2 238492348 1005
3 238492348 1006
4 238492348 1007
5 238492348 1008
6 238492348 1009
7 238492348 1022
8 238492348 1339
I am trying to some how pivot the table in order to make the table look like this instead:
accountNumber adv1 adv2 adv3 adv4 adv5 adv6 adv7 adv8
-----------------------------------------------------------------
1 238492348 1004 1005 1006 1007 1008 1009 1022 1339
Can someone assist me in how I can do this with SQL Server?
I have found this:
SELECT *
FROM
(SELECT ColNbr = ROW_NUMBER() OVER(PARTITION BY accountNUmber ORDER BY fundid,accountNumber)
,accountNumber
,fundid
FROM
#tmpS a
) src PIVOT(MAX(src.fundid) FOR src.ColNbr IN( [1]
,[2]
,[3]
,[4]
,[5]
,[6]
,[7]
,[8]
,[9]
,[10]
,[11]
,[12]
,[13]
,[14]
,[15]
,[16]
,[17]
,[18]
,[19]
,[20]
,[21]
,[22]
,[23]
,[24]
,[25]
)) pvt
And I am trying to combine the two queries to have it do it on the fly. Instead of trying to create a #tmpS table.
Any help would be greatly appreciated!
You can combine both queries like the following:
;WITH StartingData AS
(
SELECT
[accountNumber] = p.[accountNumber],
fundid = pf.fundid,
FundRanking = ROW_NUMBER() OVER (PARTITION BY p.[accountNumber] ORDER BY pf.fundid ASC) -- The order by is crucial for the pivot ordering later
FROM
[dbo].[Property] p
left join dbo.propertyfundassessment pf on p.id = pf.propertyid
left join dbo.fund f on pf.fundid = f.id
where
p.taxyear = 2018
)
SELECT
P.accountNumber,
P.[1],
P.[2],
P.[3],
P.[4],
P.[5],
P.[6],
P.[7],
P.[8],
P.[9],
P.[10],
P.[11],
P.[12],
P.[13],
P.[14],
P.[15],
P.[16],
P.[17],
P.[18],
P.[19],
P.[20],
P.[21],
P.[22],
P.[23],
P.[24],
P.[25]
FROM
StartingData AS S
PIVOT (
MAX(S.fundid) FOR S.FundRanking IN (
[1], [2], [3], [4], [5], [6], [7], [8], [9], [10],
[11], [12], [13], [14], [15], [16], [17], [18],
[19], [20], [21], [22], [23], [24], [25])
) AS P
Keep in mind what Tim said, either you hard-code the number of fundid you will be pivoting (this example hard-codes from 1 to 25), or you will have to use dynamic SQL to generate a pivot statement that dynamically pivots up to the max amount of fundid you might have to a particular accountNumber. This will generate a column for each inicial record, by accountNumber.
To make it dynamic use the following:
IF OBJECT_ID('tempdb..#AccountFunds') IS NOT NULL
DROP TABLE #AccountFunds
SELECT
[accountNumber] = p.[accountNumber],
fundid = pf.fundid,
FundRanking = ROW_NUMBER() OVER (PARTITION BY p.[accountNumber] ORDER BY pf.fundid ASC) -- The order by is crucial for the pivot ordering later
INTO
#AccountFunds
FROM
[dbo].[Property] p
left join dbo.propertyfundassessment pf on p.id = pf.propertyid
left join dbo.fund f on pf.fundid = f.id
where
p.taxyear = 2018
AND p.[accountNumber] = '238492348'
DECLARE #PivotValues VARCHAR(MAX) = STUFF (
(
SELECT DISTINCT
',' + QUOTENAME(CONVERT(VARCHAR(10), A.FundRanking))
FROM
#AccountFunds AS A
ORDER BY
',' + QUOTENAME(CONVERT(VARCHAR(10), A.FundRanking)) ASC
FOR XML
PATH ('')
),
1, 1, '')
DECLARE #SelectColumnAlias VARCHAR(MAX) = STUFF (
(
SELECT
',P.' + QUOTENAME(CONVERT(VARCHAR(10), A.FundRanking)) + ' AS adv' + CONVERT(VARCHAR(10), A.FundRanking)
FROM
#AccountFunds AS A
GROUP BY
A.FundRanking
ORDER BY
A.FundRanking ASC
FOR XML
PATH ('')
),
1, 1, '')
DECLARE #DynamicSQL VARCHAR(MAX) = '
SELECT
P.AccountNumber,
' + #SelectColumnAlias + '
FROM
#AccountFunds AS A
PIVOT (
MAX(A.fundid) FOR A.FundRanking IN (
' + #PivotValues + ')
) AS P '
--PRINT (#DynamicSQL) -- Use Print to check the query
EXEC (#DynamicSQL)
If you check the value of #PivotValues it's something like the following:
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25]
The value of #SelectColumnAlias is:
P.[1] AS adv1,P.[2] AS adv2,P.[3] AS adv3,P.[4] AS adv4,P.[5] AS adv5,P.[6] AS adv6,P.[7] AS adv7,P.[8] AS adv8,P.[9] AS adv9,P.[10] AS adv10,P.[11] AS adv11,P.[12] AS adv12,P.[13] AS adv13,P.[14] AS adv14,P.[15] AS adv15,P.[16] AS adv16,P.[17] AS adv17,P.[18] AS adv18,P.[19] AS adv19,P.[20] AS adv20,P.[21] AS adv21,P.[22] AS adv22,P.[23] AS adv23,P.[24] AS adv24,P.[25] AS adv25
And finally the full expression:
SELECT
P.AccountNumber,
P.[1] AS adv1,P.[2] AS adv2,P.[3] AS adv3,P.[4] AS adv4,P.[5] AS adv5,P.[6] AS adv6,P.[7] AS adv7,P.[8] AS adv8,P.[9] AS adv9,P.[10] AS adv10,P.[11] AS adv11,P.[12] AS adv12,P.[13] AS adv13,P.[14] AS adv14,P.[15] AS adv15,P.[16] AS adv16,P.[17] AS adv17,P.[18] AS adv18,P.[19] AS adv19,P.[20] AS adv20,P.[21] AS adv21,P.[22] AS adv22,P.[23] AS adv23,P.[24] AS adv24,P.[25] AS adv25
FROM
#AccountFunds AS A
PIVOT (
MAX(A.fundid) FOR A.FundRanking IN (
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25])
) AS P
Try this dynamic sql
IF OBJECT_ID('Tempdb..#temp')IS NOT NULL
DROP TABLE #temp
;WITH CTE(Id,accountNumber,fundid)
AS
(
SELECT 1,238492348,1004 UNION ALL
SELECT 2,238492348,1005 UNION ALL
SELECT 3,238492348,1006 UNION ALL
SELECT 4,238492348,1007 UNION ALL
SELECT 5,238492348,1008 UNION ALL
SELECT 6,238492348,1009 UNION ALL
SELECT 7,238492348,1022 UNION ALL
SELECT 8,238492348,1339
)
SELECT * ,'adv'+CAST(ROW_NUMBER()OVER(ORDER BY (SELECT 1)) AS VARCHAR(10)) AS tcol INTO #temp FROM CTE
DECLARE #Columns nvarchar(max),#IsnullColumns nvarchar(max)
,#Sql nvarchar(max)
SELECT #Columns= STUFF((SELECT ', '+tcol FROM #temp FOR XML PATH ('')),1,1,'')
SELECT #IsnullColumns=STUFF((SELECT ', '+'MAX('+QUOTENAME(tcol)+') AS ' +QUOTENAME(tcol) FROM #temp FOR XML PATH ('')),1,1,'')
SET #Sql='
SELECT accountNumber ,'+#IsnullColumns+'
FROM
(SELECT * FROM #temp
) AS SRC
PIVOT
(MAX(fundid) FOR tcol IN ('+#Columns+')
) AS PVT
GROUP BY accountNumber'
PRINT #Sql
EXEC (#Sql)
Result
accountNumber adv1 adv2 adv3 adv4 adv5 adv6 adv7 adv8
-----------------------------------------------------------------
238492348 1004 1005 1006 1007 1008 1009 1022 1339
Related
I have a record set as under
AGREEMENTID FeedbackDate DispositionCode
0003SBML00151 2017-03-08 00:00:00.000 PTP
0004SBHL00705 2017-03-17 00:00:00.000 BPTP
0007SBML01987 NULL NULL
0026MSS00108 2017-05-20 00:00:00.000 PTP
0026MSS00108 2017-03-22 00:00:00.000 PTP
0026MSS00108 2016-12-30 00:00:00.000 BPTP
0026MSS00108 2016-12-29 00:00:00.000 BPTP
0026MSS00108 2016-12-28 00:00:00.000 BPTP
0037SBHL02361 NULL NULL
0038SBML00291 2017-05-04 00:00:00.000 PTP
0038SBML00291 2017-04-24 00:00:00.000 BPTP
0038SBML00291 2017-04-11 00:00:00.000 NC
0038SBML00291 2016-12-22 00:00:00.000 PTP
0038SBML00291 2016-12-09 00:00:00.000 DC
The desired output will be
AGREEMENTID L1 L2 L3 L4 L5
0003SBML00151 PTP NULL NULL NULL NULL
0004SBHL00705 BPTP NULL NULL NULL NULL
0007SBML01987 NULL NULL NULL NULL NULL
0026MSS00108 PTP PTP BPTP BPTP BPTP
0037SBHL02361 NULL NULL NULL NULL NULL
0038SBML00291 PTP BPTP NC PTP DC
SQL Schema
Declare #T table(AGREEMENTID varchar(50),FeedbackDate varchar(50),DispositionCode varchar(10))
Insert into #T
Select '0003SBML00151','2017-03-08 00:00:00.000','PTP' union all
Select '0004SBHL00705','2017-03-17 00:00:00.000','BPTP' union all
Select '0007SBML01987',NULL,NULL union all
Select '0026MSS00108','2017-05-20 00:00:00.000','PTP' union all
Select '0026MSS00108','2017-03-22 00:00:00.000','PTP' union all
Select '0026MSS00108','2016-12-30 00:00:00.000','BPTP' union all
Select '0026MSS00108','2016-12-29 00:00:00.000','BPTP' union all
Select '0026MSS00108','2016-12-28 00:00:00.000','BPTP' union all
Select '0037SBHL02361',NULL,NULL union all
Select '0038SBML00291','2017-05-04 00:00:00.000','PTP' union all
Select '0038SBML00291','2017-04-24 00:00:00.000','BPTP' union all
Select '0038SBML00291','2017-04-11 00:00:00.000','NC' union all
Select '0038SBML00291','2016-12-22 00:00:00.000','PTP' union all
Select '0038SBML00291','2016-12-09 00:00:00.000','DC'
Select *
From #T
Here is my attempt
;with cte1 as(
SELECT AGREEMENTID, abc = STUFF(
(SELECT '.' + DispositionCode
FROM #T t1
WHERE t1.AGREEMENTID = t2.AGREEMENTID --and t1.Rn = t2.Rn
FOR XML PATH (''))
, 1, 1, '') from #T t2
group by AGREEMENTID)
--select *
--from cte1
,cte2 as(
select AGREEMENTID,
X= IIF(charindex('.',abc,1) is null,'NULL VALUE',IIF(charindex('.',abc,1) = 0,'SINGLE VALUE','MULTIPLE VALUE'))
--,COL1 = SUBSTRING(abc,1,IIF(charindex('.',abc,1) = NULL,0,IIF(charindex('.',abc,1) = 0,len(abc),(charindex('.',abc,1)-1))))
--,charindex('.',abc,1)
,abc
from cte1)
,cte3 as(
select
AGREEMENTID
,COL1 =IIF(X = 'NULL VALUE', NULL,IIF(X='SINGLE VALUE',SUBSTRING(abc,1,len(abc)),SUBSTRING(abc,1,(charindex('.',abc,1)-1))))
,abc
,OtherCols = IIF(X = 'MULTIPLE VALUE',SUBSTRING(abc,charindex('.',abc,1)+1,len(abc)),'')
from cte2)
select
AGREEMENTID
,L1 = IIF(COL1 is null, '--',COL1)
,l2 = IIF(PARSENAME(OtherCols,4)is null, '--',PARSENAME(OtherCols,4))
,l3 = IIF(PARSENAME(OtherCols,3)is null, '--',PARSENAME(OtherCols,3))
,l4 = IIF(PARSENAME(OtherCols,2)is null, '--',PARSENAME(OtherCols,2))
,l5 = IIF(PARSENAME(OtherCols,1)is null, '--',PARSENAME(OtherCols,1))
From cte3
Disadvantages
a) Slow query
b) Failed for the below case
AGREEMENTID FeedbackDate DispositionCode
0002SBML01241 2017-05-04 00:00:00.000 Today
0002SBML01241 2017-04-24 00:00:00.000 PTP
0002SBML01241 2017-04-11 00:00:00.000 PTP
0002SBML01241 2016-12-22 00:00:00.000 PTP
Actual
AGREEMENTID L1 l2 l3 l4 l5
0002SBML01241 Today -- PTP PTP PTP
Expected
AGREEMENTID L1 l2 l3 l4 l5
0002SBML01241 Today PTP PTP PTP --
This will help in case for dynamic levels:
Declare #T table(AGREEMENTID varchar(50),FeedbackDate varchar(50),DispositionCode varchar(10))
Insert into #T
Select '0003SBML00151','2017-03-08 00:00:00.000','PTP' union all
Select '0004SBHL00705','2017-03-17 00:00:00.000','BPTP' union all
Select '0007SBML01987',NULL,NULL union all
Select '0026MSS00108','2017-05-20 00:00:00.000','PTP' union all
Select '0026MSS00108','2017-03-22 00:00:00.000','PTP' union all
Select '0026MSS00108','2016-12-30 00:00:00.000','BPTP' union all
Select '0026MSS00108','2016-12-29 00:00:00.000','BPTP' union all
Select '0026MSS00108','2016-12-28 00:00:00.000','BPTP' union all
Select '0037SBHL02361',NULL,NULL union all
Select '0038SBML00291','2017-05-04 00:00:00.000','PTP' union all
Select '0038SBML00291','2017-04-24 00:00:00.000','BPTP' union all
Select '0038SBML00291','2017-04-11 00:00:00.000','NC' union all
Select '0038SBML00291','2016-12-22 00:00:00.000','PTP' union all
Select '0038SBML00291','2016-12-09 00:00:00.000','DC'
Select 'L'+convert(varchar(255),ROW_NUMBER()over(partition by AGREEMENTID order by AGREEMENTID))'rno',*
into test
From #T order by AGREEMENTID
declare #Levels nvarchar(max),#SQL NVARCHAR(MAX)
select #Levels= STUFF((select DISTINCT ','+rno from test
for xml path('')),1,1,'')
SET #SQL=
'SELECT AGREEMENTID,'+#Levels+'
FROM (select AGREEMENTID,DispositionCode,rno from test
)as TEMP'+'
PIVOT'+'
(MAX(DispositionCode)
FOR rno IN ('+#Levels+')
)AS pvt'
exec sp_executesql #SQL
Sample data to get the result
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
Declare #T table
(AGREEMENTID varchar(50),
FeedbackDate varchar(50),
DispositionCode varchar(10)
)
Insert into #T
SELECT '0003SBML00151','2017-03-08 00:00:00.000','PTP' union all
SELECT '0004SBHL00705','2017-03-17 00:00:00.000','BPTP' union all
SELECT '0007SBML01987',NULL,NULL union all
SELECT '0026MSS00108','2017-05-20 00:00:00.000','PTP' union all
SELECT '0026MSS00108','2017-03-22 00:00:00.000','PTP' union all
SELECT '0026MSS00108','2016-12-30 00:00:00.000','BPTP' union all
SELECT '0026MSS00108','2016-12-29 00:00:00.000','BPTP' union all
SELECT '0026MSS00108','2016-12-28 00:00:00.000','BPTP' union all
SELECT '0037SBHL02361',NULL,NULL union all
SELECT '0038SBML00291','2017-05-04 00:00:00.000','PTP' union all
SELECT '0038SBML00291','2017-04-24 00:00:00.000','BPTP' union all
SELECT '0038SBML00291','2017-04-11 00:00:00.000','NC' union all
SELECT '0038SBML00291','2016-12-22 00:00:00.000','PTP' union all
SELECT '0038SBML00291','2016-12-09 00:00:00.000','DC'
SELECT *,
'L'
+ Cast(Row_number()OVER(partition BY dispositioncode ORDER BY agreementid
)AS
VARCHAR(10)) AS Lcolumn
INTO #temp
FROM #T
Dynamic sql process to get the result
DECLARE #DyColumn NVARCHAR(max), #DyColumn2 NVARCHAR(max),
#Sql NVARCHAR(max)
SELECT #DyColumn = STUFF((SELECT DISTINCT ', ' + QUOTENAME(lcolumn)
FROM #temp
FOR xml path ('')), 1, 2, '')
SELECT #DyColumn2 = STUFF((SELECT DISTINCT ', ' + 'ISNULL('+QUOTENAME(lcolumn) +','+'''0'''+') AS '++QUOTENAME(lcolumn)
FROM #temp
FOR xml path ('')), 1, 2, '')
SET #Sql=N' SELECT AGREEMENTID,' + #DyColumn2
+ ' FROM ( SELECT * FROM #Temp ) AS Src PIVOT ( MIN(DispositionCode) FOR Lcolumn IN ('
+ #DyColumn + ') )AS Pvt '
PRINT #Sql
EXEC (#Sql)
I'm able to achieve the desired output, considering you're not going to have more Levels than [L5] in that case you need to have dynamic PIVOT solution.
For Dynamic Pivot solution you can check Krishnaraj's answer.
[Live Demo]
SELECT *
FROM
(
Select AGREEMENTID,
DispositionCode,
'L' + CAST(ROW_NUMBER() OVER(Partition By AGREEMENTID ORDER BY FeedbackDate DESC) AS VARCHAR(255)) as RowNum
From #T
) as PivotSource
PIVOT
(
MAX(DispositionCode) FOR RowNum IN ([L1], [L2], [L3], [L4], [L5])
) as Pvt;
Example table:
CREATE TABLE Fruit (
ID int identity(1,1) NOT NULL,
ParentID int NULL,
Name varchar(255)
);
I want to sort parent and child records from the same table in alphabetical order (more than one level deep):
Apples
--Green
----Just Sour
----Really Sour
--Red
----Big
----Small
Bananas
--etc.
I attempted this:
;WITH CTE(ID, ParentID, Name, Sort) AS
(
SELECT
ID
,ParentID
,Name
,cast('\' + Name as nvarchar(255)) AS Sort
FROM Fruit
WHERE ParentID IS NULL
UNION ALL
SELECT
a.ID
,a.ParentID
,a.Name
,cast(b.Sort + '\' + a.Name as nvarchar(255)) AS Sort
FROM Fruit a
INNER JOIN CTE b ON a.ParentID = b.ID
)
SELECT * FROM CTE Order by Sort
This produces results for the sort like:
\Apples
\Apples\Green
\Apples\Green\Just Sour
\etc.
Just when I thought things were good, it isn't reliable. For example, if an item has more than one word. Like:
\Apples
\Apples A <-- culprit
\Apples\Green
If I can expand my question while I'm at it, I'd like to show actual hyphens or something in the results:
Parent
- Child
--Grandchild
The cruddy way I quickly did this was by adding a prefix column in the table with the value of - for all records. Then I could do this:
;WITH CTE(ID, ParentID, Name, Sort, Prefix) AS
(
SELECT
ID
,ParentID
,Name
,cast('\' + Name as nvarchar(255)) AS Sort
,Prefix
FROM Fruit
WHERE ParentID IS NULL
UNION ALL
SELECT
a.ID
,a.ParentID
,a.Name
,cast(b.Sort + '\' + a.Name as nvarchar(255)) AS Sort
,cast(b.Prefix + a.Prefix as nvarchar(10)) AS Prefix
FROM Fruit a
INNER JOIN CTE b ON a.ParentID = b.ID
)
SELECT * FROM CTE Order by Sort
But that seems incorrect or not optimal.
These hierarchical queries still give me a headache, so perhaps I'm just not seeing the obvious.
I tend to use row_number() ordered by Name in this case
Example
Declare #YourTable table (id int,ParentId int,Name varchar(50))
Insert into #YourTable values
( 1, NULL,'Apples')
,( 2, 1 ,'Green')
,( 3, 2 ,'Just Sour')
,( 4, 2 ,'Really Sour')
,( 5, 1 ,'Red')
,( 6, 5 ,'Big')
,( 7, 5 ,'Small')
,( 8, NULL,'Bananas')
Declare #Top int = null --<< Sets top of Hier Try 5
Declare #Nest varchar(25) = '|-----' --<< Optional: Added for readability
;with cteP as (
Select Seq = cast(1000+Row_Number() over (Order by Name) as varchar(500))
,ID
,ParentId
,Lvl=1
,Name
From #YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(ParentId ,-1) else ID end
Union All
Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.Name)) as varchar(500))
,r.ID
,r.ParentId
,p.Lvl+1
,r.Name
From #YourTable r
Join cteP p on r.ParentId = p.ID)
Select A.ID
,A.ParentId
,A.Lvl
,Name = Replicate(#Nest,A.Lvl-1) + A.Name
,Seq --<< Can be removed
From cteP A
Order By Seq
Returns
ID ParentId Lvl Name Seq
1 NULL 1 Apples 1001
2 1 2 |-----Green 1001.1001
3 2 3 |-----|-----Just Sour 1001.1001.1001
4 2 3 |-----|-----Really Sour 1001.1001.1002
5 1 2 |-----Red 1001.1002
6 5 3 |-----|-----Big 1001.1002.1001
7 5 3 |-----|-----Small 1001.1002.1002
8 NULL 1 Bananas 1002
I'm going to guess you want this result
\Apples
\Apples\Green
\Apples A
Maybe try something like this:
SELECT *
FROM CTE
ORDER BY replace(Sort, ' ', '~')
'~' is ascii 126, You can also check using excel sorting.
I have a table ProductTransDetailPassenger the structure is
PID FirstName Email Phone NoOfAdult NoOfChild
1 ABC x#.com 111 2 0
1 XYZ y#.com 222 2 0
2 QWE z#.com 333 2 1
2 RTY c#.com 444 2 1
2 YUI v#.com 555 2 1
Based on the traveler Count(i.e No.OfAdult + No.OfChild) I need to display the result as
PID FirstName-1 Email-1 Phone-1 FirstName-2 Email-2 Phone-2 FirstName-3 Email-3 Phone-3
1 ABC x#.com 111 XYZ y#.com 222 N/A N/A N/A
2 QWE z#.com 333 RTY c#.com 444 YUI v#.com 555
The table may change dynamically according to the max number of travelers. For an PID with less number of travelers than the Max, the remaining columns need to be displayed as N/A
Can u please help me out with this!!
I have tried this implementation using the PIVOT, but the result is not as expected
Thanks in Advance,
The SQL Snippet
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct + char(10) + QUOTENAME(PTDP.FirstName) --+ QUOTENAME(AP.Type) + QUOTENAME(PTDP.EmailAddress) + QUOTENAME(PTDP.PhoneNumber) + QUOTENAME(DAY(GETDATE() - PTDP.Birthdate))
FROM ProductTransactionDetailPassenger PTDP
INNER JOIN AppParameters AP ON AP.EnumValue = PTDP.GenderTypeValue AND Ap.Context = 'Gender'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = N'SELECT ' + #cols + ' from
(
select
PTDP.ProductTransactionDetailID PID,
PTDE.ProductTransactionDetailID ID
from ProductTransactionDetailPassenger PTDP
inner join ProductTransactionDetail PTD
on PTD.ProductTransactionDetailID=PTDP.ProductTransactionDetailID
and PTD.ParentProductTransactionDetailID=00000000-0000-0000-0000-000000000000
INNER JOIN dbo.ProductTransactionDetailExtended PTDE ON PTDE.ProductTransactionDetailID = PTD.ProductTransactionDetailID
) x
pivot
(
max(x.PID) //this must be traveler count
for ' + #cols + ' in (' + #cols + ')
) p '
print #query
execute(#query)
Ref:
Pivot Dynamic Columns, no Aggregation
The query below should get you the desired results:
DECLARE #Temp TABLE
(
PID int,
PIDROW int,
FirstName varchar(25),
Email varchar(25),
Phone varchar(25)
)
INSERT INTO #Temp
SELECT
PID,
DENSE_RANK() OVER (PARTITION BY PID ORDER BY PID,FirstName) PIDROW,
FirstName,
Email,
Phone
from [dbo].[ProductTransDetailPassenger];
WITH FirstName AS
(
SELECT PID,
[1], [2], [3], [4]
FROM
(SELECT PIDROW, FirstName, PID
FROM #Temp) AS SourceTable
PIVOT
(
MAX(FirstName)
FOR PIDROW IN ([1], [2], [3], [4])
) AS PivotTable
),
Email As
(
SELECT PID,
[1], [2], [3] , [4]
FROM
(SELECT PIDROW, Email, PID
FROM #Temp) AS SourceTable
PIVOT
(
MAX(Email)
FOR PIDROW IN ([1], [2], [3], [4])
) AS PivotTable
),
Phone As
(
SELECT PID,
[1], [2], [3] , [4]
FROM
(SELECT PIDROW, Phone, PID
FROM #Temp) AS SourceTable
PIVOT
(
MAX(Phone)
FOR PIDROW IN ([1], [2], [3], [4])
) AS PivotTable
)
SELECT
f.[1] AS 'FirstName-1',
e.[1] AS 'Email-1',
p.[1] AS 'Phone-1',
f.[2] AS 'FirstName-2',
e.[2] AS 'Email-2',
p.[2] AS 'Phone-2',
f.[3] AS 'FirstName-3',
e.[3] AS 'Email-3',
p.[3] AS 'Phone-3'
FROM FirstName f
JOIN Email e on f.PID = e.PID
JOIN Phone p on f.PID = p.PID
Please don't do this. You are destroying the table as a relational table, it will be very hard to do SQL queries against the table and you will have many, many null values. You have to create another table with the id, email and phone.
How could I put those multiple rows into one line, and the contents are in different columns:
From:
ID | Subject1/Catalog/Session
10868952 | NUR/3110/D507
10868952 | NUR/3110/D512
10868952 | NUR/4010/D523
10868952 | NUR/4010/HD20
To
ID |Subject1/Catalog/Session |Subject2/Catalog/Session | Subject3/Catalog/Session |Subject4/Catalog/Session | Subject5/Catalog/Session
10868952 |NUR/3110/D507 | NUR/3110/D512 | NUR/4010/D523 | NUR/4010/HD20 |
Would be best if in the future you can provide ddl and sample data. I did this for you this time.
Here is how you could do this if you know the number of elements per row. I put links in the comments of the original post to both the static and dynamic versions of this type of approach.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
ID int,
Subject1 varchar(50)
)
insert #Something
select 10868952, 'NUR/3110/D507' union all
select 10868952, 'NUR/3110/D512' union all
select 10868952, 'NUR/4010/D523' union all
select 10868952, 'NUR/4010/HD20';
with OrderedResults as
(
select *, ROW_NUMBER() over(partition by ID order by Subject1) as RowNum
from #Something
)
select ID
, MAX(Case when RowNum = 1 then Subject1 end) as Subject1
, MAX(Case when RowNum = 2 then Subject1 end) as Subject2
, MAX(Case when RowNum = 3 then Subject1 end) as Subject3
, MAX(Case when RowNum = 4 then Subject1 end) as Subject4
from OrderedResults
group by ID
Here is how you can do this as a dynamic pivot. There are a number of concepts going on here. One is a tally table. In this code it is implemented as a cte. In my actual system I have this as a view. It generates 10,000 rows with zero reads. The tally table and most of the other concepts here were learned by the immortal Jeff Moden. If you do not know what a tally table is or how they work, check out Jeff's article here. http://www.sqlservercentral.com/articles/T-SQL/62867/
I will post some code for how to do this for this example but anybody who is unfamiliar with this technique should read his article. http://www.sqlservercentral.com/articles/Crosstab/65048/
Here is a full working example of doing this as a dynamic cross tab. When you are satisfied that the sql this generates is safe feel free to uncomment the last two lines.
LAST but certainly not least. Make sure that you fully understand what this code and how it works. It is not going to be my phone that rings at 3am when something goes wrong. You are the one who will have to be there to support this code.
if OBJECT_ID('Something') is not null
drop table Something
create table Something
(
ID int,
Subject1 varchar(50)
)
insert Something
select 10868952, 'NUR/3110/D507' union all
select 10868952, 'NUR/3110/D512' union all
select 10868952, 'NUR/4010/D523' union all
select 10868952, 'NUR/4010/HD20' union all
select 12345, 'asdfasdf'
declare #MaxCols int
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *, ROW_NUMBER() over(partition by ID order by Subject1) as RowNum
from Something
)
select ID';
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by ID order by ID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then Subject1 end) as Subject' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from Something
group by ID
order by COUNT(*) desc
)
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
--declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
--exec sp_executesql #SqlToExecute
I have a following table:
State LAB GROUP DATE CODE ID
UP A I 1-Jan 1 345
UP R S 1-Feb 1 456
UP A S 1-Jan 2 567
DL R S 1-Feb 3 678
DL T S 1-Jan 1 789
DL A S 1-Jan 2 900
MN T S 1-Jan 3 1011
MN R I 1-Feb 1 1122
MN S I 1-Feb 2 1233
I need a pivot table of following type:
STATE A R T TOTAL
UP 2 1 0 3
DL 1 1 1 3
MN 0 1 1 2
DISTINCT COUNT OF ID FOR EACH LAB FOR EACH STATE.
I then need the pivot tables filtered for following columns:
GROUP
DATE
CODE
So 1st table will have the pivot table above counting only those records which have GROUP=S
2nd table will have the pivot table above counting only those records which have CODE=1
and so on, I wish to put multiple conditions. and generate several tables one by one and export them.
If this is possible in SQL please let me know! I ruled out excel vba due to the size of table (source table will have 800,000 records approx).
Try this :-
Select [State],[A],[R],[T],Total = [A] + [R]+ [T]
from
(
Select [State],
[A] = Sum(Case when LAB='A' then 1 else 0 END) ,
[R] = Sum(Case when LAB='R' then 1 else 0 END) ,
[T] = Sum(Case when LAB='T' then 1 else 0 END)
from YourTable
group by [State]
)a
SQL FIDDLE
CREATE TABLE #t(States VARCHAR(10),LAB VARCHAR(5),GROUPs VARCHAR(5),DATEs VARCHAR(10),CODE INT,ID INT)
INSERT INTO #t values('UP','A','I','1-Jan',1,345)
INSERT INTO #t values('UP','R','S','1-Feb',1,456)
INSERT INTO #t values('UP','A','S','1-Jan',2,567)
INSERT INTO #t values('DL','R','S','1-Feb',3,678)
INSERT INTO #t values('DL','T','S','1-Jan',1,789)
INSERT INTO #t values('DL','A','S','1-Jan',2,900)
INSERT INTO #t values('MN','T','S','1-Jan',3,1011)
INSERT INTO #t values('MN','R','I','1-Feb',1,1122)
INSERT INTO #t values('MN','S','I','1-Feb',2,1233)
SELECT States,ISNULL(A,0) A,ISNULL(R,0) R,ISNULL(T,0) T,ISNULL(A,0)+ISNULL(R,0)+ISNULL(T,0) total
FROM
(
SELECT States,LAB,Count(ID) AS cnt FROM #t GROUP BY States,LAB /*apply GROUP DATE CODE condition here*/
) AS PVT
PIVOT(MAX(cnt) FOR LAB IN (A,R,T)) pvt
Another solution using PIVOT :
WITH PivotInUse AS (
SELECT state,lab,COUNT(*) AS cnt
FROM YourTable
GROUP BY state,lab
)
SELECT STATE
,COALESCE([A], 0) AS A
,COALESCE([R], 0) AS R
,COALESCE([T], 0) AS T
,COALESCE([A], 0) + COALESCE([R], 0) + COALESCE([T], 0) AS TOTAL
FROM PivotInUse
PIVOT(SUM(cnt) FOR lab IN ([A],[R],[T])) AS p;
Your sample table
SELECT * INTO #TEMP FROM
(
SELECT 'UP' [State],'A' LAB,'I' [GROUP],'1-Jan' [DATE],1 CODE,345 ID
UNION ALL
SELECT 'UP','R','S','1-Feb',1,456
UNION ALL
SELECT 'UP','A','S','1-Jan',2,567
UNION ALL
SELECT 'DL','R','S','1-Feb',3,678
UNION ALL
SELECT 'DL','T','S','1-Jan',1,789
UNION ALL
SELECT 'DL','A','S','1-Jan',2,900
UNION ALL
SELECT 'MN','T','S','1-Jan',3,1011
UNION ALL
SELECT 'MN','R','I','1-Feb',1,1122
UNION ALL
SELECT 'MN','S','I','1-Feb',2,1233
)TAB
Now you need to get the distinct count of each state and get the sum as the result to show Total
in pivoted result.
SELECT DISTINCT [State],LAB,SUM(CNT) CNT
INTO #NEWTABLE
FROM
(
SELECT DISTINCT
[State],LAB,
CASE WHEN [State] IS NULL THEN NULL ELSE COUNT([State]) OVER(PARTITION BY [State],LAB) END CNT
FROM #TEMP
)TAB
GROUP BY [State],LAB
WITH ROLLUP
Now we need to get the distinct columns for pivot(#cols) and columns to identify and replace null with zero in pivot(#NullToZeroCols)
DECLARE #cols NVARCHAR (MAX)
DECLARE #NullToZeroCols NVARCHAR (MAX)
SET #cols = SUBSTRING((SELECT DISTINCT ',['+LAB+']' FROM #NEWTABLE GROUP BY LAB FOR XML PATH('')),2,8000)
SET #NullToZeroCols = SUBSTRING((SELECT DISTINCT ',ISNULL(['+LAB+'],0) AS ['+LAB+']'
FROM #NEWTABLE GROUP BY LAB FOR XML PATH('')),2,8000)
Join the pivotted query with the #NEWTABLE to get the Total for each State
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT P.State,' + #NullToZeroCols + ',T2.CNT TOTAL FROM
(
SELECT DISTINCT [State],LAB,CNT FROM #NEWTABLE
) x
PIVOT
(
SUM(CNT)
FOR [LAB] IN (' + #cols + ')
) p
JOIN #NEWTABLE T2 ON P.[STATE]=T2.[STATE]
WHERE P.State IS NOT NULL AND T2.LAB IS NULL AND T2.[STATE] IS NOT NULL;'
EXEC SP_EXECUTESQL #query
Here is your result
Here is the SQLFiddle http://sqlfiddle.com/#!3/c2588/1 (If it shows any error while loading the page, just click RUNSQL, it will work)
Now if you want to get the result as you said DISTINCT COUNT OF ID FOR EACH LAB FOR EACH STATE, just change
OVER(PARTITION BY [State],LAB)
to
OVER(PARTITION BY [State],LAB,Id)
which will show the following result after executing the pivot query