SQL query last instance before current range - sql-server

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

Related

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

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

Workaround on Sliding Window Function in Snowflake

I've stumbled upon a problem that is giving me huge headaches, which is the following:
I have a table Deals, that contains information about this entity from our Sales CRM. I also have a table Company, that contains information about the companies pegged to those deals.
I was asked to compute a metric called Pipeline Conversion Rate, which is calculated as:
Won deals / Created Deals
Until here, everything is quite clear. Nevertheless, when computing this metric I was asked to do so in a sliding-window-function-fashion, which means to compute the metric only looking at the prior 90 days. Thing is that to look at the last 90 days of the numerator, we need to use one Date (created date); while when looking at the prior 90 days of the denominator, we should take into account the closed date (both dimensions are part of the Deals table).
There wouldn't be any problem if we could do this kind of window functions in Snowflake, as the following (I know syntax may not be exactly this one, but you get the idea):
count(deal_id) over (
partition by is_inbound, sales_agent, sales_tier, country
order by created_date range between 90 days preceding and current row
) as created_deals_last_90_days,
count(case when is_deal_won then deal_id end) over (
partition by is_inbound, sales_agent, sales_tier, country
order by created_date range between 90 days preceding and current row
) as won_deals_last_90_days
But we can't as far as I know. So my current workaround is the following (taken from this post):
select
calendar_date,
is_inbound,
sales_tier,
sales_agent,
country,
(
select count(deal_id)
from deals
where d.is_inbound = is_inbound
and d.sales_tier = sales_tier
and d.sales_agent = sales_agent
and d.country = country
and created_date between cal.calendar_date - 90 and cal.calendar_date
) as created_deals_last_90_days,
(
select count(case when is_deal_won then deal_id end)
from deals
where d.is_inbound = is_inbound
and d.sales_tier = sales_tier
and d.sales_agent = sales_agent
and d.country = country
and closed_date between cal.calendar_date - 90 and cal.calendar_date
) as won_deals_last_90_days
from calendar as cal
left join deals as d on cal.calendar_date between d.created_date and d.closed_date
*Note that I am using a calendar table here as base table, in order to have visibility on all calendar dates since without it I might say I'd be missing on those dates where there are no new deals (could happen on weekends).
Problem is that I am not getting correct figures when I cross check the raw data and the output of this query, and I have no idea how to make this (ugly) workaround, well... work.
Any ideas are more than welcome!
Well, it turns out it was way easier than I expected. After some trial-and-error, I figured out the only thing that could be failing was the JOIN condition in the outer query:
on cal.calendar_date between d.created_date and d.closed_date
This was assuming that both dates needed to be in the range, while this assumption is wrong. By tweaking the above mentioned part of the outer query to:
on cal.calendar_date >= d.created_date
It captures all those Deals that were created on or before the calendar_date, and therefore all of them since it is a mandatory field.
Maintaining the rest of the query as is, and assuming that there will be no nulls in any of the partitions, the results are the ones I expected.

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.

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

Query to create records between two dates

I have a table with the following fields (among others)
TagID
TagType
EventDate
EventType
EventType can be populated with "Passed Inspection", "Failed Inspection" or "Repaired" (there are actually many others, but simplifies to this for my issue)
Tags can go many months between a failed inspection and the ultimate repair... in this state they are deemed to be "awaiting repair". Tags are still inspected each month even after they have been identified as having failed. (and just to be clear, a “failed inspection” doesn’t mean the item being inspected doesn’t work at all… it still works, just not at 100% capacity…which is why we still do inspections on it).
I need to create a query that counts, by TagType, Month and Year the number of Tags that are awaiting repair. The end result table would look like this, for example
TagType EventMonth EventYear CountofTagID
xyz 1 2011 3
abc 1 2011 2
xyz 2 2011 2>>>>>>>>>>>>indicating a repair had been made since 1/2011
abc 2 2011 2
and so on
The "awaiting repair" status should be assessed on the last day of the month
This is totally baffling me...
One thought that I had was to develop a query that returned:
TagID,
TagType,
FailedInspectionDate, and
NextRepairDate,
then try and do something that stepped thru the months in between the two dates, but that seems wildly inefficient.
Any help would be much appreciated.
Update
A little more research, and a break from the problem to think about it differently gave me the following approach. I'm sure its not efficient or elegant, but it works. Comments to improve would be appreciated.
declare #counter int
declare #FirstRepair date
declare #CountMonths as int
set #FirstRepair = (<Select statement to find first repair across all records>)
set #CountMonths = (<select statement to find the number of months between the first repair across all records and today>)
--clear out the scratch table
delete from dbo.tblMonthEndDate
set #counter=0
while #counter <=#CountMonths --fill the scratch table with the date of the last day of every month from the #FirstRepair till today
begin
insert into dbo.tblMonthEndDate(monthenddate) select dbo.lastofmonth(dateadd(m,#counter, #FirstRepair))
set #counter = #counter+1
end
--set up a CTE to get a cross join between the scratch table and the view that has the associated first Failed Inspection and Repair
;with Drepairs_CTE (FacilityID, TagNumber, CompType, EventDate)
AS
(
SELECT dbo.vwDelayedRepairWithRepair.FacilityID, dbo.vwDelayedRepairWithRepair.TagNumber, dbo.vwDelayedRepairWithRepair.CompType,
dbo.tblMonthEndDate.MonthEndDate
FROM dbo.vwDelayedRepairWithRepair INNER JOIN
dbo.tblMonthEndDate ON dbo.vwDelayedRepairWithRepair.EventDate <= dbo.tblMonthEndDate.MonthEndDate AND
dbo.vwDelayedRepairWithRepair.RepairDate >= dbo.tblMonthEndDate.MonthEndDate
)
--use the CTE to build the final table I want
Select FacilityID, CompType, Count(TagNumber), MONTH(EventDate), YEAR(EventDate), 'zzz' as EventLabel
FROM Drepairs_CTE
GROUP BY FacilityID, CompType, MONTH(EventDate), YEAR(EventDate)`
Result set ultimately looks like this:
FacilityID CompType Count Month Year Label
1 xyz 2 1 2010 zzz
1 xyz 1 2 2010 zzz
1 xyz 1 7 2009 zzz
Here is a recursive CTE which generates table of last dates of months in interval starting with minimum date in repair table and ending with maximum date.
;with tableOfDates as (
-- First generation returns last day of month of first date in repair database
-- and maximum date
select dateadd (m, datediff (m, 0, min(eventDate)) + 1, 0) - 1 startDate,
max(eventDate) endDate
from vwDelayedRepairWithRepair
union all
-- Last day of next month
select dateadd (m, datediff (m, 0, startDate) + 2, 0) - 1,
endDate
from tableOfDates
where startDate <= endDate
)
select *
from tableOfDates
-- If you change the CTE,
-- Set this to reasonable number of months
-- to prevent recursion problems. 0 means no limit.
option (maxrecursion 0)
EndDate column from tableOfDates is to be ignored, as it serves as upper bound only. If you create UDF which returns all the dates in an interval, omit endDate in select list or remove it from CTE and replace with a parameter.
Sql Fiddle playground is here.

Resources