There is no problem for me to pivot statistic columns in my query e.g Column 1 , Column 2 , Column 3.... and so on.
But i would like to do this dynamic instead.
My data looks like this:
i want to be able to EXECUTE a store procedure to get the output result:
Exec sp_output 1 (from another window where '1' represents the PoolID (#AppPool)) to look like this:
This is my SP:
create PROCEDURE [dbo].[sp_test]
#Query as nvarchar(100) -- OUTPUT?,
#AppPool AS nvarchar(50)
AS
SELECT #Query = Attribute FROM [dbo].[Vy_UserAccess] WHERE PoolID = #AppPool
SELECT [Users],'+ #Query +' FROM
(SELECT [Pool],[Users],[RecNum],[Attribute],[Values] FROM [dbo].[Vy_UserAccess] ) AS T1
PIVOT (MAX([Values]) FOR [ATTRIBUTE] IN ('+ #Query +')) AS T2
Is this possible to achieve by just fine tuning my code or do i have to go on another direction?
You can do It in following:
QUERY
CREATE PROCEDURE [dbo].[sp_test]
#AppPool AS NVARCHAR(60)
AS
DECLARE #cols AS NVARCHAR(MAX) = '',
#sql AS NVARCHAR(MAX)
SELECT #cols += QUOTENAME([Name]) + ','
FROM (SELECT DISTINCT Attribute as Name
FROM [dbo].[Vy_UserAccess]
WHERE PoolID = #AppPool
) a
ORDER BY Name DESC
SET #cols = LEFT(#cols, LEN(#cols) - 1)
SET #sql = 'SELECT Users, ' + #cols + ' FROM
(
SELECT [Pool],[Users],[RecNum],[Attribute],[Values]
FROM [dbo].[Vy_UserAccess]
) AS T1
PIVOT (MAX([Values]) FOR [ATTRIBUTE] IN ('+ #cols +')) AS T2'
EXEC sp_executesql #sql, N'#AppPool NVARCHAR(60)', #AppPool
EXECUTION
Exec sp_test 1
QUERY WITH SAMPLE DATA
CREATE PROCEDURE sp_test
#AppPool AS NVARCHAR(60)
AS
CREATE TABLE #test
(
PoolId NVARCHAR(60),
Pool NVARCHAR(40),
Users NVARCHAR(60),
RecNum INT,
Attribute NVARCHAR(40),
[Values] NVARCHAR(20)
)
INSERT INTO #test VALUES
('1', 'FINANCE', 'User1', 2, 'DIVISION', '010'),
('1', 'FINANCE', 'User1', 1, 'COMPANY', '1'),
('1', 'FINANCE', 'User1', 1, 'DIVISION', '050')
DECLARE #cols AS NVARCHAR(MAX) = '',
#sql AS NVARCHAR(MAX)
SELECT #cols += QUOTENAME([Name]) + ','
FROM (SELECT DISTINCT Attribute as Name
FROM #test
WHERE PoolID = #AppPool
) a
ORDER BY Name DESC
SET #cols = LEFT(#cols, LEN(#cols) - 1)
SET #sql = 'SELECT Users, ' + #cols + ' FROM
(
SELECT [Pool],[Users],[RecNum],[Attribute],[Values]
FROM #test
) AS T1
PIVOT (MAX([Values]) FOR [ATTRIBUTE] IN ('+ #cols +')) AS T2'
EXEC Sp_executesql #sql, N'#AppPool NVARCHAR(60)', #AppPool
DROP TABLE #test
OUTPUT
Users DIVISION COMPANY
User1 050 1
User1 010 NULL
Related
I have the following table, used in dynamic pivot for PORT_NAME:
DECLARE #BaseQuery TABLE
(
PORT_NAME NVARCHAR(50),
BILL_ACCOUNT_NAME NVARCHAR(50),
AVERAGE float
);
I need to save the output as a table, I tried to add the following code, since the columns need to be dynamic to schedule a SQL job:
DECLARE #sql varchar(max)
SET #sql = 'CREATE TABLE ##T1 (BILL_ACCOUNT_NAME NVARCHAR(50),' + #Columns + ' float)'
EXEC(#sql)
INSERT INTO ##T1 VALUES (select #Query)
SELECT * FROM ##T1
got (Incorrect syntax near the keyword 'select'.)
I am working in SQL Server Management Studio 18 (2019 version), I tried to read the code in SSIS was not able to read the results.
You can insert your pivot result into a new table object using INTO.
Quick Example:
Executes the #Query string which creates the dbo.RESULT_PIVOT_TABLE object.
SET #Query = 'SELECT
BILL_ACCOUNT_NAME,
' + #Columns + N'
INTO dbo.RESULT_PIVOT_TABLE -- INSERT PIVOT RESULT INTO TABLE OBJECT
FROM (SELECT
PORT_NAME,
BILL_ACCOUNT_NAME,
AVERAGE
FROM #BaseQuery) AS a PIVOT (MAX(AVERAGE) FOR a.PORT_NAME IN (' + #Columns + ')) AS P';
EXEC sp_executesql #Query;
SELECT * FROM dbo.RESULT_PIVOT_TABLE;
Full Example:
DROP TABLE IF EXISTS dbo.RESULT_PIVOT_TABLE;
DROP TABLE IF EXISTS #BaseQuery;
CREATE TABLE #BaseQuery
(
PORT_NAME NVARCHAR(50),
BILL_ACCOUNT_NAME NVARCHAR(50),
AVERAGE FLOAT
);
DECLARE #Query NVARCHAR(MAX);
DECLARE #Columns NVARCHAR(MAX);
/* POPULATE TEMP DUMMY TABLE WITH FAKE DATA */
INSERT INTO #BaseQuery
SELECT
a.PORT_NAME,
a.BILL_ACCOUNT_NAME,
a.AVERAGE
FROM (SELECT
'PORT 1' AS PORT_NAME,
'NAME 1' AS BILL_ACCOUNT_NAME,
1.23 AS AVERAGE
UNION ALL
SELECT
'PORT 2' AS PORT_NAME,
'NAME 2' AS BILL_ACCOUNT_NAME,
2.34 AS AVERAGE
UNION ALL
SELECT
'PORT 3' AS PORT_NAME,
'NAME 3' AS BILL_ACCOUNT_NAME,
3.45 AS AVERAGE) a;
/* SET COLUMNS FOR PIVOT */
SELECT
#Columns = STRING_AGG(a.PORT_NAME, ',')
FROM (SELECT DISTINCT
QUOTENAME(PORT_NAME) AS PORT_NAME
FROM #BaseQuery) a;
/* SET DYNAMIC QUERY STRING THAT INSERTS PIVOT RESULT INTO TABLE OBJECT */
SET #Query = 'SELECT
BILL_ACCOUNT_NAME,
' + #Columns + N'
INTO dbo.RESULT_PIVOT_TABLE -- INSERT PIVOT RESULT INTO TABLE OBJECT
FROM (SELECT
PORT_NAME,
BILL_ACCOUNT_NAME,
AVERAGE
FROM #BaseQuery) AS a PIVOT (MAX(AVERAGE) FOR a.PORT_NAME IN (' + #Columns + ')) AS P';
EXEC sp_executesql #Query;
SELECT * FROM dbo.RESULT_PIVOT_TABLE;
Result:
BILL_ACCOUNT_NAME
[PORT 1]
[PORT 2]
[PORT 3]
NAME 1
1.23
null
null
NAME 2
null
2.34
null
NAME 3
null
null
3.45
I have a request which returns something like this:
--------------------------
Tool | Week | Value
--------------------------
Test | 20 | 3
Sense | 20 | 2
Test | 19 | 2
And I want my input to look like this:
-------------------------
Tool | W20 | W19
-------------------------
Test | 3 | 2
Sense | 2 | null
Basically, for every week I need to have a new column. The number of week and of tools is dynamic.
I have tried many things but nothing worked. Anybody have a solution ?
Try this
CREATE table #tst (
Tool varchar(50), [Week] int, Value int
)
insert #tst
values
('Test', 20, 3),
('Sense', 20,2),
('Test', 19, 2)
Here is the Dynamic Query:
DECLARE #col nvarchar(max), #query NVARCHAR(MAX)
SELECT #col = STUFF((SELECT DISTINCT ',' + QUOTENAME('W' + CAST([Week] as VARCHAR))
from #tst
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = '
SELECT *
FROM (
SELECT Tool,
Value,
''W'' + CAST([Week] as VARCHAR) AS WeekNo
FROM #tst
) t
PIVOT
(
MAX(t.Value)
FOR WeekNo IN (' + #col + ')
) pv
ORDER by Tool'
EXEC (#query)
Result
Tool W20 W19
=================
Sense 2 NULL
Test 3 2
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp
( Tool varchar(5), Week int, Value int)
;
INSERT INTO #temp
( Tool , Week , Value )
VALUES
('Test', 20, 3),
('Sense', 20, 2),
('Test', 19, 2)
;
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max),
#col NVARCHAR(max)
SELECT #columns = ISNULL(#columns + ', ', '') + N'[' +'w'+ tbl.[Week] + ']'
FROM (
SELECT DISTINCT CAST([Week] AS VARCHAR)[Week]
FROM #temp
) AS tbl
SELECT #statement = 'SELECT *
FROM
(
SELECT
Tool , ''w''+ CAST(Week AS VARCHAR) week , Value
FROM
#Temp
) src
PIVOT(MAX(Value)for Week in (' + #columns + ')) as pvt
'
EXEC sp_executesql #statement = #statement
This is how I would do it ... If I understood your question correctly
if object_id('tempdb..#InputTool') is not null drop table #InputTool
create table #InputTool (Tool nvarchar(10), [20] int, [19] int)
insert into #InputTool (Tool, [20], [19])
values
('Test', 3, 2),
('Sense', 2, null)
declare #cols nvarchar(max)
select #cols = STUFF((SELECT ',' + QUOTENAME(name)
from tempdb.sys.columns
where object_id = object_id('tempdb..#InputTool')
and Column_id > 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
declare #sqlquery nvarchar(max) =
'select Tool, Weeks, Value from (
select * from #InputTool
) as it
UNPIVOT
(
Value FOR Weeks IN (' + #cols + ')
) AS Weeks
order by Weeks desc'
execute (#sqlquery);
Give it a shot and let me know if it worked
I have a table like this:
id unit
1 mm
2 cm
3 kg
When I perform pivot operation on this, I am getting result as follows:
1 2 3
mm cm kg
Is it possible to get custom column names here, something like this:
d1 d2 d3
mm cm kg
I am using Pivot for this as:
IF OBJECT_ID('tempdb..#t') IS NOT NULL
DROP TABLE #t
GO
CREATE table #t
(id varchar(max),unit varchar(max))
insert into #t (id,unit)values
(1,'kg'),
(2,'cm'),
(3,'mm'),
(4,'m')
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max)
SELECT #columns = ISNULL(#columns + ',', '') + N'[' + cast(tbl.id as varchar(max)) + ']'
FROM (
SELECT DISTINCT id
FROM #t
) AS tbl
SELECT #statement = 'select *
INTO ##temp
from (
SELECT id,[unit]
FROM #t
) as s
PIVOT
(max(unit) FOR id in(' + #columns + ')) as pvt
'
EXEC sp_executesql #statement = #statement
SELECT * FROM ##temp
DROP TABLE #t
DROP TABLE ##temp
Is it possible?
Thanks
IF OBJECT_ID('tempdb..#t') IS NOT NULL
DROP TABLE #t
GO
CREATE TABLE #t (
id VARCHAR(10),
unit VARCHAR(100)
)
INSERT INTO #t (id, unit)
VALUES
('1', 'kg'),
('2', 'cm'),
('3', 'mm'),
('4', 'mm')
DECLARE #SQL NVARCHAR(MAX), #columns NVARCHAR(MAX)
SELECT #columns = STUFF((
SELECT ',[D' + id + ']'
FROM #t
FOR XML PATH('')), 1, 1, '')
SELECT #SQL = '
SELECT *
FROM (
SELECT [unit], col = N''D'' + id
FROM #t
) s
PIVOT (MAX(unit) FOR col IN (' + #columns + ')) p'
EXEC sys.sp_executesql #SQL
Just add a prefix to your ID. Example
SELECT #statement = 'select * INTO ##temp from
( SELECT [id] = ''d''+id,[unit] FROM #t ) as s
PIVOT
(max(unit) FOR id in(' + #columns + ')) as pvt '
Also it's terrible practice to use global temp tables! Especially one named ##temp
You can use a CASE expression with a dynamic sql query.
CREATE TABLE #t
(
id INT,
unit VARCHAR(2)
);
INSERT INTO #t VALUES
(1,'mm'),
(2,'cm'),
(3,'kg');
DECLARE #query AS VARCHAR(MAX);
SELECT #query = 'SELECT ' +
STUFF
(
(
SELECT DISTINCT ',MAX(CASE WHEN id = '+ CAST(id AS VARCHAR(10))
+ ' THEN unit END) AS d' + CAST(id AS VARCHAR(10))
FROM #t
FOR XML PATH('')
),
1,1,'');
SELECT #query += ' FROM #t;';
EXECUTE(#query);
Result
+----+----+----+
| d1 | d2 | d3 |
+----+----+----+
| mm | cm | kg |
+----+----+----+
SELECT #statement = 'select * INTO ##temp from ( SELECT ''d''+id AS [id],[unit] FROM #t ) as s PIVOT (max(unit) FOR id in(' + #columns + ')) as pvt '
Currently I have a static pivot sql query defined in a stored procedure in sql server:
ALTER PROCEDURE [dbo].[MonthRepo]
-- Add the parameters for the stored procedure here
#from datetime,
#to datetime
AS
BEGIN
DECLARE #cols nvarchar(12)
DECLARE #query nvarchar(max)
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT *
FROM (
SELECT ROUND(ds.ct_quot_rate,0) AS Quote,
ROUND(ds.ct_quot_rate,0) AS Quote_Out,
ds.isin
FROM ds
WHERE ds.datum >= #from AND ds.datum <= #to
) tbl
PIVOT (
COUNT(Quote)
FOR isin IN(AB000001,
AB000002,
AB000003,
AB000004,
AB000005)
) piv
END
How can I define this static code in dynamic query? I have declared 2 variables.
I think you're after something like this:
ALTER PROCEDURE [dbo].[MonthRepo]
-- Add the parameters for the stored procedure here
#from datetime,
#to datetime
AS
BEGIN
DECLARE #cols nvarchar(max)
DECLARE #query nvarchar(max)
SET NOCOUNT ON;
WITH vals AS (
SELECT DISTINCT isin
FROM ds
)
SELECT #cols = COALESCE(#cols + ',','') + '[' + isin + ']'
FROM vals
SET #query = '
SELECT *
FROM (
SELECT ROUND(ds.ct_quot_rate,0) AS Quote,
ROUND(ds.ct_quot_rate,0) AS Quote_Out,
ds.isin
FROM ds
WHERE ds.datum >= #from_param AND ds.datum <= #to_param
) tbl
PIVOT (
COUNT(Quote)
FOR isin IN(' + #cols + ' )
) piv'
EXECUTE sp_executesql #query, N'#from_param DATETIME, #to_param DATETIME', #from_param = #from, #to_param = #to
END
CREATE PROCEDURE [dbo].[MonthRepo]
-- Add the parameters for the stored procedure here
#from VARCHAR(20),
#to VARCHAR(20)
AS
BEGIN
IF OBJECT_ID('tempdb..#T') IS NOT NULL
DROP TABLE #T
create table #T
(
datnum varchar(20),
quote INT,
isin VARCHAR(20)
)
insert into #T (datnum,quote,isin)values ('2015-01-01',100,'AB000001'),
('2015-01-01',100,'AB000002'),
('2015-01-02',98,'AB000003'),
('2015-01-02',70,'AB000001'),
('2015-01-03',100,'AB000001')
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max)
SELECT #columns = Isnull(#columns + ', ', '') + N'[' + tbl.isin+ ']'
FROM (SELECT DISTINCT isin
FROM #T) AS tbl
--SELECT #columns
SELECT #statement = ' SELECT *
FROM (
SELECT ROUND(ds.quote,0) AS Quote,
ROUND(ds.quote,0) AS Quote_Out,
ds.isin
FROM #T ds
WHERE ds.datnum >= CONVERT(datetime,'+''''+#from+''' ,105) AND ds.datnum <= CONVERT(datetime,'''+#to+''' ,105)
) tbl
PIVOT (
COUNT(Quote)
FOR isin IN(' + #columns+ ')) as PVT
'
PRINT #statement
EXEC sp_executesql #statement = #statement
END
EXEC [MonthRepo] #from = '2015-01-01',#to = '2015-01-10'
I tried to develop this stored procedure using a temp table but that wouldn't work so I switched to using a table variable. I need to execute an interim dynamic query into the table variable and then I use that table variable to execute the final query. The problem is that I receive an error "must declare scalar variable #clms". I assume that Exec doesn't have scope for the table variable?
DECLARE #qry nvarchar(4000)
DECLARE #clms TABLE (mastcatname nvarchar(50),engdtlbeta decimal (18,4))
SET #qry='INSERT INTO #clms
SELECT distinct replace(mastcatname, '' '', '''') as mastcatname,
engdtlbeta
FROM vw_Scorecard
WHERE empsurveyid=' + cAST(#EmpSurveyID AS nvarchar(10)) + '
AND UnitID IN (' + #UnitIDs + ')
ORDER BY engdtlbeta desc, MastCatName'
EXEC(#qry)
DECLARE #cols nvarchar(1000)
SELECT #cols=COALESCE (#cols + ',[' + mastcatname + ']', '[' + mastcatname + ']')
FROM #clms
SET #qry='SELECT UnitName ,
ParentName, ' + #cols + '
FROM (
SELECT UnitName,
ParentName,
ScoreAvg,
replace(mastcatname, '' '','''') as mastcatname
FROM vw_Scorecard
WHERE UnitID IN (' + #UnitIDs + ')
AND EmpSurveyID=' + cast(#EmpSurveyID as nvarchar(5)) + ' ) p
PIVOT
(SUM(ScoreAvg) FOR mastcatname in (' + #cols + ')) as pvt'
EXEC (#qry)
This is simple minimal example. You can use INSERT EXEC statement. The key is to have table variable declared inside and outside dynamic query. At the end of dynamic query just select from table variable and insert resultset into outside table variable:
DECLARE #t TABLE ( id INT )
DECLARE #q NVARCHAR(MAX) = 'declare #t table(id int)
insert into #t values(1),(2)
select * from #t'
INSERT INTO #t
EXEC(#q)
SELECT * FROM #t
I found this attempting to do basically the same thing. I altered my SQL, and yes, it works! But then I thought, this is overcomplicating things. Why declare the table variable, insert, then select all in the dynamic SQL? Why not just select...
DECLARE #t TABLE ( id INT )
DECLARE #q NVARCHAR(MAX) = 'select 1 union select 2'
INSERT INTO #t
EXEC(#q)
SELECT * FROM #t