Generate dynamic custom intervals - sql-server-2014

I am working on a project that requires reports that can dynamically group the source data on non-standard user defined intervals such as Production Shifts within a specified start and end range. Example user may want to see production information group by shift for the last two days.
I created a table called ‘IntervalConfiguration’ that stores the configuration information required to create the grouping intervals with data as follow:
IntervalType SubIntervalType IntervalDuration IntervalDurationUnits IntervalStartReferenceTime IntervalRepeatDuration IntervalRepeatDurationUnits
------------------------- ------------------- -------------------- --------------------- -------------------------- ---------------------- ---------------------------
Production Day ProductionDay 1 Days 2013-01-07 07:00:00.000 1 Days
Production Month ProductionMonth 1 Months 2013-01-01 07:00:00.000 1 Months
Production Week ProductionWeek 1 Weeks 2013-01-07 08:45:00.000 1 Weeks
Production Year ProductionYear 1 Years 2013-01-01 08:45:00.000 1 Years
Site A - Production Shift Day 12 Hours 2013-01-06 07:00:00.000 24 Hours
Site A - Production Shift Night 12 Hours 2013-01-06 19:00:00.000 24 Hours
Site B - Production Shift Day 12 Hours 2013-01-06 06:45:00.000 24 Hours
Site B - Production Shift Night 12 Hours 2013-01-06 18:45:00.000 24 Hours
If a user selects ‘Site A - Production Shift’ as the grouping interval on the report and a start date of '01/01/2018' and end date of '01/05/2018' then the report has to create grouping intervals for day shifts where day shifts start at 7AM and ends at 7PM and night shift where night shift start at 7PM and ends at 7AM the next day.
Also, only grouping intervals completely contained within the start and end date should be returned. Below is an example of expected grouping intervals for the scenario described.
SubIntervalType IntervalStart IntervalEnd
---------------- ----------------------- -----------------------
Day 2018-01-01 07:00:00.000 2018-01-01 19:00:00.000
Night 2018-01-01 19:00:00.000 2018-01-02 07:00:00.000
Day 2018-01-02 07:00:00.000 2018-01-02 19:00:00.000
Night 2018-01-02 19:00:00.000 2018-01-03 07:00:00.000
Day 2018-01-03 07:00:00.000 2018-01-03 19:00:00.000
Night 2018-01-03 19:00:00.000 2018-01-04 07:00:00.000
Day 2018-01-04 07:00:00.000 2018-01-04 19:00:00.000
If user selects Production Month as the grouping interval with a start date of '01/01/2018' and end date of '01/01/2019' then the report should generate the following grouping intervals.
SubIntervalType IntervalStart IntervalEnd
----------------- ----------------------- -----------------------
ProductionMonth 2018-01-01 07:00:00.000 2018-02-01 07:00:00.000
ProductionMonth 2018-02-01 07:00:00.000 2018-03-01 07:00:00.000
ProductionMonth 2018-03-01 07:00:00.000 2018-04-01 07:00:00.000
ProductionMonth 2018-04-01 07:00:00.000 2018-05-01 07:00:00.000
ProductionMonth 2018-05-01 07:00:00.000 2018-06-01 07:00:00.000
ProductionMonth 2018-06-01 07:00:00.000 2018-07-01 07:00:00.000
ProductionMonth 2018-07-01 07:00:00.000 2018-08-01 07:00:00.000
ProductionMonth 2018-08-01 07:00:00.000 2018-09-01 07:00:00.000
ProductionMonth 2018-09-01 07:00:00.000 2018-10-01 07:00:00.000
ProductionMonth 2018-10-01 07:00:00.000 2018-11-01 07:00:00.000
ProductionMonth 2018-11-01 07:00:00.000 2018-12-01 07:00:00.000
I have started building the following table valued function to dynamically create the desired grouping intervals.
CREATE FUNCTION [dbo].[GetIntervals]
(
#dateRangeStart datetime,
#dateRangeEnd datetime,
#groupByInterval NVARCHAR(200)
)
RETURNS #Intervals TABLE (
IntervalType NVARCHAR(100)
,SubIntervalType NVARCHAR(100)
,IntervalStart DATETIME
,IntervalEnd DATETIME
,IntervalDurationSeconds FLOAT
)
AS
BEGIN
DECLARE #activeIntervalDateTime DATETIME = DATEADD(millisecond, 3, #dateRangeStart);
DECLARE #intervalStartTime DATETIME = DATEADD(s, 1, #dateRangeStart);
DECLARE #intervalEndTime DATETIME = DATEADD(s, 1, #dateRangeStart);
DECLARE #intervalDurationSeconds FLOAT;
DECLARE #intervalName NVARCHAR(100);
DECLARE #subIntervalType NVARCHAR(100);
WHILE #intervalStartTime <= #dateRangeEnd
BEGIN
SELECT TOP 1
#intervalName = IntervalType ,#subIntervalType = SubIntervalType , #intervalStartTime = IntervalStart, #intervalEndTime = IntervalEnd
FROM (SELECT IntervalType, SubIntervalType, IntervalDuration, IntervalDurationUnits,
IntervalRepeatDuration, IntervalRepeatDurationUnits,
CASE IntervalRepeatDurationUnits
WHEN 'Hours' THEN (DateAdd(HH, (DateDiff(HH, IntervalStartReferenceTime, ActiveTimeNormalized ) / IntervalRepeatDuration) * IntervalRepeatDuration, IntervalStartReferenceTime))
WHEN 'Days' THEN (DateAdd(D, (DateDiff(D, IntervalStartReferenceTime, ActiveTimeNormalized ) / IntervalRepeatDuration) * IntervalRepeatDuration, IntervalStartReferenceTime))
WHEN 'Months'THEN (DateAdd(MM, (DateDiff(MM, IntervalStartReferenceTime, ActiveTimeNormalized ) / IntervalRepeatDuration) * IntervalRepeatDuration, IntervalStartReferenceTime))
END AS IntervalStart,
CASE IntervalRepeatDurationUnits
WHEN 'Hours' THEN (DateAdd(HH, IntervalDuration, DateAdd(HH, (DateDiff(HH, IntervalStartReferenceTime, ActiveTimeNormalized ) / IntervalRepeatDuration) * IntervalRepeatDuration, IntervalStartReferenceTime)))
WHEN 'Days' THEN
CASE IntervalDurationUnits
WHEN 'Hours' THEN (DateAdd(HH, IntervalDuration, DateAdd(D, (DateDiff(D, IntervalStartReferenceTime, ActiveTimeNormalized ) / IntervalRepeatDuration) * IntervalRepeatDuration, IntervalStartReferenceTime)))
WHEN 'Days' THEN (DateAdd(D, IntervalDuration, DateAdd(D, (DateDiff(D, IntervalStartReferenceTime, ActiveTimeNormalized ) / IntervalRepeatDuration) * IntervalRepeatDuration, IntervalStartReferenceTime)))
END
WHEN 'Months'THEN
CASE IntervalDurationUnits
WHEN 'Hours' THEN (DateAdd(HH, IntervalDuration, DateAdd(MM, (DateDiff(MM, IntervalStartReferenceTime, ActiveTimeNormalized ) / IntervalRepeatDuration) * IntervalRepeatDuration, IntervalStartReferenceTime)))
WHEN 'Days' THEN (DateAdd(D, IntervalDuration, DateAdd(MM, (DateDiff(MM, IntervalStartReferenceTime, ActiveTimeNormalized ) / IntervalRepeatDuration) * IntervalRepeatDuration, IntervalStartReferenceTime)))
WHEN 'Months' THEN (DateAdd(MM, IntervalDuration, DateAdd(MM, (DateDiff(MM, IntervalStartReferenceTime, ActiveTimeNormalized ) / IntervalRepeatDuration) * IntervalRepeatDuration, IntervalStartReferenceTime)))
END
END AS IntervalEnd
,IntervalStartReferenceTime
FROM (
SELECT IntervalType, SubIntervalType, IntervalDuration, IntervalDurationUnits, IntervalRepeatDuration, IntervalRepeatDurationUnits, IntervalStartReferenceTime,
CASE IntervalRepeatDurationUnits
WHEN 'Hours' THEN DATEADD(MILLISECOND, -1*(DATEPART(MILLISECOND, IntervalStartReferenceTime) + 3), DATEADD(SECOND, -1*DATEPART(SECOND, IntervalStartReferenceTime), DATEADD(MINUTE, -1*DATEPART(MINUTE, IntervalStartReferenceTime), #activeIntervalDateTime)))
WHEN 'Days' THEN DATEADD(MILLISECOND, -1*(DATEPART(MILLISECOND, IntervalStartReferenceTime) + 3), DATEADD(SECOND, -1*DATEPART(SECOND, IntervalStartReferenceTime), DATEADD(MINUTE, -1*DATEPART(MINUTE, IntervalStartReferenceTime), DATEADD(HOUR, -1*DATEPART(HOUR, IntervalStartReferenceTime), #activeIntervalDateTime))))
WHEN 'Months' THEN DATEADD(MILLISECOND, -1*(DATEPART(MILLISECOND, IntervalStartReferenceTime) + 3), DATEADD(SECOND, -1*DATEPART(SECOND, IntervalStartReferenceTime), DATEADD(MINUTE, -1*DATEPART(MINUTE, IntervalStartReferenceTime), DATEADD(HOUR, -1*DATEPART(HOUR, IntervalStartReferenceTime), DATEADD(DAY, -1*(DATEPART(DAY, IntervalStartReferenceTime) - 1), #activeIntervalDateTime)))))
END AS ActiveTimeNormalized
FROM dbo.IntervalConfiguration
) norm
WHERE IntervalType = #groupByInterval) interval
WHERE (#activeIntervalDateTime > IntervalStart) and (#activeIntervalDateTime <= IntervalEnd)
ORDER BY IntervalStartReferenceTime DESC
SET #intervalDurationSeconds = DATEDIFF(SECOND, #intervalStartTime, #intervalEndTime);
IF #intervalStartTime >= #dateRangeStart AND #intervalEndTime <= #dateRangeEnd
BEGIN
INSERT INTO #Intervals(
IntervalType
,SubIntervalType
,IntervalStart
,IntervalEnd
,IntervalDurationSeconds
)
VALUES(
#intervalName
,#subIntervalType
,#intervalStartTime
,#intervalEndTime
,#intervalDurationSeconds
)
END
SET #activeIntervalDateTime = DATEADD(MILLISECOND, 3, #intervalEndTime);
END
RETURN;
END
This function however is getting increasingly complex and hard to debug also I would like to eliminate the need to use While loops within the function. My question is this, is there a simpler way to achieve my requirements and is it possible to eliminate the need for a while loop?

You can definitely simplify the query and eliminate the while loop through the use of a tally table as demonstrated below:
CREATE FUNCTION [dbo].[GetIntervals]
(
#startRange datetime,
#endRange datetime,
#groupByPeriod NVARCHAR(200)
)
RETURNS TABLE
AS
RETURN
(
WITH Tally (N) AS
(
SELECT 0 UNION ALL
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n) -- 10 rows
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n) -- 100 rows
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n) -- 1,000 rows
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d(n) -- 10,000 rows
--CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) e(n) -- 100,000 rows
--CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) f(n) -- 1,000,000 rows
),
Intervals
AS
(
SELECT I.IntervalType
,I.SubIntervalType
,I.IntervalDuration
,I.IntervalDurationUnits
,I.IntervalStartReferenceTime
,I.IntervalRepeatDuration
,I.IntervalRepeatDurationUnits
,(
CASE
WHEN I.IntervalRepeatDurationUnits = 'Years' AND N < 500 THEN DATEADD(YEAR, ((DATEDIFF(YEAR, I.IntervalStartReferenceTime, #startRange) / I.IntervalRepeatDuration) + N) * I.IntervalRepeatDuration ,I.IntervalStartReferenceTime)
WHEN I.IntervalRepeatDurationUnits = 'Months' AND N < 6000 THEN DATEADD(MONTH, ((DATEDIFF(MONTH, I.IntervalStartReferenceTime, #startRange) / I.IntervalRepeatDuration) + N) * I.IntervalRepeatDuration ,I.IntervalStartReferenceTime)
WHEN I.IntervalRepeatDurationUnits = 'Weeks' AND N < 24000 THEN DATEADD(WEEK, ((DATEDIFF(WEEK, I.IntervalStartReferenceTime, #startRange) / I.IntervalRepeatDuration) + N) * I.IntervalRepeatDuration ,I.IntervalStartReferenceTime)
WHEN I.IntervalRepeatDurationUnits = 'Days' THEN DATEADD(DAY, ((DATEDIFF(DAY, I.IntervalStartReferenceTime, #startRange) / I.IntervalRepeatDuration) + N) * I.IntervalRepeatDuration ,I.IntervalStartReferenceTime)
WHEN I.IntervalRepeatDurationUnits = 'Hours' THEN DATEADD(HOUR, ((DATEDIFF(HOUR, I.IntervalStartReferenceTime, #startRange) / I.IntervalRepeatDuration) + N) * I.IntervalRepeatDuration ,I.IntervalStartReferenceTime)
END
) AS IntervalStart
,Tally.N
FROM IntervalConfiguration I
CROSS JOIN Tally
WHERE (I.IntervalType = #groupByPeriod)
AND
(
N BETWEEN 0 AND (
CASE I.IntervalRepeatDurationUnits
WHEN 'Years' THEN DATEDIFF(YEAR, #startRange, #endRange) / I.IntervalRepeatDuration
WHEN 'Months' THEN DATEDIFF(MONTH, #startRange, #endRange) / I.IntervalRepeatDuration
WHEN 'Weeks' THEN DATEDIFF(WEEK, #startRange, #endRange) / I.IntervalRepeatDuration
WHEN 'Days' THEN DATEDIFF(DAY, #startRange, #endRange) / I.IntervalRepeatDuration
WHEN 'Hours' THEN DATEDIFF(HOUR, #startRange, #endRange) / I.IntervalRepeatDuration
END)
)
)
SELECT TOP 100 PERCENT
I.IntervalType
,I.SubIntervalType
,I.IntervalDuration
,I.IntervalDurationUnits
,I.IntervalStartReferenceTime
,I.IntervalRepeatDuration
,I.IntervalRepeatDurationUnits
,I.IntervalStart
,(
CASE I.IntervalDurationUnits
WHEN 'Years' THEN DATEADD(YEAR, I.IntervalDuration, I.IntervalStart)
WHEN 'Months' THEN DATEADD(MONTH, I.IntervalDuration, I.IntervalStart)
WHEN 'Weeks' THEN DATEADD(WEEK, I.IntervalDuration, I.IntervalStart)
WHEN 'Days' THEN DATEADD(DAY, I.IntervalDuration, I.IntervalStart)
WHEN 'Hours' THEN DATEADD(HOUR, I.IntervalDuration, I.IntervalStart)
END
) AS IntervalEnd
,N
FROM Intervals I
WHERE I.IntervalStart >= #startRange
AND
(
CASE I.IntervalDurationUnits
WHEN 'Years' THEN DATEADD(YEAR, I.IntervalDuration, I.IntervalStart)
WHEN 'Months' THEN DATEADD(MONTH, I.IntervalDuration, I.IntervalStart)
WHEN 'Weeks' THEN DATEADD(WEEK, I.IntervalDuration, I.IntervalStart)
WHEN 'Days' THEN DATEADD(DAY, I.IntervalDuration, I.IntervalStart)
WHEN 'Hours' THEN DATEADD(HOUR, I.IntervalDuration, I.IntervalStart)
END
) <= #endRange
ORDER BY I.IntervalStart
)

Related

Select aggregate data from one table between 2 date columns on row by row basis from data in another unrelated table

I am using MySQL version 5.6
I have a soil sample table and a weather table which are unrelated other than dates. I collect soil samples once to twice a week and weather data every 5 minutes(24/7).
I am trying aggregate the AVERAGE temperature and the SUM of rainfall between consecutive soil sample dates.
Soil Sample Query
SET #row_number = 0;
SET #firstDate = CONCAT(YEAR(CURRENT_DATE()),'-03-01 00:00');
SELECT
ObsDate,
StartDateTime,
EndDateTime
FROM (
SELECT
ObsDate,
#firstDate AS StartDateTime,
#firstDate:=ObsDateTime AS EndDateTime,
row_num
FROM(
SELECT
ObsDate,
CONCAT(`ObsDate`, ' ', `ObsTime`) AS ObsDateTime,
(#row_number:=#row_number + 1) AS row_num
FROM tblSoilSample
WHERE ObsDate >= CONCAT(YEAR(CURRENT_DATE()),'-03-01 00:00' AND ObsDate < CONCAT(YEAR(CURRENT_DATE()),'-10-01 00:00')
) AS a
GROUP BY ObsDate
ORDER BY row_num
) as b
WHERE row_num > 1
This produces this result (limited to 2 rows)
ObsDate StartDateTime EndDateTime
2022-05-21 2022-05-14 09:00 2022-05-21 09:30
2022-05-24 2022-05-21 09:30 2022-05-24 08:00
The weather data query
SELECT
WxDateTime,
TempOutCur,
RainCur
FROM weatherbridge
WHERE WxDateTime >= CONCAT(YEAR(CURRENT_DATE()),'-03-01 00:00') AND WxDate <= NOW()
Results (sample data) blank lines match soil dates
WxDateTime TempOutCur RainCur
5/14/2022 08:29 49.1 0.01
5/14/2022 10:25 60.1 0.00
5/14/2022 18:15 58.8 1.10
5/15/2022 09:00 63.0 0.00
5/16/2022 12:35 57.7 1.23
5/17/2022 23:45 46.3 0.00
5/21/2022 10:50 55.8 0.00
5/22/2022 22:55 55.2 0.48
5/23/2022 13:00 68.2 0.01
5/24/2022 03:05 54.7 0.61
5/24/2022 05:20 54.3 0.00
5/24/2022 13:30 54.1 0.92
The output I am trying to achieve
ObsDate AvgIntervalTemp TotalIntervalRain
2022-05-21 57.2 2.33
2022-05-24 57.6 1.10
This is one attempt that shows what I am trying to do, but prefer not to have a select within a select.
SET #row_number = 0;
SET #firstDate = CONCAT(YEAR(CURRENT_DATE()),'-03-01 00:00');
SELECT
ObsDate,
(SELECT AVG(TempOutCur) FROM wx WHERE WxDateTime >= StartDateTime AND WxDateTime <= EndDateTime) AS IntervalTemp,
(SELECT SUM(RainCur) FROM wx WHERE WxDateTime >= StartDateTime AND WxDateTime <= EndDateTime) AS IntervalRain
FROM (
SELECT
ObsDate,
StartDateTime,
EndDateTime
FROM (
SELECT
ObsDate,
#firstDate AS StartDateTime,
#firstDate:=ObsDateTime AS EndDateTime,
row_num
FROM(
SELECT
ObsDate,
CONCAT(`ObsDate`, ' ', `ObsTime`) AS ObsDateTime,
(#row_number:=#row_number + 1) AS row_num
FROM tblSoilSample
WHERE ObsDate >= CONCAT(YEAR(CURRENT_DATE()),'-03-01 00:00' AND ObsDate < CONCAT(YEAR(CURRENT_DATE()),'-10-01 00:00')
) AS a
GROUP BY ObsDate
ORDER BY row_num
) AS b
WHERE row_num > 1
) AS c
LEFT JOIN
(SELECT
WxDateTime,
TempOutCur,
RainCur
FROM weatherbridge
WHERE WxDateTime >= CONCAT(YEAR(CURRENT_DATE()),'-03-01 00:00') AND WxDate <= NOW()) as wx
ON b.ObsDate = wx.WxDate

Get specific data from 2 date for a timerange

The data is stored in DB in UTC , I have to convert local to UTC fetch it then show result in local time .
Now I am from India so if I want to search the data for today I have to query from 22nd March 6:30 PM UTC to 23rd March 6:30 PM UTC . Now Say I want to check data for every day of week until now which is 4:30 PM local . Now I wrote this query [ In simple words my objective is to get visitor number of each day from 12 AM to 4:30 PM ]
SELECT
FLOOR(TIMESTAMPDIFF(HOUR, "2017-03-16 18:29:59",
visit.date_created)/24) as dayofweek,
DAYOFWEEK(visit.date_created) day_num,
#rownum := #rownum + 1 as date_created_set_av,
count(distinct(visit.pkey)) AS Visits,
sum(revenue) AS Revenue,
sum(revenue) / count(distinct(visit.pkey)) as EPC
FROM la_20.visit, la_20.action
cross join (select #rownum := 0) r
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND visit.date_created >="2017-03-16 18:29:59"
AND visit.date_created <="2017-03-23 18:29:59"
AND TIME(visit.date_created)<="16:30:00"
GROUP BY dayofweek order by day_num
but what it does is fetch the value from 2017-03-23 00:00:00 to 2017-03-23 16:30:00 . What I need is to show result of 16th to 23rd everyday's data from 18:29:59 to 16:30:00. Remember I need the result of everyday's not one day's. Can anyone help
You should change your WHERE clause into
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND visit.date_created >="2017-03-22 18:29:59" and visit.date_created <="2017-03-23 14:29:59"
UPDATED
Today midnight: DATEADD(d,0,DATEDIFF(d,0,GETDATE()))
Today 4pm: DATEADD(HOUR, 16, DATEADD(d,0,DATEDIFF(d,0,GETDATE())))
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND visit.date_created >= DATEADD(d,0,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,0,DATEDIFF(d,0,GETDATE())))
Update 2:
Then you'll have to expand your where clause like this I guess.
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND (
(visit.date_created >= DATEADD(d, 0,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d, 0,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-1,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-1,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-2,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-2,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-3,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-3,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-4,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-4,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-5,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-5,DATEDIFF(d,0,GETDATE()))))
OR (visit.date_created >= DATEADD(d,-6,DATEDIFF(d,0,GETDATE())) and visit.date_created <= DATEADD(HOUR, 16, DATEADD(d,-6,DATEDIFF(d,0,GETDATE()))))
)
Have you tried the following where clause instead?
WHERE visit.pkey=action.pkey and (visit.is_bot = 0)
AND visit.date_created >="2017-03-22 18:29:59"
AND visit.date_created <="2017-03-23 14:29:59"
3. Edit
OK, since OP wants something different again - and why not ;-) - here is another solution that might (or might not) solve his problem:
SELECT
COUNT( CASE WHEN d between
adddate(curdate(), interval -7 day) AND addtime(adddate(curdate(), interval -7 day),'16:00:00')
THEN 1 END ) dm7,
COUNT( CASE WHEN d between
adddate(curdate(), interval -6 day) AND addtime(adddate(curdate(), interval -6 day),'16:00:00')
THEN 1 END ) dm6,
COUNT( CASE WHEN d between
adddate(curdate(), interval -5 day) AND addtime(adddate(curdate(), interval -5 day),'16:00:00')
THEN 1 END ) dm5,
COUNT( CASE WHEN d between
adddate(curdate(), interval -4 day) AND addtime(adddate(curdate(), interval -4 day),'16:00:00')
THEN 1 END ) dm4,
COUNT( CASE WHEN d between
adddate(curdate(), interval -3 day) AND addtime(adddate(curdate(), interval -3 day),'16:00:00')
THEN 1 END ) dm3,
COUNT( CASE WHEN d between
adddate(curdate(), interval -2 day) AND addtime(adddate(curdate(), interval -2 day),'16:00:00')
THEN 1 END ) dm2,
COUNT( CASE WHEN d between
adddate(curdate(), interval -1 day) AND addtime(adddate(curdate(), interval -1 day),'16:00:00')
THEN 1 END ) dm1,
COUNT( CASE WHEN d between
curdate() AND addtime( curdate() ,'16:00:00')
THEN 1 END ) today
FROM (select '2017-03-17 15:00:01' d
union all select '2017-03-19 14:00:01'
union all select '2017-03-19 12:00:01'
union all select '2017-03-19 13:00:01'
union all select '2017-03-19 11:00:01'
union all select '2017-03-20 11:20:01'
union all select '2017-03-20 11:30:01'
union all select '2017-03-20 10:40:01'
union all select '2017-03-20 10:23:01' ) dates
This is an isolated query that works directly on the sample data given in the subquery. It results in the following list ("dm1"=day minus 1):
dm7 | dm6 | dm5 | dm4 | dm3 | dm2 | dm1 | today
1 | 0 | 4 | 4 | 0 | 0 | 0 | 0
You can test it here: http://rextester.com/ITLBL24540

creation of MySQL subquery

I am working to create a graph for POS data. I
SELECT DATE_FORMAT(DATE_ADD(DATE(receipts.datenew - INTERVAL (5*60 + 30) MINUTE), INTERVAL (5*60 + 30) MINUTE), '%Y-%m-%d') AS interval_start,
CONVERT(EXTRACTVALUE(PRODUCTS.ATTRIBUTES, '/properties//entry[#key=\"maincat\"]') USING UTF8) AS MAINCAT,
ROUND(SUM(TICKETLINES.PRICE * TICKETLINES.UNITS), -3) AS DAYSALES
FROM TICKETLINES, TICKETS, RECEIPTS, PRODUCTS
WHERE TICKETLINES.PRODUCT = PRODUCTS.ID
AND TICKETLINES.TICKET = TICKETS.ID
AND TICKETS.ID = RECEIPTS.ID
AND TICKETLINES.PRODUCT IS NOT NULL
AND (receipts.datenew >= DATE_SUB(NOW(), INTERVAL 3 MONTH) AND receipts.person > 0)
GROUP BY DATE(receipts.datenew - INTERVAL (5*60 + 30) MINUTE), MAINCAT
ORDER BY interval_start, MAINCAT
And I get the following data:
interval_start MAINCAT DAYSALES
2016-12-31 Drink 45108000
2016-12-31 Food 49791000
2016-12-31 Other 109000
2017-01-01 Drink 14226000
2017-01-01 Food 27425000
2017-01-01 Other 36000
2017-01-02 Drink 20400000
2017-01-02 Food 25648000
2017-01-02 Other 109000
But I need this format:
interval_start Drink Food
2016-12-31 45108000 36000
2016-12-31 49791000 20400000
2016-12-31 109000 25648000
2017-01-01 27425000 109000
2017-01-01 14226000 49791000
I believe a subquery is the answer, but I am not able to figure it out.
Thanks in advance.
Allan
I have been able to get the data in the correct format with the following query - thank you for pointing me in the right direction:
SELECT DATE_FORMAT(DATE_ADD(DATE(receipts.datenew - INTERVAL (5*60 + 30) MINUTE), INTERVAL (5*60 + 30) MINUTE), '%Y-%m-%d') AS DATEofSALES,
SUM(CASE WHEN CONVERT(EXTRACTVALUE(PRODUCTS.ATTRIBUTES, '/properties//entry[#key=\"maincat\"]') USING UTF8) = 'Drink' THEN ticketlines.price ELSE 0 END) DRINKSALES,
SUM(CASE WHEN CONVERT(EXTRACTVALUE(PRODUCTS.ATTRIBUTES, '/properties//entry[#key=\"maincat\"]') USING UTF8) = 'Food' THEN ticketlines.price ELSE 0 END) FOODSALES,
SUM(CASE WHEN CONVERT(EXTRACTVALUE(PRODUCTS.ATTRIBUTES, '/properties//entry[#key=\"maincat\"]') USING UTF8) = 'Other' THEN ticketlines.price ELSE 0 END) OTHERSALES
FROM TICKETLINES, TICKETS, RECEIPTS, PRODUCTS
WHERE TICKETLINES.PRODUCT = PRODUCTS.ID
AND TICKETLINES.TICKET = TICKETS.ID
AND TICKETS.ID = RECEIPTS.ID
AND TICKETLINES.PRODUCT IS NOT NULL
AND (receipts.datenew >= {ts '2017-02-01 06:00:00.000'} AND receipts.datenew < {ts '2017-03-31 14:28:00.000'} AND receipts.person > 0)
GROUP BY DATE(receipts.datenew - INTERVAL (5*60 + 30) MINUTE) ORDER BY DATEofSALES

i want to calculate sum of leaverequested column month wise

empid leavefrom leaveto leaverequested
1 3/3/2014 4/3/2014 2
1 7/3/2014 8/3/2014 2
1 31/3/2014 1/4/2014 2
1 10/4/2014 11/4/2014 2
I want to calculate the sum of the leaverequested column.
Ouput:
march - 5 days
april - 3 days
This is my SQL query so far:
select Emp_id
,datename(month,leave_from) as [First]
,datename(month,leave_to) as Last
,count(DATEDIFF(Day, leave_from, leave_to)+1) as [Total Leave]
,sum(DATEDIFF(Day, leave_from, leave_to)+1) as [Total Days]
from emp_leave
group by Emp_id
,datename(month,leave_from)
,datename(month,leave_to);
Can any one help me to get this output please?
One way to go about it
WITH n AS
(
SELECT 0 n
UNION ALL
SELECT n + 1 FROM n WHERE n < 32 -- adjust as needed to max months that a leave can possibly span
), base AS
(
SELECT empid,
DATEADD(mm, n.n, DATEADD(mm, DATEDIFF(mm, 0, leavefrom), 0)) basemonth,
CASE WHEN leavefrom > DATEADD(mm, n.n, DATEADD(mm, DATEDIFF(mm, 0, leavefrom), 0))
THEN leavefrom
ELSE DATEADD(mm, n.n, DATEADD(mm, DATEDIFF(mm, 0, leavefrom), 0))
END sdate,
CASE WHEN leaveto < DATEADD(mm, n.n, DATEADD(mm, DATEDIFF(mm, 0, leavefrom) + 1, -1))
THEN leaveto
ELSE DATEADD(mm, n.n, DATEADD(mm, DATEDIFF(mm, 0, leavefrom) + 1, -1))
END edate
FROM emp_leave JOIN n
ON n.n <= DATEDIFF(month, leavefrom, leaveto)
)
SELECT empid,
YEAR(basemonth) year,
DATENAME(mm, basemonth) month,
SUM(DATEDIFF(dd, sdate, edate) + 1) total
FROM base
GROUP BY empid, basemonth
ORDER BY empid, base month
Output:
| EMPID | YEAR | MONTH | TOTAL |
|-------|------|-------|-------|
| 1 | 2014 | March | 5 |
| 1 | 2014 | April | 3 |
Here is SQLFiddle demo

Get All the week days from a given Week Number in Sql Server?

is it possible to get all the dates of a week by a given week number lets say I provide week number 2 from year 2011 and can I get all the week days starting from the first monda?
This might work:
SET DATEFIRST 1
DECLARE #wk int SET #wk = 2
DECLARE #yr int SET #yr = 2011
--define start and end limits
DECLARE #todate datetime, #fromdate datetime
SELECT #fromdate = dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4 -
datepart(dw, dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4) + 1
SELECT #todate = #fromdate + 6
;WITH DateSequence( Date ) AS
(
SELECT #fromdate AS Date
UNION ALL
SELECT dateadd(DAY, 1, Date)
FROM DateSequence
WHERE Date < #todate
)
--select result
SELECT * FROM DateSequence OPTION (MaxRecursion 1000)
It's patched up on bits and pieces found around the internet and will generate this result:
Date
-----------------------
2011-01-10 00:00:00.000
2011-01-11 00:00:00.000
2011-01-12 00:00:00.000
2011-01-13 00:00:00.000
2011-01-14 00:00:00.000
2011-01-15 00:00:00.000
2011-01-16 00:00:00.000
There's most likely better ways to do it though.