Syntax Error when trying to use Dynamic Pivot Query - sql

I've been playing around with the PIVOT function for a while. I have a table that looks like this
IdPersona IdEstadoSocio Periodo
-------------------------------
1044659 6 2021-06
721396 5 2021-06
219886 6 2021-06
1906611 7 2021-06
1027906 2 2021-06
Every ClientID is repeated once for each month. Every month new rows are added to the table with a new period associated to the row. It's an incrementing table.
What I need to do is to PIVOT the table, so that it basically ends up like this:
IdPersona 2021-01 2021-02 2021-03 2021-04
----------------------------------------
1044659 6 3 1 4
721396 5 5 2 6
219886 6 6 4 1
1906611 7 7 9 2
1027906 2 1 1 1
Naturally, I want my code to be dynamic. I don't want to harcode every month like:
SELECT *
FROM [fid].[FACT_Socios_Resumen_Periodo]
PIVOT(MAX(IdEstadoSocio)
FOR [Periodo] IN ([2021-01], [2021-02], [2021-03], [2021-04], [2021-05], [2021-06])) AS PVTTable
So I've been trying several dynamic solutions found in here, but none of them are working for me, and I don't know why. And it's driving me crazy.
This solutions gives me
Incorrect Syntax near 'FOR'
My code below:
DECLARE #cols AS NVARCHAR(MAX),#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(periodo)
FROM [fid].[FACT_Socios_Resumen_Periodo]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT IdPersona, ' + #cols + ' from
(
select IdPersona
, IdEstadoSocio
, periodo
from [fid].[FACT_Socios_Resumen_Periodo]
) x
pivot
(
max(IdEstadoSocio)
for periodo in (' + #cols + ')
) p '
execute(#query)
The second solution provided in the same link gives me
A variable that has been assigned in a SELECT statement cannot be
included in a expression or assignment when used in conjunction with a
from clause.
Which is kinda understandable, as the query tries to solve getting the DISTINCT values of [Period] in a recursive way, as far as I understood. However, everybody accepted that as a viable answer too.
My code below:
DECLARE #cols AS NVARCHAR(MAX)='';
DECLARE #query AS NVARCHAR(MAX)='';
SELECT #cols = #cols + QUOTENAME(periodo) + ',' FROM (select distinct periodo from [fid].[FACT_Socios_Resumen_Periodo] ) as tmp
select #cols = substring(#cols, 0, len(#cols)) --trim "," at end
set #query =
'SELECT * from
(
select idpersona, idestadosocio, periodo from [fid].[FACT_Socios_Resumen_Periodo]
) src
pivot
(
max(idestadosocio) for periodo in (' + #cols + ')
) piv'
execute(#query)
The third solution I tried gives me the same error noted above, with a similar syntax but not exactly the same. It tries to solve the DISTINCT [Period]s recursively.
My code below:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName = ISNULL(#ColumnName + ',', '') + QUOTENAME(periodo)
FROM (SELECT DISTINCT periodo FROM [fid].[FACT_Socios_Resumen_Periodo]) AS Periodos
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT IdPersona, ' + #ColumnName + '
FROM [fid].[FACT_Socios_Resumen_Periodo]
PIVOT(MAX(IdEstadoSocio)
FOR periodo IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
So, what exactly am I doing wrong?? Can anybody throw me a hint? I'm failing over solutions approved by the whole community, and I can't understand why. I basically tried to understand the code, copied it, and replaced columns and tables. Just that. I didn't change anything more. Thanks in advance!

Looking at your third solution.
First thing I notice is that you are not coalescing the initial null value of #ColumnName when you do your concatenation.
SELECT #ColumnName = Isnull(#ColumnName + ',', '') + QUOTENAME(periodo)
FROM (SELECT DISTINCT periodo FROM [fid].[FACT_Socios_Resumen_Periodo]) AS Periodos
That should solve your problem.
If you would PRINT the result #ColumnName and #DynamicPivotQuery, before you execute it, it will usually show you where the problems are.
For versions of sql that do not support SELECT #Variable
SQL Azure and SQL DW only support using SET when setting variable values.
You can use STRING_AGG() on these servers
set #ColumnName = (select string_agg(QUOTENAME(periodo),',') from (select distinct periodo from FACT_Socios_Resumen_Periodo) t)

Related

MS SQL Pivot dat from long with multiple columns

I want to pivot a table from long to wide that has multiple id columns.
I found solutions for one column but not really for multiple columns.
The closest solution that I could adapt for one column was this one
T-SQL PIVOT data from long form to wide by a date
My table looks more or less like this,
create table t (id int, date date, varA_id int, VarB_id int, value int)
insert into t values
(1,'2005-01-20',1, 1,197)
,(2,'2005-01-20',1,2,58)
,(3,'2005-01-20',1,3,90)
,(4,'2005-01-20',2,1,210)
,(5,'2005-01-20',2,2,133)
,(6,'2005-01-20',2,3,67)
,(7,'2005-01-20',3,1,87)
,(8,'2005-01-20',3,2,87)
,(9,'2005-01-20',3,3,87)
Actually without the date, but that's fine. I want to spread in a way that I get columns for each permutation of VarA_id and VarB_id
So my expected result would look like this
My actual table has three _id columns and more permutations, so I really need a generic solution.
Based on the other solution in my link I was hoping something like this would work. I adjust the top part that creates the column names and this would work. I dont know how to realy adjust the bottom part that fetches the values.
declare #cols nvarchar(max);
declare #sql nvarchar(max);
select #cols = stuff((
select distinct
', ' + 'VarA_'+convert(varchar(10),varA_id) + '_VarB_'+convert(varchar(10),varB_id)
from t
order by 1
for xml path (''), type).value('.','nvarchar(max)')
,1,2,'')
select #sql = '
select Id, date, ' + #cols + '
from (
select Id, date, varA_id = ''v''+convert(varchar(10),varA_id), value
from t
) as t
pivot (sum([value]) for [varA_id] in (' + #cols + ') ) p'
select #sql
exec(#sql);
The main problem with your dynamic sql?
It was that the name constructed in the source query didn't match the generated column names.
Here's a fix :
declare #cols varchar(max) = null;
declare #sql nvarchar(max);
select #cols = concat(#cols+', '+char(10), quotename(concat('VarA_', varA_id, '_VarB_', varB_id)))
from test
group by varA_id, varB_id
order by varA_id, varB_id;
-- select #cols as cols;
set #sql = 'select * '+char(10)+
'from ( ' +char(10)+
' select [date], [value], ' +char(10)+
' concat(''VarA_'',varA_id,''_VarB_'',varB_id) as Col ' +char(10)+
' from test ' +char(10)+
') as src ' +char(10)+
'pivot (sum([value]) for Col in ('+char(10)+ #cols +char(10)+')) pvt';
-- select #sql as sql;
exec(#sql);
date
VarA_1_VarB_1
VarA_1_VarB_2
VarA_1_VarB_3
VarA_2_VarB_1
VarA_2_VarB_2
VarA_2_VarB_3
VarA_3_VarB_1
VarA_3_VarB_2
VarA_3_VarB_3
2005-01-20
197
58
90
210
133
67
87
87
87
db<>fiddle here
Ok my own solution so far is to add a help column and basically just do what the other questions does. I need to improve on this, so I dont add a column and I would like better names, but at least this shows what I want.
alter table t add help_col nvarchar(10)
Update t
set help_col=convert(varchar(10),varA_id)+convert(varchar(10),varB_id)
declare #cols nvarchar(max);
declare #sql nvarchar(max);
select #cols = stuff((
select distinct
', ' + 'v'+convert(varchar(10),help_col)
from t
order by 1
for xml path (''), type).value('.','nvarchar(max)')
,1,2,'')
select #sql = '
select date, ' + #cols + '
from (
select date, help_col = ''v''+convert(varchar(10),help_col), value
from t
) as t
pivot (sum([value]) for [help_col] in (' + #cols + ') ) p'
select #sql
exec(#sql);
Which results in
select date, v11, v12, v13, v21, v22, v23, v31, v32, v33 from ( select date, help_col = 'v'+convert(varchar(10),help_col), value from t ) as t pivot (sum([value]) for [help_col] in (v11, v12, v13, v21, v22, v23, v31, v32, v33) ) p
which yields
date v11 v12 v13 v21 v22 v23 v31 v32 v33
2005-01-20 197 58 90 210 133 67 87 87 87

T-SQL Invalid length parameter passed to the LEFT or SUBSTRING function

This is my query.
I want to run query but I could not run.
Please help me.
DECLARE #cols AS NVARCHAR(MAX) = '',
#sql AS NVARCHAR(MAX)
SELECT #cols += N'' + QUOTENAME(Kriter) + ', '
FROM (
SELECT DISTINCT Kriter
FROM NetBarakatMalzemeAnalizFormu where MalzemeKodu = 'DEM2020'
) a
SET #cols = LEFT(#cols, LEN(#cols) - 1)
SET #sql = N'SELECT * FROM (
SELECT
Deger,
BalyaId,
Kriter
FROM NetBarakatMalzemeAnalizFormu where MalzemeKodu = ''DEM2020''
) StudentResults
PIVOT (
SUM([Deger])
FOR [Kriter]
IN
(' + #cols + ')
) AS PivotTable'
EXEC Sp_executesql #sql
And this is error screen
Thank you.
As Gordon said, the error is due to #Cols having an empty value. This stems from the fact that your subquery is returning no rows.
Personally, however, I recommend changing to STUFF to remove the leading character (not the trailing) and using a method other than the (can be) unreliable method of #variable += #Variable. Considering you're on SQL server 2017 then you really should be using STRING_AGG:
DECLARE #Cols nvarchar(MAX), --Notice no default value
#SQL nvarchar(MAX);
SELECT #Cols = STRING_AGG(QUOTENAME(Kriter),',') WITHIN GROUP (ORDER BY Kriter)
FROM (SELECT DISTINCT Kriter
FROM NetBarakatMalzemeAnalizFormu
WHERE MalzemeKodu = 'DEM2020') NBMAF;
SET #SQL = N'SELECT * FROM (
SELECT
Deger,
BalyaId,
Kriter
FROM NetBarakatMalzemeAnalizFormu where MalzemeKodu = ''DEM2020''
) StudentResults
PIVOT (
SUM([Deger])
FOR [Kriter]
IN
(' + STUFF(#cols,1,1,N'') + N')
) AS PivotTable;';
EXEC sys.sp_executesql #SQL;
This won't run anything, based on your error, however, won't error. It will, however, work correctly when a dataset is returned.

change column names in a dynamic pivot table result

I have a tsql dynamic pivot table query that works fine although I´m not happy with the column names in the final output.
To make it work, the user has to select up to 20 fruit names from a list of 200 fruit names. Then I build the pivot table so I end up with different column names everytime the select is run.
For example:
First time the column names are: apple, orange and pear
Second time is: .orange, banana, kiwi and apple
My question is: Is there a posibility to have static names, like for example: name of the first column always "col_1", second column "col_2" etc?
The select statement is as follows:
DECLARE #idList varchar(800)
DECLARE #sql nvarchar(max)
SELECT #idList = coalesce(#idList + ', ', '') + '['+ltrim(rtrim(id_producto)) +']'
from gestor_val_pos
group by id_producto order by id_producto
SELECT #sql = 'select * from #correlaciones pivot (max (correl)
for codigo2 in (' + #IDlist + ')) AS pvt order by codigo1;'
exec sp_executeSQL #sql
sure.. just created a new variable to hold the column aliases and Row_Number to get the column number.
DECLARE #idList varchar(800)
DECLARE #idListAlias varchar(800)
DECLARE #sql nvarchar(max)
SELECT
#idList = coalesce(#idList + ', ', '') + '['+ltrim(rtrim(id_producto)) +']',
#idListAlias = coalesce(#idListAlias + ', ', '') + '['+ltrim(rtrim(id_producto)) +'] as col_' + CONVERT(VARCHAR(10), ROW_NUMBER() OVER(ORDER BY id_producto))
from gestor_val_pos
group by id_producto order by id_producto
SELECT #sql = 'select ' + #idListAlias + ' from #correlaciones pivot (max (correl)
for codigo2 in (' + #IDlist + ')) AS pvt order by codigo1;'
exec sp_executeSQL #sql
Yes, but it'll make your query significantly more complex.
You'll need to return a list of the possible column names generated from #IDList and convert that into a SELECT clause more sophisticated than your current SELECT *.
When you've got that, use some SQL string splitting code to convert the #IDList into a table of items with a position parameter. Append AS <whatever> onto the end of any you want and use the FOR XML PATH trick to flatten it back, and you've got a SELECT clause that'll do what you want. But, as I said, your code is now significantly more complicated.
As an aside - I really hope that #idList is either completely impossible for any user input to ever reach or hugely simplified from your real code for this demonstration. Otherwise you've got a big SQL injection hole right there.

