Correlated Subquery and Arithmetic in Oracle SQL - sql

This is my second time posting, I got a lot of good feedback from the first post regarding some difficulties. I am working on improving my SQL skills and building this query but I do not understand how to approach
* i managed to correct all original errors in my question but have one last doubt *
select wo.si_number,ivh.total_cost, wo.bill_name,pnm.pn, pnm.description, woqh.approved_date,ivh.total_price,
(select sum(ivh.total_price - ivh.total_cost) / nullif(ivh.total_price,0) from invc_header ivh where wqh_auto_key = wqh.wqh_auto_key group by ivh.total_price) as CTP,
ivh.post_date, wwt.description, wqh.WQH_AUTO_KEY, wqh.POST_DESC
from wo_operation wo, parts_master pnm,wo_quote_header wqh,
wo_quote_detail wqd, invc_header ivh, wo_work_type wwt, wo_quote_header woqh
where wo.pnm_auto_key = pnm.pnm_auto_key
and wo.woo_auto_key = wqd.woo_ref
and wqd.wqh_auto_key = wqh.wqh_auto_key
and wqh.WQH_NUMBER = woqh.WQH_NUMBER
and ivh.wqh_auto_key (+)= wqh.wqh_auto_key
and wwt.wwt_auto_key (+)= wo.wwt_auto_key
and wo_type = 'External'
and wqh.POST_DESC is not NULL
and woqh.approved_date between to_date('01/01/2020', 'MM/DD/YYYY') and to_date('01/31/2020', 'MM/DD/YYYY')
order by ctp
I m not sure how to have the script only pull the rows whose ctp is < 30; how would I incorporate that?

As long as your CTP column is a number you could add that to your WHERE like show below where I added it to the end of your query's WHERE clause.
SELECT wo.si_number,
ivh.total_cost,
wo.bill_name,
pnm.pn,
pnm.description,
woqh.approved_date,
ivh.total_price,
(
SELECT SUM(ivh.total_price - ivh.total_cost) / NULLIF(ivh.total_price, 0)
FROM invc_header ivh
WHERE wqh_auto_key = wqh.wqh_auto_key
GROUP BY ivh.total_price
) AS CTP,
ivh.post_date,
wwt.description,
wqh.WQH_AUTO_KEY,
wqh.POST_DESC
FROM wo_operation wo,
parts_master pnm,
wo_quote_header wqh,
wo_quote_detail wqd,
invc_header ivh,
wo_work_type wwt,
wo_quote_header woqh
WHERE wo.pnm_auto_key = pnm.pnm_auto_key
AND wo.woo_auto_key = wqd.woo_ref
AND wqd.wqh_auto_key = wqh.wqh_auto_key
AND wqh.WQH_NUMBER = woqh.WQH_NUMBER
AND ivh.wqh_auto_key (+)= wqh.wqh_auto_key
AND wwt.wwt_auto_key (+)= wo.wwt_auto_key
AND wo_type = 'External'
AND wqh.POST_DESC IS NOT NULL
AND woqh.approved_date BETWEEN to_date('01/01/2020', 'MM/DD/YYYY') AND to_date('01/31/2020', 'MM/DD/YYYY')
AND CTP <= 30
ORDER BY ctp;
A simple less than or equal to operation will filter your results to the desired range, so you get only those less than 30 as requested. Also if you need 29 and down you can omit the =

Related

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

Multiple AND conditions in case statement

I've got an Oracle table CUST with Fields YEAR and STATUS. I'm trying to write a statement that will select the records where ((CUST.YEAR = '2017' and CUST.STATUS = 'ACTIVE') and where (CUST.YEAR = '2018' and CUST.STATUS = 'ACTIVE')). When both of these statements are true, I want to return '1' else '0'
select *
from cust
where cust.year = '2017' and cust.status = 'Active'
returns the correct number of rows (394).
select *
from cust
where cust.year = '2018' and cust.status = 'Active'
returns the correct number of rows (451).
Here's where the wheels fall off (due to my inexperience with SQL scripting). I've tried to combine the two select statements and I either gets tens of thousands of rows or errors because of incorrect syntax. This is before even attempting to use a case statement to return a comparative result (if both of these conditions, then '1' else '0').
I realize this is probably pretty elementary stuff but the syntax is beyond me as of now. Would someone be kind enough to help me construct this statement?
The few times I've posted to this forum I've learned things that help to make me more self-sufficient, so I offer my thanks in advance.
You can take advantage of IN here:
select *
from cust
where cust.year IN ('2017', '2018') and
cust.status = 'Active'
The way I understood it, or might be what you're looking for, i.e.
select *
from cust
where (cust.year = '2017' and cust.status = 'Active')
or (cust.year = '2018' and cust.status = 'Active');
which - as William says - leads to
where cust.status = 'Active'
and cust.year in ('2017', '2018')
If I have understood your requirement correctly you want to determine whether your table has records for both pairs of conditions, that is , whether you have active records for both 2017 and 2018. The solutions provided so far will assert whether either condition is true but not both.
So here is a solution which satisfies your actual requirement. We have a WITH clause which selects one active record for each year (which is all your need). The inline view then counts how many records it found. If the count is two then you have active records for both years.
with tst as (
select cust.cust_id, cust.year
from cust
where cust.year = '2017'
and cust.status = 'Active'
group by cust.cust_id, cust.year
union all
select cust.cust_id, cust.year
from cust
where cust.year = '2018'
and cust.status = 'Active'
group by cust.cust_id, cust.year
)
select cust_id
, case when cnt = 2 then 1 else 0 end as your_condition
from ( select cust_id, count(*) as cnt
from tst
group by cust_id )
/

