Error 00937 "not a single-group group function" - sql

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

Related

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');

Date field not contained in either an aggregate function or the GROUP BY clause

I am trying to calculate "Month to date" using three joined tables. It always gives me error "not contained in either an aggregate function or the GROUP BY clause" The date field is S.BUS_DAT
I tried many things but always get same error. Any advise
Here is the code I use
SELECT
-- Select from IM_IN
M.ITEM_NO,
M.DESCR,
N.QTY_ON_HND,
M.PRC_1,
N.LST_COST,
N.LST_RECV_DAT,
--Select from IM_ITEM
M.CATEG_COD,
M.ATTR_COD_1,
M.ITEM_VEND_NO,
M.ALT_1_UNIT,
M.ALT_1_NUMER,
M.LST_COST,
count (S.BUS_DAT) AS BUS_DAYS,
**sum (QTY_SOLD) OVER (PARTITION BY Month (S.BUS_DAT))as **MTD****
FROM
dbo.IM_INV N
INNER JOIN dbo.IM_ITEM M
ON
N.ITEM_NO = M.ITEM_NO
INNER JOIN
dbo.PS_TKT_HIST_LIN S
ON
N.ITEM_NO = S.ITEM_NO
Group by
M.ITEM_NO,
M.DESCR,
M.ITEM_VEND_NO,
M.CATEG_COD,
M.ATTR_COD_1,
N.QTY_ON_HND,
N.LST_COST,
N.LST_RECV_DAT,
N.LST_SAL_DAT,
M.ALT_1_UNIT,
M.ALT_1_NUMER,
M.PRC_1,
M.LST_COST
Order by M.ITEM_NO
try to include Month(S.BUS_DAT) into group by as well:
SELECT
M.ITEM_NO,
M.DESCR,
N.QTY_ON_HND,
M.PRC_1,
N.LST_COST,
N.LST_RECV_DAT,
M.CATEG_COD,
M.ATTR_COD_1,
M.ITEM_VEND_NO,
M.ALT_1_UNIT,
M.ALT_1_NUMER,
M.LST_COST,
COUNT(S.BUS_DAT) AS BUS_DAYS,
SUM(QTY_SOLD) OVER(PARTITION BY MONTH(S.BUS_DAT)) AS MTD
FROM dbo.IM_INV N
INNER JOIN dbo.IM_ITEM M ON N.ITEM_NO = M.ITEM_NO
INNER JOIN dbo.PS_TKT_HIST_LIN S ON N.ITEM_NO = S.ITEM_NO
GROUP BY M.ITEM_NO,
M.DESCR,
M.ITEM_VEND_NO,
M.CATEG_COD,
M.ATTR_COD_1,
N.QTY_ON_HND,
N.LST_COST,
N.LST_RECV_DAT,
N.LST_SAL_DAT,
M.ALT_1_UNIT,
M.ALT_1_NUMER,
M.PRC_1,
M.LST_COST,
MONTH(S.BUS_DAT)
ORDER BY M.ITEM_NO;
I believe you want:
sum(sum(QTY_SOLD)) OVER (PARTITION BY Month(S.BUS_DAT)) as MTD
Note: You will still need month(s.bus_dat) in the group by clause.
If you only want the sum for the current month, then you don't want a window function, just conditional aggregation:
sum(case when year(s.bus_dat) = year(getdate()) and month(s.bus_dat) = month(getdate())
then qty_sold
end) as mtd

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

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

Odd 'GROUP BY' error in a sub-query while using Oracle

A quick background -- I need to find the termination rates of cases that go through our company lab grouped by case type and month. So far I came up with this:
SELECT BPI.TYPE,
EXTRACT(MONTH FROM CS.RECEIVED_DATE) MONTH,
COUNT(*) termed_cases
FROM CELL_SOURCE cs
JOIN BASIC_PATHOLOGY_INFO bpi ON CS.CELL_SOURCE_ID = BPI.CELL_SOURCE_ID
JOIN RECENT_CELL_SOURCE_STATUS rcss ON CS.CELL_SOURCE_ID = RCSS.CELL_SOURCE_ID
WHERE type IS NOT NULL
AND CS.RECEIVED_DATE > to_date('03/01/2011', 'MM/DD/YYYY/')
AND RCSS.STATUS like 'Term%'
GROUP BY BPI.TYPE, EXTRACT(MONTH FROM CS.RECEIVED_DATE)
ORDER BY month, type
This finds all cases that have been termed, easy enough. However, when I want to find the rate, I get a bit of a problem. I tried using a sub-query to catch the total amount of cases per type regardless of it's status, as such:
COUNT(*)/(SELECT COUNT(*)
FROM CELL_SOURCE cs_1
JOIN BASIC_PATHOLOGY_INFO bpi_1 ON CS_1.CELL_SOURCE_ID = BPI_1.CELL_SOURCE_ID
WHERE BPI_1.TYPE = BPI.TYPE
AND EXTRACT(month from CS_1.RECEIVED_DATE) = EXTRACT(MONTH FROM CS.RECEIVED_DATE)) termed_cases
However, this throws an ORA-00979: not a GROUP BY expression error, and highlights BPI.TYPE from the sub-query.
Anyone have any idea what my error could actually be? Also, would an analytical function work better here than an aggregate function?
So, you need two counts: a total for all cases in the month and a total for just the Termed cases. The easiest way of doing this is to use a CASE() function to execute a conditional count, like this:
SELECT BPI.TYPE,
EXTRACT(MONTH FROM CS.RECEIVED_DATE) MONTH,
COUNT(*) all_cases,
sum(case when RCSS.STATUS like 'Term%' then 1 else 0 end ) termed_cases
FROM CELL_SOURCE cs
JOIN BASIC_PATHOLOGY_INFO bpi ON CS.CELL_SOURCE_ID = BPI.CELL_SOURCE_ID
JOIN RECENT_CELL_SOURCE_STATUS rcss ON CS.CELL_SOURCE_ID = RCSS.CELL_SOURCE_ID
WHERE tumor_type IS NOT NULL
AND CS.RECEIVED_DATE > to_date('03/01/2011', 'MM/DD/YYYY/')
GROUP BY BPI.TUMOR_TYPE, EXTRACT(MONTH FROM CS.RECEIVED_DATE)
ORDER BY month, tumor_type
Note that I have removed the LIKE filter from the WHERE clause.
select bpi.type , month,
termed_cases /
(select count(*)
from CELL_SOURCE cs_1
inner join BASIC_PATHOLOGY_INFO bpi_1
on CS_1.CELL_SOURCE_ID = BPI_1.CELL_SOURCE_ID
where BPI_1.TUMOR_TYPE = BPI.TUMOR_TYPE
and extract(month from CS_1.RECEIVED_DATE) = extract(MONTH FROM CS.RECEIVED_DATE)
)
from (
select BPI.TYPE,
extract(MONTH FROM CS.RECEIVED_DATE) MONTH,
count(*) termed_cases
from CELL_SOURCE cs
inner join BASIC_PATHOLOGY_INFO bpi
on CS.CELL_SOURCE_ID = BPI.CELL_SOURCE_ID
inner join RECENT_CELL_SOURCE_STATUS rcss
on CS.CELL_SOURCE_ID = RCSS.CELL_SOURCE_ID
where tumor_type is not null
and CS.RECEIVED_DATE > to_date('03/01/2011', 'MM/DD/YYYY/')
and RCSS.STATUS like 'Term%'
group by BPI.TYPE, extract(MONTH FROM CS.RECEIVED_DATE)
)
order by month, type