Issue with DateCreated - sql-server

I have a table dbo.participation:
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
User VARCHAR(MAX) NOT NULL,
ParticipationLevel TINYINT NOT NULL,
Selector VARCHAR(MAX) NOT NULL,
DateCreated DATETIME NOT NULL
I created the code below but unfortunately it shows bad performance for #DateStart and #DateStop
SELECT
dateadd(month, datediff(month, 0, DateCreated), 0) AS MDate
,COUNT(CASE WHEN ParticipationLevel >= 10 THEN Selector ELSE NULL END) AS ParticipationLevel1
,COUNT(CASE WHEN ParticipationLevel >= 30 THEN Selector ELSE NULL END) AS ParticipationLevel2
FROM
Participation
WHERE
(#DateStart IS NULL OR (#DateStart IS NOT NULL
AND DateCreated >= #DateStart))
AND (#DateEnd IS NULL OR (#DateEnd IS NOT NULL
AND DateCreate < #DateEnd))
GROUP BY
Dateadd(month, datediff(month, 0, DateCreate), 0)
Do you happen to have any ideas how to improve my code or alternatively how to modify the table to improve performance?

You need an index along the following lines
CREATE INDEX ix
ON dbo.Participation(DateCreated)
INCLUDE (ParticipationLevel);
And you should rewrite the query to get rid of the OR and to avoid the unnecessary reference to a column defined as NOT NULL.
(Note a simple COUNT(Selector) would not look up the value as SQL Server recognizes it can't be NULL but wrapping in an expression defeats this logic)
SELECT DATEADD(month, DATEDIFF(month, 0, DateCreated), 0) AS MDate,
COUNT(CASE
WHEN ParticipationLevel >= 10 THEN 1
END) AS ParticipationLevel1,
COUNT(CASE
WHEN ParticipationLevel >= 30 THEN 1
END) AS ParticipationLevel2
FROM Participation
WHERE DateCreated >= ISNULL(#DateStart, '17530101')
AND DateCreated <= ISNULL(#DateEnd, '99991231')
GROUP BY DATEDIFF(month, 0, DateCreated)
This can give a plan with a seek as below
Note that it would be possible to get rid of the sort by processing chunks of the index a month at the time (possibly in a recursive CTE) but this may be overkill.
Code for that could look something like
/*Cheap to find out from the index*/
IF #DateStart IS NULL
SELECT #DateStart = MIN(DateCreated)
FROM dbo.Participation
IF #DateStart IS NULL
SELECT #DateEnd = MAX(DateCreated)
FROM dbo.Participation
/*Adjust to start of month*/
SELECT #DateStart = DATEADD(month, DATEDIFF(month, 0, #DateStart), 0),
#DateEnd = DATEADD(month, 1 + DATEDIFF(month, 0, #DateEnd), 0);
WITH Dates
AS (SELECT #DateStart AS MDate
UNION ALL
SELECT dateadd(MONTH, 1, MDate) AS MDate
FROM Dates
WHERE dateadd (MONTH, 1, MDate) <= #DateEnd)
SELECT D.MDate,
CA.ParticipationLevel1,
CA.ParticipationLevel2
FROM Dates D
CROSS APPLY (SELECT COUNT(CASE
WHEN ParticipationLevel >= 10
THEN 1
END) AS ParticipationLevel1,
COUNT(CASE
WHEN ParticipationLevel >= 30
THEN 1
END) AS ParticipationLevel2
FROM Participation P WITH (INDEX = ix)
WHERE P.DateCreated >= D.MDate
AND P.DateCreated < DATEADD(MONTH, 1, D.MDate)
GROUP BY () /* So no grouping row returned for empty months */
) CA(ParticipationLevel1, ParticipationLevel2)
OPTION (MAXRECURSION 0);
Which gives a plan with repeated seeks and no sorts

Below two checks not needed in your WHERE clause
#DateStart IS NOT NULL AND
#DateEnd IS NOT NULL AND
SELECT dateadd(month, datediff(month, 0, DateCreated), 0) AS MDate
,COUNT(CASE WHEN ParticipationLevel >= 10 THEN Tracking ELSE NULL END) AS ParticipationLevel1
,COUNT(CASE WHEN ParticipationLevel >= 30 THEN Tracking ELSE NULL END) AS ParticipationLevel2
FROM Participation
WHERE (#DateStart IS NULL OR DateCreated >= #DateStart) AND (#DateEnd IS NULL OR DateCreate < #DateEnd)
GROUP BY Dateadd(month, datediff(month, 0, DateCreate), 0)

Related

Is there a faster way of adding up time ranges, taking overlaps into account?

I have a subquery that is taking multiple minutes to execute. If I pull out just the initial rows that are being added up, it only takes half a second with 2,400ish rows so I don't understand why the main query doing the sum is taking so long.
What I'm trying to do is for all the transactions in a date range, for all the workers assigned to those transactions, add up the scheduled hours for each worker.
The query is returning the correct data, it's just taking FOREVER to do it.
QUERY
SELECT scheduled_hours = COALESCE(sum(hours), 0), worker_sysid
FROM (
SELECT DISTINCT
B.DateR1,
B.DateR2,
hours = ABS((B.DAteR1 - B.DateR2) / 3600),
B.worker_sysid
FROM Trans A
OUTER APPLY (
SELECT
DateR1 = MIN(TRANS_START),
DateR2 = MAX(TRANS_END),
worker_sysid
FROM Trans
JOIN trans_workers ON trans_workers.trans_sysid = Trans.SYSID
LEFT JOIN Service ON Service.SYSID = Trans.SERVICESYSID
WHERE
TRANS_START <= A.TRANS_END AND TRANS_END >= A.TRANS_START
AND TRANS_START IS NOT NULL AND TRANS_END IS NOT NULL
AND TRANS_START != '' AND TRANS_END != ''
AND Trans.CHARGEBY IN ('Hours', 'Hour')
AND (
COALESCE(Service.overnight, 0) != 1
OR
COALESCE(Service.active_overnight, 0) = 1
)
AND TRANSDATE BETWEEN 80387 AND 80400 ### These are Clarion dates
AND trans_workers.deleted_at IS NULL
GROUP BY worker_sysid
) B
) A
WHERE worker_sysid IS NOT NULL
GROUP BY worker_sysid
TABLES
Trans: SYSID (int, pk), TRANSDATE (int, clarion-formatted date), TRANS_START / TRANS_END (UNIX timestamp), SERVICESYSID (int, fk), CHARGEBY (varchar)
trans_workers: trans_sysid, worker_sysid, deleted_at
Service: SYSID (int, pk)
UPDATE
Moving the trans_workers join out of the OUTER APPLY has reduced the execution time from 1 minute down to 16 seconds, so that's an improvement.
SELECT scheduled_hours = COALESCE(sum(hours), 0), worker_sysid
FROM (
SELECT DISTINCT
B.DateR1,
B.DateR2,
hours = ABS((B.DateR1 - B.DateR2) / 3600),
worker_sysid
FROM Trans A
JOIN trans_workers ON A.SYSID = trans_workers.trans_sysid
OUTER APPLY (
SELECT
DateR1 = MIN(TRANS_START),
DateR2 = MAX(TRANS_END),
Trans.SYSID
FROM Trans
LEFT JOIN Service ON Service.SYSID = Trans.SERVICESYSID
WHERE
TRANS_START <= A.TRANS_END AND TRANS_END >= A.TRANS_START
AND TRANS_START IS NOT NULL AND TRANS_END IS NOT NULL
AND TRANS_START != '' AND TRANS_END != ''
AND Trans.CHARGEBY IN ('Hours', 'Hour')
AND COALESCE(Service.overnight, 0) != 1
AND TRANSDATE BETWEEN 80387 AND 80400
GROUP BY Trans.SYSID
) B
) A
WHERE worker_sysid IS NOT NULL
GROUP BY worker_sysid
ORDER BY worker_sysid
Thanks to https://www.sqlservercentral.com/forums/topic/consolidate-overlapping-date-periods I have a query that executes in under a second and returns what appear to be the correct hours. Only problem being I don't understand what's happening.
DECLARE #start INTEGER, #end INTEGER;
SET #start = 80401; --06/02/2021
SET #end = 80414; --19/02/2021
WITH cteTemp
AS (
SELECT
worker_sysid,
BeginDate =
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY worker_sysid ORDER BY theDate) - openCnt = 0 THEN theDate
END,
EndDate =
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY worker_sysid ORDER BY theDate) - closeCnt = 0 THEN theDate
END
FROM (
SELECT
worker_sysid,
theDate = DATEADD(day, 0, DATEDIFF(day, 0, (dateadd(day,[TRANSDATE]-(4),'1801-01-01')))) + DATEADD(day, 0 - DATEDIFF(day, 0, DATEADD(second, TRANS_START - DATEDIFF(S, GETDATE(), GETUTCDATE()), '1970-01-01')), DATEADD(second, TRANS_START - DATEDIFF(S, GETDATE(), GETUTCDATE()), '1970-01-01')),
closeCnt = NULL,
openCnt = (ROW_NUMBER() OVER (PARTITION BY worker_sysid ORDER BY DATEADD(day, 0, DATEDIFF(day, 0, (dateadd(day,[TRANSDATE]-(4),'1801-01-01')))) + DATEADD(day, 0 - DATEDIFF(day, 0, DATEADD(second, TRANS_START - DATEDIFF(S, GETDATE(), GETUTCDATE()), '1970-01-01')), DATEADD(second, TRANS_START - DATEDIFF(S, GETDATE(), GETUTCDATE()), '1970-01-01'))) * 2) - 1
FROM
Trans
INNER JOIN trans_workers ON trans_workers.trans_sysid = Trans.SYSID
JOIN Service ON Service.SYSID = Trans.SERVICESYSID
WHERE
worker_sysid IS NOT NULL
AND Trans.deleted_at IS NULL
AND trans_workers.deleted_at IS NULL
AND Trans.CHARGEBY IN ('Hour', 'Hours')
AND (transCancelled IS NULL OR transCancelled != 1)
AND (
COALESCE(Service.overnight, 0) = 0
)
AND TRANSDATE BETWEEN #start AND #end
UNION ALL
SELECT
worker_sysid,
theDate = DATEADD(day, 0, DATEDIFF(day, 0, (dateadd(day,[TRANSDATE]-(4),'1801-01-01')))) + DATEADD(day, 0 - DATEDIFF(day, 0, DATEADD(second, TRANS_END - DATEDIFF(S, GETDATE(), GETUTCDATE()), '1970-01-01')), DATEADD(second, TRANS_END - DATEDIFF(S, GETDATE(), GETUTCDATE()), '1970-01-01')),
closeCnt = ROW_NUMBER() OVER (PARTITION BY worker_sysid ORDER BY worker_sysid, DATEADD(day, 0, DATEDIFF(day, 0, (dateadd(day,[TRANSDATE]-(4),'1801-01-01')))) + DATEADD(day, 0 - DATEDIFF(day, 0, DATEADD(second, TRANS_END - DATEDIFF(S, GETDATE(), GETUTCDATE()), '1970-01-01')), DATEADD(second, TRANS_END - DATEDIFF(S, GETDATE(), GETUTCDATE()), '1970-01-01'))) * 2,
openCnt = NULL
FROM
Trans
JOIN trans_workers ON trans_workers.trans_sysid = Trans.SYSID
JOIN Service ON Service.SYSID = Trans.SERVICESYSID
WHERE
worker_sysid IS NOT NULL
AND Trans.deleted_at IS NULL
AND trans_workers.deleted_at IS NULL
AND Trans.CHARGEBY IN ('Hour', 'Hours')
AND (transCancelled IS NULL OR transCancelled != 1)
AND (
COALESCE(Service.overnight, 0) = 0
)
AND TRANSDATE BETWEEN #start AND #end
)
AS baseSelected
)
SELECT scheduled_hours = SUM(hours), worker_sysid
FROM (
SELECT
dt.worker_sysid,
hours = CAST(ABS(DATEDIFF(second, MIN(dt.BeginDate), MAX(dt.EndDate))) / 3600.0 AS DECIMAL(10,2))
FROM (
SELECT
worker_sysid,
BeginDate,
EndDate,
grpID =
IIF(BeginDate IS NOT NULL, ROW_NUMBER() OVER (PARTITION BY worker_sysid ORDER BY worker_sysid, BeginDate), ROW_NUMBER() OVER (PARTITION BY worker_sysid ORDER BY worker_sysid, EndDate))
FROM
cteTemp
WHERE
BeginDate IS NOT NULL OR EndDate IS NOT NULL
)
AS dt
GROUP BY dt.worker_sysid,grpID
) AS final_table
GROUP BY worker_sysid ORDER BY worker_sysid
Bonus points to myself for conversions because the DATE of each transaction is in Clarion and the TIME of each transaction is a Unix timestamp

Count ticket number in sql

What im trying to do is only display records created for a customer that only has more then one ticketnumber in a certain month.
Select name,ticketnumber, title,
description,statename,personname,charge,createdon
from case
Where sum(ticketnumber) > 2 AND createdon >= '2017-01-01' ;
i have tried
select sum(ticketnumber) AS total
and
where sum(ticketnumber) > 2
Where am i going wrong? ticketnumber is a varchar data type
The error im getting;
Operand data type nvarchar is invalid for sum operator.
To limit the check to a single month, not just after some date, you can use dateadd(month, datediff(month, 0, createdon ) , 0) to truncate a date down to the first of a month.
Assuming you are identifying your customer by personname:
using exists():
select
name
, ticketnumber
, title
, description
, statename
, personname
, charge
, createdon
from [case] as c
where dateadd(month, datediff(month, 0, createdon ) , 0) = '20170101'
and exists (
select 1
from [case] as i
where --createdon >= '2017-01-01'
dateadd(month, datediff(month, 0, createdon ) , 0) = '20170101'
and i.personname = c.personname
having count(*) > 1
)
using in()
select
name
, ticketnumber
, title
, description
, statename
, personname
, charge
, createdon
from [case]
where dateadd(month, datediff(month, 0, createdon ) , 0) = '20170101'
and personname in (
select personname
from [case] as i
where --createdon >= '2017-01-01'
dateadd(month, datediff(month, 0, createdon ) , 0) = '20170101'
group by personname
having count(*) > 1
)
I usually just do this
SELECT Total from (select sum(ticketnumber) AS total from case)A
Where Total>2

Performance tuning for SQL Query

Hi here I am attaching my sample table structure which I want to use in my project
CREATE TABLE TESTSALESVOLUMETABLE
(
ID INT IDENTITY(1,1),
AMOUNT DECIMAL(18,2),
CREDITEDDATE DATETIME
)
and the queries I used like this
DECLARE #CURRENTDATE AS DATETIME = GETDATE()
DECLARE #PSV AS INT = 0
DECLARE #TOTAL AS INT = 0
IF (DATEPART(DAY, #CURRENTDATE) <= 15)
BEGIN
SELECT #PSV = (
SELECT Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE DATEPART(DAY, CREDITEDDATE) <= 15
AND MONTH(CREDITEDDATE) = MONTH(#CURRENTDATE)
AND YEAR(CREDITEDDATE) = YEAR(#CURRENTDATE)
)
END
ELSE
BEGIN
SELECT #PSV = (
SELECT Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE DATEPART(DAY, CREDITEDDATE) > 15
AND MONTH(CREDITEDDATE) = MONTH(#CURRENTDATE)
AND YEAR(CREDITEDDATE) = YEAR(#CURRENTDATE)
)
END
SELECT #total = (
SELECT Sum(Amount)
FROM TESTSALESVOLUMETABLE
)
SELECT #PSV 'PSV',
#total 'TOTAL'
Is there any way to increase the performance of this query
First, you don't need a subquery for setting the variable. Second, the use of functions on columns usually prevents the use of indexes. So, I would recommend something like this:
SELECT #PSV = Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE CREDITEDDATE >= DATEADD(DAY, 1 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND
CREDITEDDATE < DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE));
Then, you want an index on TESTSALESVOLUMETABLE(CREDTEDDATE, AMOUNT).
Following the guidelines from: Bad habits to kick : mis-handling date / range queries - Aaron Bertrand - 2009-10-16
First, we want to get rid of:
where datepart(day, crediteddate) <= 15
and month(crediteddate)=month(#currentdate)
and year(crediteddate)=year(#currentdate)
because:
[...] you've effectively eliminated the possibility of SQL Server taking advantage of an index. Since you've forced it to build a nonsargable condition, this means it will have to convert every single value in the table to compare it to the [value] you've presented on the right hand side [...]
Second, we want to make sure to avoid using between with datetimes because it can return unwanted rows or miss wanted rows, even when using something like between ... and dateadd(second, -1, #thrudate) or even between ... and 'yyyy-mm-ddT23:59:59.997'. (See Aaron Bertrand's article for more examples on this).
So the best way to do this would be to say:
If today is the 15th or earlier, get rows >= the 1st of this month and < the 16th of this month
If today is the 16th or later, get rows >= the 16th of this month and < the 1st of next month
Also, as Gordon Linoff mentioned, you will benefit from an index on testsalesvolumetable(crediteddate, amount). But Gordon's formulas always return the 1st and 16th of the current month.
Instead of breaking the procedure into two queries depending on the current day, we can calculate those from and thru dates and just use one query.
Here is example code both with and without using variables for the from and thru dates, along with a quick calendar test to check the resulting ranges.
rextester link for test setup: http://rextester.com/YVLI65217
create table testsalesvolumetable (crediteddate datetime not null, amount int not null)
insert into testsalesvolumetable values
('20161201',1) ,('20161202',1) ,('20161203',1) ,('20161204',1) ,('20161205',1)
,('20161206',1) ,('20161207',1) ,('20161208',1) ,('20161209',1) ,('20161210',1)
,('20161211',1) ,('20161212',1) ,('20161213',1) ,('20161214',1) ,('20161215',1)
,('20161216',1) ,('20161217',1) ,('20161218',1) ,('20161219',1) ,('20161220',1)
,('20161221',1) ,('20161222',1) ,('20161223',1) ,('20161224',1) ,('20161225',1)
,('20161226',1) ,('20161227',1) ,('20161228',1) ,('20161229',1) ,('20161230',1)
,('20161231',1) ,('20170101',1)
/* ----- without variables */
declare #psv int;
select #psv = Sum(amount)
from testsalesvolumetable
where crediteddate >= dateadd(day, (1- (day(convert(date,getdate()))/16)) - (day(convert(date,getdate()))%16), convert(date,getdate()))
and crediteddate < case
when day(convert(date,getdate()))>15
then dateadd(month, datediff(month, -1, convert(date,getdate())), 0)
else dateadd(day,15,dateadd(month, datediff(month, 0, convert(date,getdate())), 0))
end;
select psv=#psv;
--*/
/* ----- with variables */
--declare #psv int;
declare #currentdate date;
/* change to date datatype to get rid of time portion*/
set #currentdate = getdate();
--set #currentdate = '20161212'
declare #fromdatetime datetime;
declare #thrudatetime datetime;
set #fromdatetime = dateadd(day, (1- (day(#currentdate)/16)) - (day(#currentdate)%16), #currentdate);
set #thrudatetime = case
when day(#currentdate)>15
then dateadd(month, datediff(month, -1, #currentdate), 0)
else dateadd(day,15,dateadd(month, datediff(month, 0, #currentdate), 0))
end;
select #psv = sum(amount)
from testsalesvolumetable
where crediteddate >= #fromdatetime
and crediteddate < #thrudatetime;
--/*
select
psv=#psv
, CurrentDate =convert(varchar(10),#currentdate ,121)
, FromDateTime=convert(varchar(10),#fromdatetime,121)
, ThruDateTime=convert(varchar(10),#thrudatetime,121);
--*/
Rextester link for the calendar test: http://rextester.com/ESZRH30262
--/* ----- Calendar Test */
;with n as (
select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) t(n)
)
, cal as (
select DateValue=convert(datetime,dateadd(day, row_number() over (order by (select 1)) -1, '20160101'))
from n as a
cross join n as b
cross join n as c
cross join n as d
)
select
--DateValue=convert(varchar(10),DateValue,121)
minDate =convert(varchar(10),min(DateValue),121)
, maxDate =convert(varchar(10),max(DateValue),121)
, FromDatetime=convert(varchar(10),dateadd(day, (1- (day(DateValue)/16)) - (day(DateValue)%16), DateValue),121)
, ThruDatetime=convert(varchar(10),case
when day(DateValue)>15
then dateadd(m, datediff(m, -1, DateValue), 0)
else convert(varchar(10),dateadd(day, 16 - day(DateValue), DateValue),121)
end,121)
, GordonFrom = convert(varchar(10),dateadd(day, 1 - day(DateValue), cast(DateValue as date)),121)
, GordonThru = convert(varchar(10),dateadd(day, 16 - day(DateValue), cast(DateValue as date)),121)
from cal
where datevalue >= '20160101'
and datevalue < '20170101'
--/*
group by
convert(varchar(10),dateadd(day, (1- (day(DateValue)/16)) - (day(DateValue)%16), DateValue),121)
, convert(varchar(10),case
when day(DateValue)>15
then dateadd(m, datediff(m, -1, DateValue), 0)
else convert(varchar(10),dateadd(day, 16 - day(DateValue), DateValue),121)
end,121)
, convert(varchar(10),dateadd(day, 1 - day(DateValue), cast(DateValue as date)),121)
, convert(varchar(10),dateadd(day, 16 - day(DateValue), cast(DateValue as date)),121)
order by FromDateTime
I thing this will work fine
DECLARE #PSV AS INT = 0
DECLARE #TOTAL AS INT = 0
IF (DATEPART(DAY,GETDATE()) <= 15)
BEGIN
SELECT #PSV = Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE CREDITEDDATE >= DATEADD(DAY, 1 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND
CREDITEDDATE < DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE));
END
ELSE
BEGIN
SELECT #PSV = Sum(AMOUNT)
FROM TESTSALESVOLUMETABLE
WHERE CREDITEDDATE >= DATEADD(DAY, 16 - DAY(GETDATE()), CAST(GETDATE() as DATE)) AND
CREDITEDDATE < DATEADD(DAY, 31 - DAY(GETDATE()), CAST(GETDATE() as DATE));
END
SELECT #total = (
SELECT Sum(Amount)
FROM TESTSALESVOLUMETABLE
)
SELECT #PSV 'PSV',
#total 'TOTAL'

SQL: union multiple single row results sets from the same query

I have a query that, when given a specific date, examines a large dataset (around 36m and growing) and returns an aggregate. Everything is working as expected... However, my end goal is to be able to determine a yearly average of these values. Perhaps my brain is running on empty, but I'm trying to do this dynamically where I don't have to run the query 365 times and then average.....
I need to find the yearly average of the 365 results, per #inst, per #program.
Any pointers in the right direction would be most appreciated.
Query:
USE HCODS
GO
DECLARE #user_date DATETIME
DECLARE #inst VARCHAR(4)
DECLARE #program VARCHAR(4)
SELECT #user_date = '9/30/2016'
SELECT #inst = 'SAC'
SELECT #program = 'PSU';
WITH T AS (
SELECT
B.OFFENDERID
,Institution = I.ORGCOMMONID
,BedUse = B.BEDUSE
,BeginEffectiveDtTm = CAST(B.BEDASSIGNMENTDATE AS DATETIME) + CAST(B.BEDASSIGNMENTTIME AS DATETIME)
,EndEffectiveDtTm = CASE WHEN B.BEDASSIGNMENTSTATUS = 'U' THEN
(CAST(B.INMBEDSTATUSDATE AS DATETIME) + CAST(B.INMBEDSTATUSTIME AS DATETIME)) ELSE NULL END
FROM ODS.BEDASSIGNMENT (NOLOCK) B
INNER JOIN (
SELECT F.PARTYID, I.ORGCOMMONID
FROM ODS.ORGANIZATIONPROF (NOLOCK) AS F
INNER JOIN ODS.ORGANIZATIONPROF (NOLOCK) AS I ON F.ORGAREACODE = I.PARTYID
) AS I ON B.FACILITYWHEREBEDLOCATED = I.PARTYID
WHERE B.BEDASSIGNMENTDATE BETWEEN '1/1/2016' AND '12/31/2016'
AND B.BEDASSIGNMENTSTATUS IN ('U','M')
)
SELECT CAST(#user_date AS DATE)
,T.INSTITUTION
,T.BEDUSE
,COUNT(*)
FROM T
WHERE
(
(
T.BEGINEFFECTIVEDTTM <= DATEADD(second,-1,(#user_date+1))
AND
T.ENDEFFECTIVEDTTM >= #user_date
)
OR T.ENDEFFECTIVEDTTM IS NULL
)
AND T.INSTITUTION = #inst
AND T.BedUse = #program
GROUP BY
T.Institution
,T.BedUse
Result sets (each one obtained by a single running of the query)
Date |Institution |BedUse |Count
-----------|------------|-------|-------
2016-09-30 |SAC |PSU |446
2016-10-01 |SAC |PSU |421
2016-10-02 |SAC |PSU |423
etc......
While it is hard to answer the question of your data without seeing it. I can turn you onto a SQL concept of windowed functions. This in essense is doing an inline grouping to aggregate data. If I have a single set but want to fashion multiple statements over it to see different things, this statement is perfect.
So in an example I am in essence going from the 1st of January of 2015 to today(dynamic as this could be any day even after I post this). I am then picking a random number of 1 to 100 to populate my row of data in my temporary set with. I then can do my aggregate operations on that.
DECLARE #Data TABLE ( Id INT IDENTITY, val INT, dt DATETIME)
DECLARE #Start DATETIME = '1-1-2015'
SET NOCOUNT ON;
WHILE #Start <= GETDATE()
BEGIN
INSERT INTO #Data VALUES (ABS(CHECKSUM(NewId())) % 100, #Start)
SELECT #Start = DATEADD(DAY, 1, #STart)
END
SELECT DISTINCT
SUM(Val) OVER() AS TotalValues
, COUNT(*) OVER() AS rowCounts
, DATEADD(YEAR, DATEDIFF(YEAR, 0, Dt), 0) AS YearDate
, COUNT(*) OVER(PARTITION BY DATEADD(YEAR, DATEDIFF(YEAR, 0, Dt), 0)) AS DaysInYear
, SUM(Val) OVER(PARTITION BY DATEADD(YEAR, DATEDIFF(YEAR, 0, Dt), 0)) AS ValsByYear
, AVG(Val) OVER(PARTITION BY DATEADD(YEAR, DATEDIFF(YEAR, 0, Dt), 0)) AS AVGByYear
, DATEADD(Month, DATEDIFF(Month, 0, Dt), 0) AS MonthDate
, COUNT(*) OVER(PARTITION BY DATEADD(Month, DATEDIFF(Month, 0, Dt), 0)) AS DaysInMonth
, SUM(Val) OVER(PARTITION BY DATEADD(Month, DATEDIFF(Month, 0, Dt), 0)) AS ValsByMonth
, AVG(Val) OVER(PARTITION BY DATEADD(Month, DATEDIFF(MOnth, 0, Dt), 0)) AS AVGByMonth
From #Data

DATEADD not working for the last 30 days

When I run this query, it seems to work great when I run it against the date 2015-4-11. However, if I query against the last 3 days it returns the values in year, month, and week as the same. The day is accurate, but it doesn't seem to correctly add everything else.
declare #today date = '2015-1-1'
select
[Day Total] = sum(case when [AccountingDate] < dateadd(DAY, 1, #today) then [Amount] else 0 end),
[Week Total] = sum(case when [AccountingDate] < dateadd(WEEK, 1, #today) then [Amount] else 0 end),
[Month Total] = sum(case when [AccountingDate] < dateadd(MONTH, 1, #today) then [Amount] else 0 end),
[Year Total] = sum([Amount])
from
[Accounting].[dbo].[HandPay]
where
[AccountingDate] >= #today and [AccountingDate] < dateadd(year, 1, #today);
When you use a recent date, you're looking for values where the [AccountingDate] is less than some future date.
If you add a day/week/month to Today, then all data will be prior to those dates unless you have future data in your table:
declare #today date = '2015-05-12'
SELECT dateadd(DAY, 1, #today)
,dateadd(week, 1, #today)
,dateadd(month, 1, #today)
Returns: 2015-05-13, 2015-05-19, 2015-06-12
So [AccountingDate] is always less than those dates if your table only has data through today.
If you actually want to aggregate from Today to 1 day back, 1 week back, and 1 month back, you need to use -1 in your DATEADD(), use >, and modify your WHERE criteria, something like:
declare #today date = '2015-1-1'
select
[Day Total] = sum(case when [AccountingDate] > dateadd(DAY, -1, #today) then [Amount] else 0 end),
[Week Total] = sum(case when [AccountingDate] > dateadd(WEEK, -1, #today) then [Amount] else 0 end),
[Month Total] = sum(case when [AccountingDate] > dateadd(MONTH, -1, #today) then [Amount] else 0 end),
[Year Total] = sum([Amount])
from
[Accounting].[dbo].[HandPay]
where
[AccountingDate] <= #today and [AccountingDate] > dateadd(MONTH, -1, #today);
Note: When comparing a DATETIME to a DATE any time after midnight will be considered greater than the DATE value, so you'll want to make sure you're not excluding values, either by casting the DATETIME as DATE or ensuring you've accounted for the range properly, i.e.: < tomorrow's DATE instead of <= today's DATE to include records from today that have a time after 00:00:00.000

Resources