SQL Oracle Query - Left Outer Join on null Field - sql

I have this query
SELECT *
FROM (SELECT mi.visit_id, mi.event_id, mi.patient_id, mi.mrn, mi.reg_date,
mi.d_date, mi.bml_count, mi.TYPE, mblp.baby_patient_id,
mblp.baby_birthdate
FROM ajmid.km0076_motherinfo_test mi LEFT JOIN alfayezb2.mbl_patients mblp
ON mblp.mother_patient_id = mi.patient_id
--works here
AND ( TO_CHAR (mblp.baby_birthdate, 'mm/dd/YYYY') =
TO_CHAR (mi.reg_date, 'mm/dd/YYYY')
OR TO_CHAR (mblp.baby_birthdate, 'mm/dd/YYYY') =
TO_CHAR (mi.reg_date - 1, 'mm/dd/YYYY')
OR TO_CHAR (mblp.baby_birthdate, 'mm/dd/YYYY') =
TO_CHAR (mi.reg_date + 1, 'mm/dd/YYYY')
)
) bml
LEFT OUTER JOIN --doesn't work here
(SELECT ROW_NUMBER () OVER (PARTITION BY vis.patient_id ORDER BY vis.admission_date_time)
num,
vis.admission_date_time, vis.visit_id, vis.patient_id,
vis.facility_id
FROM visit vis) v ON bml.baby_patient_id = v.patient_id
WHERE v.num = 1
ORDER BY bml.reg_date
bml by itself returns 118 rows while the whole query returns 117, the reason is bml returns 1 row with baby_patient_id as null, so I used left outer join to show it, but it's still not showing !!
what can I do to show all rows of bml ?
I'm using Toad 9.6
Thank you

check the query:
SELECT ROW_NUMBER () OVER (PARTITION BY vis.patient_id ORDER BY vis.admission_date_time)
num,
vis.admission_date_time, vis.visit_id, vis.patient_id,
vis.facility_id
FROM visit vis
does it return 118 not null patient_id's?
if it returns 117, that might be the reason.(LEFT OUTER JOIN doesnot pick the records which are null on both tables)
Also, are you sure the null value of baby_patient_id in bml table is actually a NULL value and not a empty charater?(' ').

The probable cause is your filter / criteria (where clause) is eliminating a row with a null value for v.num. The WHERE filters the results after the join.
WHERE v.num = 1 -- Are all v.num equal to 1 ?
The mere action of using a criteria against a field, by definition of what NULL means, eliminates that row from consideration because NULL cannot be evaluated. You can say "WHERE id != 1" and expect to get rows where id is null because null != 1 right? Wrong. id != NULL is not defined logically. It is why we say "IS or IS NOT NULL" when dealing with NULL.

it's working finally !
I added
OR bml.baby_patient_id IS NULL
to the where clause, so the final script is
SELECT *
FROM (SELECT mi.visit_id, mi.event_id, mi.patient_id, mi.mrn, mi.reg_date,
mi.d_date, mi.bml_count, mi.TYPE, mblp.baby_patient_id,
mblp.baby_birthdate
FROM ajmid.km0076_motherinfo_test mi LEFT JOIN alfayezb2.mbl_patients mblp
ON mblp.mother_patient_id = mi.patient_id
AND ( TO_CHAR (mblp.baby_birthdate, 'mm/dd/YYYY') =
TO_CHAR (mi.reg_date, 'mm/dd/YYYY')
OR TO_CHAR (mblp.baby_birthdate, 'mm/dd/YYYY') =
TO_CHAR (mi.reg_date - 1, 'mm/dd/YYYY')
OR TO_CHAR (mblp.baby_birthdate, 'mm/dd/YYYY') =
TO_CHAR (mi.reg_date + 1, 'mm/dd/YYYY')
)
) bml
LEFT OUTER JOIN
(SELECT ROW_NUMBER () OVER (PARTITION BY vis.patient_id ORDER BY vis.admission_date_time)
num,
vis.admission_date_time, vis.visit_id, vis.patient_id,
vis.facility_id
FROM visit vis) v ON bml.baby_patient_id = v.patient_id
WHERE v.num = 1
OR bml.baby_patient_id IS NULL
ORDER BY bml.reg_date
I don't know how this was helpful, I wish someone would explain for me !
Thanks all

