How to use table variable in SQL Pivot dynamic query - sql

Error: Must declare the table variable "#Temp_FormData".
Query:
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
DECLARE #Temp_FormData TABLE
(
Id UNIQUEIDENTIFIER,
FormId UNIQUEIDENTIFIER,
ContactId UNIQUEIDENTIFIER,
FieldName NVARCHAR(256),
FieldValue NVARCHAR(MAX),
Created DATETIME
)
;WITH TBL AS(
SELECT fe.Id, fe.FormId, fe.ContactId, fd.FieldName, fd.FieldValue, fe.Created
FROM FormEntries fe (NOLOCK)
LEFT JOIN FieldData fd (NOLOCK) ON fd.FormId = fe.Id
)
INSERT INTO #Temp_FormData
SELECT * FROM TBL
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(FieldName)
FROM #Temp_FormData
GROUP BY FieldName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = N'SELECT ' + #cols + N' FROM
(
SELECT FieldValue, FieldName
FROM #Temp_FormData /* Error here. Tried enclosing variable in square brackets*/
) x
PIVOT
(
MAX(FieldValue)
FOR FieldName in (' + #cols + N')
) p'
EXEC sp_executesql #query
If I execute select * from #Temp_FormData this is the result:
Id
FormId
ContactId
FieldName
FieldValue
Created
1
100
null
Name
John
2023-01-26
1
100
null
Age
25
2023-01-26
2
200
5001
Name
Peter
2023-01-20
2
200
5001
Age
30
2023-01-20
Expected Output:
Id
FormId
ContactId
Name
Age
Created
1
100
null
John
25
2023-01-26
2
200
5001
Peter
30
2023-01-20

Related

how to convert Row to column dynamically in sql server

