Getting Error from the following script - sql-server

I am going to create SI for each record of table, getting error from stored procedure:
No column name was specified for column 6 of 'cte_Alldates'.
Please check this script and let me know where the problem is:
table screenshot
and the stored procedure:
create proc sp_calcualtteSI
as
begin
DECLARE #today datetime
SET #today = dateadd(day,datediff(day,0,current_timestamp),0)
; WITH cte_dates AS
(
SELECT DISTINCT
name, Pamount, Rateofint, cdate,
CASE
WHEN ISNULL(today, #today) < DATEADD(month, 1, DATEADD(month, datediff(month, 0, cdate), 0))
THEN ISNULL(#today, #today)
ELSE
DATEADD(month, 1, dateadd(month, datediff(month, 0, cdate), 0))
END AS MonthEnd,
ISNULL(#today, #today) AS End_date
FROM
tbl_intestcalculate),
cte_Alldates AS
(
SELECT
name, Pamount, Rateofint, cdate, monthEnd, #today
FROM
cte_dates
UNION
SELECT
name, Pamount, Rateofint,
DATEADD(month, number, monthEnd),
CASE
WHEN DATEADD(month, number + 1, monthEnd) < #today
THEN DATEADD(month, number + 1, monthEnd)
ELSE #today
END,
#today
FROM
cte_dates c
CROSS JOIN
(SELECT number
FROM master..spt_values
WHERE type = 'p' AND number BETWEEN 0 AND 11) a
WHERE
dateadd(month, number, monthEnd) < #today
)
SELECT
name, cdate, monthEnd, Pamount, Rateofint,
DATEDIFF(day, cdate, monthEnd) AS No_Of_Days,
ROUND(Pamount * Rateofint * DATEDIFF(day, cdate, monthEnd) / 36500, 2) AS SI
FROM
cte_Alldates
END

A CTE needs to have column names specified for all columns. In this case you are missing a column name for the sixth column. Your cte_Alldates CTE should read:
cte_Alldates AS
(
SELECT
name, Pamount, Rateofint, cdate, monthEnd, [day]=#today
FROM
cte_dates
UNION
SELECT
name, Pamount, Rateofint,
DATEADD(month, number, monthEnd),
CASE
WHEN DATEADD(month, number + 1, monthEnd) < #today
THEN DATEADD(month, number + 1, monthEnd)
ELSE #today
END,
[day]=#today
FROM
cte_dates c
CROSS JOIN
(SELECT number
FROM master..spt_values
WHERE type = 'p' AND number BETWEEN 0 AND 11) a
WHERE
dateadd(month, number, monthEnd) < #today
)

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

How to fix the SELECT that use a virtual Table using Group by

I would like to get the count of EventType by month.
I've trying the next 2 possibility:
First possibility
DECLARE #StartDate date
SET #StartDate = GETDATE() - 365;
WITH theDates AS
(SELECT #StartDate as theDate
UNION ALL
SELECT DATEADD(MONTH, 1, theDate)
FROM theDates
WHERE DATEADD(MONTH, 1, theDate) <= GETDATE()
)
SELECT 'Diagnosis' as EventType,
MONTH(theDate),
YEAR(theDate),
concat(MONTH(theDate),'/', YEAR(theDate)),
Count(fpd.DateOfServiceID) as Eventcount
FROM theDates dd
LEFT JOIN fact.FactPatientDiagnosis fpd ON fpd.DateOfServiceID = concat(YEAR(theDate), MONTH(theDate), DAY(theDate))
GROUP BY YEAR(dd.theDate), MONTH(dd.theDate)
I've getting the value 0 for the EventCount Column
But i know that are in the table more than 500 Diagnosis
Second Possibility
DECLARE #StartDate date
SET #StartDate = GETDATE() - 365;
WITH theDates AS
(SELECT #StartDate as theDate
UNION ALL
SELECT DATEADD(MONTH, 1, theDate)
FROM theDates
WHERE DATEADD(MONTH, 1, theDate) <= GETDATE()
)
SELECT 'Diagnosis' as EventType,
MONTH(theDate),
YEAR(theDate),
concat(MONTH(theDate),'/', YEAR(theDate)),
Count(fpd.DateOfServiceID) as Eventcount
FROM theDates dd
LEFT JOIN fact.FactPatientDiagnosis fpd ON fpd.DateOfServiceID is not NULL
GROUP BY YEAR(dd.theDate), MONTH(dd.theDate)
In this Select, I've getting the value 503 for the EventCount column
I assume you want:
SET #StartDate = GETDATE() - 365;
WITH theDates AS (
SELECT #StartDate as theDate
UNION ALL
SELECT DATEADD(MONTH, 1, theDate)
FROM theDates
WHERE DATEADD(MONTH, 1, theDate) <= GETDATE()
)
SELECT 'Diagnosis' as EventType,
YEAR(theDate), MONTH(theDate),
concat(MONTH(theDate), '/', YEAR(theDate)),
Count(fpd.DateOfServiceID) as Eventcount
FROM theDates dd LEFT JOIN
fact.FactPatientDiagnosis fpd
ON fpd.DateOfServiceID >= dd.theDate AND
fpd.DateOfServiceID < DATEADD(month, 1, dd.theDate)
GROUP BY YEAR(dd.theDate), MONTH(dd.theDate)
ORDER BY YEAR(dd.theDate), MONTH(dd.theDate);
You are generating 1 date for each month in CTE (picking current DAY), and comparing it with dateofserviceid, even some date matches, every day you will get different output.
Ideally you should be comparing only YEAR and MONTH part for the JOIN.
LEFT JOIN fact.factpatientdiagnosis fpd ON
month(fpd.dateofserviceid) = month(thedate)
AND
year(fpd.dateofserviceid) = year(thedate)
Your overall query should look like following now.
DECLARE #StartDate DATE
SET #StartDate = Getdate() - 365;
WITH thedates
AS (SELECT #StartDate AS theDate
UNION ALL
SELECT Dateadd(month, 1, thedate)
FROM thedates
WHERE Dateadd(month, 1, thedate) <= Getdate())
SELECT 'Diagnosis' AS EventType,
Month(thedate),
Year(thedate),
Concat(Month(thedate), '/', Year(thedate)),
Count(fpd.dateofserviceid) AS Eventcount
FROM thedates dd
LEFT JOIN fact.factpatientdiagnosis fpd
ON Month(fpd.dateofserviceid) = Month(thedate)
AND Year(fpd.dateofserviceid) = Year(thedate)
GROUP BY Year(dd.thedate),
Month(dd.thedate)
Why would you ever want to use Int for a date IDK. I assume your DateOfServiceID is a date representation in int format like 20180102 (yyyyMMdd). If so:
WITH myData (theDate)
AS (SELECT CONVERT(DATE, CAST(DateOfServiceId AS VARCHAR(8)), 112)
FROM fact.FactPatientDiagnosis
WHERE DateOfServiceId >= CAST(CONVERT(VARCHAR(10), DATEADD(DAY, -365, CAST(GETDATE() AS DATE)), 112) AS INT))
SELECT 'Diagnosis' AS EventType,
MONTH(theDate) AS mn,
YEAR(theDate) AS yr,
SUBSTRING(CONVERT(VARCHAR(10), theDate, 103), 4, 7) AS MonthYear,
COUNT(theDate) AS Eventcount
FROM myData
GROUP BY MONTH(theDate),
YEAR(theDate),
SUBSTRING(CONVERT(VARCHAR(10), theDate, 103), 4, 7)
ORDER BY YEAR(theDate),
MONTH(theDate);

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

Failure creating a function same as stored procedure

This is my stored procedure which I was able to create
CREATE PROCEDURE [dbo].[spGetAllStaffCollectiveAttendanceByMonth]
#Month nvarchar(9)
AS
BEGIN
Declare #StartDate DATE,
#EndDate DATE
;WITH CteMonths(n, m) AS(
SELECT 1, 'January' UNION ALL
SELECT 2, 'February' UNION ALL
SELECT 3, 'March' UNION ALL
SELECT 4, 'April' UNION ALL
SELECT 5, 'May' UNION ALL
SELECT 6, 'June' UNION ALL
SELECT 7, 'July' UNION ALL
SELECT 8, 'August' UNION ALL
SELECT 9, 'September' UNION ALL
SELECT 10, 'October' UNION ALL
SELECT 11, 'November' UNION ALL
SELECT 12, 'December'
)
SELECT #StartDate = DATEADD(MONTH, n - 1, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)),
#EndDate = DATEADD(DAY, -1, DATEADD(MONTH, n, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)))
FROM CteMonths
WHERE m = #month
SELECT
StaffAttendance.StaffId,
DATENAME(MONTH, #StartDate) AS [ForMonth], #StartDate AS StartDate, #EndDate AS EndDate,
(DATEDIFF(dd, #StartDate, #EndDate) + 1)-(DATEDIFF(wk, #StartDate, #EndDate) * 1)-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END) AS TotalWorkingDays,
SUM(StaffAttendance.AttendanceStatusId) AS TotalDaysWorked
FROM StaffAttendance
WHERE [Date] BETWEEN #StartDate AND #EndDate AND StaffAttendance.AttendanceStatusId = 1 GROUP BY StaffAttendance.StaffId
END
GO
This, here, is my function which I am trying to create but can't. Its the same thing as above except for what makes it a function.
CREATE FUNCTION [dbo].[funcGetAllStaffCollectiveAttendanceByMonth]
(
#Month nvarchar(9)
)
RETURNS TABLE
AS
RETURN
(
Declare #StartDate DATE,
#EndDate DATE
;WITH CteMonths(n, m) AS(
SELECT 1, 'January' UNION ALL
SELECT 2, 'February' UNION ALL
SELECT 3, 'March' UNION ALL
SELECT 4, 'April' UNION ALL
SELECT 5, 'May' UNION ALL
SELECT 6, 'June' UNION ALL
SELECT 7, 'July' UNION ALL
SELECT 8, 'August' UNION ALL
SELECT 9, 'September' UNION ALL
SELECT 10, 'October' UNION ALL
SELECT 11, 'November' UNION ALL
SELECT 12, 'December'
)
SELECT #StartDate = DATEADD(MONTH, n - 1, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)),
#EndDate = DATEADD(DAY, -1, DATEADD(MONTH, n, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)))
FROM CteMonths
WHERE m = #month
SELECT
StaffAttendance.StaffId,
DATENAME(MONTH, #StartDate) AS [ForMonth], #StartDate AS StartDate, #EndDate AS EndDate,
(DATEDIFF(dd, #StartDate, #EndDate) + 1)-(DATEDIFF(wk, #StartDate, #EndDate) * 1)-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END) AS TotalWorkingDays,
SUM(StaffAttendance.AttendanceStatusId) AS TotalDaysWorked
FROM StaffAttendance
WHERE [Date] BETWEEN #StartDate AND #EndDate AND StaffAttendance.AttendanceStatusId = 1 GROUP BY StaffAttendance.StaffId
END
)
But this is what I am getting
What am I doing wrong?
You have used the syntax for an inline table-valued function but you have multiple statements.
Comparison of inline and multi-statement table-valued functions.
So you could either refactor to use a single statement (higher performance) or use the multi-statement syntax as described in that link (easier).
If you wanted to do it inline you could do something along these lines (note, I don't have SSMS available right now so there may be some minor syntax errors like unmatched brackets):
CREATE FUNCTION [dbo].[funcGetAllStaffCollectiveAttendanceByMonth]
(
#Month nvarchar(9)
)
RETURNS TABLE
AS
RETURN
(
-- The StartDate should be the first day of the month that is passed as a parameter, in the current year.
-- The EndDate should be the last day of the month which the StartDate begins.
WITH cteDates AS (
SELECT StartDate = Convert(date, Concat('01', #Month, Convert(varchar(4), DatePart(Year, GetDate()))), 106),
EndDate = DateAdd(Day, -1,
DateAdd(Month, 1,
Convert(date, Concat('01', #Month, Convert(varchar(4), DatePart(Year, GetDate()))) , 106)
)
)
)
SELECT
Sa.StaffId,
DATENAME(MONTH, cteDates.StartDate) AS [ForMonth],
cteDates.StartDate,
cteDates.EndDate,
(DATEDIFF(dd, cteDates.StartDate, cteDates.EndDate) + 1)-
(DATEDIFF(wk, cteDates.StartDate, cteDates.EndDate) * 1)-
(CASE WHEN DATENAME(dw, cteDates.StartDate) = 'Sunday' THEN 1 ELSE 0 END) AS TotalWorkingDays,
SUM(sa.AttendanceStatusId) AS TotalDaysWorked
FROM StaffAttendance As sa
JOIN cteDates ON sa.[Date] BETWEEN cteDates.StartDate AND cteDates.EndDate
WHERE sa.AttendanceStatusId = 1
GROUP BY
Sa.StaffId,
DATENAME(MONTH, cteDates.StartDate),
cteDates.StartDate,
cteDates.EndDate,
(DATEDIFF(dd, cteDates.StartDate, cteDates.EndDate) + 1)-
(DATEDIFF(wk, cteDates.StartDate, cteDates.EndDate) * 1)-
(CASE WHEN DATENAME(dw, cteDates.StartDate) = 'Sunday' THEN 1 ELSE 0 END)
END
)

Resources