Related

Count of consecutive days ORACLE SQL

I need help with a query where a need to count de consecutive days like this
select
a.numcad, a.datapu , f.datapu , nvl(to_char(f.datapu, 'DD'),0)dia,
row_number() over (partition by a.numcad, f.datapu order by f.datapu)particao
from
ronda.r066apu a
left join (select t.numcad, t.numemp, t.datacc, t.datapu
from ronda.r070acc t
where t.datacc >= '21/01/2022'
and t.datacc <= trunc(sysdate)
group by t.numcad, t.numemp, t.datacc, t.datapu)f
on a.numemp = f.numemp
and a.numcad = f.numcad
and a.datapu = f.datapu
where a.numcad = 2675
and A.DATAPU >= '21/01/2022'
and A.DATAPU <= trunc(sysdate)
group by a.numcad, a.datapu, f.datapu, f.datacc
order by a.datapu
result is
between 24/01/2022 and 04/02/2022
is 12 days i need know this count , but i will ways get the '21/mes/year'
You can try:
SELECT TO_DATE('2022-01-24', 'YYYY-MM-DD') -
TO_DATE('2022-02-04', 'YYYY-MM-DD')
FROM dual
This returns 21, for example...

Error 00937 "not a single-group group function"

i face ORA-00937: "not a single-group group function" error problem with this sql query:
Any ideas please ! Thanks.
SELECT avg(count(*)) as value, 'Taux remplissage' as serie, to_char(c.datcre, 'YYYY-MM-DD') as arg
from charge c left join emplac e ON c.adr = e.adr
where e.ADR is not null and e.empsta != 'I' and e.empsta != 'V'
and (trunc(c.datcre) >= to_date('2020-11-01','YYYY-MM-DD'))
and (trunc(c.datcre) <= to_date('2021-11-30','YYYY-MM-DD'))
GROUP BY to_char(c.datcre, 'YYYY-MM-DD')
ORDER BY arg, serie
I'm not sure what you mean by avg(count(*)) in this context. This would be used (in Oracle) in a context where you want a result set with one row. But the order by suggests that you are expecting multiple rows.
If you just want the count(*) then you would use:
select count(*) as value, 'Taux remplissage' as serie,
to_char(c.datcre, 'YYYY-MM-DD') as arg
from charge c join
emplac e
ON c.adr = e.adr
where e.ADR is not null and e.empsta not in ('I', 'V') and
c.datcre >= date '2020-11-01' and
c.datcre < date '2021-12-01'
group by to_char(c.datcre, 'YYYY-MM-DD')
order by arg;
If you want the overall average on every row, you would use:
avg(count(*)) over () as average
Note the other changes to the query:
not in is much simpler than a chain of <>s.
Strictly speaking, the is not null is redundant, but I left it in.
The where clause turns the left join into an inner join anyway, so you should specify the join you are actually using.
Oracle supports the Standard SQL syntax for date constants. You might as well use it.
The date comparisons are generally going to be more efficient when you remove functions on the column. That helps the optimizer.
SELECT count(*) * 100.0 / (select count(*) from emplac) as value,'% remplissage ' || c.mag as serie, to_char(c.datcre, 'YYYY-MM-DD') as arg
from charge c left join emplac e ON c.adr = e.adr
where e.ADR is not null and e.empsta != 'I'
and (trunc(c.datcre) >= to_date('2020-11-01','YYYY-MM-DD'))
and (trunc(c.datcre) <= to_date('2021-11-30','YYYY-MM-DD'))
GROUP BY to_char(c.datcre, 'YYYY-MM-DD'), c.mag
ORDER BY arg

SQL Concat and convert date to MM/YYYY

