Someone maybe can help me to use EXISTS operator to check - ev.tfe_evento = 'Inizio lettura file' and ev.tfe_evento = 'lettura file'. I need to display all files that contain inizio lettura file and lettura file. The template of code is:
SELECT column-names
FROM table-name
WHERE EXISTS (
SELECT column-name
FROM table-name
WHERE condition
)
But how I can change my code below to follow this example?
DROP TABLE IF EXISTS #tflussi_eventi;
SELECT ev.*
INTO tab1
FROM tab1 ev (nolock)
JOIN (
SELECT lab
FROM #lab1
GROUP BY labname
) tf ON ev.labnameLIKE tf.labname+ '%'
WHERE ev.labevent= 'today'
AND ev.labevent= 'yesterday'
Sometimes I like to use window functions when checking conditions like this. Where you are looking for multiple conditions across the group.
This is not your actual query, just to give you an idea.
With window functions:
SELECT DISTINCT fileName
FROM (SELECT E.*,
CASE WHEN MAX(CASE WHEN E.tfe_evento = 'lettura file' THEN 1
ELSE 0
END
) OVER ( PARTITION BY E.FileName ) = 1
AND MAX(CASE WHEN E.tfe_evento = 'Inizio lettura file' THEN 1
ELSE 0
END
) OVER ( PARTITION BY E.FileName ) = 1
THEN 1
ELSE 0
END AS FileHasBothEventConds
FROM EVENT E
)
WHERE FileHasBothEventConds = 1;
With EXISTS:
SELECT DISTINCT fileName
FROM EVENTS E
WHERE EXISTS
( SELECT 1
FROM EVENTS E_LF
WHERE E.fileName = E_LF.fileName
AND E_FL.tfe_evento = 'lettura file'
)
AND EXISTS
( SELECT 1
FROM EVENTS E_ILF
WHERE E.fileName = E_ILF.fileName
AND E_ILF.tfe_evento = 'Inizio lettura file'
)
You should use LIKE operator instead of = for that to happen. ie:
SELECT ev.*
INTO #tflussi_eventi
FROM tflussi_eventi ev (nolock)
JOIN (
SELECT FileNameCutoff
FROM #FileCutoffTime
GROUP BY FileNameCutoff
) tf ON ev.tfe_sorgente LIKE tf.FileNameCutoff + '%'
WHERE ev.tfe_evento LIKE '%Inizio lettura file%'
-- Unnecessary because of first criteria
-- AND ev.tfe_evento = 'lettura file'
AND ev.tfe_data >= #DateToCheck
AND ev.tfe_data < #DateToCheck + 1 -- not sure what you mean here, if it is datetime, using DateAdd() is safer
However, this might suffer from performance, you should think of creating fulltext indexes and use full text search.
EDIT: While it would still suffer because you are not doing a full text search, you might save, maybe negligible time on the joined table which practically has no importance other than "existence check":
SELECT ev.*
INTO #tflussi_eventi
FROM tflussi_eventi ev (nolock)
WHERE ev.tfe_evento LIKE '%Inizio lettura file%'
-- Unnecessary because of first criteria
-- AND ev.tfe_evento = 'lettura file'
AND ev.tfe_data >= #DateToCheck
AND ev.tfe_data < #DateToCheck + 1 -- not sure what you mean here, if it is datetime, using DateAdd() is safer
and EXISTS (
SELECT *
FROM #FileCutoffTime tf
where ev.tfe_sorgente LIKE tf.FileNameCutoff + '%');
Related
Following guidance in this blog post, I've added an ef core (dotnet 6) migration into which I've pasted the t-sql to create a view.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(#"
exec('create view [dbo].[vwSpotlightExtract] as
with
exceptions as (
select id from dbo.Application
where CTaxNumber in (
select
app.CTaxNumber
from dbo.Application app
group by app.CTaxNumber
having count(*) > 1
)
union
-- duplicate bank account
select id from dbo.Application where concat(BankSortCode, ':', BankAccountNumber) in (
select concat(app.BankSortCode, ':', app.BankAccountNumber)
from dbo.Application app
where app.BankAccountNumber is not null
group by concat(app.BankSortCode, ':', app.BankAccountNumber)
having count(*) > 1
)
union
-- duplicate uprn
select id from dbo.Application where uprn in (
select app.uprn from dbo.Application app
group by app.uprn having count(*)>1
)
),
LastAppStatus as (
select app.Id, app.UTRN,
max(rh.Id) LastRebateHistoryID
from dbo.Application app
inner join dbo.RebateHistory rh on app.ID = rh.ApplicationId
where rh.ApplicationId is not null --and rh.ApplicationId not in (select ID from exceptions)
and app.RequestType = 0 -- BACS
and ISNULL(app.PaymentStopped, 0) = 0 -- Payment NOT Stopped
--and app.id not in (select id from exceptions) -- to prevent sending stuff to spotlight
group by app.Id, app.UTRN
)
select
app.UTRN [Application Number],
'Personal' [Personal or Business Bank Account? (required)], -- always Personal
REPLACE(app.BankSortCode, '-', '') [Sort code (required)],
app.BankAccountNumber [Account number (required)],
NULL [Business name (required if business)],
app.AccountPayerFirstName [First Name (required if personal)],
app.AccountPayerSurname [Surname (required if personal)],
CASE WHEN LEN(CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)) > 0 then CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx, '-', addr.SAO_End_No, addr.SAO_End_Sfx)
WHEN LEN(CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)) = 0 then CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)
WHEN LEN(CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)) = 0 and LEN (CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)) > 0 then CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)
else NULL end as
[Flat number (optional)], -- secondary addressable number or name
case when LEN(addr.PAO_Desc) > 0 then addr.PAO_Desc
when LEN(addr.SAO_Desc) < 0 then addr.SAO_Desc
ELSE NULL
end as [Building name (optional)],-- primary addressable name
CASE WHEN LEN(CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)) > 0 then CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx, '-', addr.PAO_End_No, addr.PAO_End_Sfx)
WHEN LEN(CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)) = 0 then CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)
WHEN LEN(CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)) = 0 and LEN (CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)) > 0 then CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)
end [Building number (optional)],
addr.Street_Name [Street name (required)],
addr.Postcode [Address postcode (required)],
case when app.AccountType = 0 then 'Single'
when app.AccountType = 1 then 'Joint'
end as [Single or Joint account type (if personal)], -- new field coming
convert(varchar, app.AccountPayerDOB, 103) [Date of birth (if personal)],
NULL [Limited or non-limited (if business)],
NULL [Company registration number (if business)]
, ec.EventName
, app.ID
from dbo.Application app
left outer join dbo.AcademyNonDDPayers nondd on app.CTaxNumber = nondd.CTaxNumber
inner join dbo.CTaxAddress addr on RIGHT('000000000000'+ISNULL(app.uprn,''),12) = addr.uprn and nondd.CTaxPropertyRef = addr.CTaxPropertyRef
inner join LastAppStatus las on app.ID = las.ID
inner join dbo.RebateHistory rh on las.LastRebateHistoryID = rh.ID
inner join dbo.EventCode ec on rh.EventCodeId = ec.ID
where ec.ID = 11')
");
}
This causes a failure in the release pipeline. If I take the content of the sql script from the deployment artefact and paste into SSMS then I see the following error:
I'm not sure why this is because the create view statement seems to be correctly wrapped with begin and end statements:
CREATE VIEW can’t be inside IF/BEGIN logic; it has to be the only statement in the batch. The typical workaround is to create a script like this:
IF OBJECT_ID(N'dbo.viewname', N'V') IS NULL
BEGIN
EXEC sys.sp_executesql N'CREATE VIEW dbo.viewname AS SELECT 1;';
END
GO
ALTER VIEW dbo.viewname
AS
... real view code ...
In newer versions of SQL Server, you can just do:
CREATE OR ALTER VIEW dbo.viewname
AS
... real view code ...
But that still has to live in its own batch. Meaning it can't be inside IF/BEGIN unless you use dynamic SQL for the whole view. e.g.:
IF (some condition)
BEGIN
EXEC sys.sp_executesql N'CREATE VIEW dbo.viewname
AS
... all that view code ...;';
END
Whether you'll be able to generate any of those forms from EF core, I just don't know. Sometimes we expect an ORM to do absolutely everything but, usually, it is only capable of a very small subset.
I'm not sure of the logic you're trying to get to anyway. (If a row doesn't exist in some table, create a view? That will only work once.)
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'
I am creating a view that is using that STUFF function. I want to put the result of STUFF in a variable for my view. The problem I am having is declaring my variable. It gives me the message "Incorrect Syntax near 'DECLARE'. Expecting '(' or SELECT." I already have the '(' in there. I have tried putting a BEGIN before it. I have tried putting it after the SELECT word. But nothing seems to work and I cannot find a solution in my search. I am using SQL Server 2012
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
(DECLARE #CONDITIONS AS varchar(20)
SET #CONDITIONS = (SELECT DISTINCT BD.[RequestedBurnsID]
,[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20),[ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions] WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD)
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT
,#CONDITIONS AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID
GO
You can't declare variables in a view. Could you make it into a function or stored procedure?
Edit - you might also be able to put something into a CTE (Common Table Expression) and keep it as a view.
e.g.
WITH conditions as
(
... do the STUFF here
)
SELECT blah
FROM blah
INNER JOIN conditions
(or CROSS JOIN conditions if its just one row, I can't quite decipher what your data is like)
Here is a sample query that uses a CTE (Common Table Expression) to nicely emulate internal variable construction, as described by James Casey. You can test-run it in your version of SQL Server.
CREATE VIEW vwImportant_Users AS
WITH params AS (
SELECT
varType='%Admin%',
varMinStatus=1)
SELECT status, name
FROM sys.sysusers, params
WHERE status > varMinStatus OR name LIKE varType
SELECT * FROM vwImportant_Users
yielding output:
status name
12 dbo
0 db_accessadmin
0 db_securityadmin
0 db_ddladmin
also via JOIN
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers INNER JOIN params ON 1=1
WHERE status > varMinStatus OR name LIKE varType
also via CROSS APPLY
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers CROSS APPLY params
WHERE status > varMinStatus OR name LIKE varType
Or use a CTE (common table expression) as subselect like:
WITH CTE_Time(Clock)
AS(
SELECT 11 AS [Clock] -- set var
)
SELECT
DATEPART(HOUR, GETDATE()) AS 'actual hour',
CASE
WHEN DATEPART(HOUR, GETDATE()) >= (SELECT [Clock] FROM CTE_Time) THEN 'after'
ELSE 'before'
END AS [Data]
Try put the condition subquery directly inside the the view select statement. you may CAST the XML to VARCHAR(20).
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT,
(
SELECT DISTINCT BD.[RequestedBurnsID],
[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20), [ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions]
WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD
) AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID
(Edited from the original).
In plpgsql, (PostgreSQL 9.2), I have a function defined as:
CREATE OR REPLACE FUNCTION test (patient_recid integer, tencounter timestamp without time zone)
RETURNS SETOF view_dx AS
$BODY$
#variable_conflict use_column
DECLARE
r view_dx%rowtype;
BEGIN
FOR r IN
With person AS (
select ....
)
, alldx AS (
select ....
)
............
select ... from first cte
union
select ... from second cte
union
etc., etc.,
LOOP
r.tposted = ( .
With person AS (
... SAME AS ABOVE,
alldx AS (
... SAME AS ABOVE,
)
select max(b.tposted)
from alldx b
where r.cicd9 = b.code and r.cdesc = b.cdesc);
r.treated = (
With person AS (
........SAME AS ABOVE )
, alldx AS (
........SAME AS ABOVE
)
select ...);
r.resolved = (
With person AS (
select p.chart_recid as recid
from patients p
where p.recid = patient_recid
)
...etc, etc,
RETURN NEXT r;
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION test(integer, timestamp without time zone)
OWNER TO postgres;
Edit: Essentially, I have multiple cte's defined which work well in the "For r IN" section of code with multiple unions, but when executing the LOOP...END LOOP section, each CTE needs to be redefined with each SELECT statement. Is there a good way to avoid multiple definitions of the same CTE?
Or is there a better (i.e., faster) way of doing this.
All suggestions are most welcome and appreciated.
TIA
[this is not an answer (too little information, too large program), but a hint for rewriting the stacked CTE.]
The members of the union all appear to be based on select b.* from alldx b, all with a few different extra conditions, mostly based on the existance of other tuples within the same CTE. My suggestion is to unify these, replacing them by boolean flags, as in:
WITH person AS (
SELECT p.chart_recid as recid
FROM patients p
WHERE p.recid = patient_recid
)
, alldx AS (
SELECT d.tposted, d.treated, d.resolved, d.recid as dx_recid, d.pmh, d.icd9_recid
, i.code, i.cdesc, i.chronic
FROM dx d
JOIN icd9 i ON d.icd9_recid = i.recid
JOIN person p ON d.chart_recid = p.recid
WHERE d.tposted::date <= tencounter::date
)
SELECT uni.tposted, uni.treated, uni.resolved, uni.dx_recid, uni.pmh, uni.icd9_recid
, uni.code, uni.cdesc, uni.chronic
, (uni.tposted::date = tencounter::date
) AS is_dx_at_encounter -- bitfield
, EXISTS ( -- a record from a more recent date has resolved this problem.
SELECT 1
FROM alldx x
WHERE x.resolved = true
AND uni.code = x.code AND uni.cdesc = x.cdesc AND uni.tposted = x.tposted
AND x.tposted >= uni.tposted
) AS dx_resolved -- bitfield
, EXISTS ( -- a record from a more recent date has resolved this problem.
SELECT 1
FROM alldx x
WHERE x.resolved = false
AND uni.code = x.code AND uni.cdesc = x.cdesc AND uni.tposted = x.tposted
AND x.tposted > uni.tposted
) AS dx_recurred -- bitfield
, EXISTS ( SELECT * from alldx x where x.chronic = true
AND uni.code = x.code AND uni.cdesc = x.cdesc
) AS dx_chronic -- bitfield
-- etcetera
FROM alldx uni
;
The person CTE could probably be incorporated, too.
and maybe you don't even need the final loop
but you'll have to find out which combination(s) of the resulting bitfields will be needed.
the UNION (without ALL) in the original is a terrible beast: it collects all the results from the union parts, but has to remove duplicates. This will probably introduce a sort-step, since CTE-references tend to hide their key fields or implied ordering from the calling query.
As far as I can tell, CTE's defined before the LOOP do not transfer to the LOOP itself. However, a temporary table can be defined in the BEGIN block which is available in the LOOP block. The following solution runs 50 times faster then my original code. Anybody have a better approach?
CREATE OR REPLACE FUNCTION test2 (patient_recid integer, tencounter timestamp without time zone)
RETURNS SETOF view_dx AS
$BODY$
#variable_conflict use_column
DECLARE
r view_dx%rowtype;
BEGIN
-- create table can only be created in the BEGIN block
Create temp table all_dx ON COMMIT DROP AS
With person AS (
select p.chart_recid as recid
from patients p
where p.recid = patient_recid
)
, alldx AS (
select d.tposted, d.treated, d.resolved, d.recid as dx_recid, d.pmh, d.icd9_recid, i.code, i.cdesc, i.chronic
from dx d
join icd9 i on (d.icd9_recid = i.recid)
join person p on (d.chart_recid = p.recid)
where d.tposted::date <= tencounter::date
)
select * from alldx order by tposted desc;
-- will loop through all the records produced by the unions and assign tposted, pmh, chronic, etc...
FOR r IN
With
dx_at_encounter AS ( -- get all diagnosis at time of encounter
select code, cdesc from all_dx a
where a.tposted::date = tencounter::date
)
, dx_resolved AS ( -- get most recent date of every resolved problem.
select b.* from all_dx b
join (
select a.code, a.cdesc , max(tposted) as tposted
from all_dx a
where a.resolved = true
group by code,cdesc) j
on (b.code = j.code and b.cdesc = j.cdesc and b.tposted = j.tposted)
)
, never_resolved AS ( -- get all problems that have never been resolved before time of encounter.
-- "not exists" is applied to each select output row AFTER the output row b.* is formed.
select b.code, b.cdesc from all_dx b
where not exists
(select 1
from dx_resolved d
where b.code = d.code and b.cdesc = d.cdesc)
)
, recurrent AS ( -- get all recurrent problems. (Problems that are now current after being resolved).
select b.code, b.cdesc
from all_dx b
join dx_resolved r on (b.cdesc = r.cdesc and b.tposted::date > r.tposted::date )
where (b.resolved is null or b.resolved = false)
)
, chronic_dx AS (
select b.code, b.cdesc
from all_dx b
where b.chronic = true
)
-- all diagnosis at time of encounter
select a.code,
a.cdesc
from dx_at_encounter a
union
-- all recurrent problems
select
a.code,
a.cdesc
from recurrent a
union
-- all problems that have never been resolved
select
a.code,
a.cdesc
from never_resolved a
union
--all chonic problems
select
a.code,
a.cdesc
from chronic_dx a
-- LOOP goes to END LOOP which returns back to LOOP to process each of the result records from the unions.
LOOP
r.tposted = ( -- get most recent useage of a diagnosis.
select max(b.tposted)
from all_dx b
where r.cicd9 = b.code and r.cdesc = b.cdesc);
r.treated = (
select b.treated from all_dx b
where b.tposted = r.tposted and b.code = r.cicd9 and b.cdesc = r.cdesc);
r.resolved = (
select b.resolved from all_dx b
where b.tposted = r.tposted and b.code = r.cicd9 and b.cdesc = r.cdesc);
r.pmh = (
select distinct true
from all_dx b
where
b.pmh = true and
b.code = r.cicd9 and
b.cdesc = r.cdesc );
r.chronic = (
select distinct true
from all_dx b
where
b.chronic = true and
b.code = r.cicd9 and
b.cdesc = r.cdesc);
RETURN NEXT r; -- return current row of SELECT
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION test2(integer, timestamp without time zone)
OWNER TO postgres;
I have a scenario where i have to check a variable for it's default value, and if it has i have to check EXISTS part conditionally with Table2 and if it does not have the default value, i have to check EXISTS part conditionally with Table3.
Below is a sample code:-
SELECT * FROM tbl1 WHERE EXISTS (SELECT CASE WHEN #boolVar = 0 THEN (SELECT 'X' FROM tbl2 WHERE tbl1.col1 = tbl2.col1) ELSE (SELECT 'X' FROM tbl3 where tbl1.col1 = tbl3.col1) END)
Demo query with constants for testing purpose: -
SELECT 1 WHERE EXISTS (SELECT CASE WHEN 1 = 0 THEN (SELECT 'X' WHERE 1=0)
ELSE (SELECT 'X' WHERE 1 = 2) END)
Note: - The above query always returning 1, even not a single condition is satisfying.
I know we can use OR operator for the same and any how we can achieve it, but i really want to know that in case both the tables have no rows satisfying their particular where clause, even it's returning all the rows from Table1.
I tried to explain the same with the demo query with constant values.
Please help.
When your query doesn't find any matching records, it will basically do:
SELECT 1 WHERE EXISTS (SELECT NULL)
As a row containing a null value is still a row, the EXISTS command returns true.
You can add a condition to filter out the null row:
SELECT * FROM tbl1 WHERE EXISTS (
SELECT 1 FROM (
SELECT
CASE WHEN #boolVar = 0 THEN (SELECT 'X' FROM tbl2 WHERE tbl1.col1 = tbl2.col1)
ELSE (SELECT 'X' FROM tbl3 where tbl1.col1 = tbl3.col1)
END AS Y
) Z
WHERE Y IS NOT NULL
)
Here's an alternative, just in case:
SELECT *
FROM Table1
WHERE EXISTS (
SELECT 1
FROM Table2
WHERE #var = #defValue
AND ... /* other conditions as necessary */
UNION ALL
SELECT 1
FROM Table3
WHERE #var <> #defValue
AND ... /* other conditions as necessary */
);