How to use where statement in a dynamic column - sql-server

i need to make a 'where' statement in a dynamic column.
the dynamic column came from a row item.
sample as below.
SET #paramList = STUFF((
SELECT DISTINCT ',[' + parameter + ']'
FROM #tblitems FOR XML PATH('')
)
,1,1,'')
#paramList = [item1],[item2],[item3]
using the below query i need to incorporate the where statement at the end. but the column from the #paramlist should all be equal to 1 only.
SET #query ='select no,
' + #paramList + '
FROM( SELECT * FROM #tblitems)src
PIVOT
(
max(value)
for [parameter] in (' + #paramList + ')
) as piv order by item'

Create the condition string in the same way as the column list:
SET #condition = STUFF((
SELECT DISTINCT ' AND [' + parameter + '] = 1'
FROM #tblitems FOR XML PATH('')
)
,1,5,'');
Note though that you can also use the QUOTENAME function instead of enclosing the name in square brackets manually:
SET #condition = STUFF((
SELECT DISTINCT ' AND ' + QUOTENAME(parameter) + ' = 1'
FROM #tblitems FOR XML PATH('')
)
,1,5,'');
Now that you've got the condition string, you can add it to the dynamic query:
SET #query ='SELECT no,
' + #paramList + '
FROM (SELECT * FROM #tblitems) AS src
PIVOT
(
MAX(value)
FOR [parameter] IN (' + #paramList + ')
) AS piv
WHERE ' + #conditions + '
ORDER BY item;';

Related

Sort COALESCE generated columns in a dynamic pivot table

I would like to sort the columns, but I get the following error:
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.
The years select query returns them in order, but they are shuffled when handled by the COALESCE function.
How can I prevent this? Or even better, control the sorting?
NOTE: The sorting works if I put a TOP 10 in the subquery. Super weird...
-- variables
DECLARE #sql AS varchar(max)
DECLARE #pivot_list AS varchar(max) -- Leave NULL for COALESCE technique
DECLARE #select_list AS varchar(max) -- Leave NULL for COALESCE technique
-- columns
SELECT -- top 10 makes the sorting work
-- [2018], [2020], [2017], [2019] -- not ordered
#pivot_list = COALESCE(#pivot_list + ', ', '') + '[' + PIVOT_COLUMN + ']',
#select_list = COALESCE(#select_list + ', ', '') + 'ISNULL([' + PIVOT_COLUMN + '], 0) as [' + PIVOT_COLUMN + ']'
FROM (
SELECT
-- 2017, 2018, 2019, 2020 -- ordered
cast(year(addate) as nvarchar(4)) as PIVOT_COLUMN
FROM tHE_Move M
where addate >= '01.01.2017'
group by year(addate)
-- order by year(addate) asc -- <-------------------------------------- doesn't work
) as PIVOT_COLUMNS
-- query
SET #sql = '
; WITH PivotData AS (
select
shop,
y AS PIVOT_COLUMN,
sum(revenueNoVAT) revenueNoVAT
from (
SELECT
SS.acName2 shop,
year(m.addate) y,
MI.anPVVATBase revenueNoVAT
FROM tHE_MoveItem MI
JOIN tHE_Move M ON MI.acKey = M.acKey
JOIN tHE_SetSubj SS ON M.acIssuer = SS.acSubject
WHERE m.acDocType in (
' + '''3210''' + ',
' + '''3220''' + ',
' + '''3230''' + ',
' + '''3240''' + '
)
) t1
group by
shop,
y
)
SELECT shop, ' + #select_list + '
FROM PivotData
PIVOT (
sum(revenueNoVAT)
FOR PIVOT_COLUMN
IN (' + #pivot_list + ')
) piv
order by shop desc
'
-- execution
EXEC (#sql)

Using sub-query to generate case statements

What i am trying to accomplish is comparing two rows to each other pointing out the differences from row to row. Each row has quite a few columns and I was trying to make it easily visible for which ones had changed. Code below is my thoughts, but I know this won't work, but is a start.
SELECT
(SELECT concat('Case WHEN T1.', column_name, ' <> T2.', column_name, ' THEN ''', column_name, ' Changed Values('' + CONVERT(varchar(100), T1.', column_name, ') + '', '' + CONVERT(varchar(100), T2.', column_name, ') + '')'' ELSE '''' END AS ', column_name)
FROM information_schema.columns
WHERE table_name = 'Table')
FROM
(
SELECT * FROM Table
WHERE ID = '13'
) AS T1
JOIN
(
SELECT * FROM Table
WHERE ID = '2006'
) AS T2
ON T1.CreateTimeStamp = T2.CreateTimeStamp
I got the idea because below this works fine, but I would like this to be potentially reusable code for other table without having to type out tens or hundreds of columns each time.
SELECT
Case WHEN T1.R1<> T2.R1 THEN 'Changed Values(' + CONVERT(varchar(100),T1.R1) + ', ' + CONVERT(varchar(100),T2.R1) + ')' ELSE '' END AS R1,
Case WHEN T1.R2<> T2.R2 THEN 'Changed Values(' + CONVERT(varchar(100),T1.R2) + ', ' + CONVERT(varchar(100),T2.R2) + ')' ELSE '' END AS R2
FROM
(
SELECT * FROM Table
WHERE ID = '13'
) AS T1
JOIN
(
SELECT * FROM Table
WHERE ID = '2006'
) AS T2
ON T1.CreateTimeStamp = T2.CreateTimeStamp
For the this example please assume CreateTimeStamp always equals each other between the two rows.
You would need to create the whole query as dynamic SQL. Note that I'm using QUOTENAME() to prevent SQL Injection from weirdly named columns. I'm also trying to keep a format for the code, so I won't get headaches when debugging.
DECLARE #SQL NVARCHAR(MAX);
SELECT #SQL = N' SELECT ' + NCHAR(10)
--Concatenate all columns except ID and CreateTimeStamp
+ STUFF(( SELECT REPLACE( CHAR(9) + ',CASE WHEN T1.<<ColumnName>> <> T2.<<ColumnName>> ' + CHAR(10)
+ CHAR(9) + CHAR(9) + 'THEN ''Changed Values('' + CONVERT(varchar(100),T1.<<ColumnName>>) + '', '' + CONVERT(varchar(100),T2.<<ColumnName>>) + '')'' ' + CHAR(10)
+ CHAR(9) + CHAR(9) + 'ELSE '''' END AS <<ColumnName>>', '<<ColumnName>>', QUOTENAME(COLUMN_NAME)) + NCHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Table'
AND COLUMN_NAME NOT IN( 'ID', 'CreateTimeStamp')
FOR XML PATH(''), TYPE).value('./text()[1]', 'nvarchar(max)'), 2, 1, '') + NCHAR(10)
--Add rest of the query
+ 'FROM Table AS T1 ' + NCHAR(10)
+ 'JOIN Table AS T2 ON T1.CreateTimeStamp = T2.CreateTimeStamp ' + NCHAR(10)
+ 'WHERE ID = #ID1 ' + NCHAR(10)
+ 'AND ID = #ID2;'
--PRINT for debugging purposes
PRINT #SQL;
--Execute the dynamic built code
EXECUTE sp_executesql #SQL,
N'#ID1 int, #ID2 int',
#ID1 = 13,
#ID2 = 2006;
The concatenation method is explained on this article.

SQL Server: I am getting an error while altering view when I try to add some virtual column which in turn based on virtual column

I have 20 databases, each with same table but different columns.
So to make the uniform we are creating views on top of each table in a database which will contain all the columns, as there will be one application accessing all the database.
In the view, I have to write the query in such a way that if I want to alter it and add any addition column for testing I should be able to do that.
Now in below query I am altering / creating query such that it takes all the columns of that table from the database, and then I append the other columns which are not present in it.
I need to add a column which will just concatenate some of the columns
ALTER VIEW [dbo].[AIV_PARKING]
AS
SELECT
*,
Cast(NULL AS [VARCHAR](20)) [ACTCODE],
Cast(NULL AS [VARCHAR](1)) [ACTIVATEINFO],
Cast(NULL AS [VARCHAR](20)) [VEHLICNOCHECK],
Cast(NULL AS [VARCHAR](40)) [ACTIVITY],
Cast(Isnull(vehlicnocheck, '') + '|' +
Isnull(officername, '') + '|' +
Isnull(locstreet, '') + '|' +
Isnull(locsideofstreet, '') + '|' +
Isnull(loccrossstreet1, '') + '|' +
Isnull(loccrossstreet2, '') + '|'
+ Isnull(locsuburb, '') + '|'
+ Isnull(locstate, '') + '|'
+ Isnull(locpostalcode, '') + '|'
+ Isnull(loclot, '') + '|'
+ Isnull(locpostalcode, '') + '|'
+ Isnull(Cast(officerid AS VARCHAR(20)), '')
+ Isnull(officername, '') + '|'
+ Isnull(Cast (issueno AS VARCHAR(100)), '') AS NVARCHAR(max)) AS SearchText
FROM
[dbo].parking
Here I added a column called SearchText which concatenates other columns, but I get an error
Invalid column name 'VehLicNoCheck'
Is there any way I can add this column to this view?
I also tried to do to something below but I got the same error:
CAST(CASE
WHEN NOT EXISTS
(
Select 1 from INFORMATION_SCHEMA.COLUMNS
Where Column_name ='VehLicNoCheck'
and table_name='Parking'
)
THEN ''
ELSE ISNULL(VehLicNoCheck,'')
END as nvarchar(max)
)
you could create a view that normalizes the uncommon columns to rows, where the values for the common columns are just repeated, e.g:
select id, col, value from parking
unpivot (value for col in (actcode, vehLicNoCheck, etc.)) x
the code to dynamically generate the view would be something like:
declare #sql varchar(max) = 'select id, col, value from parking unpivot (value for col in ('
select #sql += quotename(name) +',' from sys.columns where object_id=object_id('parking') and name not in ('id')
set #sql = substring(#sql, 1, len(#sql) - 1) + '))x'
exec(#sql)
this does not make sense at all.
the [ACTCODE], [ACTIVATEINFO] are all NULL value
so basically SearchText is just a string of '|||||'
you might as well, just do this
SELECT *,
CAST( NULL AS varchar) [ACTCODE],
CAST( NULL AS varchar) [ACTIVATEINFO],
CAST( NULL AS varchar) [VEHLICNOCHECK],
CAST( NULL AS varchar) [ACTIVITY],
'||||||' as SearchText
FROM [dbo].PARKING
Maybe if you can explain what are you trying to achieve here, we can point you to the right direction
EDIT :
You will need to use Dynamic SQL. You will need a list of all column names
-- declare a table variable for all the columns that you required
declare #columns table
(
id int identity,
name varchar(100)
)
-- for example these are the required columns
insert into #columns
values ('ACTCODE'), ('ACTIVATEINFO'), ('VEHLICNOCHECK'), ('ACTIVITY')
-- The Query to create the view
declare #sql nvarchar(max)
select #sql = N'CREATE VIEW [AIV_PARKING] AS' + char(13) + 'SELECT' + char(13)
select #sql = #sql
+ case when t.name is not null
then quotename(c.name) + ','
else 'CAST (NULL AS VARCHAR(10)) AS ' + quotename(c.name) + ','
end
+ char(13)
from #columns c
left join sys.columns t on c.name = t.name
and t.object_id = object_id('PARKING')
order by c.id
select #sql = #sql
+ case when t.name is not null
then 'ISNULL(' + quotename(c.name) + ', '''')'
else ''
end
+ ' + ''|'''
+ ' + '
from #columns c
left join sys.columns t on c.name = t.name
and t.object_id = object_id('PARKING')
order by c.id
select #sql = left(#sql, len(#sql) - 8) + ' AS SearchText' + char(13)
+ 'FROM PARKING'
-- print out to view the complete create view statement
print #sql
-- execute it
exec sp_executesql #sql

Dynamic pivot - Incorrect syntax error near #cols

I have a view in Sql Server called 'ITEMS_PRICE'.
Some of it's columns are the 'Name' column and 'FormulaResult_Cost' column.
What I am trying to do, is using dynamic pivot, make all rows of 'Name' field as columns and take as value, the value of 'FormulaResult_Cost'.
Here is my SQL Query:
DECLARE #query NVARCHAR(4000)
DECLARE #cols NVARCHAR(4000)
SELECT #cols = STUFF((SELECT distinct '' + QUOTENAME(p.Name)
from ITEMS_PRICE AS p
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') ,1,0,'')
--select STUFF(#cols, 1,0,'')
set #query = 'SELECT p.ItemCode, p.ItemName, p.ItmsGrpCod, p.ItmsGrpNam, p.Name, p.FormulaResult_Cost, ' + #cols + '
from
ITEMS_PRICE as p
pivot
(
max(p.FormulaResult_Cost)
for p.Name in (' + '[' #cols + ']' + ')
) AS t'
EXECUTE(#query)
The error that I get is:
Msg 102, Level 15, State 1, Line 29 Incorrect syntax near '#cols'.
Also, I don't want the dynamic pivot to have any aggregate functions. I just read in some forums that you have to use, at least max() function in order to be correct the for ... in code.
You're missing a + before #cols on this line: for p.Name in (' + '[' #cols + ']' + ') should be for p.Name in (' + '[' + #cols + ']' + ').
But I don't believe you need the extra square brackets in there, as the QUOTENAME method will add them, so it should read:
for p.Name in (' + #cols + ')
Also, if you're doing for p.Name in (#cols), your STUFF function should return comma separated values, so I would update your declaration to to include a comma after the distinct:
SELECT #cols = STUFF((SELECT distinct ',' + QUOTENAME(p.Name)
from ITEMS_PRICE AS p
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') ,1,0,'')
Full query:
DECLARE #query NVARCHAR(4000)
DECLARE #cols NVARCHAR(4000)
SELECT #cols = STUFF((SELECT distinct ',' + QUOTENAME(p.Name)
from ITEMS_PRICE AS p
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') ,1,0,'')
--select STUFF(#cols, 1,0,'')
set #query = 'SELECT p.ItemCode, p.ItemName, p.ItmsGrpCod, p.ItmsGrpNam,
p.Name, p.FormulaResult_Cost, ' + #cols + '
from
ITEMS_PRICE as p
pivot
(
max(p.FormulaResult_Cost)
for p.Name in (' + #cols + ')
) AS t'
EXECUTE(#query)
Add + before #cols. ('[' #cols + ']')
set #query = 'SELECT p.ItemCode, p.ItemName, p.ItmsGrpCod,
p.ItmsGrpNam, p.Name, p.FormulaResult_Cost, ' + #cols + '
FROM
ITEMS_PRICE as p
pivot
(
max(p.FormulaResult_Cost)
for p.Name in (''' + #cols + ''')
) AS t'

i don't want to pass worked_dates dynamically ,i want to pass worked_dates in between range in pivot

SELECT *
FROM (
SELECT [TM_UserID],
[FullName],
[Worked_dte],
[Worked_Hours]
FROM #Reporting_User_Timesheet
WHERE [worked_dte] BETWEEN '2014-04-04'
AND '2014-04-06'
) AS sourceTable
Pivot(sum([Worked_Hours]) FOR [Worked_dte] IN ([2014-04-04], [2014-04-05], [2014-04-06])) AS PivotTable
A shot in the dark, obviously, but here's how I understood your question.
First, you need a calendar table to get the dates for the range:
http://www.dbdelta.com/calendar-table-and-datetime-functions/
After that, construct the needed PIVOT query and execute it dynamically:
declare #range_start date, #range_end date;
select #range_start = '20140404', #range_end = '20140406';
declare #collist nvarchar(max);
SET #collist = stuff((select distinct ',' + QUOTENAME(convert(varchar,date,112))
FROM calendar
WHERE Datue BETWEEN #range_start AND #range_end
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
declare #q nvarchar(max);
set #q = '
SELECT *
FROM (
SELECT [TM_UserID],
[FullName],
[Worked_dte],
[Worked_Hours]
FROM #Reporting_User_Timesheet
WHERE [worked_dte] BETWEEN ''' + CONVERT(varchar, #range_start, 112) + '''
AND ''' + CONVERT(varchar, #range_end, 112) + '''
) AS sourceTable
Pivot (
sum([Worked_Hours]) FOR [Worked_dte] IN (' + #collist + ')
) AS PivotTable
';
exec (#q);

Resources