Here is my tables structure
Result
id ResultId CategoryId Total Attempted Score
----------------------------------------------------
1 8 1 30 25 20
2 8 2 30 30 19
3 8 3 30 27 21
4 7 1 20 15 10
5 7 2 20 20 15
Category
Id CategoryName
-----------------------
1 General
2 Aptitude
3 Technical
I want data in the below format
For ResultId = 8
Id General Aptitude Technical Total
--------------------------------------------------
8 20 19 21 60
For ResultId = 7
Id General Aptitude Total
-------------------------------------
7 10 15 25
I need a help to fetch the data in above format.
NOTE: The final fetched data contains score from Result table but having a column names from category table and according to CategoryId in Result table. Column name will be dynamic
Tried the below code (just for testing) but didn't work
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME(CategoryName)
FROM ( SELECT DISTINCT CategoryName
FROM [dbo].[Category] c) AS PivotExample
SET #SQLQuery =
N'SELECT DISTINCT ' + #PivotColumns + '
FROM [dbo].[Category] c
PIVOT( SUM(c.Id)
FOR CategoryName IN (' + #PivotColumns + ')) AS P'
EXEC sp_executesql #SQLQuery
My query will give you the expected output. You can check the output in SQL Fiddle
--dynamic with case
DECLARE #Sql NVARCHAR(4000) = NULL
DECLARE #ColumnHeaders NVARCHAR(4000);
SET #ColumnHeaders = STUFF((
SELECT DISTINCT ',' + 'Max(CASE WHEN rn =' + quotename(rn, '''') + ' THEN Score else null end ) as ' + CategoryName + CHAR(10) + CHAR(13)
FROM (
SELECT CategoryName, row_number() OVER (ORDER BY CategoryName) rn
FROM ( SELECT DISTINCT CategoryName FROM Category) t0
) t1
FOR XML PATH('')
,TYPE
).value('.', 'varchar(max)'), 1, 1, '');
--print #ColumnHeaders
SET #sql = N' ;with cte as (
select * , Row_number() Over(Partition by ResultId Order by ResultId ) rn from
Result)
Select ResultId, ' + #ColumnHeaders + ', SUM(Score) Total from cte Group by
ResultId ';
EXECUTE sp_executesql #sql

Create a sparse matrix from observations

I am new to SQL server, I am a R user but R can't be used with my huge dataset (not enough memory).
What I want to do:
I want to create a sparse matrix from a table with only 2 columns, I dont have any value column, it seems easy but I didn't find the right way to do it.
My data:
ID_patient | ID_product
-----------------------
123 A
123 B
111 C
222 A
333 D
333 E
Ouput wanted:
ID_patient | A | B | C | D | E |
----------------------------------------------------
123 1 1
111 1
222 1
333 1 1
I have read that I can use the GROUP BY function or Pivot feature but what I have tried so far failed.
Edit
I don't know all the products, so the right way to do that is by using dynamic pivot ?
You can try something like PIVOT
See demo
Select * from
(select *, copy=Id_product from t)t
pivot
(
count(copy) for ID_product in ([A],[B],[C],[D],[E]))p
If you don't know A, B, C, D, .. before hand then you should go for dynamic pivot
update:
updated dynamic piv demo
declare #cols nvarchar(max)
declare #query nvarchar(max)
select #cols= Stuff((select ','+ quotename( ID_product) from
(select distinct id_product from t) t for xml path ('')),1,1,'')
select #query='Select * from
( select *, copy=Id_product from t ) t
pivot
(count(copy) for ID_product in ( '+#cols+' ))p '
exec(#query)
Try this
IF OBJECT_ID('tempdb..#Temp')IS NOT NULL
DROP TABLE #Temp
DECLARE #temp AS TABLE (ID_patient INT, ID_product varchar(10))
INSERT INTO #temp
SELECT 123,'A' UNION ALL
SELECT 123,'B' UNION ALL
SELECT 111,'C' UNION ALL
SELECT 222,'A' UNION ALL
SELECT 333,'D' UNION ALL
SELECT 333,'E'
SELECT * INTO #Temp
FROM #temp
DECLARE #Sql nvarchar(max),
#Col nvarchar(max),
#Col2 nvarchar(max)
SELECT #Col=STUFF((SELECT DISTINCT ', '+QUOTENAME(ID_product) FROM #Temp FOR XML PATH ('')),1,1,'')
SELECT #Col2=STUFF((SELECT DISTINCT ', '+'ISNULL('+QUOTENAME(ID_product)+','' '') AS '+QUOTENAME(ID_product) FROM #Temp FOR XML PATH ('')),1,1,'')
SET #Sql='
SELECT ID_patient,'+#Col2+'
FROM
(
SELECT *, DENSE_RANK()OVER( PARTITION BY ID_patient ORDER By ID_patient) AS [Val] FROM #Temp
)AS Src
PIVOT
(MAX(Val) FOR ID_product IN ('+#Col+')
)AS PVT '
PRINT #Sql
EXEC (#Sql)
Result
ID_patient A B C D E
------------------------------
111 0 0 1 0 0
123 1 1 0 0 0
222 1 0 0 0 0
333 0 0 0 1 1

Is this possible with SQL Server

I have a table which has events and the day they occurred:
Table 'Events':
Name Day
-----------
A 1
B 2
A 2
B 3
I need output columns to be a date range based on query input with rows being which event happened on the day so:
Desired output:
Day-1 Day-2 Day-3
-----------------
A A -
- B B
If this is possible can anyone give me a sample query that could generate this output based on a date range. There are all sorts of I have no clue how to even approach this issues here like an unknown number of columns.
You can use conditional aggregation:
select max(case when day = 1 then name end) as day_1,
max(case when day = 2 then name end) as day_2,
max(case when day = 3 then name end) as day_3
from t
group by name;
Note: This returns NULL rather than -. I think NULL makes more sense.
Try this one...
Table Script and Sample data
CREATE TABLE [TableName](
[Name] [nvarchar](50) NULL,
[Day] [int] NULL
)
INSERT [TableName] ([Name], [Day]) VALUES (N'A', 1)
INSERT [TableName] ([Name], [Day]) VALUES (N'B', 2)
INSERT [TableName] ([Name], [Day]) VALUES (N'A', 2)
INSERT [TableName] ([Name], [Day]) VALUES (N'B', 3)
Query (dynamic PIVOT)
DECLARE #startDay AS INT;
DECLARE #endDay AS INT;
SET #startDay = 1;
SET #endDay = 3;
DECLARE #cols AS NVARCHAR(max) = Stuff((SELECT DISTINCT ',' + Quotename([day])
FROM TableName
WHERE [Day] >= #startDay AND [Day] <= #endDay
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE #query AS NVARCHAR(max) = 'SELECT '+ #cols +'
FROM (SELECT *,
Dense_rank() OVER (ORDER BY NAME) AS dr
FROM TableName) sq
PIVOT(Max([name])
FOR [day] IN ('+ #cols +') ) pvt ';
EXECUTE(#query)
Output
+--------+---+--------+
| 1 | 2 | 3 |
+--------+---+--------+
| A | A | (null) |
| (null) | B | B |
+--------+---+--------+
Online Demo: http://www.sqlfiddle.com/#!18/c688b/8/0
If you also want to use custom column name, try this...
DECLARE #startDay AS INT;
DECLARE #endDay AS INT;
SET #startDay = 1;
SET #endDay = 3;
DECLARE #cols AS NVARCHAR(max) = Stuff((SELECT DISTINCT ',' + Quotename([day])
FROM TableName
WHERE [Day] >= #startDay AND [Day] <= #endDay
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE #colNames AS NVARCHAR(max) = Stuff((SELECT DISTINCT ',' + Quotename([day]) + ' AS Days' + CONVERT(NVARCHAR(MAX), [day])
FROM TableName
WHERE [Day] >= #startDay AND [Day] <= #endDay
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE #query AS NVARCHAR(max) = 'SELECT '+ #colNames +'
FROM (SELECT *,
Dense_rank() OVER (ORDER BY NAME) AS dr
FROM TableName) sq
PIVOT(Max([name])
FOR [day] IN ('+ #cols +') ) pvt ';
EXECUTE(#query)
Output
+-------+-------+-------+
| Days1 | Days2 | Days3 |
+-------+-------+-------+
| A | A | NULL |
| NULL | B | B |
+-------+-------+-------+
Online Demo: http://www.sqlfiddle.com/#!18/c688b/9/0

SQL Server Pivot on multiple fields

I have searched this website for all possible solutions but still can't find an answer for my Pivot problem.
I have a table with the following data.
Portfolio | Date | TotalLoans | ActiveLoans | TotalBalance
--------------------------------------------------------------------
P1 | 2015-12-31 | 1,000 | 900 | 100,000.00
P1 | 2015-11-30 | 1,100 | 800 | 100,100.00
P1 | 2015-10-31 | 1,200 | 700 | 100,200.00
I am trying to create a pivot with the following output (only where Portfolio = P1)
Field | 2015-12-31 | 2015-11-30 | 2015-10-31 |
-----------------------------------------------------
TotalLoans | 1,000 | 1,100 | 1,200 |
ActiveLoans | 900 | 800 | 700 |
TotalBalance | 100,000 | 100,100 | 100,200 |
Ideally, I am looking for a dynamic pivot, but a static query would do as well and I can try a dynamic query out of that.
You need first to UNPIVOT your table. You can do it using this query:
SELECT Portfolio, [Date], Val, ColType
FROM (SELECT Portfolio,
[Date],
TotalLoans,
ActiveLoans,
TotalBalance
FROM mytable
WHERE Portfolio = 'P1') AS srcUnpivot
UNPIVOT (
Val FOR ColType IN (TotalLoans, ActiveLoans, TotalBalance)) AS unpvt
Output:
Portfolio Date Val ColType
===============================================
P1 2015-12-31 1000 TotalLoans
P1 2015-12-31 900 ActiveLoans
P1 2015-12-31 100000 TotalBalance
P1 2015-11-30 1100 TotalLoans
P1 2015-11-30 800 ActiveLoans
P1 2015-11-30 100100 TotalBalance
P1 2015-10-31 1200 TotalLoans
P1 2015-10-31 700 ActiveLoans
P1 2015-10-31 100200 TotalBalance
Note: All unpivoted fields must be of the same type. The query above assumes a type of int for all fields. If this is not the case then you have to use CAST.
Using the above query you can apply PIVOT:
SELECT Portfolio, ColType, [2015-12-31], [2015-11-30], [2015-10-31]
FROM (
... above query here ...
PIVOT (
MAX(Val) FOR [Date] IN ([2015-12-31], [2015-11-30], [2015-10-31])) AS pvt
This is Giorgos Betsos solution as dynamic SQL. This will deal without the need to write the date values explicitly.
Please: If you like this: Do not mark this solution as accepted, set the acceptance to Giorgos Betsos. There's the hard work! But you may vote on it :-)
CREATE TABLE #tbl(Portfolio VARCHAR(10),[Date] DATE,TotalLoans DECIMAL(10,4),ActiveLoans DECIMAL(10,4),TotalBalance DECIMAL(10,4));
INSERT INTO #tbl VALUES
('P1','20151231',1000,900,100000.00)
,('P1','20151130',1100,800,100100.00)
,('P1','20151031',1200,700,100200.00);
DECLARE #pvtColumns VARCHAR(MAX)=
(
STUFF(
(
SELECT DISTINCT ',['+CONVERT(VARCHAR(10), [Date] ,126) + ']'
FROM #tbl
FOR XML PATH('')
)
,1,1,'')
);
DECLARE #cmd VARCHAR(MAX)=
'SELECT Portfolio, ColType, ' + #pvtColumns +
' FROM (
SELECT Portfolio, [Date], Val, ColType
FROM (SELECT Portfolio,
[Date],
TotalLoans,
CAST(ActiveLoans AS DECIMAL(10,4)) AS ActiveLoans,
TotalBalance
FROM #tbl AS mytable
WHERE Portfolio = ''P1'') AS srcUnpivot
UNPIVOT (
Val FOR ColType IN (TotalLoans, ActiveLoans, TotalBalance)) AS unpvt
) AS srcPivot
PIVOT (
MAX(Val) FOR [Date] IN (' + #pvtColumns + ')) AS pvt';
EXEC (#cmd);
You need to use Dyanmic SQL and construct them. See examples at http://social.technet.microsoft.com/wiki/contents/articles/17510.t-sql-dynamic-pivot-on-multiple-columns.aspx
Here is the code
CREATE procedure CrossTab
(
#select varchar(2000),
#PivotCol varchar(100),
#Summaries varchar(100),
#GroupBy varchar(100),
#OtherCols varchar(100) = Null
)
AS
set nocount on
set ansi_warnings off
declare #sql varchar(8000)
Select #sql = ''
Select #OtherCols= isNull(', ' + #OtherCols,'')
create table #pivot_columns (pivot_column_name varchar(100))
Select #sql='select ''' + replace( + #PivotCol,',',''' as pivot_column_name union all select ''')+''''
insert into #pivot_columns
exec(#sql)
select #sql=''
create table #pivot_columns_data (pivot_column_name varchar(100),pivot_column_data varchar(100))
Select #PivotCol=''
Select #PivotCol=min(pivot_column_name) from #pivot_columns
While #PivotCol>''
Begin
insert into #pivot_columns_data(pivot_column_name,pivot_column_data)
exec
(
'select distinct ''' + #PivotCol +''' as pivot_column_name, convert(varchar(100),' + #PivotCol + ') as pivot_column_data from
('+
#select
+'
) T'
)
Select #PivotCol=min(pivot_column_name) from #pivot_columns where pivot_column_name>#PivotCol
end
select
#sql = #sql + ', ' +
replace(
replace(
#Summaries,'(','(CASE WHEN ' + Pivot_Column_name + '=''' +
pivot_column_data + ''' THEN '
),
')[', ' END) as [' + pivot_column_data
)
from #pivot_columns_data
order by pivot_column_name
exec
(
'select ' + #GroupBy +#OtherCols +#sql +
' from (
'+
#select
+'
) T
GROUP BY ' + #GroupBy
)
drop table #pivot_columns
drop table #pivot_columns_data
set nocount off
set ansi_warnings on
Usage
EXEC CrossTab
'SELECT LastName, OrderDate,shipcountry FROM northwind..Employees Employees
INNER JOIN northwind..Orders Orders ON (Employees.EmployeeID=Orders.EmployeeID) ',
'shipcountry,Year(OrderDate)',
'Count(LastName)[]',
'LastName'
Do it in several steps:
if object_id('tempdb..#Data') is null
CREATE TABLE #Data
([Portfolio] varchar(2), [Date] datetime,
[TotalLoans] decimal(9,2), [ActiveLoans] int, [TotalBalance] decimal(9,2))
;
INSERT INTO #Data
([Portfolio], [Date], [TotalLoans], [ActiveLoans], [TotalBalance])
VALUES
('P1', '2015-12-31', 1000, 900, 100000.00),
('P1', '2015-11-30', 1100, 800, 100100.00),
('P2', '2015-10-31', 1200, 700, 100200.00)
;
WITH Transposed AS (
--First reorganise the data, creating unions like this, by column
--Assumption is that you are not interested in [Portfolio]
SELECT [Portfolio], [Date], [TotalLoans] AS Amount, 'TotalLoans' Field FROM #Data
UNION SELECT [Portfolio], [Date], [ActiveLoans], 'ActiveLoans' FROM #Data
UNION SELECT [Portfolio], [Date], [TotalBalance], 'TotalBalance' FROM #Data
)
SELECT Field, [2015-10-31], [2015-11-30], [2015-12-31] --You can build a string with all the dates from your original data source
FROM (
SELECT [Date], [Field], [Amount] FROM Transposed
) d
PIVOT (
MAX(Amount)
FOR [Date] IN ([2015-10-31], [2015-11-30], [2015-12-31])
) p

How to pivot table rows into columns with dynamic name

I'm having a very tough time trying to figure out how to do
CID sUSER VALUE
------------------
001 235 10
001 188 20
001 04 5
002 235 11
002 188 12
002 04 13
I would like it displayed as follows
CID 04 188 235
-------------------
001 5 20 10
002 13 12 11
Can someone please show me the Sql code please ?
Lets Assume you have filtered data in #tmpCID; now try below script
Declare #sUser As Varchar(Max)
SELECT #sUser= Case isNull(#sUser,'')
When '' then '['+ Rtrim(sUser) + ']'
Else COALESCE(#sUser + ',[', ',') + Rtrim(sUser)+ ']'
End
FROM (SELECT DISTINCT sUser FROM #tmpCID) U
Declare #Query As Varchar(Max)
Set #Query='Select CID, ' + #sUser + ' From #tmpCID
Pivot (AVG(Value)
For sUser IN ('+ #sUser + ')) As P'
EXEC (#Query)
Try this:
CREATE PROCEDURE YourProcedureName
as
Begin
DECLARE #sUSER AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #sUSER = STUFF((SELECT distinct ',' + QUOTENAME(sUSER) FROM testPIVOT
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
SELECT #query = 'WITH PivotData AS
(
SELECT CID, sUSER, VALUE
FROM dbo.YourTableName
)
SELECT CID, '+ #sUSER +'
FROM PivotData
PIVOT(max(VALUE) FOR sUSER IN('+ #sUSER +')) AS P;';
execute(#query);
End
Hope it could help you!
Thanks.