Dividing counts on db2?

Hello I have a query where I am pulling the count of new hires who have downloaded our app and the count of new hires.
I am trying to divide the two to find the percentage of those who have downloaded the app
but whenever I run my query my result is 0
both data and driver_id are varchar
here is my query
SELECT ROUND(COUNT(DATA) / (
SELECT COUNT(D2.DRIVER_ID)
FROM DRIVER D2
WHERE ACTIVE_IN_DISP = 'True'
AND START_DATE >= '10/1/2015'
), 4)
FROM CUSTOM_DATA C
,DRIVER D
WHERE CUSTDEF_ID = '50'
AND SRC_TABLE_KEY = DRIVER_ID
AND ACTIVE_IN_DISP = 'True'
AND START_DATE >= '10/1/2015'
thanks in advance!
Problem is you are getting truncate to INT
convert your data to float before the division
SELECT ROUND( COUNT(DATA) *1.0 /
(SELECT COUNT(D2.DRIVER_ID)
....
Just use conditional aggregation. It is easier and less prone to error:
SELECT ROUND(AVG(CASE WHEN CUSTDEF_ID = '50' THEN 1.0 ELSE 0
END), 4)
FROM CUSTOM_DATA C JOIN
DRIVER D
ON SRC_TABLE_KEY = DRIVER_ID
WHERE ACTIVE_IN_DISP = 'True' AND
START_DATE >= '2015-10-01';
Not having to repeat the logic in the WHERE using a subquery makes the query much less prone to error. Plus, it is probably faster to get rid of the subquery as well.

SQL Oracle Query - Left Outer Join on null Field

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

Running sum with aggregate function

I am retrieving the results of the mlog table and calculate the subtotal of the qtyn with the help of following code 1. I am stuck with how to join my second code criteria with the first.
Thanks for any help
1.
SELECT autn, date, itcode, qtyn, out,
date, phstock,
qtyn + COALESCE(
(SELECT SUM(qtyn) FROM dbo.mlog b
WHERE b.autn < a.autn
AND itcode = '40'), 0) AS balance
FROM dbo.mlog a
WHERE (itcode = '40')
ORDER BY autn
2.
date >=(SELECT MAX([date]) FROM mlog)
To append a condition to the code, use AND or OR. EG:
SELECT a.autn, a.date, a.itcode, a.qtyn, a.out,
a.date, a.phstock,
a.qtyn + COALESCE(
(SELECT SUM(b.qtyn) FROM dbo.mlog b
WHERE b.autn < a.autn
AND b.itcode = '40'), 0) AS balance
FROM dbo.mlog a
WHERE (a.itcode = '40' AND a.date >= (SELECT MAX([c.date]) FROM mlog c) )
ORDER BY a.autn
Not tested, but should do what you want
I have heard that SQL Server is rather inefficient with coalesce(), because it runs the first part twice. Here is an alternative way of writing this:
with ml as (
SELECT ml.autn, ml.date, ml.itcode, ml.qtyn, ml.out, ml.date, ml.phstock
FROM dbo.mlog ml
WHERE ml.itcode = '40' AND ml.date >= (SELECT MAX(ml1.date]) FROM mlog ml1)
)
select ml.*,
(select sum(m1l.qtyn) from ml ml1 where ml1.autn <= ml.autn) as balance
from ml
ORDER BY ml.autn
I also wonder if the where clause would be more efficient as:
WHERE ml.itcode = '40' AND ml.date = (SELECT top 1 ml1.date FROM mlog ml1 order by ml1.date desc)