Calculate the duration between two dates based on holiday from calendar table - sql-server

I have two tables : Calendar and Request_Stages:
I want to calculate the duration between DATE)FROM and DATE_TO for each request stage.
The desired result:
What I have tried:
SELECT Req.requestID, Req.STAGE_ID COUNT(DAT.holiday) as duration
FROM REQUEST Req
JOIN [dbo].[STG_ACCR_DATE] DAT
ON DAT.DATE >= Req.DATE_FROM
AND DAT.FDATE <= Req.DATE_TO
WHERE DAT.OFF_DAY = 0 --TO CALCULATE ONLY WORKING DAYS
GROUP BY Req.request_ID, Req.STAGE_ID
ORDER BY Req.request_ID, Req.STAGE_ID
The problem with my current result:
it doesn't SHOW the stages with zero working day, for example if a stage start date and end date are equal, the desired result is one '1' working day, but my query is returning zero '0' and doesn't show it in the results with this issue, stages records are lost.
Any suggestion to fix my query or new solution idea are appreciate it, probably I am thinking wrong, so any solution is welcome.

The posted query doesn't really match up with the problem as presented.
select *,
datediff(day, DATE_FROM, DATE_TO) + 1 - (
select count(*) from CALENDAR as c
where c.DATE between rs.DATE_FROM and rs.DATE_TO and c.IS_HOLIDAY = 1
) as DURATION
from REQUEST_STAGES as rs

Related

Group dates by considering look ahead days

I am using SQL Server 2017. I have a table Requests, to make things simple, there's only one column RequestDate. For example,
RquestDate
4/11
4/12
4/13
4/16
4/18
I need to group by RequestDate by considering look ahead days. If look ahead day is 0, the result should be the same as raw table.
If look ahead day is 1, it means when I look at 4/11, I need to check if 4/12 exists, if so, group 4/12 into 4/11.
The result is:
4/11 --it groups 4/12
4/13
4/16
4/18
If look ahead day is 2, when looking at 4/11, it groups 4/12, 4/13 into it.
The result is:
4/11 -- group 4/12 and 4/13.
4/16 -- group 4/18
So this problem is different from the typical gap and island problem. Because when group dates, there could be gap there, e.g, when look ahead day is 2, 4/16 groups 4/17 and 4/18.
I tried some ways but can't find a decent solution.
A recursive common table expression could work.
Select start request date using a min() function.
Use that same date as the grouping start date.
Step 1 and 2 make up the recursion anchor / start row.
Recursively go looking for the next request date. This date is higher than the previous date (r.RequestDate > c.RequestDate) and does not have another row
that follow the same criteria before it (not exists ... r2.RequestDate < r.RequestDate).
If the current request date (from step 3) falls within the look ahead interval length, then maintain the grouping start date (then c.RequestGroupDate), otherwise start a new group on the current request date (else r.RequestDate).
Step 3 and 4 make up the recursive part of the CTE.
After the recursion every request date as a corresponding request grouping date. The group by r.RequestGroupDate clause reduces the result output to the distinct values.
Sample data
create table Requests
(
RequestDate date
);
insert into Requests (RequestDate) values
('2021-04-11'),
('2021-04-12'),
('2021-04-13'),
('2021-04-16'),
('2021-04-18');
Solution
declare #lookAhead int = 1; -- look ahead days parameter
with rcte as
(
select min(r.RequestDate) as RequestDate,
min(r.RequestDate) as RequestGroupDate
from Requests r
union all
select r.RequestDate,
case
when datediff(day, c.RequestGroupDate, r.RequestDate) <= #lookAhead
then c.RequestGroupDate
else r.RequestDate
end
from rcte c
join Requests r
on r.RequestDate > c.RequestDate
where not exists ( select 'x'
from Requests r2
where r2.RequestDate > c.RequestDate
and r2.RequestDate < r.RequestDate )
)
select r.RequestGroupDate
from rcte r
group by r.RequestGroupDate;
Result
For #lookAhead = 1:
RequestGroupDate
----------------
2021-04-11
2021-04-13
2021-04-16
2021-04-18
For #lookahead = 2:
RequestGroupDate
----------------
2021-04-11
2021-04-16
Fiddle to see things in action.

