Group By with row values as column name and group concat - sql-server

I am searching for a solution for the SQL problem:
I have input table like this:
RId
AId
Type
76
734
TKI
76
528
NPlat
76
735
TKI
77
713
Plat
77
749
IO
77
739
TKI
77
714
NPlat
78
518
Plat
73
519
Plat
73
518
Plat
And I want this kind of output:
RId
TKI
IO
NPlat
Plat
73
518, 519
76
734, 735
528
77
739
749
714
713
78
518
I tried with PIVOT, but it's not working. Also tried with the GROUP BY and PARTITION BY together, but no success.
Can anybody have any idea to solve this?
Note: I am using Microsoft SQL Server 2016 (SP3).
Edited:
My attempts:
select
RId,
case when Type = 'TKI' then STRING_AGG(AId, ' ') END AS TKI,
case when Type = 'IO' then STRING_AGG(AId, ' ') END AS IO,
case when Type = 'NPlat' then STRING_AGG(AId, ' ') END AS NPlat,
case when Type = 'Plat' then STRING_AGG(AId, ' ') END AS Plat
from tbl_A
group by RId, Type;
STRING_AGG is not a generic function in SQL server 2016.
select
RId,
[TKI] = COUNT(*) over(partition by Type),
[IO] = COUNT(*) over(partition by Type),
[NPlat] = COUNT(*) over(partition by Type),
[Plat] = COUNT(*) over(partition by Type)
from tbl_A
group by RId, Type
order by RId;

You can concatenate the Aid into csv format first and then perform the pivot
select *
from (
select Rid, [Type],
Aid = stuff(
(select ',' + convert(varchar(10), x.Aid)
from tbl x
where x.Rid = t.Rid
and x.[Type] = t.[Type]
order by x.Aid
for xml path('')), 1, 1, '')
from tbl t
group by Rid, [Type]
) d
pivot
(
max(Aid)
for [Type] in ([TKI], [IO], [NPlat], [Plat])
) p
dbfiddle demo

Simple Use Stuff with Pivot :
SELECT RID,[TKI],[IO],[NPlat],[Plat]
FROM
(SELECT RID,TYPE,AID = STUFF((
SELECT ',' + CONVERT(VARCHAR,AID)
FROM test t
WHERE t.RID = test.RID AND t.TYPE = test.type
FOR XML PATH('')
), 1, 1, '')
FROM test
)sorce
PIVOT
(
max(AID) FOR type IN ([TKI],[IO],[NPlat],[Plat])
) AS PivotTable

Related

Automatically generate columns name in CTE using SQL Server

