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

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.

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

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)

Rows to Columns RDLC

i have data in below format. this data is coming through SQL Query.
i want to show it in below format either by query or by rdlc report.
You need to use dynamic SQL to make it.
From your expected result you can try to follow thoes step to make it.
use row_number function make row number by Name, because we need to join base on that row_number.
get the use MAX and MIN to make row number calendar table. from 1 to max(rn). the table can let use outer join
declare a var #tables to make the OUTER JOIN execute SQL (each LEFT JOIN maen a group of Crew#).
declare a var #col to make column, which you want to select (Employee) from each table.
then use execute dynamic execute it.
look like this.
create table T
(
Name varchar(50),
Employee VARCHAR(50)
)
insert into T values ('Crew#1','TR123');
insert into T values ('Crew#1','311');
insert into T values ('Crew#2','DDD');
insert into T values ('Crew#2','12121');
insert into T values ('Crew#1','SDDAS');
insert into T values ('Crew#3','31114312');
insert into T values ('Crew#3','DD14124D');
insert into T values ('Crew#3','1214124121');
insert into T values ('Crew#3','SDD412AS');
DECLARE #tables AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#col AS NVARCHAR(MAX);
SET #tables = STUFF((SELECT distinct ' LEFT JOIN ' + ' (SELECT * FROM CTE WHERE Name = '''+Name+''') '+QUOTENAME(Name)+' on t1.smallRN = '+QUOTENAME(Name)+'.rn'
FROM T
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #col = STUFF((SELECT distinct ', ' + QUOTENAME(Name)+'.Employee as '''+ QUOTENAME(Name) +''''
FROM T
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #col = substring(#col,1, len(#col))
set #query = '
WITH CTE AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) rn
FROM T
),CTE1 AS(
SELECT MIN(rn) smallRN,MAX(rn) bigRN
FROM CTE
UNION ALL
SELECT smallRN+1,bigRN
FROM CTE1
WHERE smallRN < bigRN
)
SELECT '+#col+'
FROM CTE1 t1 ' + #tables
execute(#query)
sqlfiddle
Creatin tbale
First we will create a temp table where we will stock the data that you have and your table
create table #table1
(
[Crew Name] varchar(500) ,
Employee varchar(500)
)
INsert into #table1
values (....)
select * from #table1
Dynamic selection
then we will create a dynamic query to get the columns that we have, that way we can add as much crews as we want,
declare #DynamicPivotQuery as nvarchar(max)
declare #ColumnName as nvarchar(max)
select #ColumnName = ISNULL(#ColumnName +',','') + QUOTENAME([Crew Name])
from (select distinct [Crew Name] from #table1) as Country
set #DynamicPivotQuery = N'select ' +#ColumnName + '
from #table1
Pivot ( MAX(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
'
exec (#DynamicPivotQuery)
this way we will get only the first row for every column
so we have to find a way to aggregate and get the other columns as well just to demonstrate i will union the Mmin also this is where i stoped my testes but you can do more then this with some testes
now the union :
declare #DynamicPivotQuery as nvarchar(max)
declare #ColumnName as nvarchar(max)
select #ColumnName = ISNULL(#ColumnName +',','') + QUOTENAME([Crew Name])
from (select distinct [Crew Name] from #table1) as Country
set #DynamicPivotQuery = N'select ' +#ColumnName + '
from #table1
Pivot ( MAX(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
union
select ' +#ColumnName + '
from #table1
Pivot ( MIN(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
'
exec (#DynamicPivotQuery)
here is the result :
if you follow this way i'm sure that you will find a way to union all the result
You can add this result into a temp table
then add a column which will be a reference into this temp table
then use pivot function
To know more about pivot Visit :
https://msdn.microsoft.com/en-us/azure/data-lake-analytics/u-sql/pivot-and-unpivot-u-sql
you can use also SSIS to a very handy tool and easy to use
Using dynamic PIVOT if you dont have a set Crew columns.
DECLARE #ColumnString VARCHAR(256)
DECLARE #ColumnHeadrer VARCHAR(256)
DECLARE #sql varchar(1000)
CREATE TABLE #ColumnValue
(
Value VARCHAR(500),
ColumnHeader VARCHAR(256)
)
INSERT INTO #ColumnValue (Value, ColumnHeader)
SELECT DISTINCT '[' + CrewName + ']',
'ISNULL(' + CrewName + ','''') AS ' + CrewName
FROM CrewTable
SELECT #ColumnString = COALESCE(#ColumnString + ',', '') + Value,
#ColumnHeadrer = COALESCE(#ColumnHeadrer + ',', '') + ColumnHeader
FROM #ColumnValue
SET #sql =
'
SELECT ' + #ColumnHeadrer + '
FROM
(
SELECT Employee,
CrewName,
ROW_NUMBER() OVER(PARTITION BY CrewName ORDER BY CrewName) AS rnk
FROM CrewTable
) AS P
PIVOT
(
MAX(Employee) FOR [CrewName] IN ('+#ColumnString+')
) AS pv
'
EXEC (#sql)

SQL Unpivot, Cross Apply, Dynamic Query?

I'm at crossroads. Can somebody please help me... send me down the right path.
I want to compare / present data from 2 database tables as follows:
Application Database: Many tables have triggers that copy update/delete changes (auditing) to another database.
Audit Database: The information copied from the triggers in the application database
What I want to do should be fairly straightforward. Visually below, is what I want to do to compare data for what changes were made.
I have a working version with CROSS APPLY and UNIONS (it's long and manually typed out for the columns, tables, etc. sucks). The columns are NOT dynamic which makes hundreds of lines of code gross and unmanageable. There has to be a more elegant design. Please any ideas.
I only need to return ONE specific row (ID) from both tables, for comparison.
APP DB
colA colB colC colD
1 hello foo date
APP Audit DB
colA colB colC colD
1 hi bar date
THIS IS WHAT IS WISH TO OUTPUT:
colA_data ColumnName oldData newData
1 colB hi hello
1 colC bar foo
1 colD date date
I hope I have made sense of what I want to accomplish.
I would like to read column names dynamic (not hard), and then put the results side by side like about for reporting reasons. Obviously matching the columns and putting them into rows.
Sample code would be so much appreciated.
Probably the easiest thing to do is using UNPIVOT:
1. Static version
Just to introduce the use of UNPIVOT here is a simple static version that should solve your problem:
declare #appDB table( [colA] int, [colB] nvarchar(50),[colC] nvarchar(50),[colD] nvarchar(50))
declare #auditDB table( [colA] int, [colB] nvarchar(50),[colC] nvarchar(50),[colD] nvarchar(50))
insert into #appDB select 1,'hello', 'foo', 'date'
insert into #auditDB select 1,'hi', 'bar', 'date'
select old.ColA_data, old.ColumnName, old.OldData, new.NewData
from(
select o.colA as ColA_data, o.ColumnName, o.OldData
from #auditDB s
unpivot ([OldData] for [ColumnName] in ([colB], [colC], [colD])) o
) OLD
inner join
(
select n.colA as ColA_data, n.ColumnName, n.NewData
from #appDB t
unpivot ([NewData] for [ColumnName] in ([colB], [colC], [colD])) n
) NEW
on new.ColA_data = old.ColA_data and new.ColumnName = old.ColumnName
Results:
2. Dynamic version
Now the complete version. You can use dynamic SQL to change the columns retrieving them from SQL Server INFORMATION_SCHEMA metadata.
Please note that in this example I added a new column (ColE)
if OBJECT_ID('appDB') is not null drop table appDB
if OBJECT_ID('auditDB') is not null drop table auditDB
create table appDB (colA int, colB nvarchar(50),colC nvarchar(50),colD nvarchar(50),colE nvarchar(50))
create table auditDB(colA int, colB nvarchar(50),colC nvarchar(50),colD nvarchar(50),colE nvarchar(50))
insert into appDB select 1,'hello', 'foo', 'date','time'
insert into auditDB select 1,'hi', 'bar', 'date','time'
declare #cols nvarchar(max)='' --this variable holds all the dates that will become column names
declare #sql nvarchar(max)='' --this variable contains the TSQL dinamically generated
select #cols = #cols + ', [' +COLUMN_NAME + ']'
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME='appDB'
and COLUMN_NAME <>'colA'
set #cols = RIGHT(#cols, len(#cols)-2)
set #sql= #sql + ' select old.ColA_data, old.ColumnName, old.OldData, new.NewData '
set #sql= #sql + ' from('
set #sql= #sql + ' select o.colA as ColA_data, o.ColumnName, o.OldData'
set #sql= #sql + ' from auditDB s '
set #sql= #sql + ' unpivot ([OldData] for [ColumnName] in ('+#cols+')) o'
set #sql= #sql + ' ) OLD'
set #sql= #sql + ' inner join'
set #sql= #sql + ' ('
set #sql= #sql + ' select n.colA as ColA_data, n.ColumnName, n.NewData'
set #sql= #sql + ' from appDB t '
set #sql= #sql + ' unpivot ([NewData] for [ColumnName] in ('+#cols+')) n'
set #sql= #sql + ' ) NEW'
set #sql= #sql + ' on new.ColA_data = old.ColA_data and new.ColumnName = old.ColumnName'
exec(#sql)
Results:

Create table on SQL Server from dynamic pivot results

Is there a way to directly store the results of a dynamic pivot query into a fixed table? As the result is dynamic I can't create the table by specifying the columnnames and methods like "create table MyTable as (pivot select statement)" seem to fail on SQL server ("Incorrect syntax near the keyword 'AS'").
I have tried to format the SQL below to get a SELECT - INTO - FROM structure but failed to do so. Any help is obviously greatly appreciated!
The SQL used for the pivot is (build thanks to this great website!):
declare #pivot varchar(max), #sql varchar(max)
create table pivot_columns (pivot_column varchar(100))
insert into pivot_columns
select distinct DateField from MyTable order by 1
select #pivot=coalesce(#pivot+',','')+'['+pivot_column+']'from pivot_columns
set #sql = 'SELECT * FROM (select DateField, RefCode, SumField from MyTable) p
PIVOT
(sum(SumField) FOR DateField IN ( ' + #pivot + ') )
AS pvl'
drop table pivot_columns
exec (#sql)
Unless I am not following what you are trying to do you should be able to add the INTO mynewTable to your sql that you are going to execute and you should get the new table.
declare #pivot varchar(max), #sql varchar(max)
create table pivot_columns (pivot_column varchar(100))
insert into pivot_columns
select distinct DateField from MyTable order by 1
select #pivot=coalesce(#pivot+',','')+'['+pivot_column+']'from pivot_columns
set #sql = 'SELECT * INTO mynewTable FROM (select DateField, RefCode, SumField from MyTable) p
PIVOT
(sum(SumField) FOR DateField IN ( ' + #pivot + ') )
AS pvl'
drop table pivot_columns
exec (#sql)
I just test creating a new table in the following script and it gives me a new table that is in the DB for use:
create table t
(
[month] int,
[id] nvarchar(20),
[cnt] int
)
insert t values (4,'TOTAL',214)
insert t values (5,'TOTAL',23)
insert t values (6,'TOTAL',23)
insert t values (4,'FUNC',47)
insert t values (5,'FUNC',5)
insert t values (6,'FUNC',5)
insert t values (4,'INDIL',167)
insert t values (5,'INDIL',18)
insert t values (6,'INDIL',18)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(month)
FROM t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT *
INTO tabletest from
(
select month, id, cnt
from t
) x
pivot
(
sum(cnt)
for month in (' + #cols + ')
) p '
execute(#query)
drop table t

Resources