how to rotate a table-valued in SQL Server - 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

Related

SQL Server Dynamic Column and its counter

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.

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;

Dynamic sql generation

I need to generate a dynamic sql in below specified format where my table is a parameter i.e, Number of columns is not static
For example, below may be the table schema
ID Name
1 asd
2 xyz
I need a query which generates the select statement as below
select 'ID :' + ID + ',Name :'+Name from table
The output from generated above sql will be like this
ID : 1, Name:asd
ID : 2, Name:xyz
If the table has more number of columns, select statement that needs to be changes varies as below
select 'ID :' + ID + ',Name :'+Name + ',Col3 :' + Col3 ...from table
Could someone help me regarding this
Thanks,
Sree
Here is one option which uses a little XML and string manipulation
I should add, NULL values will be excluded.
Example
Declare #YourTable Table ([ID] varchar(50),[Name] varchar(50))
Insert Into #YourTable Values
(1,'asd')
,(2,'xyz')
Select stuff(
replace(
replace(
replace(
replace(
(Select * from #YourTable for XML RAW)
,'<row ',',')
,'="',':')
,'" ',',')
,'"/>','')
,1,1,'')
Returns
(No column name)
ID:1,Name:asd,ID:2,Name:xyz
Use the information schema views. They contain all the information you need to generate your dynamic sql. The rest is just simple SQL and patience.
I am able to achieve this using below sql
DECLARE #TableName VARCHAR(MAX) = 'tableName'
DECLARE #SQL VARCHAR(MAX) = 'SELECT ''{''+'''
SELECT #SQL = #SQL + '
"'+COLUMN_NAME+'":"''' + '+coalesce(CAST('+COLUMN_NAME+' AS VARCHAR(MAX)),'''')+''",' FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #TableName
SET #SQL = LEFT(#SQL,LEN(#SQL)-1) + '
}'' FROM ' + #TableName
PRINT #SQL
Thanks,
Sree

Concatenate columns of multiple columns and multiple rows into one varchar value, when no of columns is dynamic

