Finding Events During a Timeframe - sql-server

Trying to figure out a query that could work for multiple situations. In a nutshell the data can be in one of twosituations. Lets say I'm looking for the record_id for events happened during a given time frame: 6/26/2012 10:00AM and 6/27/2012 11:00AM The records can look like this in the database:
Record Event Time
1 Start 6/26/2012 10:05AM
1 End 6/26/2012 10:45AM
2 Start 6/26/2012 09:55AM
2 End 6/26/2012 11:05AM
Getting record 1 is easy, just using the between function, but I'm stumbling trying to figure out a query to return both records 1 and 2.
Suggestions?

You can simply widen the range, but this may bring back different records than you intend.
SELECT RECORD, EVENT, TIME
FROM SO_RecordEvent AS R
WHERE TIme BETWEEN '6/26/2012 9:55AM' AND '6/26/2012 11:06AM'
This will return all records for anything that has either a start or end time within the range, including the associated records falling outside of the range (In other words records that only have one time in the range - but began before or ended after - it will still show the start or end time outside of it) and allow you you shorten your range.
;WITH X AS
(
SELECT RECORD,EVENT,TIME FROM SO_RecordEvent
)
SELECT R.RECORD,R.EVENT,R.TIME
FROM SO_RecordEvent AS R
INNER JOIN X ON R.Record = X.Record
WHERE X.TIme BETWEEN '6/26/2012 10:05AM' AND '6/26/2012 11:05AM'
GROUP BY R.RECORD,R.EVENT,R.TIME
But I think you may really want something like this, which truly gives you everything that was started during that time, even if it started AND ended outside of the range, as is your record 2 example.
EDIT
Changed logic - instead of addressing situations decided to think about it this way - anything that started within start and end, anything that ended within start and end, and anything that started before and ended after. I think this covers anything that runs during this time (starts before ends within, starts and ends within, starts within and ends after, and starts before and ends after)
SELECT X.RECORD,X.TIME AS STARTTIME,Y.TIME AS ENDTIME
FROM SO_RecordEvent AS X
INNER JOIN SO_RecordEvent Y ON Y.Record = X.Record AND Y.EVENT = 'END'
WHERE X.EVENT = 'START'
AND
((X.TIME >= '6/26/2012 10:00AM' AND X.TIME <= '6/26/2012 11:00AM')
OR (Y.TIME >= '6/26/2012 10:00AM' AND Y.TIME <= '6/26/2012 11:00AM')
OR (X.TIME <= '6/26/2012 10:00AM' AND Y.TIME >= '6/26/2012 11:00AM'))
Variables to play with:
DECLARE #START datetime, #END datetime
SET #START = '6/26/2012 10:00AM'
SET #END = '6/26/2012 11:00AM'
SELECT X.RECORD,X.TIME AS STARTTIME,Y.TIME AS ENDTIME
FROM SO_RecordEvent AS X
INNER JOIN SO_RecordEvent Y ON Y.Record = X.Record AND Y.EVENT = 'END'
WHERE X.EVENT = 'START'
AND
((X.TIME >= #START AND X.TIME <= #END)
OR (Y.TIME >= #START AND Y.TIME <= #END)
OR (X.TIME <= #START AND Y.TIME >= #END))

I'd probably join the table to itself - something like this:
SELECT *
FROM
Table leftside
JOIN Table rightside
ON leftside.Record = rightside.Record
WHERE
leftside.Event = 'Start'
and rightside.Event = 'End'
and [whatever time you want] >= leftside.Time
and [whatever time you want] <= rightside.Time
EDIT: Example to demonstrate
create table thingy (
record int,
event varchar(10),
time datetime )
insert thingy (record,event,time) values (1,'Start','6/26/2012 10:05AM')
insert thingy (record,event,time) values (1,'End','6/26/2012 10:45AM ')
insert thingy (record,event,time) values (2,'Start','6/26/2012 09:55AM')
insert thingy (record,event,time) values (2,'End','6/26/2012 11:05AM')
DECLARE #mytime datetime
SET #mytime = '6/26/2012 9:58AM'
SELECT *
FROM
thingy leftside
JOIN thingy rightside
ON leftside.Record = rightside.Record
WHERE
leftside.Event = 'Start'
and rightside.Event = 'End'
and #mytime >= leftside.Time
and #mytime <= rightside.Time
SET #mytime = '6/26/2012 10:10AM'
SELECT *
FROM
thingy leftside
JOIN thingy rightside
ON leftside.Record = rightside.Record
WHERE
leftside.Event = 'Start'
and rightside.Event = 'End'
and #mytime >= leftside.Time
and #mytime <= rightside.Time
Seems to give what I would expect - 1 row for the first query, 2 for the second.

Get record #s of all events that fall within the given range:
SELECT Record
FROM atable
WHERE Time BETWEEN #StartTime AND #EndTime
Now just get all rows whose record #s match those in the above query's result set, i.e. like this:
SELECT *
FROM atable
WHERE Record IN (
SELECT Record
FROM atable
WHERE Time BETWEEN #StartTime AND #EndTime
)

The simplest way to do this would be using the following:
WITH recs AS (
SELECT Record, MIN(tme) AS startTime, MAX(tme) AS endTime
FROM records
GROUP BY Record
)
SELECT * FROM
records
WHERE startTime >= #eventsAfter
AND endTime <= #eventsBefore
(this is how I interpreted your question, at least)

Related

Pivot Time Between into a Matrix

How can I pivot a start, end, start break, and end break times into a matrix that looks like the below? Ideally, I'd like to accomplish this in SQL Server.
Essentially, I'm trying to visualize a timesheet (start time, end time, start break, end break), and pivot across the day (broken down by 15-minute increments) as 1 or 0.
Start Time
End Time
Break Start
Break Start
0:00
0:15
0:30
0:45
1:00
1:15
1:30
1:45
2:00
2:15
0:15
2:00
0:45
1:15
0
1
1
0
0
1
1
1
0
0
Revised version that handles time sheet entries that don't start and end exactly on the 15min intervals.
-- Create test timesheet data
DROP TABLE IF EXISTS #Timesheets
GO
CREATE TABLE #Timesheets(ID INT, StartTime TIME, EndTime TIME, BreakStart TIME, Breakend TIME)
INSERT INTO #Timesheets(ID, StartTime,EndTime,BreakStart,Breakend)
VALUES
(1,'00:15','02:00','00:45','01:15'),
(2,'08:03','16:50','12:05','13:00'),
(3,'09:00','10:15','09:35','09:55')
-- Recursively create a set of time periods in 15mins intervals
DECLARE #StartTime TIME = '00:00'
DECLARE #EndTime TIME = DATEADD(mi,1425,#StartTime)
;WITH TimePeriods AS
(
SELECT #StartTime AS StartTime,DATEADD(mi,15,#StartTime) AS EndTime, CONVERT(VARCHAR(5),#StartTime,108) AS TimePeriod
UNION ALL
SELECT DATEADD(mi,15,StartTime),DATEADD(mi,30,StartTime), CONVERT(VARCHAR(5),DATEADD(mi,15,StartTime),108) FROM TimePeriods
WHERE StartTime < #EndTime
)
--select STRING_AGG( 'ISNULL('+QUOTENAME(Timeperiod)+',0) AS '+QUOTENAME(Timeperiod) ,',') from TimePeriods
-- Map shifts to the time periods
,Shifts AS
(
SELECT
tp.TimePeriod
,ts.*
FROM
TimePeriods tp
JOIN #Timesheets ts
ON
( -- Shifts
((ts.StartTime >= tp.StartTime AND ts.StartTime < tp.EndTime OR ts.EndTime > tp.StartTime AND ts.EndTime < tp.EndTime)
OR (tp.StartTime > ts.StartTime AND tp.StartTime < ts.EndTime)
OR (tp.EndTime > ts.StartTime AND tp.EndTime < ts.EndTime))
)
)
-- Map Breaks to the Time Periods
,Breaks AS
(
SELECT
tp.TimePeriod
,tb.*
FROM
TimePeriods tp
JOIN #Timesheets tb
ON
( -- Breaks
((tb.BreakStart >= tp.StartTime AND tb.BreakStart < tp.EndTime OR tb.BreakEnd > tp.StartTime AND tb.BreakEnd < tp.EndTime)
OR (tp.StartTime > tb.BreakStart AND tp.StartTime < tb.BreakEnd)
OR (tp.EndTime > tb.BreakStart AND tp.EndTime < tb.BreakEnd))
)
)
-- Join the Time Periods to both shifts and breaks, create an Active field which deducts breaks from shifts
,TimeSheetPerPeriod AS
(
select
s.*
,CASE WHEN ISNULL(s.id,0)-ISNULL(b.id,0)>0 THEN 1 ELSE 0 END AS IsActive
FROM TimePeriods tp
JOIN Shifts s ON tp.TimePeriod = s.TimePeriod
LEFT JOIN Breaks b ON tp.TimePeriod = b.TimePeriod
)
-- Pivot the data into the desired format
SELECT
id,StartTime,EndTime,BreakStart,Breakend
,ISNULL([00:00],0) AS [00:00],ISNULL([00:15],0) AS [00:15],ISNULL([00:30],0) AS [00:30],ISNULL([00:45],0) AS [00:45],ISNULL([01:00],0) AS [01:00],ISNULL([01:15],0) AS [01:15],ISNULL([01:30],0) AS [01:30],ISNULL([01:45],0) AS [01:45],ISNULL([02:00],0) AS [02:00],ISNULL([02:15],0) AS [02:15],ISNULL([02:30],0) AS [02:30],ISNULL([02:45],0) AS [02:45],ISNULL([03:00],0) AS [03:00],ISNULL([03:15],0) AS [03:15],ISNULL([03:30],0) AS [03:30],ISNULL([03:45],0) AS [03:45],ISNULL([04:00],0) AS [04:00],ISNULL([04:15],0) AS [04:15],ISNULL([04:30],0) AS [04:30],ISNULL([04:45],0) AS [04:45],ISNULL([05:00],0) AS [05:00],ISNULL([05:15],0) AS [05:15],ISNULL([05:30],0) AS [05:30],ISNULL([05:45],0) AS [05:45],ISNULL([06:00],0) AS [06:00],ISNULL([06:15],0) AS [06:15],ISNULL([06:30],0) AS [06:30],ISNULL([06:45],0) AS [06:45],ISNULL([07:00],0) AS [07:00],ISNULL([07:15],0) AS [07:15],ISNULL([07:30],0) AS [07:30],ISNULL([07:45],0) AS [07:45],ISNULL([08:00],0) AS [08:00],ISNULL([08:15],0) AS [08:15],ISNULL([08:30],0) AS [08:30],ISNULL([08:45],0) AS [08:45],ISNULL([09:00],0) AS [09:00],ISNULL([09:15],0) AS [09:15],ISNULL([09:30],0) AS [09:30],ISNULL([09:45],0) AS [09:45],ISNULL([10:00],0) AS [10:00],ISNULL([10:15],0) AS [10:15],ISNULL([10:30],0) AS [10:30],ISNULL([10:45],0) AS [10:45],ISNULL([11:00],0) AS [11:00],ISNULL([11:15],0) AS [11:15],ISNULL([11:30],0) AS [11:30],ISNULL([11:45],0) AS [11:45],ISNULL([12:00],0) AS [12:00],ISNULL([12:15],0) AS [12:15],ISNULL([12:30],0) AS [12:30],ISNULL([12:45],0) AS [12:45],ISNULL([13:00],0) AS [13:00],ISNULL([13:15],0) AS [13:15],ISNULL([13:30],0) AS [13:30],ISNULL([13:45],0) AS [13:45],ISNULL([14:00],0) AS [14:00],ISNULL([14:15],0) AS [14:15],ISNULL([14:30],0) AS [14:30],ISNULL([14:45],0) AS [14:45],ISNULL([15:00],0) AS [15:00],ISNULL([15:15],0) AS [15:15],ISNULL([15:30],0) AS [15:30],ISNULL([15:45],0) AS [15:45],ISNULL([16:00],0) AS [16:00],ISNULL([16:15],0) AS [16:15],ISNULL([16:30],0) AS [16:30],ISNULL([16:45],0) AS [16:45],ISNULL([17:00],0) AS [17:00],ISNULL([17:15],0) AS [17:15],ISNULL([17:30],0) AS [17:30],ISNULL([17:45],0) AS [17:45],ISNULL([18:00],0) AS [18:00],ISNULL([18:15],0) AS [18:15],ISNULL([18:30],0) AS [18:30],ISNULL([18:45],0) AS [18:45],ISNULL([19:00],0) AS [19:00],ISNULL([19:15],0) AS [19:15],ISNULL([19:30],0) AS [19:30],ISNULL([19:45],0) AS [19:45],ISNULL([20:00],0) AS [20:00],ISNULL([20:15],0) AS [20:15],ISNULL([20:30],0) AS [20:30],ISNULL([20:45],0) AS [20:45],ISNULL([21:00],0) AS [21:00],ISNULL([21:15],0) AS [21:15],ISNULL([21:30],0) AS [21:30],ISNULL([21:45],0) AS [21:45],ISNULL([22:00],0) AS [22:00],ISNULL([22:15],0) AS [22:15],ISNULL([22:30],0) AS [22:30],ISNULL([22:45],0) AS [22:45],ISNULL([23:00],0) AS [23:00],ISNULL([23:15],0) AS [23:15],ISNULL([23:30],0) AS [23:30],ISNULL([23:45],0) AS [23:45]
FROM
(
SELECT distinct *
FROM TimeSheetPerPeriod
) AS SourceTable
PIVOT
(
MAX(IsActive)
FOR TimePeriod IN ([00:00],[00:15],[00:30],[00:45],[01:00],[01:15],[01:30],[01:45],[02:00],[02:15],[02:30],[02:45],[03:00],[03:15],[03:30],[03:45],[04:00],[04:15],[04:30],[04:45],[05:00],[05:15],[05:30],[05:45],[06:00],[06:15],[06:30],[06:45],[07:00],[07:15],[07:30],[07:45],[08:00],[08:15],[08:30],[08:45],[09:00],[09:15],[09:30],[09:45],[10:00],[10:15],[10:30],[10:45],[11:00],[11:15],[11:30],[11:45],[12:00],[12:15],[12:30],[12:45],[13:00],[13:15],[13:30],[13:45],[14:00],[14:15],[14:30],[14:45],[15:00],[15:15],[15:30],[15:45],[16:00],[16:15],[16:30],[16:45],[17:00],[17:15],[17:30],[17:45],[18:00],[18:15],[18:30],[18:45],[19:00],[19:15],[19:30],[19:45],[20:00],[20:15],[20:30],[20:45],[21:00],[21:15],[21:30],[21:45],[22:00],[22:15],[22:30],[22:45],[23:00],[23:15],[23:30],[23:45])
) AS PivotTable

Update Column using calculation in last year from another table

I am trying to update (using Inner joins for three tables) item stats STAT for table IM_ITEM by highlighting items that sold less than 12 as "D" (Discontinue).
The 2nd table PS_TKT_HIST_LIN has the Quantity sold column QTY_SOLD for each item on each day and the date column BUS_DAT.
I also need a third table IM_INV to filter the data, I need to say the last received date LST_RECV_DAT for these items is earlier than "2019-01-01" and last sales date LST_SAL_DAT is after "2019-01-01". I used the following code
UPDATE M
SET M.STAT = 'D'
FROM
dbo.IM_ITEM AS M
INNER JOIN
IM_INV AS N
ON
M.ITEM_NO = N.ITEM_NO
INNER JOIN
dbo.PS_TKT_HIST_LIN S`
ON
M.ITEM_NO = S.ITEM_NO
WHERE
CONVERT(DATETIME, FLOOR(CONVERT(FLOAT, N.LST_RECV_DAT))) <= '2019-01-01'
AND CONVERT(DATETIME, FLOOR(CONVERT(FLOAT, N.LST_SAL_DAT))) >= '2019-01-01'
AND M.STAT = 'A'
AND SUM(case when DATEPART(YYYY, (BUS_DAT)) = DATEPART(YYYY, DATEADD(YYYY, -1, getdate()))
AND DATEPART(yyyy, (BUS_DAT)) = DATEPART(yyyy, DATEADD(YYYY, -1, getdate()))
then qty_sold else 0)<12
It comes with an error
Any advise please
You should use HAVING clause instead of Sum in where.
You can use CTE to achieve the value, then update accordingly.
;with cte as(
select ITEM_NO, ..
from ..
group by ITEM_NO
having .. < 12
)
update M
set SET M.STAT = 'D'
from dbo.IM_ITEM AS M
inner join cte on M.ITEM_NO = cte.ITEM_NO
You can't use an aggregate function in where clause unless defined under subquery.

SQL - Finding Gaps in Coverage

I am running this problem on SQL server
Here is my problem.
have something like this
Dataset A
FK_ID StartDate EndDate Type
1 10/1/2018 11/30/2018 M
1 12/1/2018 2/28/2019 N
1 3/1/2019 10/31/2019 M
I have a second data source I have no control over with data something like this:
Dataset B
FK_ID SpanStart SpanEnd Type
1 10/1/2018 10/15/2018 M
1 10/1/2018 10/25/2018 M
1 2/15/2019 4/30/2019 M
1 5/1/2019 10/31/2019 M
What I am trying to accomplish is to check to make sure every date within each TYPE M record in Dataset A has at least 1 record in Dataset B.
For example record 1 in Dataset A does NOT have coverage from 10/26/2018 through 11/30/2018. I really only care about when the coverage ends, in this case I want to return 10/26/2018 because it is the first date where the span has no coverage from Dataset B.
I've written a function that does this but it is pretty slow because it is cycling through each date within each M record and counting the number of records in Dataset B. It exits the loop when it finds the first one but I would really like to make this more efficient. I am sure I am not thinking about this properly so any suggestions anyone can offer would be helpful.
This is the section of code I'm currently running
else if #SpanType = 'M'
begin
set #CurrDate = #SpanStart
set #UncovDays = 0
while #CurrDate <= #SpanEnd
Begin
if (SELECT count(*)
FROM eligiblecoverage ec join eligibilityplan ep on ec.plandescription = ep.planname
WHERE ec.masterindividualid = #IndID
and ec.planbegindate <= #CurrDate and ec.planenddate >= #CurrDate
and ec.sourcecreateddate = #MaxDate
and ep.medicaidcoverage = 1) = 0
begin
SET #Result = concat('NON Starting ',format(#currdate, 'M/d/yyyy'))
BREAK
end
set #CurrDate = #CurrDate + 1
end
end
I am not married to having a function it just could not find a way to do this in queries that wasn't very very slow.
EDIT: Dataset B will never have any TYPEs except M so that is not a consideration
EDIT 2: The code offered by DonPablo does de-overlap the data but only in cases where there is an overlap at all. It reduces dataset B to:
FK_ID SpanStart SpanEnd Type
1 10/1/2018 10/25/2018 M
instead of
FK_ID SpanStart SpanEnd Type
1 10/1/2018 10/25/2018 M
1 2/15/2019 4/30/2019 M
1 5/1/2019 10/31/2019 M
I am still futzing around with it but it's a start.
I would approach this by focusing on B. My assumption is that any absent record would follow span_end in the table. So here is the idea:
Unpivot the dates in B (adding "1" to the end dates)
Add a flag if they are present with type "M".
Check to see if any not-present records are in the span for A.
Check the first and last dates as well.
So, this looks like:
with bdates as (
select v.dte,
(case when exists (select 1
from b b2
where v.dte between b2.spanstart and b2.spanend and
b2.type = 'M'
)
then 1 else 0
end) as in_b
from b cross apply
(values (spanstart), (dateadd(day, 1, spanend)
) v(dte)
where b.type = 'M' -- all we care about
group by v.dte -- no need for duplicates
)
select a.*,
(case when not exists (select 1
from b b2
where a.startdate between b2.spanstart and b2.spanend and
b2.type = 'M'
)
then 0
when not exists (select 1
from b b2
where a.enddate between b2.spanstart and b2.spanend and
b2.type = 'M'
)
when exists (select 1
from bdates bd
where bd.dte between a.startdate and a.enddate and
bd.in_b = 0
)
then 0
when exists (select 1
from b b2
where a.startdate between b2.spanstart and b2.spanend and
b2.type = 'M'
)
then 1
else 0
end)
from a;
What is this doing? Four validity checks:
Is the starttime valid?
Is the endtime valid?
Are any intermediate dates invalid?
Is there at least one valid record?
Start by framing the problem in smaller pieces, in a sequence of actions like I did in the comment.
See George Polya "How To Solve It" 1945
Then Google is your friend -- look at==> sql de-overlap date ranges into one record (over a million results)
UPDATED--I picked Merge overlapping dates in SQL Server
and updated it for our table and column names.
Also look at theory from 1983 Allen's Interval Algebra https://www.ics.uci.edu/~alspaugh/cls/shr/allen.html
Or from 2014 https://stewashton.wordpress.com/2014/03/11/sql-for-date-ranges-gaps-and-overlaps/
This is a primer on how to setup test data for this problem.
Finally determine what counts via Ranking the various pairs of A vs B --
bypass those totally Within, then work with earliest PartialOverlaps, lastly do the Precede/Follow items.
--from Merge overlapping dates in SQL Server
with SpanStarts as
(
select distinct FK_ID, SpanStart
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanStart < t1.SpanStart
and t2.SpanEnd >= t1.SpanStart)
),
SpanEnds as
(
select distinct FK_ID, SpanEnd
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanEnd > t1.SpanEnd
and t2.SpanStart <= t1.SpanEnd)
),
DeOverlapped_B as
(
Select FK_ID, SpanStart,
(select min(SpanEnd) from SpanEnds as e
where e.FK_ID = s.FK_ID
and SpanEnd >= SpanStart) as SpanEnd
from SpanStarts as s
)
Select * from DeOverlapped_B
Now we have something to feed into the next steps, and we can use the above as a CTE
======================================
with SpanStarts as
(
select distinct FK_ID, SpanStart
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanStart < t1.SpanStart
and t2.SpanEnd >= t1.SpanStart)
),
SpanEnds as
(
select distinct FK_ID, SpanEnd
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanEnd > t1.SpanEnd
and t2.SpanStart <= t1.SpanEnd)
),
DeOverlapped_B as
(
Select FK_ID, SpanStart,
(select min(SpanEnd) from SpanEnds as e
where e.FK_ID = s.FK_ID
and SpanEnd >= SpanStart) as SpanEnd
from SpanStarts as s
),
-- find A row's coverage
ACoverage as (
Select
a.*, b.SpanEnd, b.SpanStart,
Case
When SpanStart <= StartDate And StartDate <= SpanEnd
And SpanStart <= EndDate And EndDate <= SpanEnd
Then '1within' -- starts, equals, during, finishes
When EndDate < SpanStart
Or SpanEnd < StartDate
Then '3beforeAfter' -- preceeds, meets, preceeded, met
Else '2overlap' -- one or two ends hang over spanStart/End
End as relation
From Coverage_A a
Left Join DeOverlapped_B b
On a.FK_ID = b.FK_ID
Where a.Type = 'M'
)
Select
*
,Case
When relation1 = '2' And StartDate < SpanStart Then StartDate
When relation1 = '2' Then DateAdd(d, 1, SpanEnd)
When relation1 = '3' Then StartDate
End as UnCoveredBeginning
From (
Select
*
,SUBSTRING(relation,1,1) as relation1
,ROW_NUMBER() Over (Partition by A_ID Order by relation, SpanStart) as Rownum
from ACoverage
) aRNO
Where Rownum = 1
And relation1 <> '1'

SQL SUM() on one field with other fields on other conditions (the most efficient way)

I have a stored procedure to retrieve total orders (in $$$) on particular date and at the same time need to find out how much was a cash transaction vs credit card transaction. There are also others transaction type (eg. paypal) but does not need to be returned (don't ask me why).
I have two choices, writing all in 1 query or go through cursor. Here is my 1 query
SELECT #TotalOrder = SUM([TotalValue]),
#TotalCash = (SELECT SUM([TotalValue]) FROM [dbo].[tblOrder] dbChild WHERE (dbChild.[OrderId] = dbMain.[OrderId]) AND (dbChild.[PaymentType] = 0)),
#TotalCard = (SELECT SUM([TotalValue]) FROM [dbo].[tblOrder] dbChild WHERE (dbChild.[OrderId] = dbMain.[OrderId]) AND (dbChild.[PaymentType] = 1))
FROM [dbo].[tblOrder] dbMain
WHERE [PaymentDate] BETWEEN #StartDate AND #EndDate;
Is the query above ok? Alternatively, I can use cursor which loop through each record and accumulate the #TotalOrder, #TotalCash, and #TotalCard.
SELECT [TotalValue], [PaymentType]
FROM [dbo].[tblOrder] dbMain
WHERE [PaymentDate] BETWEEN #StartDate AND #EndDate;
--then use cursor to loop through each of the record.
What do you think? Which one is the most efficient way? If both is not efficient, do you have any other suggestions?
The first method will most likely be the fastest, however you can refactor to use a single query:
SELECT #TotalOrder = SUM([TotalValue]),
#TotalCash = SUM(CASE WHEN [PaymentType] = 0 THEN [TotalValue] ELSE 0 END)
#TotalCard = SUM(CASE WHEN [PaymentType] = 1 THEN [TotalValue] ELSE 0 END)
FROM [dbo].[tblOrder] dbMain
WHERE [PaymentDate] BETWEEN #StartDate AND #EndDate;

Getting current semester in SQL SERVER

I'm having a trouble on this query it says Incorrect syntax near the keyword 'BETWEEN' but when I tried to individually execute the BETWEEN conditions without CASE WHEN it runs normally, Also in relation to the Title is their a proper way to get the semester in SQL SERVER?
DECLARE #ActiveSemester INT
SET #ActiveSemester = ISNULL((SELECT [value]
FROM spms_tblProfileDefaults WHERE eid = 7078 and ps_id = 1), (SELECT [value]
FROM spms_configs WHERE actions = 'semester_active') )
SELECT FORMAT((SUM((DATEDIFF(MINUTE, a.actual_start_time, a.actual_end_time)
- isnull(datediff(minute, break_start, break_end), 0))/ 60.0)), '0.00')
FROM spms_tblSubTask as a
LEFT JOIN pmis.dbo.employee as b ON a.eid = b.eid
LEFT JOIN pmis.dbo.time_reference as c ON c.code = ISNULL(b.TimeReference, 'TIME14')
LEFT JOIN dbo.spms_vwOrganizationalChart as e ON a.eid = e.eid
cross apply
(
select break_start = case when c.break_from between a.actual_start_time and a.actual_end_time
then c.break_from
when a.actual_start_time between c.break_from and c.break_to
then a.actual_start_time
else NULL
end,
break_end = case when c.break_to between a.actual_start_time and a.actual_end_time
then c.break_to
when a.actual_end_time between c.break_from and c.break_to
then a.actual_end_time
end
) as d
WHERE
b.Shift = 0 and a.eid = 7078 and
YEAR(a.start_date_time) = YEAR(GETDATE()) and a.action_code = 1
and
(CASE
WHEN
#ActiveSemester = 1
THEN
a.start_date_time BETWEEN CAST(CONCAT('01/01/',YEAR(GETDATE())) as date) AND
CAST(CONCAT('06/30/',YEAR(GETDATE())) as date)
ELSE
a.start_date_time BETWEEN CAST(CONCAT('07/01/',YEAR(GETDATE())) as date) AND
CAST(CONCAT('12/31/',YEAR(GETDATE())) as date)
END)
It seems that the other answer was Deleted and here's what I did as the other points out about the proper use of CASE WHEN
WHERE
'other condition' and
a.start_date_time BETWEEN
(CASE
WHEN #ActiveSemester = '1'
THEN
CAST(CONCAT('01/01/',YEAR(GETDATE())) as date)
ELSE
CAST(CONCAT('07/01/',YEAR(GETDATE())) as date)
END)
AND
(CASE
WHEN #ActiveSemester = '1'
THEN
CAST(CONCAT('06/30/',YEAR(GETDATE())) as date)
ELSE
CAST(CONCAT('12/31/',YEAR(GETDATE())) as date)
END)
with this, I get the results I wanted. Thanks
Assuming #ActiveSemester can take on the values 0 or 1, I'd just write this as:
WHERE
b.Shift = 0 and a.eid = 7078 and
YEAR(a.start_date_time) = YEAR(GETDATE()) and a.action_code = 1
and
#ActiveSemester = ((MONTH(a.start_date_time)-1)/6)
Now, it's worth noting that this expression cannot use an index on start_date_time to satisfy this final predicate. If that's an issue, and the query isn't performing well, then I'd either recommend persisted computed columns in this table to extract the year and month value from the start_date_time column, modify the above query to use those columns and add appropriate indexes, or to populate a calendar table which has such values (and semester) properly represented and join to that table above, to again simplify the expressions and allow indexes to be used.
Note, also, that the column name worries me. If it does contain a time portion as well as a date, then note that the expressions in your original query would have excluded any values that occurred after midnight on 30th June and 31st December1. I presume that wasn't your intent and the query above doesn't suffer this same shortcoming.
1Because 2018-06-30T00:01:00 is strictly later than 2018-06-30T00:00:00 (which is what CAST(CONCAT('06/30/',YEAR(GETDATE())) as date) expands out to) and so isn't BETWEEN the start and end dates you gave.

Resources