Defining dynamic PIVOT sql query in SQL Server - sql

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'

Related

dynamic pivot with parameter passed in

I have created this stored procedure that works when hard coded
(in the where clause at #WeekStart and #WeekEnd),
If I try to add parameters to the query #WeekStart and #WeekEnd I get the following error:
Must declare the scalar variable "#WeekStart".
My goal is to do do something like this instead of having to hard code it:
exec dbo.GetTotals #WeekStart='2022-04-11',#WeekEnd='2022-04-25'
The stored procedure:
CREATE PROCEDURE [dbo].[GetTotals]
#WeekStart Date,
#WeekEnd Date
AS
begin
set nocount on;
--get row names
DECLARE
#columns NVARCHAR(MAX) = '',
#sql NVARCHAR(MAX) = '';
-- select the category names
SELECT
#columns+=QUOTENAME(DepartmentName) + ','
FROM
DepartmentTable
ORDER BY
DepartmentName;
--set#colums variable
SET #columns = LEFT(#columns, LEN(#columns) - 1);
-- construct dynamic SQL
SET #sql ='
SELECT * FROM
(
select
JobCode,
DepartmentName,
(COALESCE(MonTime, 0)+COALESCE(TueTime, 0)+COALESCE(WenTime, 0)+COALESCE(ThurTime, 0)+COALESCE(FriTime, 0)
+COALESCE(SatTime, 0)+COALESCE(SunTime, 0)) as total
from TimeSheetTable
INNER JOIN DepartmentTable ON TimeSheetTable.DeptId=DepartmentTable.Id
inner join JobCodeTable on TimeSheetTable.JobId=JobCodeTable.Id
--This Works--
-- Where WeekStartDate Between ''2022-04-11'' and ''2022-04-11'' --
--This is throwing an erro--
Where WeekStartDate Between #WeekStart and #WeekEnd
) t
PIVOT(
sum(total)
FOR DepartmentName IN ('+ #columns +')
)pivot_table
ORDER BY JobCode
'
---- execute the dynamic SQL
EXECUTE sp_executesql #sql;
end
exec sp_executesql #Sql, N' #WeekStart Date, #WeekEnd Date', #WeekStart = #WeekStart, #WeekEnd = #WeekEnd

SQL Server Dynamic SQL - Get output from list of tables

I am trying to loop through a temp table variable that contains a list of table names. I want to simply count the rows in each table where a DateTracked column is greater than 30 days. I am having trouble dynamically changing the FROM #tblName variable to store the record count and then insert it into my tracking table. Eventually I will use a cursor to loop through each, but I just want to get this logic down first for a single table. Here is my test code:
DECLARE #tblName as NVARCHAR(MAX)
DECLARE #q as NVARCHAR(MAX)
SET #q = 'Select Count(DateTracked) FROM Audit.' + #tblName + ' WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))'
--DECLARE #tblNameTable TABLE
--(
-- tableName NVARCHAR(MAX)
--)
--INSERT INTO #tblNameTable VALUES (N'myTestTable')
DECLARE #ExpectedRecordsToMove AS TABLE (col1 int)
INSERT INTO #ExpectedRecordsToMove EXECUTE sp_executesql #q, N'#tblName nvarchar(500)', #tblName = 'myTestTable'
SELECT * FROM #ExpectedRecordsToMove
Found solution.
DECLARE #tblName as NVARCHAR(MAX) = 'tblAutoDispatch_DispatchStatus_Map_Tracking'
DECLARE #q as NVARCHAR(MAX) = 'SELECT Count(DateTracked) FROM Audit.' + #tblName + ' WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))'
DECLARE #ExpectedRecordsToMove TABLE
(
ExpectedRecordsToMove Int
)
INSERT INTO #ExpectedRecordsToMove
EXECUTE sp_executesql #q
SELECT * FROM #ExpectedRecordsToMove
Note: Answer provided by OP on question.
sp_executesql also allows for output parameters. It is possible to assign variable inside the inner query and return the value.
DECLARE #tblName AS NVARCHAR(MAX) = N'tblAutoDispatch_DispatchStatus_Map_Tracking';
DECLARE #q AS NVARCHAR(MAX)
= N'SELECT #rowcount = COUNT(DateTracked) FROM Audit.' + #tblName
+ N' WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))';
DECLARE #rowcount INT;
EXECUTE sp_executesql
#query = #q,
#parameters = N'#rowcount INT OUTPUT',
#rowcount = #rowcount OUTPUT;
SELECT
#rowcount;
You can actually do this without a cursor, by building up the query in one go
You must use QUOTENAME to ensure table names do not cause syntax errors
You cannot parameterize a table name, it must be inserted directly into dynamic SQL
DECLARE #tablenames TABLE (schemaName sysname, tblName sysname);
-- insert schema and table names
DECLARE #sql nvarchar(max) = N'
SELECT
ISNULL(tblName, ''Grand Total'') AS tblName,
SUM(rowcount) AS rowcount
FROM (
' +
(
SELECT STRING_AGG(CAST(
N' SELECT ' + QUOTENAME(tn.schemaName, '''') + N'.' + QUOTENAME(tn.tblName, '''') + N' AS tblName,
Count(DateTracked) AS rowCount
FROM ' + QUOTENAME(tn.schemaName) + N'.' + QUOTENAME(tn.tblName) + N'
WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))'
AS nvarchar(max)), N'
UNION ALL
')
FROM #tablenames tn
JOIN sys.tables t ON tn.schemaName = SCHEMA_NAME(t.schema_id) AND tn.tblName = t.name
) + N'
) AS tables
GROUP BY ROLLUP(tblName);
';
PRINT #sql; -- for testing
EXEC (#sql);
If you don't have STRING_AGG on your version of SQL Server, you must use FOR XML instead

Pivot Dynamic row to Column with output parameter?

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

How to execute string as query inside a CTE in SQL Server

I am using CTE like below... But I am getting an error like
No column name was specified for column 1 of 'TempResult'.
Also here I am passing #Query from another stored procedure, and in this procedure I need to execute that #Query inside CTE TempResult.
CREATE PROCEDURE [dbo].[SelectAllProjectPaging]
#CurrentPage int,
#RecordsPerPage int,
#Column varchar(50),
#Query nvarchar(max)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #query nvarchar(max)
SET #query = 'ROW_NUMBER() OVER(ORDER BY ProjectList.ProjectId DESC) as RowNumber,' + #Query
-- Insert statements for procedure here
DECLARE #FirstRecord int, #LastRecord int
SELECT #FirstRecord = (#CurrentPage - 1) * #RecordsPerPage
SELECT #LastRecord = (#CurrentPage * #RecordsPerPage + 1);
WITH TempResult as
(
Select #query
)
SELECT TOP (#LastRecord - 1) *
FROM TempResult
WHERE RowNumber > #FirstRecord
AND RowNumber < #LastRecord;
SELECT COUNT(*) as count
FROM ProjectList
End
You need to make the entire query with dynamic sql. something like this:
SET #query= 'WITH TempResult as
(
SELECT + ' + #query +
')
SELECT TOP (' + CAST((#LastRecord - 1) AS VARCHAR(20)) +
') * FROM TempResult
WHERE RowNumber > ' + CAST(#FirstRecord AS VARCHAR(20)) +
' AND RowNumber < ' + CAST(#LastRecord AS VARCHAR(20)) + '; '
EXECUTE #query
Edit
To the comment. Yes it should select what I can see. The #query is equals to:
set #query = 'ROW_NUMBER() OVER(ORDER BY ProjectList.ProjectId DESC) as RowNumber,'+ #query
Then you will need:
SELECT ' ROW_NUMBER() OVER(ORDER BY ProjectList.ProjectId DESC) as RowNumber,' + #query
Your "With TempResult As" has to be extended a bit, containing the column names to be used.
Example
With TempResult (column1, column2, column3) as
(
select #query
)
Ref:
WITH expression_name [ ( column_name [,...n] ) ]
AS
( CTE_query_definition )
From http://technet.microsoft.com/en-us/library/ms190766%28v=sql.105%29.aspx

How do I name a column as a date value

the results look like this but wher the column name says 'Today' I want it to be todays date.
Try this technique:
declare #dt datetime
declare #sql varchar(100)
set #dt = getdate()
set #sql = 'select 1 as [ ' + convert( varchar(25),#dt,120) + ']'
exec (#sql)
In your Case:
declare #dt datetime
declare #sql varchar(100)
set #dt = getdate()
set #sql = 'select 0 as [ ' + convert( varchar(25),#dt,120) + ']'
exec (#sql)
I would return an integer representing a day offset and parse it in the client, failing that you are going to have to use dynamic SQL or do something with the underlying column name itself;;
declare #sql nvarchar(128) = '
select
col1,
col2,
0 as [' + cast(getdate() as nvarchar(32)) + ']
from T'
exec(#sql)
Or
--today
declare #now varchar(32) = cast(getdate() as varchar(32))
--result to temp table
select col1, col2, 0 as [Now] into #T from TheTable
--rename col
exec tempdb..sp_rename '#T.Now', #now, 'COLUMN'
--select
select * from #T