SQL Server stored procedure to produce a calendar - sql-server

I have a stored procedure that returns the timeslots throughout the day, along with the capacity for each timeslot.
ALTER PROCEDURE [dbo].[TimeSlots_Sel]
(
#dateReqd date,
#typeReqd varchar(5) = 'ATI'
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #timeNow time(0) = CONVERT(time(0),GETDATE())
IF #dateReqd >= CONVERT(date, GETDATE())
BEGIN
DECLARE #tblWIP AS dbo.tvpWIP
INSERT INTO #tblWIP
SELECT [Type], [status_id], Scheduled_Call
FROM dbo.WIP
WHERE status_id = 70
AND Scheduled_Call >= #dateReqd
AND Scheduled_Call < DATEADD(dd,1,#dateReqd)
SELECT ts.SlotTime,
dbo.funCapacity(#tblWIP, #dateReqd, ts.SlotTime, #typeReqd) AS [Capacity]
FROM dbo.TimeSlots ts
WHERE NOT EXISTS
(
SELECT *
FROM dbo.Downtime dt
WHERE dbo.funStrToDateTime(YEAR(#dateReqd),MONTH(#dateReqd),DAY(#dateReqd), DATEPART(HOUR, ts.SlotTime),DATEPART(MINUTE, ts.SlotTime))
BETWEEN
dbo.funStrToDateTime(YEAR(#dateReqd),dt.StartMonth, dt.StartDay, dt.StartHour, dt.StartMin)
AND dbo.funStrToDateTime(YEAR(#dateReqd),dt.EndMonth, dt.EndDay, dt.EndHour, dt.EndMin)
)
AND dbo.funCapacity(#tblWIP, #dateReqd, ts.SlotTime, #typeReqd) > 0
AND
(
#dateReqd > GETDATE()
OR
ts.SlotTime >= #timeNow
)
ORDER BY SlotTime
END
END
So I get information like this
SlotTime Capacity
--------------------
09:00:00 3
09:30:00 3
10:00:00 3
10:30:00 3
11:00:00 3
11:30:00 2
12:00:00 2
12:30:00 2
But now I need to display a grid showing capacity for the next 7 days by date. Any ideas how I could modify the stored procedure to return something like this
SlotTime Sun 14/11/2010 Mon 15/11/2010 Tue 16/11/2010 Wed 17/11/2010 Thu 18/11/2010 Fri 19/11/2010 Sat 20/11/2010
09:00:00 3 3 3 1 3 1 1
09:30:00 3 3 3 3 3 3 3
10:00:00 3 3 3 1 3 3 3
10:30:00 3 3 3 1 3 1 2
11:00:00 3 3 3 3 3 3 3
11:30:00 3 3 3 1 3 3 3
I do have a similar procedure for creating a calendar which does a similar thing but I am finding it very difficult to display what I want. Here is the other stored procedure:
ALTER PROCEDURE [dbo].[Calendar_Sel] ( #StartDate DATE )
AS
BEGIN
SET NOCOUNT ON
DECLARE #EndDate DATE = DATEADD(Day,6,#StartDate)
DECLARE #dates VARCHAR(MAX)
DECLARE #datevals VARCHAR(MAX)
DECLARE #sql NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(100)
;WITH cte AS
(
SELECT #StartDate AS DateColumn UNION ALL
SELECT DATEADD(Day,1,DateColumn) FROM cte WHERE DateColumn < #EndDate
)
SELECT DateColumn INTO #temp FROM cte
SET #dates = STUFF((SELECT ',[' + CONVERT(VARCHAR,DateColumn,112) + ']' FROM #temp FOR XML PATH('') ),1,1,'')
SET #datevals = STUFF((SELECT ',COALESCE([' + CONVERT(VARCHAR,DateColumn,112) + '],'''') AS [' + CONVERT(VARCHAR,DateColumn,112) + ']' FROM #temp FOR XML PATH('') ),1,1,'')
SET #sql = '
DECLARE #EndDate DATE = DATEADD(Day,6,#StartDate)
;WITH cte AS
(
SELECT #StartDate AS DateColumn UNION ALL
SELECT DATEADD(Day,1,DateColumn) FROM cte WHERE DateColumn < #EndDate
)
,cte1 AS
(
SELECT DISTINCT r.RoomID, CONVERT(VARCHAR,DateColumn,112) AS DateColumn,
CAST(CASE WHEN EXISTS(SELECT * FROM dbo.Payments WHERE BookingID = b.BookingID) THEN 1 ELSE 0 END AS CHAR(1))
+ CAST(b.BookingID AS CHAR(15)) + c.Surname AS CompoundKey
FROM dbo.Bookings b
JOIN dbo.BookingsDetails bd
ON b.BookingID = bd.BookingID
JOIN dbo.Customers c
ON c.CustomerId = b.CustomerId
JOIN dbo.Rooms r
ON r.RoomId = bd.RoomId
JOIN cte
ON cte.DateColumn BETWEEN b.DateArrive AND DATEADD(Day,-1,b.DateLeave)
)
SELECT Room,
Price1 = dbo.funStayPrice(#StartDate,DATEADD(Day,1,#StartDate),RoomTypeID,0),
Price2 = dbo.funStayPrice(DATEADD(Day,1,#StartDate),DATEADD(Day,2,#StartDate),RoomTypeID,0),
Price3 = dbo.funStayPrice(DATEADD(Day,2,#StartDate),DATEADD(Day,3,#StartDate),RoomTypeID,0),
Price4 = dbo.funStayPrice(DATEADD(Day,3,#StartDate),DATEADD(Day,4,#StartDate),RoomTypeID,0),
Price5 = dbo.funStayPrice(DATEADD(Day,4,#StartDate),DATEADD(Day,5,#StartDate),RoomTypeID,0),
Price6 = dbo.funStayPrice(DATEADD(Day,5,#StartDate),DATEADD(Day,6,#StartDate),RoomTypeID,0),
Price7 = dbo.funStayPrice(DATEADD(Day,6,#StartDate),DATEADD(Day,7,#StartDate),RoomTypeID,0),
SUBSTRING([' + CONVERT(VARCHAR,#StartDate,112) + '],16,12) AS Entry1,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,1,#StartDate),112) + '],16,12) AS Entry2,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,2,#StartDate),112) + '],16,12) AS Entry3,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,3,#StartDate),112) + '],16,12) AS Entry4,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,4,#StartDate),112) + '],16,12) AS Entry5,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,5,#StartDate),112) + '],16,12) AS Entry6,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,6,#StartDate),112) + '],16,12) AS Entry7,
SUBSTRING([' + CONVERT(VARCHAR,#StartDate,112) + '],2,15) AS ID1,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,1,#StartDate),112) + '],2,15) AS ID2,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,2,#StartDate),112) + '],2,15) AS ID3,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,3,#StartDate),112) + '],2,15) AS ID4,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,4,#StartDate),112) + '],2,15) AS ID5,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,5,#StartDate),112) + '],2,15) AS ID6,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,6,#StartDate),112) + '],2,15) AS ID7,
SUBSTRING([' + CONVERT(VARCHAR,#StartDate,112) + '],1,1) AS Status1,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,1,#StartDate),112) + '],1,1) AS Status2,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,2,#StartDate),112) + '],1,1) AS Status3,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,3,#StartDate),112) + '],1,1) AS Status4,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,4,#StartDate),112) + '],1,1) AS Status5,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,5,#StartDate),112) + '],1,1) AS Status6,
SUBSTRING([' + CONVERT(VARCHAR,DATEADD(Day,6,#StartDate),112) + '],1,1) AS Status7
FROM
(
SELECT r.RoomName, r.RoomName + '' ['' + rt.TypeDescription + '']'' AS Room, rt.RoomTypeID, '+ #datevals + '
FROM cte1
PIVOT (MAX(CompoundKey) FOR DateColumn IN (' + #dates + ')) AS p
RIGHT OUTER JOIN dbo.Rooms r
ON r.RoomID = p.RoomID
JOIN dbo.RoomTypes rt
ON rt.RoomTypeID = r.RoomTypeID
) x
ORDER BY CASE WHEN ISNUMERIC(RoomName) = 1 THEN CAST(RoomName AS INT) ELSE 0 END, RoomName';
SET #ParmDefinition = N'#StartDate DATE';
EXEC sp_executesql #sql, #ParmDefinition,
#StartDate = #StartDate;
END
If anyone could help me to return the capacity table I would be very grateful. I am looking to display this information in a GridView and already have the column headings, so I just need the right stored procedure with the data.
Thanks very very much for any help. Any suggestions I am sure would be a great help.

I've tried to simplify your request down a bit and come up with this solution. Basically, we are getting a list of dates and times cross joined with a function to get the capacity (i used a random number here) then pivoting on the dates. Since the columns will be dynamic, we need to use a dynamic query to get the column headers.
Hopefully, you can use this to apply to your own query.
CREATE TABLE #TimeSlots (SlotTime TIME)
INSERT INTO #TimeSlots VALUES ('09:00:00')
INSERT INTO #TimeSlots VALUES ('09:30:00')
INSERT INTO #TimeSlots VALUES ('10:00:00')
INSERT INTO #TimeSlots VALUES ('10:30:00')
INSERT INTO #TimeSlots VALUES ('11:00:00')
INSERT INTO #TimeSlots VALUES ('11:30:00')
CREATE TABLE #DateSlots (Dates DATE)
INSERT INTO #DateSlots VALUES (GETDATE())
INSERT INTO #DateSlots VALUES (DATEADD(DAY, 1, GETDATE()))
INSERT INTO #DateSlots VALUES (DATEADD(DAY, 2, GETDATE()))
INSERT INTO #DateSlots VALUES (DATEADD(DAY, 3, GETDATE()))
INSERT INTO #DateSlots VALUES (DATEADD(DAY, 4, GETDATE()))
INSERT INTO #DateSlots VALUES (DATEADD(DAY, 5, GETDATE()))
INSERT INTO #DateSlots VALUES (DATEADD(DAY, 6, GETDATE()))
INSERT INTO #DateSlots VALUES (DATEADD(DAY, 7, GETDATE()))
DECLARE #PivotColumnHeaders VARCHAR(MAX)
SELECT #PivotColumnHeaders = COALESCE(#PivotColumnHeaders + ',[' + CAST(Dates AS VARCHAR) + ']', '[' + CAST(Dates AS VARCHAR) + ']')
FROM #DateSlots
WHERE Dates BETWEEN GETDATE() AND DATEADD(DAY, 7, GETDATE())
DECLARE #PivotTableSQL NVARCHAR(MAX)
SET #PivotTableSQL = N'
SELECT *
FROM (
SELECT SlotTime, cast(Dates as varchar) Dates, Abs(Checksum(NewId())) % 4 AS Capacity
FROM #TimeSlots
CROSS JOIN #DateSlots
WHERE Dates BETWEEN GETDATE() AND DATEADD(DAY, 7, GETDATE())
) AS Source
PIVOT
(
MAX(Capacity)
FOR Dates IN (
' + #PivotColumnHeaders + '
)
) AS PivotTable
'
EXECUTE(#PivotTableSQL)
DROP TABLE #TimeSlots
DROP TABLE #DateSlots

Related

Getting dynamic query select fields to show in Crystal Reports

I have what I consider to be a pretty complicated query (at least to me) and I decided to attempt to solve it using dynamic SQL. However I have two issues that I have not been able to solve.
Situation
In a table, a user can enter an item which has an amount, week and status.
So the data is supposed to resembles this format.
Week 1 Week 2
---------+-----------+-------------
Status 1 | 50 25
Status 2 10 20
And this is the data in SQL.
Status 1 | Week 1 | 25
Status 1 | Week 1 | 25
Status 1 | Week 2 | 25
Status 2 | Week 1 | 2
Status 2 | Week 1 | 8
Status 2 | Week 1 | 10
Status 2 | Week 1 | 10
For each status I sum the amount based on weeks using a dynamic pivot table.
What have I attempted:
--EXEC usp_weekReport #weeks=1, #year='2019'
ALTER PROCEDURE usp_weekReport
(#weeks INT,
#year NVARCHAR(4))
AS
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME([week])
FROM
(SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = #year
AND ([week] IN (#weeks)
OR [week] IN (#weeks + 1)
OR [week] IN (#weeks + 2)
OR [week] IN (#weeks + 3)
OR [week] IN (#weeks + 4)
OR [week] IN (#weeks + 5))
GROUP BY P.[week]) AS x;
SET #sql = N'
SELECT p.[statusName],' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
--PRINT #sql;
--EXEC sp_executesql #sql;
CREATE TABLE #reportResult
(
statusName nvarchar(50),
weekA INT DEFAULT 0,
weekB int DEFAULT 0
--weekC int DEFAULT 0,
--weekD int DEFAULT 0,
--weekE int DEFAULT 0,
--weekF int DEFAULT 0
)
INSERT #reportResult Exec(#sql)
SELECT statusName, weekA,weekB--,weekC,weekD,weekE,weekF -- here you have "static SELECT with field names"
FROM #reportResult
DROP TABLE #reportResult
To solve this, I have the above code and while it works(returns the values in SQL) I have two issues.
Problems
My first problem is that, I cannot use this code in conjunction with creating a report in Crystal Reports. When I import the stored procedure, the columns shows up blank. I looked at the following link. [Select field Names from Dynamic SQL query][1]
[1]: SELECT fieldnames FROM dynamic SQL query and even if I attempted to model my answer after what was said to work exactly. It does not seem to work for me as my data columns are still blank in Crystal.
I had the idea of calling my first stored procedure from a separate stored procedure but given of how my answer is returned (not just a single value that I can assign to a variable, currently thinking of returning a table value function) I doubt that that would work.
A second problem that developed is because my 'weeks' are dynamic (up to 6 weeks) I cant create the temp table with "Spare" columns or I get an error (incorrect number of columns), as you see i commented them out and I also can't use "Select into"
Appreciate any assistance or ideas offered.
Try following, it uses Global TEMP table:
ALTER PROCEDURE usp_weekReport
#weeks INT,
#year NVARCHAR(4)
AS
BEGIN
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns+=N', p.'+QUOTENAME([week])
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = #year
AND ([week] IN(#weeks)
OR [week] IN(#weeks + 1)
OR [week] IN(#weeks + 2)
OR [week] IN(#weeks + 3)
OR [week] IN(#weeks + 4)
OR [week] IN(#weeks + 5))
GROUP BY P.[week]
) AS x;
SET #sql = N'
SELECT p.[statusName],'+STUFF(#columns, 1, 2, '')+'
into ##reportResult
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('+STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')+')
) AS p;';
EXEC sp_executesql #sql;
SELECT *
FROM ##reportResult;
IF OBJECT_ID('tempdb..##reportResult') IS NOT NULL
BEGIN
DROP TABLE ##reportResult;
END;
END
Here is a solution without the Global temp table:
ALTER PROCEDURE usp_weekReport
#weeks INT,
#year NVARCHAR(4)
AS
BEGIN
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX), #COLCOUNT int, #TableColumns NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns+=N', p.'+QUOTENAME([week])
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = #year
AND ([week] IN(#weeks)
OR [week] IN(#weeks + 1)
OR [week] IN(#weeks + 2)
OR [week] IN(#weeks + 3)
OR [week] IN(#weeks + 4)
OR [week] IN(#weeks + 5))
GROUP BY P.[week]
) AS x;
SELECT #COLCOUNT = count(*)
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = #year
AND ([week] IN(#weeks)
OR [week] IN(#weeks + 1)
OR [week] IN(#weeks + 2)
OR [week] IN(#weeks + 3)
OR [week] IN(#weeks + 4)
OR [week] IN(#weeks + 5))
GROUP BY P.[week]
) AS x;
SELECT #TableColumns = CASE When #COLCOUNT = 1 THEN 'weekA'
When #COLCOUNT = 2 THEN 'weekA, weekB'
When #COLCOUNT = 3 THEN 'weekA, weekB, weekC'
When #COLCOUNT = 4 THEN 'weekA, weekB, weekC, weekD'
When #COLCOUNT = 5 THEN 'weekA, weekB, weekC, weekD, weekE'
When #COLCOUNT = 6 THEN 'weekA, weekB, weekC, weekD, weekE, weekF'
end;
CREATE TABLE #reportResult
(
statusName nvarchar(50),
weekA INT DEFAULT 0,
weekB int DEFAULT 0,
weekC int DEFAULT 0,
weekD int DEFAULT 0,
weekE int DEFAULT 0,
weekF int DEFAULT 0
)
SET #sql = N'
INSERT INTO #reportResult (statusName,' + #TableColumns + ')
SELECT p.[statusName],'+STUFF(#columns, 1, 2, '')+'
into ##reportResult
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('+STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')+')
) AS p;';
EXEC sp_executesql #sql;
SELECT *
FROM #reportResult;
IF OBJECT_ID('tempdb..#reportResult') IS NOT NULL
BEGIN
DROP TABLE #reportResult;
END;
END

Use MAX(Date) value inside a SQL recursion

I have SQL query with performs recursion based on selected Min(Date) and Max(Date) from more than 1 table. But when i try to run the query it throws GROUP BY, HAVING, or aggregate functions are not allowed in the recursive part of a recursive common table expression 'ctedaterange'. exception. I also tried using Top 1 Date but still no hope.
SQL Query
ALTER VIEW [dbo].[CYExtraction]
AS
WITH ctedaterange
AS (SELECT
[Dates] = (SELECT
MIN(CreatedOn) AS CreatedOn
FROM (SELECT
MIN(CreatedOn) AS CreatedOn
FROM (SELECT
MIN(CreatedOn) AS CreatedOn
FROM Items) AS it
UNION ALL
SELECT
*
FROM (SELECT
MIN(CreatedOn) AS CreatedOn
FROM Bibs) AS bib
UNION ALL
SELECT
*
FROM (SELECT
MIN(CreatedOn) AS CreatedOn
FROM Porders) AS po) AS AllItems)
UNION ALL
SELECT
[dates] + 1
FROM ctedaterange
WHERE [dates] + 1 <= (SELECT
MAX(CreatedOn) AS CreatedOn
FROM (SELECT
MAX(CreatedOn) AS CreatedOn
FROM (SELECT
MAX(CreatedOn) AS CreatedOn
FROM Items) AS it
UNION ALL
SELECT
*
FROM (SELECT
MAX(CreatedOn) AS CreatedOn
FROM Bibs) AS bib
UNION ALL
SELECT
*
FROM (SELECT
MAX(CreatedOn) AS CreatedOn
FROM Porders) AS po) AS AllItems))
SELECT
[Dates] AS PK_Date,
DATENAME(MONTH, [Dates]) + ' ' + CAST(YEAR([Dates]) AS varchar(4)) AS
Month,
CASE
WHEN DATEPART(m, [Dates]) <= 3 THEN 'Q1 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE CASE
WHEN DATEPART(m, [Dates]) <= 6 THEN 'Q2 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE CASE
WHEN DATEPART(m, [Dates]) <= 9 THEN 'Q3 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE CASE
WHEN DATEPART(m, [Dates]) <= 12 THEN 'Q4 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE ''
END
END
END
END AS 'Quarter',
CASE
WHEN DATEPART(m, [Dates]) <= 6 THEN 'HY1 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE CASE
WHEN DATEPART(m, [Dates]) <= 12 THEN 'HY2 ' + CAST(YEAR([Dates]) AS varchar(4))
ELSE ''
END
END AS 'HalfYear',
YEAR([Dates]) AS 'Year',
CAST('1-' AS varchar(5)) + CAST(DATEPART(M, [Dates]) AS varchar(10)) + '-' + CAST(YEAR([Dates]) AS varchar(4)) AS DateName
FROM ctedaterange
GO
The main thing is that I need to wrap it inside my SQL Views. Any help to my problem will be appreciated.
Thanks,
You can move the aggregations to an extra cte, so this might be what you want:
ALTER VIEW [dbo].[CYExtraction]
(PK_Date, [Month], [Quarter], [HalfYear], [Year], [DateName])
AS
WITH cteAllItems (mindates, maxdates) AS (
SELECT MIN(CreatedOn), MAX(CreatedOn) FROM Items
UNION ALL
SELECT MIN(CreatedOn), MAX(CreatedOn) FROM Bibs
UNION ALL
SELECT MIN(CreatedOn), MAX(CreatedOn) FROM Porders
),
ctedaterange (Dates, MaxDate) AS (
SELECT MIN(mindates), MAX(maxdates)
FROM cteAllItems
UNION ALL
SELECT DATEADD(day, 1, Dates), MaxDate
FROM ctedaterange
WHERE DATEADD(day, 1, Dates) <= MaxDate
)
SELECT
Dates AS PK_Date,
DATENAME(MONTH, Dates) + N' ' + DATENAME(YEAR, Dates),
N'Q' + DATENAME(QUARTER, Dates) + N' ' + DATENAME(YEAR, Dates),
N'HY' + CAST((MONTH(Dates)-1)/6+1 AS nvarchar(1)) + N' ' + DATENAME(YEAR, Dates),
YEAR(Dates),
CONVERT(nvarchar(10), DATEADD(MONTH, DATEDIFF(MONTH, 0, Dates), 0), 105)
FROM ctedaterange
GO

How to pivot columns in SQL Server

I'm new to SQL Server and I am trying to pivot rows into columns
select SalesOrExpense, store_no, total
from myTable
SalesOrExpense Store_No total ($)
-------------------------------------
Expense 22 100
Sales 22 400
to look like this
Store_No Expense Sales
---------------------------------
22 100 400
Could someone help point me in the right direction?
Thanks
We can even do it by executing dynamic sql query..
Query
declare #sql as varchar(max);
select #sql = stuff((
select ', sum(case [SalesOrExpense] when ' + char(39) +
[SalesOrExpense] + char(39) +
' then [total] else 0 end) as [' + [SalesOrExpense] + ']'
from [your_table_name]
for xml path('')
)
, 1, 2, ''
);
select #sql = 'select [Store_No], ' + #sql +
' from [your_table_name] group by [Store_No];';
exec(#sql);
CREATE TABLE #Table1
([SalesOrExpense] varchar(7), [Store_No] int, [total] int)
;
INSERT INTO #Table1
([SalesOrExpense], [Store_No], [total])
VALUES
('Expense', 22, 100),
('Sales', 22, 400)
select *
from
(
select *
from #Table1
) src
pivot
(
max(total)
for SalesOrExpense in ([Expense], [Sales])
) piv;
CREATE TABLE #Table1
([SalesOrExpense] varchar(7), [Store_No] int, [total] int);
INSERT INTO #Table1
([SalesOrExpense], [Store_No], [total])
VALUES
('Expense', 22, 100),
('Sales', 22, 400);
SELECT Store_No,
SUM(CASE WHEN SalesOrExpense='Expense' THEN TOTAL END) AS Expense,
SUM(CASE WHEN SalesOrExpense='Sales' THEN TOTAL END) AS Sales
FROM #table1 GROUP BY Store_No;
OUTPUT:-
-------------------------
Store_No | Expense |Sales
---------------------------
22 100 400

SQL Server dynamic pivot with multiple columns

Here's the scenario I am in. I have my data in the following format.
My source data
IssuedOn Country Sales Transactions
------------------------------------------
29-Aug-16 India 40 8
29-Aug-16 Australia 15 3
29-Aug-16 Canada 15 3
30-Aug-16 India 50 10
30-Aug-16 Australia 25 5
30-Aug-16 Canada 10 2
31-Aug-16 India 100 25
31-Aug-16 Australia 30 10
31-Aug-16 Canada 55 12
This is the output I am looking for
Expected output
IssuedDate Australia Canada India TotalSales Transactionscount
---------------------------------------------------------------------
29-Aug-16 15 15 40 70 14
30-Aug-16 25 10 50 85 17
31-Aug-16 30 55 100 185 47
I have been able to pivot the data on country and get the "Total Sales" column. However, I am not able to get the "Total Transactions" column right.
Here's the code to generate the source data table. Would really help if someone can guide me.
Create Table tbl1
(
IssuedOn date,
Country varchar(100),
Sales bigint,
Transactions bigint
)
Insert into tbl1(IssuedOn, Country, Sales, Transactions)
Values ('2016-08-29', 'India', 40, 8),
('2016-08-29', 'Australia', 15, 3),
('2016-08-29', 'Canada', 15, 3),
('2016-08-30', 'India', 50, 10),
('2016-08-30', 'Australia', 25, 5),
('2016-08-30', 'Canada', 10, 2),
('2016-08-31', 'India', 100, 25),
('2016-08-31', 'Australia', 30, 10),
('2016-08-31', 'Canada', 55, 12)
select *
from tbl1
A more verbose dynamic SQL query, without the use of a stored procedure is as follows:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #pivotSales AS NVARCHAR(MAX);
DECLARE #sql AS NVARCHAR(MAX);
SELECT #cols = ISNULL(#cols + ', ', '') + QUOTENAME(Country)
FROM (SELECT DISTINCT Country FROM tbl1) AS Countries
SET #pivotSales = N'SELECT IssuedOn, ' + #cols +'
FROM (SELECT IssuedOn, Country, Sales FROM tbl1) AS sales
PIVOT(SUM(Sales) FOR Country IN (' + #cols + ')) AS pvt';
SET #sql = ';WITH CTE_SalesPivot AS (
'+#pivotSales+'
),
CTE_SalesTotal AS (
SELECT IssuedOn, SUM(Sales) AS [Grand Total]
FROM tbl1
GROUP BY IssuedOn
),
CTE_Transactions AS (
SELECT IssuedOn, SUM(Transactions) AS [Transaction Count]
FROM tbl1
GROUP BY IssuedOn
)
SELECT CTE_SalesPivot.IssuedOn, ' + #cols + ', CTE_SalesTotal.[Grand Total], CTE_Transactions.[Transaction Count]
FROM
CTE_SalesPivot
INNER JOIN CTE_SalesTotal ON CTE_SalesPivot.IssuedOn = CTE_SalesTotal.IssuedOn
INNER JOIN CTE_Transactions ON CTE_SalesPivot.IssuedOn = CTE_Transactions.IssuedOn';
EXEC sp_executesql #sql;
The following is the stored procedure used for the bulk of my dynamic pivots
Exec [prc-Pivot] 'tbl1','Country','sum(Sales)[]','IssuedOn','sum(Transactions)[Transactions],sum(Sales)[TotalSales]'
IssuedOn Transactions TotalSales Australia Canada India
2016-08-29 14 70 15 15 40
2016-08-30 17 85 25 10 50
2016-08-31 47 185 30 55 100
The stored procedure
ALTER PROCEDURE [dbo].[prc-Pivot] (
#Source varchar(1000), -- Any Table or Select Statement
#PvotCol varchar(250), -- Field name or expression ie. Month(Date)
#Summaries varchar(250), -- aggfunction(aggValue)[optionalTitle]
#GroupBy varchar(250), -- Optional additional Group By
#OtherCols varchar(500) ) -- Optional Group By or aggregates
AS
--Exec [prc-Pivot] 'Select Year=Year(TR_Date),* From [Chinrus-Series].[dbo].[DS_Treasury_Rates]','''Q''+DateName(QQ,TR_Date)','avg(TR_Y10)[-Avg]','Year','count(*)[Records],min(TR_Y10)[Min],max(TR_Y10)[Max],Avg(TR_Y10)[Avg]'
--Exec [prc-Pivot] '#Temp','Attribute','max(Description)[]','ID','count(*)[Records]'
Set NoCount On
Declare #Vals varchar(max),#SQL varchar(max);
Set #Vals = ''
Set #OtherCols= IsNull(', ' + #OtherCols,'')
Set #Source = case when #Source Like 'Select%' then #Source else 'Select * From '+#Source end
Create Table #TempPvot (Pvot varchar(100))
Insert Into #TempPvot
Exec ('Select Distinct Convert(varchar(100),' + #PvotCol + ') as Pvot FROM (' + #Source + ') A')
--Select #Vals = #Vals + ', isnull(' + Replace(Replace(#Summaries,'(','(CASE WHEN ' + #PvotCol + '=''' + Pvot + ''' THEN '),')[', ' END),NULL) As [' + Pvot ) From #TempPvot Order by Pvot
Select #Vals = #Vals + ', isnull(' + Replace(Replace(#Summaries,'(','(CASE WHEN ' + #PvotCol + '=''' + Pvot + ''' THEN '),')[', ' END),0) As [' + Pvot ) From #TempPvot Order by Pvot
Drop Table #TempPvot
Set #SQL = Replace('Select ' + Isnull(#GroupBy,'') + #OtherCols + #Vals + ' From (' + #Source + ') PvtFinal ' + case when Isnull(#GroupBy,'')<>'' then 'Group By ' + #GroupBy + ' Order by ' + #GroupBy else '' end,'Select , ','Select ')
--Print #SQL
Exec (#SQL)
here's a simple way to do it using aggregate case expressions.
DECLARE #cols NVARCHAR(MAX),
#sql NVARCHAR(MAX)
SELECT #cols = STUFF((
SELECT ',' + 'SUM(CASE WHEN Country = ''' + Country + ''' THEN Sales END) AS ' + QUOTENAME(Country)
FROM (SELECT DISTINCT Country FROM tbl1) t
ORDER BY Country
FOR XML PATH('')
),1, 1, '')
SET #sql = N'
SELECT IssuedOn, ' + #cols + ',
SUM(Sales) AS TotalSales,
SUM(Transactions) AS TransactionCount
FROM tbl1
GROUP BY IssuedOn
'
EXEC(#sql)
this will generate a query that should look like this.
SELECT IssuedOn,
SUM(CASE WHEN Country = 'Australia' THEN Sales END) AS [Australia],
SUM(CASE WHEN Country = 'Canada' THEN Sales END) AS [Canada],
SUM(CASE WHEN Country = 'India' THEN Sales END) AS [India],
SUM(Sales) AS TotalSales,
SUM(Transactions) AS TransactionCount
FROM tbl1
GROUP BY IssuedOn

SQL Server Dynamic Pivot Query

I am creating a dynamic pivot query that shows the total NetAmount per Week of every Customers within a given date range. The problem is it doesn't ADD ALL the NetAmount within the Week. Here are the data of tblSampleSalesInvoices:
Here is my script.
CREATE PROCEDURE uspSalesWeeklySummary
(
#CustomerId INT,
#FromDate DATETIME,
#ToDate DATETIME
)
AS
SET NOCOUNT ON
DECLARE #Query AS VARCHAR(MAX)
DECLARE #DateStart DATETIME = #FromDate
DECLARE #tmp TABLE ([Date] VARCHAR(MAX))
DECLARE #Month VARCHAR(MAX)
DECLARE #Day VARCHAR(MAX)
DECLARE #ColumnHeader VARCHAR(MAX)
DECLARE #Headers VARCHAR(MAX)
WHILE DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #DateStart), '19050101') <= DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #ToDate), '19050101')
BEGIN
SET #month = DATENAME(Month, DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #DateStart), '19050101'))
SET #day = CAST( DATEPART(DD, DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #DateStart), '19050101')) AS VARCHAR(MAX) )
SET #ColumnHeader = 'Week ' + CAST(DatePart(WEEK,#DateStart) AS VARCHAR(MAX)) + ' - ' + CAST(Year(DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #DateStart), '19050101')) AS VARCHAR(MAX)) + ' - ' + #month + ' - ' + #day
INSERT INTO #tmp ([Date])
VALUES (#ColumnHeader)
SET #DateStart = DATEADD(DD, 7, #DateStart)
END
SELECT #Headers = ISNULL(#Headers + ',','') + QUOTENAME(t.[Date])
FROM #tmp t
SET #Headers = #Headers + ',[Grand Total]'
SET #Query =
'
DECLARE #CustomerId INT = ' + CAST(#CustomerId AS VARCHAR) + '
DECLARE #FromDate DATETIME = CAST(''' + CAST(#FromDate AS VARCHAR) + ''' AS DATETIME)
DECLARE #ToDate DATETIME = CAST(''' + CAST(#ToDate AS VARCHAR) + ''' AS DATETIME)
DECLARE #Headers VARCHAR(MAX) = ''' + CAST(#Headers AS VARCHAR(MAX)) + '''
SELECT *
FROM
(
SELECT c.CustomerName AS CustomerName,
''Week '' + CAST(DatePart(WEEK,si.TransactionDate) AS VARCHAR(MAX)) + '' - ''
+ CAST(Year(DATEADD(WEEK, DATEDIFF(WEEK, ''19050101'', si.TransactionDate), ''19050101'')) AS VARCHAR(MAX)) + '' - ''
+ CAST(DATENAME(Month, DATEADD(WEEK, DATEDIFF(WEEK, ''19050101'', si.TransactionDate), ''19050101'')) AS VARCHAR(MAX)) + '' - ''
+ CAST(DATEPART(DD, DATEADD(WEEK, DATEDIFF(WEEK, ''19050101'', si.TransactionDate), ''19050101'')) AS VARCHAR(MAX)) AS Header,
SUM(si.NetAmount) AS NetAmount
FROM tblSampleSalesInvoices si
LEFT OUTER JOIN tblSampleCustomers c ON c.Id = si.CustomerId
WHERE (si.TransactionDate BETWEEN #FromDate AND DATEADD(WEEK, DATEDIFF(WEEK, ''19050101'', DATEADD(DD, 7 , #ToDate)), ''19050101''))
AND (si.CustomerId = #CustomerId OR #CustomerId = 0)
GROUP BY c.CustomerName, si.TransactionDate
UNION ALL
SELECT c.CustomerName AS CustomerName,
''Grand Total'' AS Header,
SUM(si.NetAmount) AS NetAmount
FROM tblSampleSalesInvoices si
LEFT OUTER JOIN tblSampleCustomers c ON c.Id = si.CustomerId
WHERE (si.TransactionDate BETWEEN #FromDate AND DATEADD(WEEK, DATEDIFF(WEEK, ''19050101'', DATEADD(DD, 7 , #ToDate)), ''19050101''))
AND (si.CustomerId = #CustomerId OR #CustomerId = 0)
GROUP BY c.CustomerName
) AS BaseData
PIVOT
(
SUM(NetAmount)
FOR Header IN (' + #Headers + ')
) AS Pivoting'
EXEC (#Query)
GO
EXEC uspSalesWeeklySummary 0,'01/01/2016','02/01/2016'
In this script the SUM of NetAmount will be 10000 only because From the Day of Jan 1, 2016 until Feb 1,2016 only 1 transaction (TR0002) has been made. But when I place Dec 27,2015 and Feb 1,2016 in the parameters. It shows only the NetAmount of TR0001 instead of 25000 which the SUM of TR0001 and TR0002.
If you are looking for week numbers relative to a start date and can create # tables then this might suit
CREATE PROCEDURE uspSalesWeeklySummary
(
#CustomerId INT,
#FromDate DATETIME,
#ToDate DATETIME
)
AS
SET NOCOUNT ON
set datefirst 1
/*
create table tblSampleSalesInvoices (id int,transactionNo int,customerid int,TransactionDate date,netamount int)
insert into tblSampleSalesInvoices values
(1,1,1,'2015-12-29',15000),
(2,2,1,'2016-01-01',15000),
(3,3,2,'2016-03-01',15000),
(4,4,3,'2016-04-01',15000),
(5,5,4,'2016-06-01',15000),
(6,6,1,'2016-09-01',15000),
(7,7,2,'2016-10-01',15000),
(8,8,3,'2016-12-01',15000),
(9,9,4,'2017-01-01',15000),
(10,10,1,'2017-04-01',15000),
(11,11,2,'2017-07-01',15000),
(12,12,3,'2017-10-01',15000),
(13,13,4,'2017-12-01',15000)
*/
--declare #CustomerId INT = 1
--declare #FromDate DATETIME = '2015-12-27'
--declare #ToDate DATETIME = '2016-02-01'
DECLARE #Query AS VARCHAR(MAX)
DECLARE #DateStart DATETIME = #FromDate
IF OBJECT_ID(N'tempdb..#Tempdates') IS NOT NULL
BEGIN
DROP TABLE #Tempdates
END
declare #id int = 0
create table #tempdates (id int,[lodate] date, [hidate] date ,yyyy varchar(4), mm varchar(max), dd int,txt varchar(max))
DECLARE #Month VARCHAR(MAX)
DECLARE #Day VARCHAR(MAX)
DECLARE #ColumnHeader VARCHAR(MAX)
DECLARE #Headers VARCHAR(MAX)
WHILE DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #DateStart), '19050101') <= DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #ToDate), '19050101')
BEGIN
set datefirst 1
SET #month = DATENAME(Month, DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #DateStart), '19050101'))
SET #day = CAST( DATEPART(DD, DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #DateStart), '19050101')) AS VARCHAR(MAX) )
SET #ColumnHeader = 'Week ' + CAST(DatePart(WEEK,#DateStart) AS VARCHAR(MAX)) + ' - ' +
CAST(Year(DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #DateStart), '19050101')) AS VARCHAR(MAX)) + ' - ' + #month + ' - ' + #day
set #id = #id + 1
insert into #tempdates (id,[lodate],[hidate],yyyy,mm,dd,txt)
values (#id,#datestart,dateadd(dd,6,#datestart),
year(#datestart),
datename(month,#datestart),day(#datestart),
'Week ' + cast(#id as varchar(2)) + ' - ' + cast(datename(month,#datestart) as varchar(max)) + ' - ' + cast(day(#datestart) as varchar(max))
)
SET #DateStart = DATEADD(DD, 7, #DateStart)
END
update #tempdates
set hidate = #todate where id = #id
SELECT #Headers = ISNULL(#Headers + ',','') + QUOTENAME(t.[txt])
FROM #tempdates t
SET #Headers = #Headers + ',[Grand Total]'
--select #headers headers
--select * from #tempdates
set #query =
'
DECLARE #CustomerId INT = ' + CAST(#CustomerId AS VARCHAR) + '
DECLARE #FromDate DATETIME = CAST(''' + CAST(#FromDate AS VARCHAR) + ''' AS DATETIME)
DECLARE #ToDate DATETIME = CAST(''' + CAST(#ToDate AS VARCHAR) + ''' AS DATETIME)
DECLARE #Headers VARCHAR(MAX) = ''' + CAST(#Headers AS VARCHAR(MAX)) + '''
SELECT *
FROM
(
SELECT si.customerid,
t.txt as header,
SUM(si.NetAmount) AS NetAmount
FROM tblsamplesalesinvoices si
left join #tempdates t on si.Transactiondate between lodate and hidate
--LEFT OUTER JOIN tblSampleCustomers c ON c.Id = si.CustomerId
WHERE si.TransactionDate BETWEEN #FromDate AND DATEADD(WEEK, DATEDIFF(WEEK, ''19050101'', DATEADD(DD, 7 , #ToDate)), ''19050101'')
AND (si.CustomerId = #CustomerId OR #CustomerId = 0)
GROUP BY -- c.CustomerName,
si.customerid
,
t.txt
UNION ALL
SELECT si.CustomerId,
''Grand Total'' AS Header,
SUM(si.NetAmount) AS NetAmount
FROM tblsamplesalesinvoices si
-- LEFT OUTER JOIN tblSampleCustomers c ON c.Id = si.CustomerId
WHERE (si.TransactionDate BETWEEN #FromDate AND DATEADD(WEEK, DATEDIFF(WEEK, ''19050101'', DATEADD(DD, 7 , #ToDate)), ''19050101''))
AND (si.CustomerId = #CustomerId OR #CustomerId = 0)
GROUP BY si.customerid
) s
PIVOT
(
SUM(NetAmount)
FOR Header IN (' + #Headers + ')
) AS Pivoting'
--select #query
exec (#query)
IF OBJECT_ID(N'tempdb..#Tempdates') IS NOT NULL
BEGIN
DROP TABLE #Tempdates
END
go
Please note dates are UK localised

Resources