Get dates for last 30 days dynamically in SQL - sql

I have below SQL which gives me count of files received in particular country according to date. But here dates are hard coded. I want them dynamically. I want it in such a way that whenever I run this query, I get result for last 30 days. Below is the SQL:
with t (Country ,Date,total)
as
(
select b.country as Market, CAST(a.ProcessDate AS Date) AS DATE, count(a.ProcessDate) AS total from Log a LEFT JOIN File b ON a.FileID = b.FileID where a.ProcessDate BETWEEN '2022-11-01' AND '2022-11-07' GROUP BY b.country, CAST(a.ProcessDate AS DATE)
)
Select
*
from
(
Select
Date,
Total,
Country
from t
) x
Pivot(
sum(total)
for Date in (
[2022-11-01],
[2022-11-02],
[2022-11-03],
[2022-11-04]
)
) as pivottable
Below is Result of the query with dummy data:
Country
2022-11-01
2022-11-02
2022-11-03
2022-11-04
Brazil
2
1
Chile
1
1
Switzerland
1
Below is the structure of MasterFile and FileProcessLog with dummy data:
MasterFile:
FileID
Country
1
Brazil
2
Brazil
3
Chile
4
Chile
5
Switzerland
FileProcessLog:
FileID
ProcessDate
1
2022-11-01T15:31:53.0000000
2
2022-11-01T15:32:28.0000000
3
2022-11-02T15:33:34.0000000
4
2022-11-03T15:33:34.0000000
5
2022-11-04T15:37:10.0000000

