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
Related
I have a table that looks like this:
Agent_id
break_id
time
1
1
15
1
2
12
1
2
12
I used pivot to get this structure:
Agent_id
1
2
1
15
24
The problem is that I need to get the count for the pivoted columns, in the example I need to have a structure like this:
Agent_id
1
2
count1
count2
1
15
24
1
2
And I'm not sure on how to do it ... this is the query so far.
DECLARE #COLUMNS VARCHAR(MAX)
DECLARE #QUERY nVARCHAR(MAX)
SELECT #COLUMNS = COALESCE(#COLUMNS + ', ','') + QUOTENAME([break_id])
FROM
(SELECT DISTINCT [break_id] FROM test) AS B
ORDER BY B.[break_id]
SET #QUERY = '
SELECT agent_id,
'+#COLUMNS+'
FROM (
SELECT TOP (1000)
agent_id,break_id,time_inbreak
FROM test
) as pivotData
PIVOT (
SUM(time_inbreak)
FOR break_id IN ('+#COLUMNS+')
) as pivotResult
'
EXEC sp_executesql #QUERY
Any help is greatly appreciated
Unfortunately, PIVOT can only pivot a single column. But we can do multiple columns using conditional aggregation SUM(CASE WHEN... and COUNT(CASE WHEN...:
DECLARE #COLUMNS VARCHAR(MAX)
DECLARE #QUERY nVARCHAR(MAX)
SELECT #COLUMNS = COALESCE(#COLUMNS + ', ','') +
'Sum' + QUOTENAME([break_id]) +
' = SUM(CASE WHEN break_id = ' + break_id + ' THEN time_inbreak END), Count' +
QUOTENAME([break_id]) + ' = COUNT(CASE WHEN break_id = ' + break_id + ' THEN 1 END)'
FROM
(SELECT DISTINCT [break_id] FROM test) AS B
ORDER BY B.[break_id]
OPTION (MAXDOP 1);
SET #QUERY = '
SELECT agent_id,
'+#COLUMNS+'
FROM (
SELECT TOP (1000)
agent_id,break_id,time_inbreak
FROM test
) as pivotData
GROUP BY agent_id;
';
PRINT #QUERY
EXEC sp_executesql #QUERY
I must say, I'm not sure how safe it is to aggregate the columns like that, especially in the face of parallelism. Preferably use STRING_AGG or FOR XML PATH(''), TYPE. At the very least I have added OPTION (MAXDOP 1) to prevent parallelism
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
I have an Excel-sheet as below, I already dump the data into database (as the sample data below) from this excel in normalized form.
Now I want to get the similar view of excel from database's data. I tried this, but given in wrong format. Good if somebody given the same result view as excel with column name and inner join.
I do not want to hardcore as the year expand.
declare #tblyear table(id int, year int)
insert into #tblyear values (1,2012), (2,2013),(3,2014) ,(4,2015),(5,2016)
declare #ChargeableYearDetails table ( id int, year int, CumulativeHrs numeric(18,2), CumulativeChargeableHrs numeric(18,2))
--take only 2 row year wise for the sample
insert into #ChargeableYearDetails values
(1, 1, 1657.75, 1243.50),
(2, 1, 3925.50, 3044.75),
(3, 2, 870.25, 568.25),
(4, 2, 2517.75, 1808.00),
(5, 3, 189.50, 99.00),
(6, 3, 1982.75, 1295.25),
(7, 4, 539.00, 351.00),
(8, 4, 2542.75, 1924.75),
(9, 5, 874.50, 596.50),
(9, 5, 2721.50, 2175.50)
select * from #tblyear
select * from #ChargeableYearDetails
/*I tried this , but given wrong result*/
select * from #ChargeableYearDetails
pivot
(
max(CumulativeHrs)
FOR year in ([1],[2],[3],[4],[5])
) as p
My answer is a little bit complicated, but I should post it. I use dynamic sql and pivoting.
DECLARE #columnsH nvarchar(500),
#columnsCH nvarchar(500),
#columns nvarchar(1000),
#sql nvarchar(4000)
CREATE TABLE #tblyear (id int, [year] int)
INSERT INTO #tblyear VALUES (1,2012), (2,2013),(3,2014) ,(4,2015),(5,2016)
CREATE TABLE #ChargeableYearDetails (id int, [year] int, CumulativeHrs numeric(18,2), CumulativeChargeableHrs numeric(18,2))
INSERT INTO #ChargeableYearDetails VALUES
(1, 1, 1657.75, 1243.50),(2, 1, 3925.50, 3044.75),(3, 2, 870.25, 568.25),
(4, 2, 2517.75, 1808.00),(5, 3, 189.50, 99.00),(6, 3, 1982.75, 1295.25),
(7, 4, 539.00, 351.00),(8, 4, 2542.75, 1924.75),(9, 5, 874.50, 596.50),
(9, 5, 2721.50, 2175.50)
SELECT #columnsH = STUFF((SELECT DISTINCT ',' + QUOTENAME('CumulativeHrsYY'+ CAST([Year] AS NVARCHAR(4))) FROM #tblyear FOR XML PATH('')),1,1,'')
SELECT #columnsCH = STUFF((SELECT DISTINCT ',' + QUOTENAME('CumulativeChargeableHrs'+ CAST([Year] AS NVARCHAR(4))) FROM #tblyear FOR XML PATH('')),1,1,'')
SELECT #columns = STUFF((SELECT DISTINCT ',' + QUOTENAME('CumulativeHrsYY'+ CAST([Year] AS NVARCHAR(4))) +',' + QUOTENAME('CumulativeChargeableHrs'+ CAST([Year] AS NVARCHAR(4))) FROM #tblyear FOR XML PATH('')),1,1,'')
SELECT #sql = '
SELECT '+ #columns+'
FROM (
SELECT *
FROM (
SELECT ''CumulativeHrsYY''+ CAST(t.[Year] AS NVARCHAR(4)) as [Year],
c.CumulativeHrs,
ROW_NUMBER() OVER (PARTITION BY c.[year] ORDER BY c.[year]) as rn
FROM #ChargeableYearDetails c
LEFT JOIN #tblyear t
ON t.ID = c.[year]
) as t
pivot
(
max(CumulativeHrs)
FOR [year] in ('+#columnsH+')
) as p
) as part1
LEFT JOIN (
SELECT *
FROM (
SELECT ''CumulativeChargeableHrs''+ CAST(t.[Year] AS NVARCHAR(4)) as [Year],
c.CumulativeChargeableHrs,
ROW_NUMBER() OVER (PARTITION BY c.[year] ORDER BY c.[year]) as rn
FROM #ChargeableYearDetails c
LEFT JOIN #tblyear t
ON t.ID = c.[year]
) as t
pivot
(
max(CumulativeChargeableHrs)
FOR [year] in ('+#columnsCH+')
) as p
) as part2
ON part1.rn = part2.rn'
EXEC(#sql)
DROP TABLE #ChargeableYearDetails
DROP TABLE #tblyear
Output:
CumulativeHrsYY2012 CumulativeChargeableHrs2012 CumulativeHrsYY2013 CumulativeChargeableHrs2013 CumulativeHrsYY2014 CumulativeChargeableHrs2014 CumulativeHrsYY2015 CumulativeChargeableHrs2015 CumulativeHrsYY2016 CumulativeChargeableHrs2016
1657.75 1243.50 870.25 568.25 189.50 99.00 539.00 351.00 874.50 596.50
3925.50 3044.75 2517.75 1808.00 1982.75 1295.25 2542.75 1924.75 2721.50 2175.50
Using dynamic sql, something like this will work. Have changed your table var to temp tables. You will have to change it to accommodate for multiple rows. Just add a group by to the pivot. Pls refer this msdn blog post on how to pivot on multiple fields
DECLARE #cols AS NVARCHAR(MAX), #cols2 AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME('Cumulative Hours ' + cast(year as varchar(4)))
from #tblyear
group by year, id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #cols2 = STUFF((SELECT ',' + QUOTENAME('Cumulative Chargable Hours ' + cast(year as varchar(4)))
from #tblyear
group by year, id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
DECLARE #colsMax AS NVARCHAR(MAX), #cols2Max AS NVARCHAR(MAX)
select #colsMax = STUFF((SELECT ', MAX(' + QUOTENAME('Cumulative Hours ' + cast(year as varchar(4))) + ') ' + QUOTENAME('Cumulative Hours ' + cast(year as varchar(4)))
from #tblyear
group by year, id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #cols2Max = STUFF((SELECT ', MAX(' + QUOTENAME('Cumulative Chargable Hours ' + cast(year as varchar(4))) + ') ' + QUOTENAME('Cumulative Chargable Hours ' + cast(year as varchar(4)))
from #tblyear
group by year, id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #colsMax + ',' + #cols2Max + N' from
(
select CumulativeHrs, CumulativeChargeableHrs
, ''Cumulative Hours '' + cast(b.year as varchar(4)) as [CumHours]
, ''Cumulative Chargable Hours '' + cast(b.year as varchar(4)) as [CumChargableHours]
from #ChargeableYearDetails a join #tblyear b on a.year = b.id
) query
pivot
(
max(CumulativeHrs)
for CumHours in (' + #cols + N')
) p
pivot
(
max(CumulativeChargeableHrs)
for CumChargableHours in (' + #cols2 + N')
) p2
'
print #query
exec sp_executesql #query;
You can do this using dynamic sql.. this will create some MAX(CASE WHEN) statements for each year/hrs combo.. then execute the sql by using EXEC sp_executesql #sql or just EXEC(#sql)
DECLARE #Sql NVARCHAR(MAX),
#Cols NVARCHAR(MAX)
SELECT #Cols = COALESCE(#Cols + ', ', '') +
'MAX(CASE WHEN Year = ' + CAST(id AS VARCHAR) + ' THEN CumulativeHrs END) AS [Cumulative Hrs YTD - ' + CAST([year] AS VARCHAR) + '], ' +
'MAX(CASE WHEN Year = ' + CAST(id AS VARCHAR) + ' THEN CumulativeChargeableHrs END) AS [Cumulative Chargeable Hrs ' + CAST([year] AS VARCHAR) + '] '
FROM tblyear
ORDER BY [year] DESC
SET #Sql = 'SELECT ' + #Cols
+ ' FROM (SELECT *, Row_number() Over (Partition by [year] order by id) Rn FROM ChargeableYearDetails) t '
+ ' GROUP BY Rn'
EXEC sp_executesql #Sql
you won't be able to use table variables in this type of query without defining them as types first, but i'm guessing you're using actual tables.
Row_number was also added to break each record per year into separate rows
It is possible to use the PIVOT clause, but this has two disadvantages. Firstly, it can only pivot a single column. Secondly it can't handle dynamic columns; you have to pre-specify them.
In this example, I'm using two CTEs to pivot each column, then joining the result sets.
You might do better to implement this in a mid-tier layer programmed in C#.
WITH cteHrs AS
(
SELECT id, [1], [2], [3], [4], [5]
FROM
(SELECT id, [year], CumulativeHrs
FROM #ChargeableYearDetails) AS H
PIVOT
(
SUM(CumulativeHrs)
FOR [year] IN ([1], [2], [3], [4], [5])
) as pvtH
),
cteChgHrs AS
(
SELECT id, [1], [2], [3], [4], [5]
FROM
(SELECT id, [year], CumulativeChargeableHrs
FROM #ChargeableYearDetails) AS C
PIVOT
(
SUM(CumulativeChargeableHrs)
FOR [year] IN ([1], [2], [3], [4], [5])
) as pvtC
)
SELECT COALESCE(C.id, H.id) AS 'id',
C.[1] AS 'Cum Hrs 2012', H.[1] AS 'Cum Chg Hrs 2012',
C.[2] AS 'Cum Hrs 2013', H.[2] AS 'Cum Chg Hrs 2013',
C.[3] AS 'Cum Hrs 2014', H.[3] AS 'Cum Chg Hrs 2014',
C.[4] AS 'Cum Hrs 2015', H.[4] AS 'Cum Chg Hrs 2015',
C.[5] AS 'Cum Hrs 2016', H.[5] AS 'Cum Chg Hrs 2016'
FROM cteHrs AS H
FULL OUTER JOIN cteChgHrs AS C
ON C.id = H.id;
The result set is this.
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
I was trying on a sql query where we have search on month wise
for example my table is like
Username visited Visisted_Dated
A 1 01/11/2010
B 1 10/11/2010
A 1 03/12/2010
B 1 06/12/2010
B 1 06/12/2010
A 1 03/02/2011
B 1 05/02/2011
A 1 11/03/2011
A 1 20/03/2011
B 1 01/03/2011
Now if i want to search the users for no of visited between Feb to April i need to get output as
Users Nov_2010 Dec_2010 Jan_2011 Feb_2011 March_2011
A 1 1 0 1 2
B 1 2 0 1 1
Please let me know the way to proceed.
Thanks
I'm afraid you have to make use of dynamic SQL:
DECLARE #Sql VARCHAR(8000)
DECLARE #ColumnNames VARCHAR(1000)
DECLARE #BeginDate DATETIME
DECLARE #EndDate DATETIME
SET #BeginDate = '2010-11-1'
SET #EndDate = '2011-4-1'
SET #ColumnNames = ''
WHILE #BeginDate <= #EndDate
BEGIN
SET #ColumnNames = #ColumnNames + ',[' + DateName(month,#BeginDate) + '_' + Cast(Year(#BeginDate) AS VARCHAR(4)) + ']'
SET #BeginDate = DateAdd(Month, 1, #BeginDate)
END
IF Len(#ColumnNames) > 0
SET #ColumnNames = Right(#ColumnNames, Len(#ColumnNames) - 1)
PRINT #ColumnNames
SET #Sql = '
WITH U AS
(
SELECT UserName, DateName(month,Visited_Dated) + ''_'' + Cast(Year(Visited_Dated) AS VARCHAR(4)) AS VisitedMonth, Visited
FROM Users
)
SELECT *
FROM U
PIVOT (
SUM(Visited) FOR VisitedMonth IN (' + #ColumnNames + ')
) AS P'
EXEC (#Sql)
There it´s a solution based on PIVOT
SELECT UserName,[201011], [201012], [201101], [201102], [201103]
FROM
(SELECT UserName,Visited,convert(varchar(6),[Visited_Dated],112) Periodo
FROM [Table]) AS st
PIVOT
(
COUNT(Visited)
FOR Periodo IN ([201011], [201012], [201101], [201102], [201103])
) AS pt;