I am building a pivot query inside a CTE. I have a table Table_1:
Store Week xCount
------- ---- ------
101 1 138
105 1 37
109 1 59
101 2 282
109 2 97
105 3 60
109 3 87
This is the query I used to pivot Table_1:
with CTE as
(
select
*
from
(select
store, week, xCount
from
table_1) src
pivot
(sum(xcount)
for week in ([1], [2], [3])
) piv;
)
Select *
From CTE
And this is the result I got:
| STORE | 1 | 2 | 3 |
+-------+-----+-----+-----+
| 101 | 138 | 282 | null|
| 105 | 37 | null| 60 |
| 109 | 59 | 97 | 87 |
The result is fine, but now there is one more WEEK added.
I want to develop a CTE with pivot query that will automatically generate distinct weeks and create a column on that basis.
I did some research and found a recursive CTE can be used to do this. I am new to recursive CTE, so please anyone can help me to solve this issue.
I also tried dynamic pivot query but CTE does not allow dynamic query.
Please help.
dynamic pivot doesn't work inside CTE
No, but a CTE works inside a dynamic query:
{assuming you have declared the variables used below}
SELECT #Cols = {query to get the column names in a comma-separated string}
SET #sql='
with CTE as
(
select
*
from
(select
store, week, xCount
from
table_1) src
pivot
(sum(xcount)
for week in ('+#Cols+')
) piv;
)
Select *
From CTE
'
EXEC (#sql)
Can i use recursive CTE?
No this isn't an appropriate use-case for a recursive CTE.
/* Variable to hold unique Week to be used in PIVOT clause */
DECLARE #Weeks NVARCHAR(MAX) = N''
/* Extract unique Week names with pivot formattings */
SELECT #Weeks = #Weeks + ', [' + COALESCE(week, '') + ']'
FROM (SELECT DISTINCT week FROM table_1) DT
/* Remove first comma and space */
SELECT #Weeks = LTRIM(STUFF(#Weeks , 1, 1, ''))
/* Variable to hold t-sql query */
DECLARE #CTEStatement NVARCHAR(MAX) = N''
/* Generate dynamic PIVOT query here */
SET #CTEStatement=N'
;WITH CTE as
( SELECT *
FROM
(SELECT
store
,week
,xCount
FROM
table_1) SRC
PIVOT
(SUM(xcount)
FOR week in ('+ #Weeks +')
) PIV;
)
SELECT *
FROM CTE
'
EXEC (#CTEStatement)

How to write an SQL Pivot Query for this scenario?

I am using SQL Server 2014 and I have the following query that runs fine:
SELECT b.FOH_PMSCONFNUM,
a.FOC_ACCOUNT,
a.FOC_PROPERTY,
a.FOC_TCODE,
a.FOC_NETAMOUNT
FROM P5FOLIOCHARGE a
LEFT JOIN P5FOLIOHEADER b ON a.FOC_ACCOUNT = b.FOH_ACCOUNT
where b.FOH_PMSCONFNUM = '1458' AND FOC_NETAMOUNT NOT LIKE '-%'
It gives me the following output:
FOH_PMSCONFNUM FOC_ACCOUNT FOC_PROPERTY FOC_TCODE FOC_NETAMOUNT
125 52 BMA ROOMS 1,200
125 52 BMA zBev 900
125 52 BMA zTel 200
125 52 BMA ROOMS 1,200
125 52 BMA zSpa 500
125 52 BMA zTel 100
I am having a tough time writing the pivot query so that my output turns out as follows:
FOH_PMSCONFNUM FOC_ACCOUNT FOC_ PROPERTY ROOMS zBev zTel zSpa
125 52 BMA 2,400 900 300 500
Also, while running this pivot query, there are 2 things I need to consider:
(1) I must keep this statement "AND FOC_NETAMOUNT NOT LIKE '-%'", so that the pivot does not sum negative figures that are present in the FOC_NETAMOUNT column.
(2) For illustration purposes here, I have shown only a few items that exist in the FOC_TCODE column. In reality, I don't know how many items exists (may be around 30) and my aim is to output those items as column headers.
It is Note (2) above that is a hard nut to crack (for me at least!).
You need to use Dynamic pivot
In Pivot source query make the negative values as zero so that it won't be used in SUM aggregate of pivot
DECLARE #sql NVARCHAR(max),
#cols VARCHAR(max)
SET #cols = (SELECT DISTINCT a.FOC_TCODE + ','
FROM P5FOLIOCHARGE a
LEFT JOIN P5FOLIOHEADER b
ON a.FOC_ACCOUNT = b.FOH_ACCOUNT
WHERE b.FOH_PMSCONFNUM = '1458'
AND FOC_NETAMOUNT NOT LIKE '-%'
FOR xml path(''))
SELECT #cols = LEFT(#cols, Len(#cols) - 1)
SET #sql = 'SELECT *
FROM (SELECT b.FOH_PMSCONFNUM,
a.FOC_ACCOUNT,
a.FOC_PROPERTY,
a.FOC_TCODE,
CASE WHEN a.FOC_NETAMOUNT > 0 THEN a.FOC_NETAMOUNT ELSE 0 END AS FOC_NETAMOUNT
FROM P5FOLIOCHARGE a
LEFT JOIN P5FOLIOHEADER b ON a.FOC_ACCOUNT = b.FOH_ACCOUNT
where b.FOH_PMSCONFNUM = ''1458''
AND FOC_NETAMOUNT NOT LIKE ''-%'')a
PIVOT (Sum(FOC_NETAMOUNT)
FOR FOC_TCODE IN (' + #cols + ')) pv '
EXEC Sp_executesql #sql

t-sql grouping rows between ranges

I have this query that returns Quantity based discount list
with cte([item code],price,discount,rngLow,rngHigh,id) as
(
select 'xxx-xxxxxxx' as [item code],l.t$pric,l.t$disc,lqanp=l.t$qanp,hqanp=h.t$qanp,id = row_number() over(partition by h.t$qanp order by h.t$qanp) from EdiCatalog l
join ediCatalog h on l.comno=h.comno and l.t$cpls=h.t$cpls and l.t$cuno=h.t$cuno and h.t$item=l.t$item and l.t$qanp < h.t$qanp
where l.comno=#comno and l.t$cpls=#cpls and l.t$cuno=#cuno
)
select * from cte
returning result set
How do I transform the result set to this
You can start with this:
SELECT *
FROM cte a
WHERE rngHigh=(
SELECT MIN(rngHigh)
FROM cte b
WHERE b.rngLow=a.rngLow
)
Which will give you this result set:
discount rngLow rngHigh
40 1 9
68 9 23
73 23 47
75 47 299
#Frazz,
Here is what I have now
with little alteration to what you suggested..
SELECT rngLow=case rngLow
when 1 then rnglow
else rnglow+1 end,rngHigh,discount,id
FROM cte a
WHERE rngHigh=(
SELECT MIN(rngHigh)
FROM cte b
WHERE b.rngLow=a.rngLow
)

Column to comma seperated value partition by ID

i have on sql server 2008 table like
EmployeeCertificationHistoryId EmployeeCertificationID EmployeeID CertificationID CertificationDate
1 244 2192 1 2/15/2006
2 185 2058 87 4/10/2010
3 245 2240 102 8/11/2013
4 246 2249 104 11/23/2005
5 247 2221 101 6/12/2013
6 248 2238 84 NULL
7 245 2240 102 8/11/2013
8 249 2240 102 8/4/2013
10 253 2175 84 6/19/2013
11 254 2239 105 2/5/2011
12 255 2239 111 11/22/2012
9 96 1468 92 12/6/2010
13 256 2239 110 11/22/2012
i need to comma seperate certificationid per employeeid.
for eg. for 2239=>105,111,110
i have written a query but it is giving all certificate id in one column. my query is
SELECT STUFF(
(SELECT ',' + CAST(C.CertificationID AS VARCHAR(100))
FROM tbl_PM_EmployeeCertificationMatrixHistory C
ORDER BY c.CertificationID
FOR XML PATH('')),1,1,'') AS CSV
GO
i just need employeeid and certificationid.but i am unable to sort it out.
You need a correlated subquery and a list of employees. The following gets the list of employees from the same table but you might have another table with this information:
SELECT e.EmployeeID,
STUFF((SELECT ',' + CAST(C.CertificationID AS VARCHAR(100))
FROM tbl_PM_EmployeeCertificationMatrixHistory C
where c.EmployeeID = e.EmployeeID
ORDER BY c.CertificationID
FOR XML PATH('')
),1, 1,'') AS CSV
from (select distinct EmployeeID
from tbl_PM_EmployeeCertificationMatrixHistory
) e;
You just need to add EmployeeID to the query as well as a WHERE and DISTINCT
SELECT DISTINCT A.EmployeeID, STUFF(
(SELECT ',' + CAST(C.CertificationID AS VARCHAR(100))
FROM tbl_PM_EmployeeCertificationMatrixHistory C
WHERE C.EmployeeID = A.EmployeeID
ORDER BY c.CertificationID
FOR XML PATH('')),1,1,'') AS CSV
FROM tbl_PM_EmployeeCertificationMatrixHistory A
GO
If you want to return only DISTINCT values in the the CSV list, add GROUP BY c.CertificationID above the ORDER BY

SqlServer Hierarchical parent/child query with sort of children inside parent

I have a table with data like this-
ID ParentID ProductTypeName
1 NULL Electronics
200 1 TV
300 200 Plasma
67 NULL KitchenAppliances
78 67 Stoves
82 78 Electric
99 78 Gas
23 200 LED
65 300 LG
66 300 Sony
I would like to get the data in the following format -
ID ParentID ProductTypeName Level Sort(Or Some kind of sort value)
1 NULL Electronics 0 1
200 1 TV 1 110
300 200 LED 2 120
65 300 LG 3 12010
66 300 Sony 3 12020
23 200 Plasma 2 100030
67 NULL KitchenAppliances 0 10000010
78 67 Stoves 1 1000001010
82 78 Electric 2 100000101020
99 78 Gas 2 100000101030
To display data in tree in this format. Here please note the children within each parent are sorted as well. Indentation is giving so as to give a better idea of the result -
Electronics
TV
LED
LG
Sony
Plasma
KitchenAppliances
Stoves
Electric
Gas
This is the query I wrote, but does not seem to be working.
The sort number logic seems to be broken.
Could someone help with this.
Any help appreciated. Thanks.
;WITH cte (ID, ParentID, [Level], [Name], Sort) AS(
SELECT sc1.ID,
NULL,
0,
sc1.Name,
cast(row_number()over(partition by sc1.ParentCategoryID order by sc1.Name) as varchar(max)) as Sort
FROM TableData sc1
WHERE sc1.ID is null
UNION ALL
SELECT sc2.ID,
sc2.ParentID,
g2.[level] + 1,
sc2.Name,
g2.Sort + cast(row_number()over(partition by sc2.ParentCategoryID order by sc2.Name) as varchar(max))Sort
FROM dbo.TableData sc2
INNER JOIN cte g2
ON sc2.ParentID = g2.ID
You are doing it almost right. The only things I changed are: condition WHERE sc1.ID is null in non-recursive part of the cte changed to WHERE sc1.ParentID is null, and the way sort key (path) is calculated:
;WITH cte (ID, ParentID, [Name], Level, SortPath, DisplayPath)
AS(
SELECT sc1.ID, NULL, sc1.Name, 0,
cast(row_number() over (partition by sc1.ParentCategoryID order by sc1.Name) as varbinary(max)),
cast(row_number() over (partition by sc1.ParentCategoryID order by sc1.Name) as varchar(max))
FROM dbo.TableData sc1
WHERE sc1.ParentID is null
UNION ALL
SELECT sc2.ID, sc2.ParentID, sc2.Name, g2.Level + 1
g2.SortPath + cast(row_number() over (partition by sc2.ParentCategoryID order by sc2.Name) as binary(4)),
g2.DisplayPath + '.' + cast(row_number() over (partition by sc1.ParentCategoryID order by sc1.Name) as varchar(10))
FROM dbo.TableData sc2
JOIN cte g2 ON sc2.ParentID = g2.ID
)
select ID, ParentID, Name, DisplayPath
from cte
order by SortPath
As you can see, there are two paths calculated, first one is for sorting of elements and second one is for viewing. In case if path of the tree element is not supposed to be shown anywhere, you may leave only SortPath.

Resources