SQL Server Dynamic Column and its counter - sql-server

My data looks like this
This is how I have attempted it so far
is giving the events columns duplicated as shown in the left side query above, not sure why
Output is required in 2 formats pivot by Event Date
and by Event Type
I tried to insert the SQL code but it didn't work with formatting, sorry
-- to create a table
Create Table TestStudentEvents (Pid int, StudentID int, EventName varchar(50), EventDate DateTime);
Insert into TestStudentEvents values
(1, 1, 'SA', '2021-05-10'), (2, 1, 'SA', '2021-05-12'), (3, 1, 'AA', '2021-05-11'),
(4, 2, 'SA', '2021-05-10'), (5, 2, 'SA', '2021-05-12'), (6, 2, 'AB', '2021-05-11')
--select * from #StudentEvents
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SELECT #cols = STUFF((SELECT distinct ',' +
QUOTENAME(EventName)
from TestStudentEvents AS t
where [StudentID] = 1 or [StudentID] = 2
group by EventName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
-- select #cols
SELECT #query = 'SELECT * , '+ #cols + '
FROM
(
select distinct [EventName], COUNT(EventName) as NoOfEvents, [StudentID]
FROM TestStudentEvents
where [StudentID] =1 or [StudentID] = 2
group by [EventName], [StudentID]
) AS t
PIVOT
(
Max(NoOfEvents)
FOR [EventName] IN (' + #cols + ')' +
') p ';
execute(#query);
Drop table TestStudentEvents;

Just replace the * with actual columns in the dynamic SQL.
Please see the db<>fiddle here.

Related

how to rotate a table-valued in SQL Server

I have a table above and I want to convert it to a table as shown below
What methods can I use to do this?
I tried using PIVOT but I don't know how to change the Score column
So I hope I can find a solution to the above problem
I have done that
But I want to use a table variable instead of a table. How to do it?
I want to replace Table your_table_name with a variable #table.
But SQL says Must declare the scalar variable "#table" but I have declared it before.
It's hard to tell what you tried with PIVOT and why it didn't work, but:
SELECT Name, aa, bb, cc
FROM dbo.YourTableName
PIVOT
(
MAX(score) FOR subjects IN ([aa],[bb],[cc])
) AS p;
Working example in this fiddle.
based on your comments, you want to use table variables. For this reason, you will need to create a table type
use below example
CREATE TYPE MyTableType AS TABLE
(Name char(1), subjects char(2), score int);
GO
declare #sql as Nvarchar(max);
declare #your_table_name AS MyTableType;
INSERT INTO #your_table_name VALUES
('a', 'aa', 5),
('a', 'bb', 6),
('a', 'cc', 3),
('b', 'bb', 7),
('b', 'cc', 8);
select #sql = N'select [name],' + stuff((
select distinct
',max(case [subjects] when ' + char(39) + [subjects] + char(39)
+
' then [score] end) [' + [subjects] + ']'
from #your_table_name
for xml path('')
), 1, 1, '');
select #sql += N'from #your_table_name group by [name];';
PRINT #SQL
EXEC sp_executesql #SQL,
N'#your_table_name MyTableType READONLY',
#your_table_name=#your_table_name
I found this method here and this answer explains in a better way
https://stackoverflow.com/a/12876775/13800469
You can do this by executing a dynamically creates query.
Query
declare #sql as varchar(max);
select #sql = 'select [name],' + stuff((
select distinct
',max(case [subjects] when ' + char(39) + [subjects] + char(39)
+
' then [score] end) [' + [subjects] + ']'
from your_table_name
for xml path('')
), 1, 1, '');
select #sql += ' from your_table_name group by [name];';
exec(#sql);
Find demo here

How to write a dynamic columns PIVOT query with multiple records?

EDIT2: I made a new usable example, reached the next step, but still have no clue on how to do some stuff. Check, please.
I have the table below:
CREATE TABLE [dbo].[MyTable] (
[ID] [int] IDENTITY(1,1) NOT NULL
,[Name] [nvarchar](50) NULL
,[Quantity] [int] NULL
,[Period] [nvarchar](10) NULL
)
INSERT [dbo].[MyTable] VALUES
('foo', 1, 'Jan'),
('bar', 2, 'Jan'),
('foo', 1, 'Jan'),
('kin', 1, 'Jan'),
('blat', 5, 'Jan'),
('foo', 3, 'Feb'),
('bar', 1, 'Feb'),
('kin', 2, 'Feb'),
('blat',4, 'Feb'),
('foo', 1, 'Feb'),
('kin', 7, 'Feb'),
('blat', 1, 'Feb'),
('foo', 3, 'Mar'),
('bar', 1, 'Mar'),
('kin', 1, 'Mar'),
('blat', 1, 'Mar'),
('bar', 1, 'Mar'),
('kin', 2, 'Mar'),
('blat', 3, 'Mar')
And I want to achieve the result in the pic by using a PIVOT in a query:
My query so far:
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME(Period)
FROM (SELECT Period FROM dbo.MyTable AS p
GROUP BY Period) AS x;
SET #sql = N'
SELECT ' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT Name, Period, Quantity
FROM dbo.MyTable AS p
) AS j
PIVOT
(
SUM(Quantity) FOR Period IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
EXEC sp_executesql #sql;
Gives me the result below:
How can I add row names (foo, bar, kin, blat), the column name Name, the SUMs etc.?
EDIT3: How to 'ORDER BY' the columns and the records?
Good day Tomo,
Now that we have the queries to create the table and insert the data, it is simple and fast to help you :-)
Please check if the bellow solution fit your needs
select [Name],[Jan],[Feb],[Mar],[Jan]+[Feb]+[Mar] AS Grand_Total
from (
SELECT [Name], [Quantity],[Period]
FROM [MyTable]
) src
pivot
(
SUM(Quantity)
for Period in ([Jan],[Feb],[Mar])
) piv
GO
Next step is to convert this into dynamic PIVOT which is very simple once we have the static solution, if you need dynamic PIVOT.But first confirm the static PIVOT returns what you need and that you actually need a dynamic solution (maybe this fit your needs as it is)
Update: add "Grand Total" at the bottom using ROLLUP
;With MyCTE as(
select [Name],[Jan],[Feb],[Mar],[Jan]+[Feb]+[Mar] AS Grand_Total
from (
SELECT [Name], [Quantity],[Period]
FROM [MyTable]
) src
pivot
(
SUM(Quantity)
for Period in ([Jan],[Feb],[Mar])
) piv
)
select ISNULL([Name],'Totoal') as Name ,SUM([Jan]) [Jan],SUM([Feb]) [Feb],SUM([Mar]) [Mar],SUM([Grand_Total]) [Grand_Total]
from MyCTE
GROUP BY ROLLUP ([Name])
GO
Adding Dynamic Pivot version as the OP asked for
DECLARE
#ColumnsList1 AS NVARCHAR(MAX),
#ColumnsList2 AS NVARCHAR(MAX),
#ColumnsList3 AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #ColumnsList1 = STUFF(
(SELECT distinct ',' + QUOTENAME([Period]) FROM [MyTable] FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
,1,1,'')
PRINT #ColumnsList1
SET #ColumnsList2 = REPLACE (#ColumnsList1, ',','+')
PRINT #ColumnsList2
SET #ColumnsList3 = (
SELECT distinct ',SUM(' + QUOTENAME([Period]) + ') as ' + QUOTENAME([Period])
FROM [MyTable] FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
PRINT #ColumnsList3
SET #query = '
;With MyCTE as(
select [Name],'+#ColumnsList1+','+#ColumnsList2+' AS Grand_Total
from (
SELECT [Name], [Quantity],[Period]
FROM [MyTable]
) src
pivot
(
SUM(Quantity)
for Period in ('+#ColumnsList1+')
) piv
)
select ISNULL([Name],''Totoal'') as Name '+#ColumnsList3+',SUM([Grand_Total]) [Grand_Total]
from MyCTE
GROUP BY ROLLUP ([Name])
'
execute(#query)
GO

Pivot getting multiple records

I'm working on pivot functionality.
I'm having 2 tables one is userinfo and another is hobbies.
My table will be as like below image.
I'm saving the hobbies with comma separated data in userinfo.
I want all the comma separated hobbies with their respective column names.
I tried, but getting indivdual records for each hobbies instead of the single row
as like below image.
My code is as follows :
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
--drop table Temptbl1
--drop table Temptbl2
IF EXISTS (SELECT * FROM Temptbl1)
drop table Temptbl1
IF EXISTS (SELECT * FROM Temptbl2)
drop table Temptbl2
SELECT * INTO Temptbl1 FROM UserInfo CROSS APPLY dbo.SplitData(Hobbies,',')
--select * from Temptbl1
Select * into Temptbl2 from Temptbl1 s,Hobbies h where s.DividedItem=h.Hid
--select * from Temptbl2
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(HName) from Hobbies
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
print #cols
set #query = 'SELECT distinct UId,UName,UAge,Hid, '+#cols +' from ( select
* from Temptbl2 )
x
pivot
(max(DividedItem) for HName in (' + #cols + ') ) p '
select * from Temptbl2
exec(#query)
You are selecting all columns from the temptbl2 and you do not need them. The Hid column is selected there and it is making the rows unique and the pivot fails to aggregate the results correctly. Just change it to this:
set #query = 'SELECT distinct UId,UName,UAge, '+#cols +' from ( select UId,UName,UAge, DividedItem, HName
from Temptbl2 )
x
pivot
(max(DividedItem) for HName in (' + #cols + ') ) p '
And you will be OK.
Here is full working example on SQL Server 2016 SP1. I am using string_split to split the data and temporary table to store it. Also, change a little bit the join syntax (your is obsolete and should not be used). You can adapt the code below to work on your environment easily:
DECLARE #hobbies TABLE
(
[HiD] INT
,[HName] VARCHAR(12)
);
INSERT INTO #hobbies ([HiD], [HName])
VALUES (1, 'Reading')
,(2, 'Singing')
,(3, 'Dancing');
DECLARE #UserInfo TABLE
(
[UID] INT
,[UName] VARCHAR(12)
,[UAddress] VARCHAR(12)
,[UAge] TINYINT
,[Hobbies] VARCHAR(12)
)
INSERT INTO #UserInfo ([UID], [UName], [UAddress], [UAge], [Hobbies])
VALUES (1, 'Abc', 'addr1', 25, '2,3')
,(2, 'Def', 'addr2', 27, '1,2,3')
,(3, 'Ghi', 'addr3', 20, '1');
DROP TABLE IF EXISTS #TEST;
SELECT *
INTO #TEST
FROM #UserInfo UI
CROSS APPLY string_split(UI.[Hobbies], ',') HS
INNER JOIN #hobbies H
ON HS.[value] = h.[HiD]
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
Select #cols = STUFF((SELECT distinct ',' + QUOTENAME(HName) from #hobbies
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
set #query = 'SELECT distinct UId,UName,UAge, '+#cols +' from ( select uid, uname, uage, hname, value
from #TEST )
x
pivot
(max(value) for HName in (' + #cols + ') ) p '
exec(#query)

t-sql Pivot on all values

I have a table with around 10 rows. I want to pivot on all values in one column to a one-row multi column result. It looks as though there is no way to get around the "For ContactTypeID in ([1],[2])" syntax.
ContactTypeID int
ContactType varchar(20)
Sample data:
1 Customer
2 Vendor
...
5 BillTo
I want to return a single row with
Customer Vendor BillTo, etc
1 2 5
But like I said, I don't want to have to specify each ContactTypeID by number. Is there way to specify "for all"?
Thank you.
You need a dynamic pivot.
Here's the code, for your reference. Hope it helps.
CREATE TABLE tablename (ContactTypeID int, ContactType varchar(20));
INSERT INTO tablename VALUES (1, 'Customer'), (2, 'Vendor'), (5, 'BillTo');
DECLARE #cols NVARCHAR (MAX);
SELECT #cols = COALESCE (#cols + ',[' + ContactType + ']',
'[' + ContactType + ']')
FROM (SELECT DISTINCT [ContactType] FROM tablename) PV
ORDER BY [ContactType]
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT * FROM
(
SELECT * FROM tablename
) x
PIVOT
(
MIN(ContactTypeID)
FOR [ContactType] IN (' + #cols + ')
) p
'
EXEC SP_EXECUTESQL #query;

