Pivot Sql with no aggregate - sql

I learned I can't pivot text without aggregation max() & min().
I am trying to figure out a workaround but the answers to similar questions are sailing over my head. would anyone have tips to workaround that?
data table:
pax
codex
mis
dog1
hair
10
dog1
face
10
dog1
eye
5
dog1
smell
7
dog1
yellow
7
dog1
green
8
dog1
blue
9
dog1
tan
10
desired output:
pax
10
10
5
7
7
8
9
10
dog1
hair
face
eye
smell
yellow
green
blue
tan
actual outcome:
pax
10
5
7
8
9
dog1
hair
eye
smell
green
blue
I used this code:
DECLARE #cols AS NVARCHAR(MAX)='';
DECLARE #query AS NVARCHAR(MAX)='';
SELECT #cols = #cols + QUOTENAME(mis) + ',' FROM (select distinct mis from #dd) as tmp
select #cols = substring(#cols, 0, len(#cols)) --trim "," at end
set #query =
'SELECT * from
(
select pax,codex,mis from #dd
) src
pivot
(
max(codex) for mis in (' + #cols + ')
) piv'
execute(#query)

Try this
Select
pax,
concat(mis, '-', rn) as mis_new,
codex
from (
Select
pax,
mis,
row_number() over (partition by pax, mis
order by mis ) rn
from table
) t
pivot (
max(codex) for (
mis_new in ('10-1','10-2', '5-1','7-1','7-2','8-1','9-1','10-3')
) pvt

It looks like standard conditional aggregation over a row-number would serve better:
DECLARE #cols AS NVARCHAR(MAX) =
(
SELECT CONCAT(',MIN(CASE WHEN mis = ', QUOTENAME(mis, ''''), ' AND rn = ', rn, ' THEN codex END) ', QUOTENAME(mis))
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY pax ORDER BY mis) rn
FROM dd
) as tmp
GROUP BY mis, rn
FOR XML PATH(''), TYPE
).value('text()[1]','nvarchar(max)');
DECLARE #query AS NVARCHAR(MAX) = '
SELECT
pax' + #cols + '
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY pax ORDER BY mis) rn
FROM dd
) dd
GROUP BY pax;
';
PRINT #query; --for testing
EXEC sp_executesql
#query;
db<>fiddle
Note the use of FOR XML to aggregate. Variable coalescing should not be used, due to unpredictability.

Related

Getting the counts of the number of months a user has been using a particular service in SQL

I have data like below:
user_id month_id service
895 201612 S
262 201612 V
5300 201612 BB
Now there can be users who have used more than one service in a year, and I would like to have a query which gives me that. For example say 895 has used S for 3 months and BB for 4 months and then his latest service is V. So :
user_id S BB V
895 3 4 5
How do I do this pivot and count in SQL , can someone please help me?
Here is how you would do it dynamically:
DECLARE #Cols AS NVARCHAR(MAX)
,#Query AS NVARCHAR(MAX);
SET #Cols = STUFF((
SELECT DISTINCT ',' + QUOTENAME(service)
FROM YourTable
FOR XML PATH('')
,TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #Query = 'SELECT user_id, ' + #Cols + ' from
(
select user_id
, month_id
, service
from YourTable
) x
pivot
(
COUNT(month_id)
for service in (' + #Cols + ')
) p '
EXECUTE(#Query)
Edit: Didn't realize it was a COUNT of month_id and not DATEDIFF(mm, CONVERT(DATETIME, month_id + ''01'',GETDATE()). Updated.

Prefixed Columns are not allowed in PIVOT Operator

My Query as below :
select * from
(
SELECT ShoperCompCode, Name, Qty, Value, Tb, ASP, UPT, AST, CTS, class2cd, CashSaleQty FROM MBDSR
)A
PIVOT (AVG(CashSaleQty) FOR class2cd IN ("")) AS PVT
Tables as below :
ShoperCompCode Name Qty Value Tb ASP UPT AST CTS class2cd CashSaleQty
MB1 TEST 35 77064 6 2201.83 5.83 12844 0.44 Jeans 1
MB2 TEST2 5 11095 2 2219 2.5 5547.5 0.06 T-shirt 2
MB3 TEST3 0 0 0 0 0 0 0 Jeans 3
Now, i don't know the class2cd value wheather it will be "Jeans" or "T-shirt"
I.e (Class2Cd data is not static, it will be dynamic)..
Problem has been resolved check my updated code.
DECLARE #cols AS NVARCHAR(MAX),#query AS NVARCHAR(MAX)
Select #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(Class2Cd)
from MBDSR
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT * from
(
SELECT ShoperCompCode, Name, Qty, Value, Tb, ASP, UPT, AST, CTS, class2cd, CashSaleQty FROM MBDSR
) x
pivot
(
AVG(CashSaleQty) FOR class2cd IN (' + #cols + ')
) p '
execute(#query);

Pivot SQL table with dynamic year columns

I'm having trouble figuring this out. I've checked similar posts but they only have one column as pivoted as a row. While I need to pivot
I have the following query:
SELECT
Year([Date]) as Year
,SUM([Drop]) as [Drop]
,SUM([TicketsDistributed]) as [TicketsDistributed]
,SUM([TicketsSold]) as [TicketsSold]
,SUM([GrossTickets]) as [GrossTickets]
,SUM([GrossTickets])/SUM(TicketsSold) as 'Per Cap'
FROM [dbo].[Tickets]
group by [Date]
Which give me this result:
Year Drop TicketsDistributed TicketsSold GrossTickets Per Cap
2016 222 100 5000 4000.00 0.800000
2015 222 110 5000 4000.00 0.900000
2014 222 120 5000 4000.00 1.00000
And I would like the following:
2016 2015 2014
Drop 222 222 222
TicketsDistributed 100 110 120
TicketsSold 5000 5000 5000
GrossTickets 4000 4000 4000
Per Cap 0.8 0.9 1
Based on the suggested answer this is what I have so far but it's not working
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Year(Date))
from [dbo].[SpringTrainings] t
cross apply
(
select SUM([Drop]) as [Drop]
,SUM([TicketsDistributed]) as [TicketsDistributed]
,SUM([TicketsSold]) as [TicketsSold]
,SUM([GrossTickets]) as [GrossTickets]
,SUM([GrossTickets])/SUM(TicketsSold) as PerCap
FROM [dbo].[SpringTrainings]
) c ([Drop],[TicketsDistributed],[TicketsSold],[GrossTickets],PerCap)
group by Year(Date)
order by Year(Date)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [Drop],[TicketsDistributed],[TicketsSold],[GrossTickets],PerCap,' + #cols + '
from
(
select SUM([Drop]) as [Drop]
,SUM([TicketsDistributed]) as [TicketsDistributed]
,SUM([TicketsSold]) as [TicketsSold]
,SUM([GrossTickets]) as [GrossTickets]
,SUM([GrossTickets])/SUM(TicketsSold) as PerCap
FROM [dbo].[SpringTrainings]
) x
pivot
(
max(Year([Date]))
for Year([Date]) in (' + #cols + ')
) p '
execute sp_executesql #query;
If you wish to keep using the pivot operator within T-SQL, then first you need to "unpivot" your existing query so you have Year, Label, and Value. While there is an unpivot operator in T-SQL personally I find using CROSS APPLY and VALUES to be much simpler and equally as fast (for more on his approach read this article by Brad Schultz), I particularly like it because I can visualize the result easily by the way I layout the value pairs.
SELECT
d.Year
, a.label
, a.value
FROM (
SELECT
YEAR([Date]) AS [Year]
, SUM([Drop]) AS [Drop]
, SUM([TicketsDistributed]) AS [TicketsDistributed]
, SUM([TicketsSold]) AS [TicketsSold]
, SUM([GrossTickets]) AS [GrossTickets]
, SUM([GrossTickets]) / SUM(TicketsSold) AS [PerCap]
FROM [dbo].[Tickets]
GROUP BY
[Year]
) AS t
CROSS APPLY ( /* now transform into 5 rows per year but just 1 value column */
VALUES
('Drop',t.Drop)
, ('TicketsDistributed',t.TicketsDistributed)
, ('TicketsSold',t.TicketsSold)
, ('GrossTickets',t.GrossTickets)
, ('PerCap',t.PerCap)
) AS a (label, value)
That query (above) replaces the derived table x in your dynamic SQL. Once the data has been massaged into that form the pivot looks way simpler:
) x
pivot
(
max([x.Value])
for [x.Year] in ([2014],[2015],[2016])
) p
For your #cols I would suggest something simple like this:
SELECT DISTINCT
QUOTENAME(Year([date]))
FROM [dbo].[Tickets]
TIP: if you need a way to order the rows, include that in the cross apply too, like this:
CROSS APPLY ( /* now transform into 5 rows per year but just 1 value column */
VALUES
(1, 'Drop',t.Drop)
, (2, 'TicketsDistributed',t.TicketsDistributed)
, (3, 'TicketsSold',t.TicketsSold)
, (4, 'GrossTickets',t.GrossTickets)
, (5, 'PerCap',t.PerCap)
) AS a (row_order, label, value)
and then that [row_order] can be used after the pivot is performed.
So the overall could look like this:
DECLARE #cols AS nvarchar(max)
, #query AS nvarchar(max)
SELECT #cols = STUFF((
SELECT DISTINCT
',' + QUOTENAME(YEAR([date]))
FROM [dbo].[Tickets]
FOR xml PATH (''), TYPE
)
.value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
SET #query = 'SELECT [Year], [Label], '
+ #cols
+ ' FROM (
SELECT
d.Year
, a.label
, a.value
FROM (
SELECT
YEAR([Date]) AS [Year]
, SUM([Drop]) AS [Drop]
, SUM([TicketsDistributed]) AS [TicketsDistributed]
, SUM([TicketsSold]) AS [TicketsSold]
, SUM([GrossTickets]) AS [GrossTickets]
, SUM([GrossTickets]) / SUM(TicketsSold) AS [PerCap]
FROM [dbo].[Tickets]
GROUP BY
[Year]
) AS d
CROSS APPLY (
VALUES
(1,''Drop'',t.Drop)
, (2,''TicketsDistributed'',t.TicketsDistributed)
, (3,''TicketsSold'',t.TicketsSold)
, (4,''GrossTickets'',t.GrossTickets)
, (5,''PerCap'',t.PerCap)
) AS a (row_order,label,value)
) x
pivot
(
max([x.Value])
for [x.Year] in (' + #cols + ')
) p
ORDER BY [row_order]'
EXECUTE sp_executesql #query;

pivoting rows to columns in tsql

I have the following table with the following sample data
ID Language Question SubQuestion SubSubQuestion TotalCount TotalPercent
3 E 9 0 1 88527 73%
3 E 9 0 2 19684 16%
3 E 9 0 3 12960 11%
3 E 9 0 9 933 1%
I want all in one row like this
ID Language TotalCount901 TotalPercent901 TotalCount902 TotalPercent902 TotalCount903 TotalPercent903
3 E 88527 73% 19684 16% 12960 11%
I've tired using the pivot command, but it dosnt to work for me.
I made a few assumptions based on your column names, but it looks like you want to use something similar to this. This applies both an UNPIVOT and then a PIVOT to get the values in the columns you requested:
select *
from
(
select id,
language,
col + cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)) col,
value
from
(
select id, language,
cast(TotalCount as varchar(10)) TotalCount,
totalPercent,
question, subquestion, SubSubQuestion
from yourtable
) usrc
unpivot
(
value
for col in (totalcount, totalpercent)
) un
) srcpiv
pivot
(
max(value)
for col in ([TotalCount901], [totalPercent901],
[TotalCount902], [totalPercent902],
[TotalCount903], [totalPercent903],
[TotalCount909], [totalPercent909])
) p
See SQL Fiddle with Demo
Note: when performing the UNPIVOT the columns need to be of the same datatype. If they are not, then you will need to convert/cast to get the datatypes the same.
If you have an unknown number of values to transform, you can use dynamic sql:
DECLARE #query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsPivot
= STUFF((SELECT ','
+ QUOTENAME(c.name +
cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)))
from yourtable t
cross apply sys.columns as C
where C.object_id = object_id('yourtable') and
C.name in ('TotalCount', 'TotalPercent')
group by c.name, t.question, t.subquestion, t.subsubquestion
order by t.SubSubQuestion
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'select *
from
(
select id,
language,
col + cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)) col,
value
from
(
select id, language,
cast(TotalCount as varchar(10)) TotalCount,
totalPercent,
question, subquestion, SubSubQuestion
from yourtable
) usrc
unpivot
(
value
for col in (totalcount, totalpercent)
) un
) srcpiv
pivot
(
max(value)
for col in (' + #colsPivot + ')
) p '
execute(#query)
See SQL Fiddle with Demo

SQL rotate rows to columns...dynamic number of rows [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
SQL Server dynamic PIVOT query?
I have a dataset that has the below structure.
CREATE TABLE #TempTable
(
Measure_ID INT,
measurement DECIMAL(18, 4)
)
INSERT INTO #TempTable
VALUES
(1,2.3)
,(1,3.4)
,(1,3.3)
,(2,3)
,(2,2.3)
,(2,4.0)
,(2,4.5)
I need to produce output that will look like this.
1,2.3,3.4,3.3
2,3,2.3,4.0,4.5
Basically its a pivot on Measure_ID. Unfortunately, there can be an unlimited number of measure_id's. So Pivot is out.
I'm hoping to avoid CURSORS, but will if that turns out to be the best approach.
If you have an unknown number of values, then you can use a PIVOT with dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ','
+ QUOTENAME('Measurement_' + cast(rn as varchar(10)))
from temptable
cross apply
(
select row_number() over(partition by measure_id order by measurement) rn
from temptable
) x
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT measure_id, ' + #cols + ' from
(
select measure_id, measurement,
''Measurement_''
+ cast(row_number() over(partition by measure_id order by measurement) as varchar(10)) val
from temptable
) x
pivot
(
max(measurement)
for val in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle With Demo
If you have a known number of values, then you can hard-code the values, similar to this:
SELECT measure_id, [Measurement_1], [Measurement_2],
[Measurement_3], [Measurement_4]
from
(
select measure_id, measurement,
'Measurement_'
+ cast(row_number() over(partition by measure_id order by measurement) as varchar(10)) val
from temptable
) x
pivot
(
max(measurement)
for val in ([Measurement_1], [Measurement_2],
[Measurement_3], [Measurement_4])
) p
See SQL Fiddle With Demo
Both queries will produce the same results:
MEASURE_ID | MEASUREMENT_1 | MEASUREMENT_2 | MEASUREMENT_3 | MEASUREMENT_4
==========================================================================
1 | 2.3 | 3.3 | 3.4 | (null)
2 | 2.3 | 3 | 4 | 4.5