Related
I have a table in long format which I convert to wide format dynamically.
The code was heavily influenced by: SQL Server dynamic PIVOT query?
create table #temp
(
ID int,
category varchar(15),
Answer varchar (5)
)
insert into #temp values ('1', 'breakfast','yes')
insert into #temp values ('1', 'lunch','no')
insert into #temp values ('1', 'dinner','yes')
insert into #temp values ('2', 'breakfast','no')
insert into #temp values ('2', 'lunch', 'yes')
insert into #temp values ('2', 'dinner', 'no')
select * from #temp
Which I can convert into wide format:
DECLARE #cols AS VARCHAR(MAX)='';
DECLARE #query AS VARCHAR(MAX)='';
SELECT #cols = #cols + QUOTENAME(category) + ',' FROM (select distinct category from #temp ) as tmp
select #cols = substring(#cols, 0, len(#cols))
exec (
'SELECT ID, '+#cols+', concat('+#cols+' )as NewCol from
(
select ID, category,answer from #temp
) pivotexample
pivot
(
max(Answer) for category in (' + #cols + ')
) as pivotexample2'
)
drop table #temp
The distinct values in the category column can change so I needed a dynamic solution (as above). This give the below pivoted output:
The issue I have is how can I insert a separator in the concatenation part that creates NewColumn in the pivot.
Also when I then run a select * from pivotexample2 query, it says Invalid object name 'pivotexample2'. I don't understand why this is, because this is the alias I have given it and want to reference it for things like joins further in the pipeline. How can I give it an alias so I can refence it again? Is it possible to put the pivot within a CTE so I can refence it again?
You can use concat_ws:
DECLARE #cols AS VARCHAR(MAX)='';
DECLARE #query AS VARCHAR(MAX)='';
SELECT #cols = #cols + QUOTENAME(category) + ',' FROM (select distinct category from #temp ) as tmp
select #cols = substring(#cols, 0, len(#cols))
exec (
'SELECT ID, '+#cols+', concat_ws('','', '+#cols+' )as NewCol from
(
select ID, category,answer from #temp
) pivotexample
pivot
(
max(Answer) for category in (' + #cols + ')
) as pivotexample2'
)
drop table #temp
It would return:
ID
breakfast
dinner
lunch
NewCol
1
yes
yes
no
yes,yes,no
2
no
no
yes
no,no,yes
DBFiddle: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=45dce502caf3b71662b963fb52dff94e
The question has been asked before, but in a slightly different scenario (one that doesn't seem to fit to my question) so..
I have data that looks like this
Name |Item |Note
George|Paperclip |Two boxes
George|Stapler |blue one
George|Stapler |red one
George|Desk lamp |No light bulb
Mark |Paperclip |One box 2"
Mark |Paperclip |One box 4"
Mark |Block Notes|a blue one
..? |..? |..?
And I would want to pivot by name, to obtain
Name |Paperclip|Stapler|Desk Lamp|Block Notes
George| 1| 2| 1| NULL
Mark | 2| NULL | NULL | 1
I've follower the examples like
Convert Rows to columns using 'Pivot' in SQL Server
but I'm far from a solution.. can someone please give me an hand?
Thanks!
edit: the actual code
drop table #temp2
SELECT DISTINCT *,
CASE WHEN Item IS NULL THEN NULL ELSE COUNT(Item) OVER(PARTITION BY Name) END CNT
INTO #TEMP2
FROM [ISPBIGFIX].[dbo].[C_INV_ErroriTavolette_v11]
DECLARE #cols NVARCHAR (MAX)
DECLARE #Columns2 NVARCHAR (MAX)
SET #cols = SUBSTRING((SELECT DISTINCT ',['+Item+']' FROM #TEMP2 GROUP BY Item FOR XML PATH('')),2,8000)
SET #Columns2 = SUBSTRING((SELECT DISTINCT ',ISNULL(['+Item+'],0) AS ['+Item+']' FROM #TEMP2 GROUP BY Item FOR XML PATH('')),2,8000)
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT Name,' + #Columns2 + ' FROM
(
SELECT Name,ErrorType,CNT FROM #TEMP2
) x
PIVOT
(
SUM(CNT)
FOR [Item] IN (' + #cols + ')
) p
WHERE Name IS NOT NULL;'
EXEC SP_EXECUTESQL #query
Try this, It follows the same example mentioned here:Convert Rows to columns using 'Pivot' in SQL Server
--Drop Sample temp Table
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL
BEGIN
DROP TABLE #temp2
END
--create Sample temp Table
create Table #temp2
(
[name] varchar(255),
Item varchar(255),
note varchar(255)
)
--Insert Sample Data
insert into #temp2
values( 'George','Paperclip','Two boxes'),
('George','Stapler','blue one'),
('George','Stapler','red one'),
('George','Desk lamp','No light bulb'),
('Mark','Paperclip','One box 2'),
('Mark','Paperclip','One box 4'),
('Mark','Block Notes','a blue one')
DECLARE #cols AS NVARCHAR(MAX), #cols2 AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
--Generate Columns from Data
--Generate Columns from Data
select #cols = STUFF((SELECT ', isnull(' + QUOTENAME(Item) + ',0) as' + QUOTENAME(Item)
from #temp2
group by Item
order by Item
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #cols2 = STUFF((SELECT ', ' + QUOTENAME(Item)
from #temp2
group by Item
order by Item
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--Pivot Query
set #query = 'SELECT [name],' + #cols + ' from
(
select [Name], Item, count(*) as xcount
from #temp2
group by Name, Item
) x
pivot
(
sum(xCount)
for Item in (' + #cols2+ ')
) p '
execute(#query);
--Drop Sample Temp Table
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL
BEGIN
DROP TABLE #temp2
END
Try This Dynamic Sql
IF OBJECT_ID('dbo.TT')IS NOT NULL
DROP TABLE TT
;WITH CTE(Name ,Item ,Note)
AS
(
SELECT 'George','Paperclip' ,'Two boxes' UNION ALL
SELECT 'George','Stapler' ,'blue one' UNION ALL
SELECT 'George','Stapler' ,'red one' UNION ALL
SELECT 'George','Desk lamp' ,'No light bulb' UNION ALL
SELECT 'Mark' ,'Paperclip' ,'One box 2' UNION ALL
SELECT 'Mark' ,'Paperclip' ,'One box 4' UNION ALL
SELECT 'Mark' ,'Block Notes','a blue one'
)
SELECT *,CASE WHEN Item IS NOT NULL THEN 1 ELSE 0 END AS Item2 INTO TT FROM CTE
SELECT * FROM TT
DECLARE #Sql nvarchar(max),
#Sqlcol nvarchar(max),
#ISNULLSqlcol nvarchar(max)
SELECT #Sqlcol=STUFF((SELECT DISTINCT ', '+QUOTENAME(Item)
FROM TT FOR XML PATH ('')),1,1,'')
SELECT #ISNULLSqlcol=STUFF((SELECT DISTINCT ', '+'ISNULL(SUM('+QUOTENAME(Item) +'),''0'') AS '+QUOTENAME(Item)
FROM TT FOR XML PATH ('')),1,1,'')
SET #Sql='SELECT Name,'+#ISNULLSqlcol+'FROM
(
SELECT * FROM TT
) AS SRc
PIVOT
(
SUM(Item2) FOR Item IN('+#Sqlcol+')
) AS Pvt GROUP BY Name'
Print #Sql
EXEC (#Sql)
Try this query (I think its much more clearly). I use the code of Kashif Qureshi to create a temporary table, but my code is different in PIVOT part
--Drop Sample temp Table
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL
BEGIN
DROP TABLE #temp2
END
--create Sample temp Table
create Table #temp2
(
[name] varchar(255),
Item varchar(255),
note varchar(255)
)
--Insert Sample Data
insert into #temp2
values( 'George','Paperclip','Two boxes'),
('George','Stapler','blue one'),
('George','Stapler','red one'),
('George','Desk lamp','No light bulb'),
('Mark','Paperclip','One box 2'),
('Mark','Paperclip','One box 4'),
('Mark','Block Notes','a blue one')
--- PIVOT
DECLARE #v_query VARCHAR(8000) -- main query
DECLARE #v_columns VARCHAR(8000) -- columns
SET #v_columns =''
-- Get string columns
SELECT #v_columns += '[' + CONVERT(VARCHAR, Item) +'],' FROM (SELECT DISTINCT Item FROM #temp2) AS temp
-- Delete the last comma
SET #v_columns = LEFT(#v_columns,LEN(#v_columns)-1)
-- Main query
SET #v_query = 'SELECT Name, ' + #v_columns +' FROM
(
SELECT Name, Item FROM #temp2
) T
PIVOT
(
Count(Item)
FOR Item IN ('+ #v_columns +')
) PVT'
EXEC (#v_query)
-- DROP
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL
BEGIN
DROP TABLE #temp2
END
The answer by Kashif works. Here is a version that leverages STRING_AGG
--Drop Sample temp Table
DROP TABLE IF EXISTS #temp2
--Create Sample temp Table
CREATE Table #temp2 (
[name] varchar(255),
Item varchar(255),
note varchar(255)
)
--Insert Sample Data
INSERT INTO #temp2
VALUES ( 'George','Paperclip','Two boxes'),
('George','Stapler','blue one'),
('George','Stapler','red one'),
('George','Desk lamp','No light bulb'),
('Mark','Paperclip','One box 2'),
('Mark','Paperclip','One box 4'),
('Mark','Block Notes','a blue one')
DECLARE #cols AS NVARCHAR(MAX), #cols2 AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
--Generate Columns from Data
SET #cols = (SELECT STRING_AGG (' isnull(' + QUOTENAME(Item) + ',0) as' + QUOTENAME(Item), ',') as colR
FROM (SELECT ITEM FROM (SELECT ITEM FROM #temp2 GROUP BY ITEM) x GROUP BY ITEM) Y )
SET #cols2 = (SELECT STRING_AGG (QUOTENAME(Item), ',') as colR
FROM (SELECT ITEM FROM (SELECT ITEM FROM #temp2 GROUP BY ITEM) x GROUP BY ITEM) Y )
SET #query = 'SELECT [name],' + #cols + '
FROM (
SELECT [Name], Item, COUNT(*) as xcount
FROM #temp2
GROUP BY Name, Item
) x
PIVOT (
SUM(xCount)
FOR Item IN (' + #cols2+ ')
) p '
EXECUTE (#query);
--Drop Sample Temp Table
DROP TABLE IF EXISTS #temp2
I have a request which returns something like this:
--------------------------
Tool | Week | Value
--------------------------
Test | 20 | 3
Sense | 20 | 2
Test | 19 | 2
And I want my input to look like this:
-------------------------
Tool | W20 | W19
-------------------------
Test | 3 | 2
Sense | 2 | null
Basically, for every week I need to have a new column. The number of week and of tools is dynamic.
I have tried many things but nothing worked. Anybody have a solution ?
Try this
CREATE table #tst (
Tool varchar(50), [Week] int, Value int
)
insert #tst
values
('Test', 20, 3),
('Sense', 20,2),
('Test', 19, 2)
Here is the Dynamic Query:
DECLARE #col nvarchar(max), #query NVARCHAR(MAX)
SELECT #col = STUFF((SELECT DISTINCT ',' + QUOTENAME('W' + CAST([Week] as VARCHAR))
from #tst
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = '
SELECT *
FROM (
SELECT Tool,
Value,
''W'' + CAST([Week] as VARCHAR) AS WeekNo
FROM #tst
) t
PIVOT
(
MAX(t.Value)
FOR WeekNo IN (' + #col + ')
) pv
ORDER by Tool'
EXEC (#query)
Result
Tool W20 W19
=================
Sense 2 NULL
Test 3 2
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp
( Tool varchar(5), Week int, Value int)
;
INSERT INTO #temp
( Tool , Week , Value )
VALUES
('Test', 20, 3),
('Sense', 20, 2),
('Test', 19, 2)
;
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max),
#col NVARCHAR(max)
SELECT #columns = ISNULL(#columns + ', ', '') + N'[' +'w'+ tbl.[Week] + ']'
FROM (
SELECT DISTINCT CAST([Week] AS VARCHAR)[Week]
FROM #temp
) AS tbl
SELECT #statement = 'SELECT *
FROM
(
SELECT
Tool , ''w''+ CAST(Week AS VARCHAR) week , Value
FROM
#Temp
) src
PIVOT(MAX(Value)for Week in (' + #columns + ')) as pvt
'
EXEC sp_executesql #statement = #statement
This is how I would do it ... If I understood your question correctly
if object_id('tempdb..#InputTool') is not null drop table #InputTool
create table #InputTool (Tool nvarchar(10), [20] int, [19] int)
insert into #InputTool (Tool, [20], [19])
values
('Test', 3, 2),
('Sense', 2, null)
declare #cols nvarchar(max)
select #cols = STUFF((SELECT ',' + QUOTENAME(name)
from tempdb.sys.columns
where object_id = object_id('tempdb..#InputTool')
and Column_id > 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
declare #sqlquery nvarchar(max) =
'select Tool, Weeks, Value from (
select * from #InputTool
) as it
UNPIVOT
(
Value FOR Weeks IN (' + #cols + ')
) AS Weeks
order by Weeks desc'
execute (#sqlquery);
Give it a shot and let me know if it worked
I asked this question and it was marked as a duplicate of How to pivot unknown number of columns & no aggregate in SQL Server?, but that answer doesn't help me.
I have a table of data that looks like this, with an unknown number of rows and values.
RecID Name Value
1 Color Red
2 Size Small
3 Weight 20lbs
4 Shape Square
I need a query that will return the data like this, building out a column for each row. I cannot hard code anything except the column headers 'Name' and 'Value'.
Color Size Weight Shape
Red Small 20lbs Square
Here is what I have so far that is partly working:
INSERT INTO #Table VALUES
(1,'Color' ,'Red'),
(2,'Size' ,'Small'),
(3,'Weight','20lbs'),
(4,'Shape' ,'Square')
;with mycte
as
(
SELECT rn,cols,val
FROM (SELECT row_number() over(order by Name) rn, Name, Value
FROM #Table) AS src1
UNPIVOT (val FOR cols
IN ( [Name], [Value])) AS unpvt
)
SELECT *
FROM (SELECT rn,cols,val
FROM mycte) AS src2 PIVOT
( Max(val) FOR rn IN ([1], [2], [3])) AS pvt
Which returns:
cols 1 2 3
Name Color Shape Size
Value Red Square Small
Two problems with this that I can't seem to resolve.
I don't need the column headers and the first column that has cols, Name, Value in it.
Can't figure out how to have it build a column for each row without specifying the [x] identifiers.
Any guidance would be great I've been stuck on this a while now.
declare #collist nvarchar(max)
SET #collist = stuff((select distinct ',' + QUOTENAME(name)
FROM #t -- your table here
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
declare #q nvarchar(max)
set #q = '
select *
from (
select rn, name, Value
from (
select *, row_number() over (partition by name order by RecID desc) as rn
from #t -- your table here
) as x
) as source
pivot (
max(Value)
for name in (' + #collist + ')
) as pvt
'
exec (#q)
Until now, I have reached to following code, hope it helps you,
Current outpu comes as
Color Shape Size Weight
Red NULL NULL NULL
NULL NULL Small NULL
NULL NULL NULL 20lbs
NULL Square NULL NULL
Create table DyTable
(
tid int,
Name varchar(20),
Value varchar(20)
)
INSERT INTO DyTable VALUES
(1,'Color' ,'Red'),
(2,'Size' ,'Small'),
(3,'Weight','20lbs'),
(4,'Shape' ,'Square')
DECLARE #Cols NVARCHAR(MAX);
DECLARE #Cols1 NVARCHAR(MAX);
SELECT #Cols = STUFF((
SELECT DISTINCT ', ' + QUOTENAME(Name)
FROM DyTable
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,'')
,#Cols1 = STUFF((
SELECT DISTINCT ', max(' + QUOTENAME(Name) + ') as ' + Name
FROM DyTable
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,'')
DECLARE #Sql NVARCHAR(MAX)
Select #Cols
SET #Sql = 'Select '+ #Cols1 +'
from (SELECT ' + #Cols + '
FROM DyTable t
PIVOT (MAX(Value)
FOR Name
IN (' + #Cols + ')
)P)a'
EXECUTE sp_executesql #Sql
I have a table that consists of pageno int, groupid varchar(10). Each row in the table is distinct. There can be multiple groupid and pageno combinations. The amount of pagenos is unknown and the amount of groupids is unknown. The data might look like this:
pageno groupid
101105 mpadilla
101105 gentry
100100 mpadilla
100100 gentry
I would like to have a result set returned that shows the pagenos as columns and the groupids as rows and an x where they intersect (meaning it exists for that pageno/groupid combo).
Would I use sql pivot for this? If so, give me an example please. If not, please provide your example and explanation. I'm a little confused.
Your requirements are not entirely clear but if I am understanding your question you can use the PIVOT function to get the result.
The basic syntax for this query of query would be the following if you had a limited number of pagenos:
select groupid, [101105], [100100]
from
(
select pageno, groupid,
flag = 'X'
from yourtable
) d
pivot
(
max(flag)
for pageno in ([101105], [100100])
) piv;
See SQL Fiddle with Demo.
Then if you have an unknown number of values, you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(pageno)
from yourtable
group by pageno
order by pageno
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT groupid, ' + #cols + '
from
(
select pageno, groupid,
flag = ''X''
from yourtable
) x
pivot
(
max(flag)
for pageno in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. Both versions will give a result:
| GROUPID | 100100 | 101105 |
| gentry | X | X |
| mpadilla | X | X |
| test | X | (null) |
Edit, if you have nulls that you want to replace, then I would create a second list of column names this one will be used for the final select list and it will include coalesce to replace the null. The code will be:
DECLARE #cols AS NVARCHAR(MAX),
#colsNull AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(pageno)
from yourtable
group by pageno
order by pageno
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsNull = STUFF((SELECT ', coalesce(' + QUOTENAME(pageno) +', '''') as '+QUOTENAME(pageno)
from yourtable
group by pageno
order by pageno
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT groupid, ' + #colsNull + '
from
(
select pageno, groupid,
flag = ''X''
from yourtable
) x
pivot
(
max(flag)
for pageno in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. The result will be:
| GROUPID | 100100 | 101105 |
| gentry | X | X |
| mpadilla | X | X |
| test | X | |
I answered my own question. What I did was add an additional column called customized to my original table. This column only contains an x for each customized page. Kind of redundant I know but it seems that the pivot table needs 3 columns. So here is my dynamic code that I came up with and works:
DECLARE #query VARCHAR(4000), #groupids VARCHAR(8000)
SELECT #groupids = STUFF(( SELECT DISTINCT
'],[' + LTRIM(groupid)
FROM DistinctPages
ORDER BY '],[' + LTRIM(groupid)
FOR XML PATH('')
), 1,2, '') + ']'
SET #query =
'SELECT * FROM (SELECT pageno, groupid, customized FROM DistinctPages)t
PIVOT (MAX(customized) FOR groupid
IN ('+#groupids+')) AS CustomizedPagesPerGroups'
EXECUTE (#query)