SQL query last instance before current range

I have a query that pulls all studies from the previous week based on the av_summary column. I need to add a column that will pull the study prior to the most recent study no matter how long ago the previous study was performed. I only need the date of the last study. f.creation_datetime is the column both current study and previous study would come from.
select distinct
f.patient_name
,t.patient_mrn
,p.accession_number
,p.performed_start_time
,p.procedure_id
,SUBSTRING(CAST(s.av_summary as NVARCHAR(MAX)), CHARINDEX('This suggests
the stenosis', CAST(s.av_summary as NVARCHAR(MAX))) ,
LEN(CAST(s.av_summary as NVARCHAR(MAX)))) as AV_Summary
,(select TOP 1 (p1.performed_start_time)
from dbo.T_TCS_PROCEDURE as p1
where
p1.patient_id = p.patient_id
and
p1.performed_start_time < p.performed_start_time
and p1.procedure_type_id = p.procedure_type_id
order by p1.performed_start_time DESC
) as Last_Echo
from dbo.folders as f
join dbo.T_TCS_PROCEDURE as p
on p.procedure_id = f.procedure_id
join dbo.T_ECHO_SUMMARY as s
on s.procedure_id = f.procedure_id
join dbo.T_CON_DISPATCHER_EVENT_TRACK as t
on t.procedure_id = f.procedure_id
where
CAST( f.creation_datetime AS DATE ) > DATEADD( DAY, -14, CAST( GETDATE()
AS DATE))
and
CAST(s.av_summary as NVARCHAR(MAX)) like '%This suggests the stenosis is%'
and
LEN(LTRIM(RTRIM(t.patient_mrn))) > 0
I need to add a column that will pull the study prior to the most
recent study no matter how long ago the previous study was performed.
Add the column as a subquery that gets the TOP 1 date before the date of the current row's study.
I was finally able to figure out how to get the data I wanted for the previous study. I had to correlate the subquery on a couple different columns and I used a completely different table. The database I am working with is a complete mess, but I managed to figure it out. The company that created the database couldn't even figure it out. Thank you all for your help on this. It is much appreciated.
,(select TOP 1 (p1.performed_start_time)
from dbo.T_TCS_PROCEDURE as p1
where
p1.patient_id = p.patient_id
and
p1.performed_start_time < p.performed_start_time
and
p1.procedure_type_id = p.procedure_type_id
order by p1.performed_start_time DESC
) as Last_Echo

Crystal Reports sales report showing all dates even when not existing

I have been searching for answers on this for a couple of days but not found any useful results.
A bit of the backstory:
I have about ~20k items that i'm trying to do a lifetime sales history report on.Some items have history from 1/1/2005. Sales are only noted on dates they occur.
Trying to graph first lifetime of sales history by days, first 6mo of sales, and last 30 days of sales. I don't have permission to add a temp table, so i am working with importing an excel file. (and for some reason you can't right-outer-join it to invoice date. im guessing because it's external?)
My problem is that there HAS to be a simple way to tell crystal to include/print missing dates without a lookup table. I've already got the start and end dates passed to parameters.
Isn't there a way to dynamically generate missing dates between {?PM-Start} to {?PM-End}? Using a lookup table to check if all days from 1/1/2005-currentdate match is 90,000,000,000 extra bloops to check.
That would take hours to run. It should be able to grab minumum(sale_date), and maximum(sale_date) and plop a record for each day whether it exists in the DB or not. (How is this not already a basic function?)
Or am I just missing something super simple?
Isn't there any way to dynamically generate missing dates between {?PM-Start} to {?PM-End}? Using a lookup table to check if all days from 1/1/2005-currentdate match is 90,000,000,000 extra bloops to check.
By using a query like below you can generate a calendar view to fill missing date:
;with years(yyyy) as (
select 2005
union all
select yyyy + 1
from years
where yyyy < datepart(year, getdate())
), months(mm) as (
select 1
union all
select mm + 1
from months
where mm < 12
), allDays(dd) as (
select 1
union all
select dd + 1
from allDays
where dd < 31
), calendar as (
select --datefromparts(y.yyyy, m.mm, d.dd) [date]
cast(cast(y.yyyy as varchar(4))+'-'+cast(m.mm as varchar(2))+'-'+cast(d.dd as varchar(2)) as date) [date]
from allDays d
cross join
months m
cross join
years y
where isdate(cast(y.yyyy as varchar(4))+ '/'+cast(m.mm as varchar(2))+'/'+cast(d.dd as varchar(2))) <> 0
)
select *
from calendar;
SQL Fiddle Demo

How to do WHERE <before> an aggregate function (Postgres)

It's hard to explain from the title, but this is my SQL:
SELECT
SUM("payments"."amount"),
"invoices"."property_id"
FROM "payments"
JOIN "invoices"
ON "payments"."invoice_id" = "invoices"."id"
GROUP BY "property_id"
It returns the sum of all Payment records (amount column) for a particular Property (which is connected through it's invoices).
In other words:
Property has_many: :invoices
Invoice has_one: :payment
I'm trying to select payments between a particular date range though, but it has to happen "before" the aggregate function (so do the exact query above, but only for 2017-01-01 through 2017-02-01). The field would be generated_at on Payment
You are looking for a WHERE clause. (WHERE is executed before aggregation; HAVING is executed after.) Suggested date literals in PostgreSQL are ANSI standard DATE 'YYYY-MM-DD'. Date ranges are usually checked with >= start day and < end day + 1 (in order to deal properly with the time part if any).
SELECT
SUM(p.amount),
i.property_id
FROM payments p
JOIN invoices i ON p.invoice_id = i.id
WHERE p.generated_at >= DATE '2017-01-01'
AND p.generated_at < DATE '2017-02-02'
GROUP BY i.property_id;

Need help comparing difference between datetime stamps in SQL DB

I have a MSSQL server 2012 express DB that logs user activities. I need some help creating a query to compare timestamps on the user activities based on the text in the notes. I am interested in figuring out how long it takes my users to perform certain activities. The activities they are performing are stored in text in a NOTES column. I want to build a query that will tell me the time difference for each [INVOICEID] from the ‘START NOTE’ to the next note for that invoice by that user. The note that is entered is always the same for the start of the timer (for the purposes of this I used ‘START NOTE’ to indicate the start of the timer, but I have multiple activites I will need to do this for so I plan on simply changing that text in the query), but the end of the timer the text of the note will vary because it will be user entered data. I want to find the time difference between ‘START NOTE’ and the note that immediately follows ‘START NOTE’ entered by the same USERID for the same INVOICEID. Please see the SQLfiddle for an example of my data:
http://sqlfiddle.com/#!3/a00d7/1
With the data in the sql fiddle I would want the results of this query to be:
INVOICE ID USERID TIME_Difference
100 5 1 day
101 5 3 days
102 5 9 days
(time_difference does not need to be formatted like that, standard SQL formatting is fine)
I don’t really know where to start with this. Please let me know if you can help.
Thanks
select a.userid,a.invoiceid,min(a.added),min(b.added),datediff(DAY,min(a.added),min(b.added)) from om_note a
left join om_note b on a.userid=b.userid and a.invoiceid = b.invoiceid and a.added < b.added
where a.notes = 'START NOTE' group by a.userid,a.invoiceid
;with x as (
select
o.*, sum(case when notes='START NOTE' then 1 else 0 end)
over(partition by o.invoiceid, o.userid order by o.added) as grp
from om_note o
),
y as (
select *,
row_number() over(partition by x.invoiceid, x.userid, x.grp order by x.added) as rn
from x
where grp > 0
)
select y1.invoiceid, y1.userid, datediff(hour, y1.added, y2.added)
from y y1
inner join y y2
on y1.invoiceid=y2.invoiceid and y1.userid=y2.userid and y1.grp=y2.grp
where y1.rn=1 and y2.rn=2

Resources