Newbie here. Thanking you in advance for your help!
I have the following code to count unique records based on 3 data elements:
count(distinct concat(a.emp_nbr, b.acct_nbr, c.event_dt))
The event_dt is stored in the db as [DATE].
the issue is that i don't want to EXCLUDE 'DD' in the concat match statement. I want to match based on the MM/YYYY only. I believe the answer is to convert the [DATE] to MM/YYYY.
Ultimately i want to count distinct records based on emp_nbr, acct_nbr and mm/yyyy of event_dt.
Thanks!
MODIFIED
here is the query:
select distinct
B.emp_nbr, A.cust_nbr, E.cntry_enti_nbr, A.event_dt,
count(distinct concat(B.emp_nbr, E.cntry_enti_nbr, A.event_dt))
from customer A
left join user_profile B on A.owner_ID = B.owner_ID
left join account_owner E on A.cust_nbr = E.cust_nbr
where CAST(A.ramp_dt AS date format 'MM/DD/YYYY') between '01/01/2020' and '08/31/2020'
group by 1,2,3,4
Here's a sample of the results that are returned:
ln...emp_nbr.....cust_nbr.....ctry_enti_nbr event_dt......'Unique Identifier'
1....123.........87543290......488807........5/15/2020.........1
2....123.........62524497......488807........2/28/2020.........1 **
3....123.........62524497......488807........2/26/2020.........1 **
4....789.........62524497......488807........2/26/2020.........1
5....876.........62524497......488807........2/26/2020.........1
Line 2 or 3 should NOT be flagged as '1' (unique)
EDIT: changed code based on dnoeth's reco to add the count(*) OVER (PARTITION):
select distinct
B.emp_nbr, A.cust_nbr, E.cntry_enti_nbr, A.event_dt,
to_char(A.event_dt, 'YYYYMM') as Event_Month,
case when COUNT(*) OVER (PARTITION BY B.emp_nbr, A.cntry_enti_nbr, Event_Month)
= 1 then 1 else 0 end as Unique_Monthly_Event
from customer A
left join user_profile B on A.owner_ID = B.owner_ID
left join account_owner E on A.cust_nbr = E.cust_nbr
where CAST(A.event_dt AS date format 'MM/DD/YYYY') between '01/01/2020' and '08/31/2020'
group by 1,2,3,4
works fine for the previous case, however in another case it shows all ZERO for unique:
ln...emp_nbr.....cust_nbr.....ctry_enti_nbr event_dt......'Unique Identifier'
1....123.........78473466......863429........5/31/2020.........0
2....123.........78473466......863429........5/29/2020.........0
3....123.........78473466......863429........5/5/2020..........0
I would like to see ONE record with a unique identifier of "1"
Line 2 or 3 should NOT be flagged as '1' (unique)
... because there's more than one row per emp_nbr/cust_nbr/ctry_enti_nbr?
Then you need a simple Group Count:
select
B.emp_nbr, A.cust_nbr, E.cntry_enti_nbr, A.event_dt,
-- I would like to see ONE record with a unique identifier of "1"
-- this returns the row with the latest event date
case
when row_number()
over (partition by B.emp_nbr, A.cust_nbr, E.cntry_enti_nbr
order by event_dt desc) = 1
then 1
else 0
end
from customer A
left join user_profile B on A.owner_ID = B.owner_ID
left join account_owner E on A.cust_nbr = E.cust_nbr
where CAST(A.ramp_dt AS date format 'MM/DD/YYYY') between '01/01/2020' and '08/31/2020'
group by 1,2,3,4
You can simply use EXTRACT() function to extract year and month from the date. For example:
SELECT COUNT(DISTINCT CONCAT(a.emp_nbr, b.acct_nbr, EXTRACT(YEAR FROM c.event_dt), EXTRACT(MONTH FROM c.event_dt)))
FROM ...
You can use to_char(). Something like this:
count(distinct concat(a.emp_nbr, '|', b.acct_nbr, '|', to_char(c.event_dt, 'YYYYMM'))
or more commonly:
count(distinct a.emp_nbr || '|' || b.acct_nbr || '|' || to_char(c.event_dt, 'YYYYMM')
Note that this also puts a delimiter between
Your query looks off. I think you just want:
select A.cust_nbr, E.cntry_enti_nbr, to_char(c.event_dt, 'YYYYMM'),
count(*)
from customer A left join
user_profile B
on A.owner_ID = B.owner_ID left join
account_owner E
on A.cust_nbr = E.cust_nbr
where CAST(A.ramp_dt AS date format 'MM/DD/YYYY') between '01/01/2020' and '08/31/2020'
group by A.cust_nbr, E.cntry_enti_nbr, to_char(c.event_dt, 'YYYYMM');

Aggregate SQL Query, GROUP BY Causing Issues

Everything in this query works except for the second LEFT JOIN, where BEGIN_DATE and END_DATE are. Because I have to group by the additional columns, so they can be used in the "on join", I am getting false numbers. Is there any way to do this without having to group by. I hope this makes sense. Basically because I have to include BEGIN_DATE AND END_DATE in the group by, everything gets lost.
SELECT
to_char(T1.CALL_TIMESTAMP,'YYYY-IW') AS OMONTH
,COUNT(T1.HOUSE) AS NODECALLS
,T3.NODE_CODE
,T5.NODECUSTCOUNT
,T1.CALL_CATEGORY_LVL_3
,sum((CASE WHEN T1.TC_WIP_TRANSACTION_ID IS NOT NULL THEN 1 ELSE 0 END )) AS TC
,sum((CASE WHEN T1.TC_WIP_TRANSACTION_ID IS NOT NULL THEN 1 ELSE 0 END ))/nullif(COUNT(T1.HOUSE), 0) AS SVRATEPERCALL
,COUNT(T1.HOUSE)/ nullif(T5.NODECUSTCOUNT, 0) AS CALLRATE
FROM CVKOMNZP.NZKOMUSER.NFOV_INBD_REMEDY_CALL_DETAILS T1
LEFT JOIN
(
SELECT T2.NODE_CODE,T2.BEGIN_DATE,T2.END_DATE,T2.HOUSE,T2.CORP
FROM CVKOMNZP.NZKOMUSER.D_HOUSEHOLD_CH_HIST T2
) T3
ON T1.CORP = T3.CORP AND T1.HOUSE = T3.HOUSE AND (T1.CALL_TIMESTAMP BETWEEN T3.BEGIN_DATE AND T3.END_DATE)
LEFT JOIN
(
SELECT count(ADM_HOUSEHOLD_ID) AS NODECUSTCOUNT,NODE_CODE,BEGIN_DATE, END_DATE
FROM CVKOMNZP.NZKOMUSER.D_HOUSEHOLD_CH_HIST
WHERE HOUSE_STATUS_CODE = 2
AND END_DATE = '2999-12-31 00:00:00'
AND T1.CALL_TIMESTAMP BETWEEN BEGIN_DATE AND END_DATE
GROUP BY NODE_CODE,BEGIN_DATE,END_DATE
) T5
ON T5.NODE_CODE = T3.NODE_CODE AND T1.CALL_TIMESTAMP BETWEEN T5.BEGIN_DATE AND T5.END_DATE
WHERE T1.EXCLUSION_FLAG = 'N'
AND T1.CALL_TIMESTAMP >= To_Date ('07-29-2017', 'MM-DD-YYYY' ) AND T1.CALL_TIMESTAMP <= To_Date ('07-31-2017', 'MM-DD-YYYY' )
GROUP BY
to_char(T1.CALL_TIMESTAMP,'YYYY-IW')
,T3.NODE_CODE
,T5.NODECUSTCOUNT
,T1.CALL_CATEGORY_LVL_3
If I am understanding this right, you want to get a COUNT without grouping by BEGIN and END DATE. However, because your Subquery (2nd LEFT JOIN) needs to include the BEGIN and NEED, you do not know how to group without it.
If this is the case, you'll need a subquery for your count and JOIN it back to the same table.
FYI: Your T1.CALL_TIMESTAMP does not make sense in this subquery since you don't have a table called T1. I renamed it to "a". Feel free to change it to what you want.
See if this make sense
LEFT JOIN
(
SELECT a.BEGIN_DATE,
a.END_DATE,
node.NODECUSTCOUNT,
a.node_code
FROM CVKOMNZP.NZKOMUSER.D_HOUSEHOLD_CH_HIST a
/**Subquery to get a COUNT of all the Node based on NODE_CODE.
You link this back to your query above using the NODE CODE**/
JOIN ( SELECT count(ADM_HOUSEHOLD_ID) AS NODECUSTCOUNT,
NODE_CODE
FROM CVKOMNZP.NZKOMUSER.D_HOUSEHOLD_CH_HIST
GROUP BY NODE_CODE ) node on node.node_code = a.node_code
WHERE a.HOUSE_STATUS_CODE = 2
AND a.END_DATE = '2999-12-31 00:00:00'
AND a.CALL_TIMESTAMP BETWEEN BEGIN_DATE AND END_DATE
) ..JOIN THIS BACK TO YOUR MAIN TABLE

Oracle ORA-00979 - "not a GROUP BY expression"

Can anybody please help me with this particular query? I'm having ORA-00979 when trying to run this:
select t0.title, count (1) as count0, (select count (1)
from contract c1, se se1
where c1.c_id = se1.c_id
and se1.svc_id = 3
and se1.deleted = 0
and c1.deleted = 0
and c1.c_date between to_date ('07.10.2000', 'dd.mm.yyyy')
and to_date ('22.11.2010', 'dd.mm.yyyy')
and c1.company = 0
and c1.tdata.tariff = c0.tdata.tariff
) as count1
from contract c0, se se0, tariff t0
where c0.c_id = se0.c_id
and se0.svc_id = 3
and se0.deleted = 0
and c0.deleted = 0
and c0.c_date between to_date ('21.11.2000', 'dd.mm.yyyy')
and to_date ('06.01.2011', 'dd.mm.yyyy')
and c0.company = 0
and t0.tariff_id = c0.tdata.tariff
group by t0.title
The problem is your subquery with the select count(1) part. Just because it's got a count in it doesn't actually make it an aggregate. It's still a subquery that will be applied to every row and as you can see it uses the value c0.tdata.tariff which is not part of the group.
Looks like that scalar subquery is causing the problem -- it is neither a group function, nor is it in the GROUP BY list.
Probably you could workaround it with something like:
select t0.title, count (1) as count0, SUM(select count (1) ...) AS count1
...
Considering this seems to be two instances of the same query just over different dates (I might be wrong here...it's been a long day), you could probably just simplify it and rewrite like this:
select
t0.title,
count (case when c0.c_date between to_date ('21.11.2000', 'dd.mm.yyyy')
and to_date ('06.01.2011', 'dd.mm.yyyy') then 1 end) as count0,
count (case when c0.c_date between to_date ('07.10.2000', 'dd.mm.yyyy')
and to_date ('22.11.2011', 'dd.mm.yyyy') then 1 end) as count1
from
contract c0,
se se0,
tariff t0
where
c0.c_id = se0.c_id
and se0.svc_id = 3
and se0.deleted = 0
and c0.deleted = 0
and (c0.c_date between to_date ('21.11.2000', 'dd.mm.yyyy')
and to_date ('06.01.2011', 'dd.mm.yyyy')
or c0.c_date between to_date ('07.10.2000', 'dd.mm.yyyy')
and to_date ('22.11.2010', 'dd.mm.yyyy'))
and c0.company = 0
and t0.tariff_id = c0.tdata.tariff
group by t0.title
Your group by needs to include all of the non-aggregate columns from your select list. In this case, the group by is missing the count1 returned by your subquery.