So i have this dynamic query that returns a result set having dynamic number of columns like so:
In this result set we have columns for ID, FacilityName and cycleNum will always be there but number of task columns can vary Task1, Task2, Task3..... upto Taskn.
The final result set I need is as follows:
For this i have tried this query:
Select distinct FacilityName,
substring(
(
Select ',' + a
From (SELECT ID, FacilityName, 'Cycle-'+ cast(CycleNum as varchar)+'::' + 'Task1~' + cast(Task1 as varchar) + ',Task2~' + cast(Task2 as varchar) + ',Task3~' + cast(Task3 as varchar) + ';' as a FROM #tempTable) ST1
Where ST1.FacilityName = ST2.FacilityName
ORDER BY ST1.FacilityName
For XML PATH ('')
), 2, 1000) CycleData
From (SELECT ID, FacilityName, 'Cycle-'+ cast(CycleNum as varchar)+'::' + 'Task1~' + cast(Task1 as varchar) + ',Task2~' + cast(Task2 as varchar) + ',Task3~' + cast(Task3 as varchar)+ ';' as a FROM #tempTable) ST2
This will work with following test data:
create table #tempTable
(
ID int,
FacilityName varchar(50),
CycleNum int,
Task1 datetime,
Task2 datetime,
Task3 datetime
)
Insert into #tempTable values
(1, 'A', 1, convert(varchar(10), getdate(), 126), convert(varchar(10), dateadd(day,1,getdate()), 126), convert(varchar(10), dateadd(day,2,getdate()), 126)),
(2, 'A', 2, convert(varchar(10), getdate(), 126), convert(varchar(10), dateadd(day,1,getdate()), 126), convert(varchar(10), dateadd(day,2,getdate()), 126)),
(3, 'B', 1, convert(varchar(10), getdate(), 126), convert(varchar(10), dateadd(day,1,getdate()), 126), convert(varchar(10), dateadd(day,2,getdate()), 126)),
(4, 'B', 2, convert(varchar(10), getdate(), 126), convert(varchar(10), dateadd(day,1,getdate()), 126), convert(varchar(10), dateadd(day,2,getdate()), 126))
But I cant think of any way to extend this to use dynamic columns. The list of all columns is saved in a master table so we can get comma-separated list of columns from there if needed.
Here is a dynamic way
Slightly different approach from your static query. To make it dynamic, Use while loop or CURSOR to generate the Task1 + Task2 + ..TaskN. Then use it in Select query.
DECLARE #columns VARCHAR(50)='Task1,Task2,Task3', -- Pass the list of column names
#int INT = 1,
#sql VARCHAR(8000)
SET #sql = ' ;WITH cte
AS (SELECT *,
''Cycle-'' + Cast(CycleNum AS VARCHAR(10)) + ''::'' '
WHILE #int <= Len(#columns) - Len(Replace(#columns, ',', '')) -- To find the number of Tasks in list
BEGIN
SET #sql += + '+''Task' + Cast(#int AS VARCHAR(10))
+ '~''+' + 'Cast(Task'
+ Cast(#int AS VARCHAR(10))
+ ' AS VARCHAR(50)) + '','''
SET #int += 1
END
SET #sql += ' AS concat_dates
FROM #tempTable)
SELECT DISTINCT FacilityName,
LEFT(CycleData, Len(CycleData) - 1)
FROM cte a
CROSS apply(SELECT b.concat_dates + '',''
FROM cte b
WHERE a.FacilityName = b.FacilityName
FOR xml path('''')) cs (CycleData)
'
--print #sql -- uncomment it to debug if you have any error when executing dynamic code
EXEC (#sql)
Not to worry about the usage of While Loop/CURSOR since we are not doing any resource intensive operations inside the loop.
Static Query will looking like this
;WITH cte
AS (SELECT *,
'Cycle-' + Cast(CycleNum AS VARCHAR(10))
+ '::' + 'Task1~' + Cast(Task1 AS VARCHAR(50))
+ ',' + 'Task2~' + Cast(Task2 AS VARCHAR(50))
+ ',' AS concat_dates
FROM #tempTable)
SELECT DISTINCT FacilityName,
LEFT(CycleData, Len(CycleData) - 1)
FROM cte a
CROSS apply(SELECT b.concat_dates + ','
FROM cte b
WHERE a.FacilityName = b.FacilityName
FOR xml path('')) cs (CycleData)
Use system tables. I prefer short and effective solutions :). This is guide, I'm sure you can finish it yourself with concentating strings (if you are not sure, add comment and I will add whole code or more description).
declare #columns varchar(max)
SELECT #columns = isnull(#columns + '+', '') + '''' + COLUMN_NAME + '~'' + Cast(' + COLUMN_NAME + ' as varchar(50))'
FROM tempdb.INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME like '#tempTable%'
AND c.COLUMN_NAME LIKE 'Task%' -- filter columns you are interested in
declare #sql varchar(4000) = 'select *, ' + #columns + ' from #tempTable'
print #sql
exec (#sql)

SQL Server: What's wrong with this dynamic pivot?

I am trying to create a dynamic pivot but it keeps telling me "you must declare the table variable #venta".
Here is the code, what am I doing wrong?
DECLARE #venta TABLE
(
LAPSO_DOC CHAR(6),
ID_EXT_ITM CHAR(3),
ID_TALLA CHAR(6),
VENTA INT
)
INSERT INTO #venta
VALUES
('201601', 'VER', 'L', '20'),
('201603', 'ROJ', 'XL', '40'),
('201604', 'NEG', 'S', '60'),
('201608', 'BLA', 'M', '80');
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SELECT #columns=STUFF ((SELECT DISTINCT'],['+ LAPSO_DOC
FROM #venta
GROUP BY '],['+LAPSO_DOC FOR XML PATH('') ),1,2,'')+']'
SET #sql = 'SELECT
(
SELECT LAPSO_DOC,ID_EXT_ITM,ID_TALLA,VENTA
FROM #venta
)
PIVOT
(
SUM(CANTIDAD_CAP) FOR LAPSO_DOC IN ('+#columns+')
) AS y;';
EXECUTE (#sql)
It is a scope issue.
The table variable is not available to the dynamic sql
Perhaps drop it down to a temp table
There are more problems with your query than you expect...
CREATE TABLE #venta (
LAPSO_DOC char(6),
ID_EXT_ITM char(3),
ID_TALLA char(6),
VENTA int
)
INSERT INTO #venta
VALUES ('201601', 'VER', 'L', '20'),
('201603', 'ROJ', 'XL', '40'),
('201604', 'NEG', 'S', '60'),
('201608', 'BLA', 'M', '80');
DECLARE #columns nvarchar(max),
#sql nvarchar(max);
SELECT
#columns = STUFF((SELECT DISTINCT
'],[' + LAPSO_DOC
FROM #venta
GROUP BY '],[' + LAPSO_DOC
FOR xml PATH (''))
, 1, 2, '') + ']'
SET #sql = 'SELECT ID_EXT_ITM,ID_TALLA,' + #columns + '
FROM #venta
PIVOT
(
SUM(VENTA) FOR LAPSO_DOC IN (' + #columns + ')
) AS y;';
EXEC (#sql)
DROP TABLE #venta
As #John Cappelletti stated, you can't use table variable in a dynamic SQL; unless you have a Table type defined in database or move to a Temp table.
There is error in your logic. I'm quite not sure what you are trying to achieve, but the above code gives you error-free output.
You don't have CANTIDAD_CAP column anywhere defined but used in in SQL.
You are not supposed to SELECT the column LAPSO_DOC
You may better study some online articles on PIVOT - like this and this.
EDIT:
A coding standard
NEVER declare a column name same as table name, and vice versa.

Resources