How to sort or order date in dynamic Pivot Sql Server 2008

I have the following table
The table after pivoting is as follows. How do I maintain the date order in pivoted columns?
Here is how you can do it dynamically.
CREATE TABLE TEST
(
NAME VARCHAR(10),
DATECOL DATE,
VALUECol INT
)
INSERT INTO TEST
VALUES
('A', '01/JAN/2014', 10),
('B', '01/JAN/2014', 20),
('A', '26/JAN/2014', 20),
('B', '26/JAN/2014', 30),
('A', '01/FEB/2014', 40),
('B', '01/FEB/2014', 50),
('A', '26/FEB/2014', 60),
('B', '26/FEB/2014', 70)
DECLARE #colsPivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #colsPivot = STUFF((SELECT distinct ',' + QUOTENAME(DATECOL)
from TEST
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select *
from
(
select NAME,DATECOL,VALUECOL
from TEST
) x1
pivot
(
max(VALUECOL)
for DATECOL in ('+ #colspivot +')
) p'
exec(#query)
Check this on SQL Fiddle
I got my own way to resolve this problem
CREATE TABLE #TEMP (Name varchar(10), [DATE] datetime, Value int)
INSERT #TEMP VALUES
('A','01/JAN/2014',10),
('B','01/JAN/2014',20),
('A','26/JAN/2014',20),
('B','26/JAN/2014',30),
('A','01/FEB/2014',40),
('B','01/FEB/2014',50),
('A','26/FEB/2014',60),
('B','26/FEB/2014',70)
Now we assign date in the format DD/MMM/YYYY to the variable
(Note: The [DATE] column should be of the type datetime/date)
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + REPLACE(CONVERT(NVARCHAR, [DATE], 106), ' ', '/') + ']',
'[' + REPLACE(CONVERT(NVARCHAR, [DATE], 106), ' ', '/') + ']')
FROM (SELECT DISTINCT [DATE] FROM #TEMP) PV
ORDER BY [DATE]
After pivot the columns will be in perfect Date order with the format DD/MMM/YYYY
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT * FROM
(
SELECT Name,REPLACE(CONVERT(NVARCHAR, [DATE], 106), '' '', ''/'') [DATE] , value FROM #TEMP
) x
PIVOT
(
SUM(value)
FOR [DATE] IN (' + #cols + ')
) p;'
EXEC SP_EXECUTESQL #query
Like this you can convert to any date formats and can maintain the order of date in pivoted columns
by editing the conversion type in SELECT #cols and inner SELECT of Pivot statement

Resources