Create function as below to return last 30 day dates:
CREATE FUNCTION [dbo].[RETURNDATE]()
RETURNS
#ParsedList table
(
DATEINFO DATE
)
AS
BEGIN
DECLARE #Counter Int
SET #Counter=1
WHILE ( #Counter <= 30)
BEGIN
--PRINT 'The counter value is = ' + CONVERT(VARCHAR,Convert(Date,DATEADD(DAY, -(#Counter), getdate())))
INSERT INTO #ParsedList (DATEINFO)
VALUES (CONVERT(VARCHAR,Convert(Date,DATEADD(DAY, -(#Counter), getdate()))))
SET #Counter = #Counter + 1
END
RETURN
END
now use inside your code as below:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME([DateInfo])
from [DBO].RETURNDATE()
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'WITH t (Country ,Date,total) AS (
SELECT b.country as Market,
CAST(a.ProcessDate AS Date) AS DATE,
COUNT(a.ProcessDate) AS total
FROM [dbo].[FileProcessLog] a
LEFT JOIN [dbo].[MasterFile] b ON a.FileID = b.FileID where a.ProcessDate BETWEEN ''2022-11-01'' AND ''2022-11-07''
GROUP BY b.country, CAST(a.ProcessDate AS DATE)
)
SELECT * FROM (SELECT Date,Total,Country from t) x
PIVOT(SUM(total)
FOR Date IN ('
+ #cols +
')
) as PIVOTTABLE
'
execute(#query)
I think your full answer is ready now. Happy Coding.

Related

SQL Pivot and Group By By Date and Totals

I am using SQL Server 13.0 Developer Edition.
I can't make the correct structure for the SQL code with Pivot and Group by clauses.
I have data like;
Id
OperationType
Date
ResultCode
1
BeginTransaction
2022-12-01 16:54:30
-28
2
BeginTransaction
2022-12-02 18:54:30
-30
3
BeginTransaction
2022-12-02 18:54:30
-30
4
BeginTransaction
2022-12-03 14:54:30
-10
5
BeginTransaction
2022-12-03 11:54:30
-5
6
BeginTransaction
2022-12-05 10:54:30
-3
and I want to see total number of ResultCodes per day but I want to generate ResultCode columns dynamicly because I have so much different result codes. Query result should be like;
Day
-3
-5
-10
-28
-30
Total
2022-12-01
0
0
0
1
0
1
2022-12-02
0
0
0
0
2
2
2022-12-03
0
1
1
0
0
2
2022-12-05
1
0
0
0
0
1
I wrote this query but it says The incorrect value "ResultCode" is supplied in the PIVOT operator.
Select * from (SELECT CAST(Date as date),
COUNT(ResultCode) as Result,
COUNT(*) AS Totals
FROM OperationLogs
WHERE OperationType = 'Begin'
GROUP BY CAST(StartTime as date)
) As Source
PIVOT (
COUNT(Result) FOR Result IN ([ResultCode])
) AS PivotTable
ORDER BY ForDate
Can anyone help me with how can I group by date and also have counts for ResultCodes as colums and a Total by day?
CREATE TABLE #ResultCodes (
Id INT,
OperationType VARCHAR(50),
[Date] DateTime,
ResultCode INT
)
INSERT INTO #ResultCodes(Id,OperationType,[Date],ResultCode) VALUES
(1,'BeginTransaction','2022-12-01 16:54:30',-28),
(2,'BeginTransaction','2022-12-02 18:54:30',-30),
(3,'BeginTransaction','2022-12-02 18:54:30',-30),
(4,'BeginTransaction','2022-12-03 14:54:30',-10),
(5,'BeginTransaction','2022-12-03 11:54:30',-5),
(6,'BeginTransaction','2022-12-05 10:54:30',-3)
DECLARE #COLUMNS AS NVARCHAR(MAX)
DECLARE #QUERY AS NVARCHAR(MAX)
SET #COLUMNS = STUFF((SELECT ',' + QUOTENAME(ResultCode)
FROM #ResultCodes GROUP BY ResultCode ORDER BY ResultCode DESC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #QUERY = N'
SELECT [Date],'+#COLUMNS+', tc AS "Total"
FROM (
SELECT
[tc] = COUNT(CAST([Date] AS date)) over(partition by CAST([Date] AS date)),
CAST([Date] AS date) AS "Date",
[ResultCode]
FROM #ResultCodes
) AS tb
PIVOT (
COUNT([ResultCode])
FOR [ResultCode]
IN (
'+#COLUMNS+'
)
) AS P';
EXEC(#QUERY)
DROP TABLE IF EXISTS #ResultCodes
You can find the answer here : https://www.sqlshack.com/dynamic-pivot-tables-in-sql-server/
and below
USE [tempdb]
IF OBJECT_ID ('[Date]') IS NOT NULL DROP TABLE [Date]
CREATE TABLE [Date] ([Id] int , [OperationType] varchar(50) , [Date] datetime , [ResultCode] int)
INSERT INTO [Date] VALUES (1, 'BeginTransaction', '2022-12-01 16:54:30', -28) , (2, 'BeginTransaction', '2022-12-02 18:54:30', -30) , (3, 'BeginTransaction', '2022-12-02 18:54:30', -30) , (4, 'BeginTransaction', '2022-12-03 14:54:30', -10) , (5, 'BeginTransaction', '2022-12-03 11:54:30', -5) , (6, 'BeginTransaction', '2022-12-05 10:54:30', -3)
DECLARE #pivotcolumns varchar(500) = STUFF((SELECT ',' + QUOTENAME(CAST([ResultCode] AS varchar(20))) FROM [Date] GROUP BY [ResultCode] ORDER BY [ResultCode] FOR XML PATH('')),1,1,'')
DECLARE #SqlStatement NVARCHAR(MAX)
SET #SqlStatement = N'
SELECT * , SUM('+REPLACE(#pivotcolumns, ',', '+')+') OVER (PARTITION BY CAST([Date] AS date)) AS "Total" FROM (
SELECT
CAST([Date] AS date) AS "Date", [ResultCode]
FROM [Date]
) AS t1
PIVOT (
COUNT([ResultCode])
FOR [ResultCode]
IN (
'+#pivotcolumns+'
)
) AS PivotTable
';
EXEC(#SqlStatement)

Trying to Sum up Cross-Tab Data in SQL

I have a table where every ID has one or more places, and each place comes with a count. Places can be repeated within IDs. It is stored in rows like so:
ID ColumnName DataValue
1 place1 ABC
1 count1 5
2 place1 BEC
2 count1 12
2 place2 CDE
2 count2 6
2 place3 BEC
2 count3 9
3 place1 BBC
3 count1 5
3 place2 BBC
3 count2 4
Ultimately, I want a table where every possible place name is its own column, and the count per place per ID is summed up, like so:
ID ABC BEC CDE BBC
1 5 0 0 0
2 0 21 6 0
3 0 0 0 9
I don't know the best way to go about this. There are around 50 different possible place names, so specifically listing them out in a query isn't ideal. I know I ultimately have to pivot the data, but I don't know if I should do it before or after I sum up the counts. And whether it's before or after, I haven't been able to figure out how to go about summing it up.
Any ideas/help would be greatly appreciated. At this point, I'm having a hard time finding where to even start. I've seen a few posts with similar problems, but nothing quite as convoluted as this.
EDIT:
Right now I'm working with this to pivot the table, but this leaves me with columns named place1, place2, .... count1, count2,...
and I don't know how to appropriately sum up the counts and make new columns with the place names.
DECLARE #cols NVARCHAR(MAX), #query NVARCHAR(MAX);
SET #cols = STUFF(
(
SELECT DISTINCT
','+QUOTENAME(c.[ColumnName])
FROM #temp c FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)'), 1, 1, '');
SET #query = 'SELECT [ID], '+#cols+'from (SELECT [ID],
[DataValue] AS [amount],
[ColumnName] AS [category]
FROM #temp
)x pivot (max(amount) for category in ('+#cols+')) p';
EXECUTE (#query);
Your table structure is pretty bad. You'll need to normalize your data before you can attempt to pivot it. Try this:
;WITH IDs AS
(
SELECT DISTINCT
id
,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
,Place = datavalue
FROM #temp
WHERE ISNUMERIC(datavalue) = 0
)
,Counts AS
(
SELECT DISTINCT
id
,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
,Cnt = CAST(datavalue AS INT)
FROM #temp
WHERE ISNUMERIC(datavalue) = 1
)
SELECT
piv.id
,ABC = ISNULL(piv.ABC, 0)
,BEC = ISNULL(piv.BEC, 0)
,CDE = ISNULL(piv.CDE, 0)
,BBC = ISNULL(piv.BBC, 0)
FROM (SELECT i.id, i.Place, c.Cnt FROM IDs i JOIN Counts c ON c.id = i.id AND c.ColId = i.ColId) src
PIVOT ( SUM(Cnt)
FOR Place IN ([ABC], [BEC], [CDE], [BBC])
) piv;
Doing it with dynamic SQL would yield the following:
SET #query =
';WITH IDs AS
(
SELECT DISTINCT
id
,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
,Place = datavalue
FROM #temp
WHERE ISNUMERIC(datavalue) = 0
)
,Counts AS
(
SELECT DISTINCT
id
,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
,Cnt = CAST(datavalue AS INT)
FROM #temp
WHERE ISNUMERIC(datavalue) = 1
)
SELECT [ID], '+#cols+'
FROM
(
SELECT i.id, i.Place, c.Cnt
FROM IDs i
JOIN Counts c ON c.id = i.id AND c.ColId = i.ColId
) src
PIVOT
(SUM(Cnt) FOR Place IN ('+#cols+')) piv;';
EXECUTE (#query);
Try this out:
SELECT id,
COALESCE(ABC, 0) AS ABC,
COALESCE(BBC, 0) AS BBC,
COALESCE(BEC, 0) AS BEC,
COALESCE(CDE, 0) AS CDE
FROM
(SELECT id,
MIN(CASE WHEN columnname LIKE 'place%' THEN datavalue END) AS col,
CAST(MIN(CASE WHEN columnname LIKE 'count%' THEN datavalue END) AS INT) AS val
FROM t
GROUP BY id, RIGHT(columnname, 1)
) src
PIVOT
(SUM(val)
FOR col in ([ABC], [BBC], [BEC], [CDE])) pvt
Tested here: http://rextester.com/XUTJ68690
In the src query, you need to re-format your data, so that you have a unique id and place in each row. From there a pivot will work.
If the count is always immediately after the place, the following query will generate a data set for pivoting.
The result data set before pivoting has the following columns:
id, placename, count
select placeTable.id, placeTable.datavalue, countTable.datavalue
from
(select *, row_number() over (order by id, %%physloc%%) as rownum
from test
where isnumeric(datavalue) = 1
) as countTable
join
(select *, row_number() over (order by id, %%physloc%%) as rownum
from test
where isnumeric(datavalue) <> 1
) as placeTable
on countTable.id = placeTable.id and
countTable.rownum = placeTable.rownum
Tested on sqlfidde mssqlserver: http://sqlfiddle.com/#!6/701c91/18
Here is one other approach using PIVOT operator with dynamic style
declare #Col varchar(2000) = '',
#Query varchar(2000) = ''
set #Col = stuff(
(select ','+QUOTENAME(DataValue)
from table where isnumeric(DataValue) = 0
group by DataValue for xml path('')),1,1,'')
set #Query = 'select id, '+#Col+' from
(
select id, DataValue,
cast((case when isnumeric(DataValue) = 1 then DataValue else lead(DataValue) over (order by id) end) as int) Value
from table
) as a
PIVOT
(
sum(Value) for DataValue in ('+#Col+')
)pvt'
EXECUTE (#Query)
Note : I have used lead() function to access next rows data if i found character string values and replace with numeric data values
Result :
id ABC BBC BEC CDE
1 5 NULL NULL NULL
2 NULL NULL 21 6
3 NULL 9 NULL NULL

SQL query to get customers yearly net amount and Year to Year growth percentage

Here is the scenario,
I am creating a report where, for each customer yearly ordered amount will be shown. And than the growth percentage comparing the previous years.
What I have tried so far :
SELECT * FROM (select MrCode MrCode17,MrName MrName17, SUM(NetAmt) NetAmount17,SUM(TotalNetAmt) TotalNetAmount17 from TASKORDER_REGISTER where UCODE = 'SR01'
AND TaskOrder_Dt between '2017-1-1 00:00:00' and '2017-12-31 00:00:00'
GROUP BY MrCode,MrName) as abc
FULL JOIN
(select MrCode MrCode16,MrName MrName16, SUM(NetAmt) NetAmount16,SUM(TotalNetAmt) TotalNetAmount16 from TASKORDER_REGISTER where UCODE = 'SR01'
AND TaskOrder_Dt between '2016-1-1 00:00:00' and '2016-12-31 00:00:00'
GROUP BY MrCode,MrName) AS bbb
ON abc.MrCode17 = bbb.MrCode16
FULL JOIN
(select MrCode MrCode15,MrName MrName15, SUM(NetAmt) NetAmount15,SUM(TotalNetAmt) TotalNetAmount15 from TASKORDER_REGISTER where UCODE = 'SR01'
AND TaskOrder_Dt between '2015-1-1 00:00:00' and '2015-12-31 00:00:00'
GROUP BY MrCode,MrName) AS ccc
ON bbb.MrCode16 = ccc.MrCode15
What is the result :
I am not sure what is the best procedure to accmplish the task. There must sume better queries to get data. And I could not create the growth percentage yet.
Please use following query it will return Year,Difference and [Difference Precantage].
Please add other columns in result set
IF OBJECT_ID('tempdb..#SalesData') IS NOT NULL BEGIN DROP TABLE #SalesData END
IF OBJECT_ID('tempdb..#FinalData') IS NOT NULL BEGIN DROP TABLE #FinalData END
SELECT IDENTITY(int, 1,1) AS ID_Num ,year(TaskOrder_Dt) SalesYear,SUM(NetAmt) TotalAmount
INTO #SalesData
FROM TASKORDER_REGISTER GROUP BY year(TaskOrder_Dt)
SELECT
[current].ID_Num,
[current].TotalAmount,
[current].SalesYear,
[current].TotalAmount - ISNULL([next].TotalAmount, 0) AS Diff,
(([current].TotalAmount - ISNULL([next].TotalAmount,0)) * ISNULL([next].TotalAmount,1)) * 100 AS Diffper
INTO #FinalData
FROM #SalesData AS [current]
LEFT JOIN #SalesData AS [next]
ON [next].ID_Num = (SELECT MAX(ID_Num) FROM #SalesData WHERE ID_Num < [current].ID_Num)
DECLARE #cols AS NVARCHAR(MAX),#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(SalesYear) FROM #FinalData FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'),1,1,'')
SELECT #query = 'SELECT * FROM (SELECT [SalesYear],Diff,Diffper FROM #FinalData) X
PIVOT (AVG(Diff) for [SalesYear] in (' + #cols + ')) P'
EXEC SP_EXECUTESQL #query

how can I use Pivot using a variable in SQL?

I have paycode numbers and Names from table Paycodes
and I have Amount in monthlyTransaction. as follows:
Paycodes
Code Name
1 Basic Salary
2 Variable Deduction Amount
3 Fixed/Var Insurance PayCode
MonthlyTransaction
Code Amount
1 3000
2 10000
1 130000
1 150000
3 120000
I want it to be like this using pivot
Basic Salary Variable Deduction Amount Fixed/Var Insurance PayCode
31000 10000 120000
I want to use pivot to sum the Amount of each Paycode and I used this:-
DECLARE #data AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
-- DECLARE #data TABLE
--(
-- PaycodeName NVARCHAR(max)
--)
--INSERT INTO #data
-- ( PaycodeName )
--select dbo.Paycode.PayCodeName FROM dbo.Paycode
select #data = Paycode.PayCodeName FROM Paycode
set #query = 'SELECT * FROM (
SELECT Paycode.Name , MonthlyTransaction.Amount
From MonthlyTransaction
LEFT JOIN dbo.Paycode ON Paycode.code = MonthlyTransaction.Paycode
) AS s
PIVOT
(
SUM(Amount)
FOR Paycode.Name IN ('+#data+')
) AS pvt '
EXECUTE Sp_executesql #query
When I print #data it retrieve the last paycode only !
Can anyone help ?
To get all the paycodes in #data use
SELECT #data = #data + Paycode.PayCodeName + ', '
FROM Paycode
Then remove the last comma
the answer is :
DECLARE #codeNameCol NVARCHAR(max)
SELECT #codeNameCol= COALESCE(#codeNameCol + ',','') + QUOTENAME(RTRIM(LTRIM(PayCodeName)))
FROM (SELECT DISTINCT PayCodeName FROM Paycode) AS codeNameCol
DECLARE #querys NVARCHAR(max)
Set #querys='
SELECT *
FROM ( SELECT dbo.EmpAssignment.EmployeeId ,
PayCodeName ,
Amount
FROM dbo.MonthlyTransaction
LEFT JOIN dbo.Paycode ON Paycode.code = MonthlyTransaction.Paycode
LEFT JOIN dbo.EmpAssignment ON EmpAssignment.EmpId = MonthlyTransaction.EmpId
LEFT JOIN dbo.PayrollGroup ON PayrollGroup.PayrollGroup = EmpAssignment.PayrollGroup
) DataTable PIVOT
( SUM(Amount) FOR PayCodeName IN ( '+#codeNameCol+' ) ) PivotTable;'
EXEC (#querys)
This should do the trick
SELECT 1 Code, 'Basic Salary' Name
INTO #Paycode
UNION
SELECT 2 Code, 'Variable Deduction Amount' Name
UNION
SELECT 3 Code, 'Fixed/Var Insurance PayCode' Name
SELECT 1 Code, 3000 Amount
INTO #MonthTrans
UNION
SELECT 2 Code, 10000 Amount
UNION
SELECT 1 Code, 130000 Amount
UNION
SELECT 1 Code, 150000 Amount
UNION
SELECT 3 Code, 120000 Amount
DECLARE #data AS NVARCHAR(MAX)
SELECT #data = ISNULL(#data,'') + '[' +CAST(Code AS varchar) + '], '
FROM #Paycode
SELECT #data=SUBSTRING(#data, 0, LEN(#data))
DECLARE #query AS NVARCHAR(MAX)
SELECT #query = 'SELECT *
FROM
#MonthTrans M
PIVOT (
SUM(Amount) FOR Code IN (' + #data + ')
) pvt'
EXECUTE Sp_executesql #query
drop table #paycode
drop table #MonthTrans

SQL Pivot - Dynamic Columns, No Aggregation

I'm trying to do a Pivot, but I'm not very experienced with pivots and I'm stuck - I can't figure out how to structure the query.
What I have:
Data Types (types of measurements that are recorded)
Locations
Data Sources (things at each location that will be measured)
Data Readings (measurements of the sources)
Additional information:
The number of Sources at any one Location can change
There will never be more than 5 sources at a single Location
Only 1 Reading is saved per Source/Type/date
In the returned table:
Table shows Data_Type info and Readings for a single Location and date
Columns: Data_Name, Units, Is_Required (from Data_Type table), plus one column for each Source
Rows: one row for each Data_Type
Rows should be ordered by Type_Display_Order
Sources (extra columns) should be ordered by Source_Display_Order
Some Readings are optional, and some Sources aren't measured daily - these still need to be included in the table
Example:
Table: Data_Type
Data_Type_ID Data_Name Units Is_Required (BIT) Type_Display_Order
-----------------------------------------------------------------------
1 Height In. 1 2
2 Length In. 0 3
3 Weight Lbs. 1 1
Table: Location
Location_ID Location
-----------------------
1 West
2 East
Table: Data_Source
Data_Source_ID Location_ID Source_Name Source_Display_Order
----------------------------------------------------------------
1 1 WCS 2
2 2 ECS 1
3 1 WBN 1
Table: Data_Reading
Data_Reading_ID Data_Type_ID Data_Source_ID Reading Reading_Date
----------------------------------------------------------------------
1 1 1 5 6/3/2016
2 3 2 3 5/1/2016
3 1 1 7 5/1/2016
4 2 3 2 6/3/2016
5 3 1 4 6/3/2016
Desired results from query for Location = "West", Date = 6/3/2016:
Data_Type_ID Data_Name Units Is_Required WBN WCS
---------------------------------------------------------
3 Weight Lbs. 1 NULL 4
1 Height In. 1 NULL 5
2 Length In. 0 NULL NULL
This solution seems to be similar: Pivot Dynamic Columns, no Aggregation but I'm still having some problems.
This is what I have so far:
DECLARE #date DATE, #locationID INT
SET #date = CAST('6/3/2016' AS DATE)
SET #locationID = 1
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(s.Source_Name)
FROM Data_Source s
WHERE s.Location_ID = #locationID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT Data_Type_ID, Data_Name, Units, Is_Required, ' + #cols +
' FROM
(
SELECT
t.Data_Type_ID
, t.Data_Name
, t.Units
, t.Is_Required
, r.Reading
, s.Source_Name
FROM
Data_Type t
LEFT JOIN
Data_Reading r ON t.Data_Type_ID = r.Data_Type_ID
LEFT JOIN
Data_Source s ON r.Data_Source_ID = s.Data_Source_ID
WHERE
r.Reading_Date = CAST(CAST(' + #date + ' AS NVARCHAR(10)) AS DATE)
AND s.Location_ID = CAST(' + #locationID + ' AS INT)
) x
PIVOT
(
MIN(Reading)
for Source_Name in (' + #cols + ')
) p '
I have the query working properly now, but I still have a few problems:
#cols is not sorted by Source_Display_Order
rows are not sorted by Type_Display_Order (I did have ORDER BY in the inner SELECT statement for part X, but I was getting errors saying I can't have an ORDER BY clause there)
Date comparison in WHERE statement doesn't work - for some reason, it always computes as False, even when the dates are the same
Solved!
DECLARE #date DATE, #locationID INT
SET #date = CAST('6/3/2016' AS DATE)
SET #locationID = 1
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT ',' + QUOTENAME(s.Source_Name)
FROM Data_Source s
WHERE s.Location_ID = #locationID
ORDER BY s.Source_Display_Order
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query =
'SELECT
Data_Type_ID
, Data_Name
, Units
, Is_Required
, ' + #cols + '
FROM
(
SELECT
t.Data_Type_ID
, t.Data_Name
, t.Units
, t.Is_Required
, r.Reading
, s.Source_Name
, t.Type_Display_Order
FROM
Data_Type t
LEFT JOIN
Data_Reading r ON t.Data_Type_ID = r.Data_Type_ID
LEFT JOIN
Data_Source s ON r.Data_Source_ID = s.Data_Source_ID
WHERE
r.Reading_Date = ''' + CAST(#date AS NVARCHAR(10)) + '''
AND s.Location_ID = ' + CAST(#locationID AS NVARCHAR(20)) + '
) x
PIVOT
(
MIN(Reading)
for Source_Name in (' + #cols + ')
) p
ORDER BY
Type_Display_Order'
EXECUTE(#query)
To fix my problems:
Convert #date to NVARCHAR before adding to #query string and include extra quotes to surround the new NVARCHAR in quotes within #query
Remove DISTINCT clause from #cols and add ORDER BY (all of the names in my table are unique, so the DISTINCT is unnecessary)
Add Type_Display_Order to the inner SELECT statement, and add ORDER BY after the PIVOT statement