sql pivot with rows join - sql

is theres a way to join the rows based on Id and LevelReferenceNo in sql pivot function?
below is the query have been tried
declare #col nvarchar(max)
declare #squery nvarchar(max)
set #col = (select string_agg(quotename(LevelNumber), ',') from (select distinct LevelNumber from OrgLevelTable) e);
set #squery = N'select p.Id, p.LevelReferenceNo,'+ #col + ' from dbo.OrgLevelTable
PIVOT(max(LevelName) for LevelNumber IN('+#col+')) as p';
exec(#squery)
my purpose on this, why I did not use a different table on each org. levels is to handle dynamic org. levels incrementing or decrementing E.g (3 org levels become 5 or more on the upcoming months or 5 become 4 and so on)

Related

Finding duplicate rows in table with many columns

I have a table with 122 columns and ~200K rows. There are duplicate rows in this table. What query can I use to return these duplicate rows? Normally I would GROUP BY all rows and COUNT, but with 122 columns, this becomes unwieldy.
Basically, I'm looking for a query that does this:
SELECT *, COUNT(*) AS NoOfOccurrences
FROM TableName
GROUP BY *
HAVING COUNT(*) > 1
If you are using SSMS you can right-click on the table and pick "Select Top 1000 rows..." - SSMS will generate the select query for you - then all you have to do is add GROUP BY then copy the column list and paste it after that. Add the HAVING COUNT(*) > 1 and the COUNT(*) AS NoOfOccurrences and run.
I suggest that you include an ORDER BY clause as well so that the duplicated rows are displayed together in your results.
If you are not using SSMS then you can run this dynamic SQL
-- Get the list of columns
-- There are loads of ways of doing this
declare #colList nvarchar(MAX) = '';
select #colList = #colList + c.[name] +','
from sys.columns c
join sys.tables t on c.object_id =t.object_id
where t.[name] = 'tblFees';
-- remove the trailing comma
set #colList = LEFT(#colList,LEN(#colList)-1);
-- generate dynamic SQL
declare #sql1 nvarchar(max) = 'SELECT *, COUNT(*) AS NoOfOccurrences FROM TableName GROUP BY '
declare #sql2 nvarchar(max) = ' HAVING COUNT(*) > 1'
declare #sql nvarchar(max) = CONCAT(#sql1,#colList, #sql2)
--print #sql
-- run the SQL
exec sp_executesql #sql
For other ways of generating comma separated lists see Converting row values in a table to a single concatenated string

Syntax Error when trying to use Dynamic Pivot Query

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)

How to keep column name after sum aggregation in SQL query

I am querying SQL Server where I have multiple columns with sum aggregation. I'm wondering if there is a way to keep column name after doing sum over each of them.
I want to avoid using aliases immediately after the selected column as their numbers and names change over time.
Example of my result:
My query:
DECLARE #SQLQUERY AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
DECLARE #SumColumns as NVARCHAR(MAX)
SELECT #PivotColumns = COALESCE(#PivotColumns + ',','') + QUOTENAME(description)
FROM [example-base].dbo.table
SELECT #SumColumns = 'sum('+Replace(#PivotColumns,',','),sum(')+')'
SET #SQLQUERY = N'SELECT TOP(100) Weeks, Years, Code,'+#SumColumns+ '
FROM [example-base].dbo.table
group by Weeks,Years,Code'
EXECUTE sp_executesql #SQLQUERY
Is there a way to keep column name on which is done sum?
Be aware of the potential performance issues by building a query with text. Moreover, your result may be not predictable while the number of columns can evolve between two executions.
To add an alias to your columns, you may use this statement :
DECLARE #SumColumns AS nvarchar(MAX);
SELECT #SumColumns = CONCAT(COALESCE(#PivotColumns + ',', ''), 'SUM(', QUOTENAME(description), ') AS ', QUOTENAME(description))
FROM [example-base].dbo.table
...a union..?
SET #SQLQUERY = N'
SELECT TOP(0) Weeks, Years, Code,'+#PivotColumns+ '
FROM [example-base].dbo.table
UNION ALL
SELECT TOP(100) Weeks, Years, Code,'+#SumColumns+ '
FROM [example-base].dbo.table
group by Weeks,Years,Code'

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.

Getting access to a dynamic table from dynamic sql

Good day StackOverflow
The table that I create from my dynamic sql can have any number of columns as it is a pivot table.
-- Pivot the table so we get the UW as columns rather than rows
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = '
SELECT *
FROM #PreProcessed
PIVOT (SUM(Quotes)
FOR [UW] IN (' + #UWColumns + ')
) AS bob'
I run this code to run my dynamic sql.
EXEC sp_executesql #SQL,
N'#UWColumns nvarchar(MAX)',
#UWColumns
My question is, how do I store the resulting table? Especially when I don't know how many columns it will have or even what the columns will be called?
I tried the code below but it doesn't work
INSERT INTO #Temp
EXEC sp_executesql #SQL,
N'#UWColumns nvarchar(MAX)',
#UWColumns
Thanks everyone
SQL Server uses SELECT * INTO ...., as opposed to the CREATE TABLE AS syntax. So you'll need to modify your dynamic sql to:
SELECT * INTO <YOUR TABLE>
FROM #PreProcessed
PIVOT (SUM(Quotes)
FOR [UW] IN (' + #UWColumns + ')
) AS bob'
The only way I could find around this problem was to do all of my calculations in the dynamic sql. Which meant I had to work on two tables.
-- Pivot the table so we get the UW as columns rather than rows
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = '
SELECT * INTO #temp
FROM #PreProcessed
PIVOT (SUM(Quotes)
FOR [UW] IN (' + #UWColumns + ')
) AS bob
SELECT DISTINCT t1.Date, d.Declines AS ''Declines'' , '+#UWColumns+'
FROM #temp AS t1 LEFT OUTER JOIN
#Declines AS d ON t1.DATE = d.DATE
'
PRINT #SQL
EXEC(#SQL)