Dynamic pivoting SQL Server 2012

I am making attempts to run my first dynamic pivot in SQL Server 2012.
My #temp table that I am using for the dynamic pivoting looks like this.
YearMonth Agreement nr Discount
------------------------------------
201303 123 1
201303 12 0
201304 1 0
I am running this code and it does not work:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName = ISNULL(#ColumnName + ',', '') + QUOTENAME(YearMonth )
FROM (SELECT DISTINCT YearMonth FROM #FINAL) AS Courses
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT [Agreement nr],YearMonth , ' + #ColumnName + '
FROM #FINAL
PIVOT(
COUNT(agreement nr)
FOR YearMonth IN (' + #ColumnName + ') AS PVTTable'
--Execute the Dynamic Pivot Query
EXECUTE #DynamicPivotQuery;
The error message I am getting is
FOR YearMonth IN ([201403]) AS PVTTable' is not a valid identifier.
What am I missing here?
The cause of the error is that you're missing a parenthesis before you alias the Pivot. More than this however your pivot was rather inefficient.
You should select what you need for the source table in your pivot otherwise it could run for a long time and produce a lot of rows with null returns.
The below is fixed and hopefully more efficient:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(YearMonth )
FROM (SELECT DISTINCT YearMonth FROM #FINAL) AS Courses
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT ' + #ColumnName + '
FROM (Select [Agreement nr], YearMonth from #FINAL) src
PIVOT(
COUNT([Agreement nr])
FOR YearMonth IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXECUTE sp_executesql #DynamicPivotQuery;
You forgot to close pivot.
PIVOT(
COUNT(Kundavtalid)
FOR YearMonth IN (' + #ColumnName + ')
) AS PVTTable' -- here you miss pathernesis
You are missing a parenthesis
SET #DynamicPivotQuery =
N'SELECT [Agreement nr],YearMonth , ' + #ColumnName + '
FROM #FINAL
PIVOT(
COUNT([agreement nr])
FOR YearMonth IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query

SQL Server 2008 - Dynamic Pivot on one column, group by another, maintain reference to third

I've just started learning SQL and am struggling with dynamically transposing a three column table correctly. I previously hard coded the query, but now need it to be dynamic as the values in my pivot column may change in the future.
Here's my starting point:
questionid | DebriefingQuestionResults | OperationSessionRecordID
------------------------------------------------------------------
32 | 3 | 8071
34 | 0 | 8071
36 | 1 | 8071
32 | 2 | 8074
34 | 6 | 8074
36 | 5 | 8074
And here's what I want to produce:
OperationSessionRecordID | 32 | 34 | 36
----------------------------------------------
8071 | 3 | 0 | 1
8074 | 2 | 6 | 5
There are only three [questionid] values (32, 34, and 36), at the moment but this may change in the future, hence wanting a dynamic query. There are about 12000 [OperationSessionRecordID] values. All columns are of the type int not null.
Based on this answer I've got this so far, but am not sure how to proceed as it throws the error shown below.
USE training_db
--Test to see if table exists, if so drop ready for creation--
IF OBJECT_ID('TheatreMan.DebriefingQuestionsResultsPivoted','U') IS NOT NULL
DROP TABLE TheatreMan.DebriefingQuestionsResultsPivoted
--Declare query and variable names--
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get Distinct values of the PIVOT column--
SET #ColumnName = STUFF((SELECT DISTINCT ',' + QUOTENAME(c.questionid)
FROM dbo.DebriefingQuestionsResultsTEMP
FOR XML PATH (''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
'SELECT OperationSessionRecordID, ' + #ColumnName + '
(select questionid,
DebriefingQuestionResults
OperationSessionRecordID
FROM dbo.DebriefingQuestionsResultsTEMP)
x
PIVOT (
min(DebriefingQuestionResults)
for questionid in (' + #ColumnName + ')
)
AS PIV'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
When I run this it throws this error, so something's obviously wrong with my #ColumnName variable, but I can't work out what it is.
Msg 4104, Level 16, State 1, Line 9
The multi-part identifier "c.questionid" could not be bound.
Any help would be most appreciated!
SB
This should work:
declare #collist nvarchar(max)
SET #collist = stuff((select distinct ',' + QUOTENAME(questionid)
FROM #t -- your table here
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
declare #q nvarchar(max)
set #q = '
select OperationSessionRecordID, ' + #collist + '
from (
select OperationSessionRecordID, DebriefingQuestionResults, questionid
from (
select *
from #t -- your table here
) as x
) as source
pivot (
sum(DebriefingQuestionResults)
for questionid in (' + #collist + ')
) as pvt
'
exec (#q)
try this, I think you just misspelled variable name.
USE training_db
--Test to see if table exists, if so drop ready for creation--
IF OBJECT_ID('TheatreMan.DebriefingQuestionsResultsPivoted','U') IS NOT NULL
DROP TABLE TheatreMan.DebriefingQuestionsResultsPivoted
--Declare query and variable names--
DECLARE # USE training_db
--Test to see if table exists, if so drop ready for creation--
IF OBJECT_ID('TheatreMan.DebriefingQuestionsResultsPivoted','U') IS NOT NULL
DROP TABLE TheatreMan.DebriefingQuestionsResultsPivoted
--Declare query and variable names--
DECLARE #QuestionPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get Distinct values of the PIVOT column--
SET #ColumnName = STUFF((SELECT DISTINCT ',' + QUOTENAME(c.questionid)
FROM dbo.DebriefingQuestionsResultsTEMP
FOR XML PATH (''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
'SELECT OperationSessionRecordID, ' + #ColumnName + '
(select questionid,
DebriefingQuestionResults
OperationSessionRecordID
FROM dbo.DebriefingQuestionsResultsTEMP
ORDER BY OperationSessionRecordID ASC
)x
PIVOT
(
min(DebriefingQuestionResults)
for questionid in (' + #ColumnName + ')
)
AS PIV'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get Distinct values of the PIVOT column--
SET #ColumnName = STUFF((SELECT DISTINCT ',' + QUOTENAME(c.questionid)
FROM dbo.DebriefingQuestionsResultsTEMP
FOR XML PATH (''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
'SELECT OperationSessionRecordID, ' + #ColumnName + '
(select questionid,
DebriefingQuestionResults
OperationSessionRecordID
FROM dbo.DebriefingQuestionsResultsTEMP)
x
PIVOT (
min(DebriefingQuestionResults)
for questionid in (' + #ColumnName + ')
)
AS PIV'
ORDER BY OperationSessionRecordID ASC
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery