Count and Sum inside a select with multiple sums and counts - oracle

I'm using sub-query factoring and I have a query that returns invoice lines, and in the end I have this final sub-query:
I've already tried Partition but without success
SELECT
COUNT(CASE WHEN PC <> 0 THEN 1 END) AS A_LINECOUNT,
SUM(CASE WHEN PC > 0 THEN NR ELSE 0 END) AS B_PRODUCTCOUNT,
COUNT(CASE WHEN ALLOW_PAY = 1 THEN 1 END) AS C_INVOICECOUNT, --- ERROR
SUM(CASE WHEN ALLOW_PAY = 1 THEN MISSING_VALUE ELSE 0 END) AS D_INVOICETOTAL, --- ERROR
COUNT(CASE WHEN IS_NON_LIQUIDABLE_PRODUCT = 1 THEN 1 END) AS E_CONDITIONCOUNT,
COUNT(CASE WHEN IS_LIQUIDABLE_PRODUCT = 1 THEN 1 END) AS F_CONDITIONCOUNT
FROM MAIN_Q
The calculation of C_INVOICECOUNT and D_INVOICETOTAL is not correct because their values are repeated within each line of the invoice. Please consider that um MAIN_Q i also have a document_id where i can group by.
thanks

Maybe I understood correctly, maybe not, but this is too long for comment. If yes, C_INVOICECOUNT can be count as:
count(distinct case when allow_pay = 1 then document_id end)
But the problem is with D_INVOICETOTAL. You have repeated values for each invoice here and details which do not repeat. If so, add row numbering to your query:
select main_q.*, row_number() over (partition by document_id) rn from main_q
and then in problematic places use rn = 1:
select ...
count(case when rn = 1 and allow_pay = 1 then 1 end),
sum(case when rn = 1 and allow_pay = 1 then missing_value else 0 end)
...
from (select main_q.*, row_number() over (partition by document_id) rn from main_q)
Only first rows for each invoice will be analysed. Of course you can add row_number in earlier step.

Related

ORACLE - FLATTEN DATA

I am sure that this question has been asked before but possibly not in this way.
Here is the data:
I need the rows flattened and the end output:
Any ideas?
As you already said we exactly can use row_number here and then use the row_number to pivot it,
select *
from
(
select userid,make,model,
row_number() over (partition by userid order by make,model) rn
from table1
)
pivot
(
max(make) make,max(model) model
for rn in (1,2,3,4,5)
)
Or
Using conditional aggregation which many prefers over PIVOT
select userid
,max(case when rn = 1 then make end) make_1
,max(case when rn = 1 then model end) model_1
,max(case when rn = 2 then make end) make_2
,max(case when rn = 2 then model end) model_2
,max(case when rn = 3 then make end) make_3
,max(case when rn = 3 then model end) model_3
from
(
select userid,make,model,
row_number() over (partition by userid order by make,model) rn
from table1
)
group by userid;
In both cases you can say the disadvantage is hard coding the row numbers but this is how it works or else you may opt for dynamic SQL if necessary.

How to force a set of possible values in conditional aggregation?

In Oracle I have a query that uses conditional aggregation to display totals.
See http://sqlfiddle.com/#!4/ab1915/2
select ts.description,
sum(case when ta.group_number = 1 then 1 else 0 end) group_one
,sum(case when ta.group_number = 2 then 1 else 0 end) group_two
,sum(case when ta.group_number = 3 then 1 else 0 end) group_three
from ta
join ts on ta.status_id=ts.status_id
group by ts.description;
However, I need the results to also show another row for the status "cancelled", which is not currently showed because there are not any record with that status, but I need to display "cancelled" with 0 counts anyway,
Any idea how I would do that?
The DDL for this example is
create table ta (group_number number, status_id number);
create table ts (status_id number, description varchar(111));
insert into ta (group_number,status_id) values (1,1);
insert into ta (group_number,status_id) values (2,1);
insert into ta (group_number,status_id) values (3,2);
insert into ta (group_number,status_id) values (3,3);
insert into ta (group_number,status_id) values (3,3);
Use an outer join so you pick up all of the ts valueswhich means you need to reverse the order of the tables; and I'd use count() instead of sum():
select ts.description,
count(case when ta.group_number = 1 then 1 end) group_one
,count(case when ta.group_number = 2 then 1 end) group_two
,count(case when ta.group_number = 3 then 1 end) group_three
,count(case when ta.group_number = 4 then 1 end) group_four
from ts
left join ta on ta.status_id=ts.status_id
group by ts.description;
DESCRIPTION GROUP_ONE GROUP_TWO GROUP_THREE
-------------------- ---------- ---------- -----------
started 1 1 0
finished 0 0 2
cancelled 0 0 0
progressing 0 0 1
Updated SQL Fiddle
The case expression can evaluate to anything when matched, it just has to be non-null. From the docs:
If you specify expr, then COUNT returns the number of rows where expr is not null.
so where the case is not matched it evaluates to null, which is not counted. (You can have else null if you prefer to be explicit, but I tend to prefer brevity...)

Oracle sqlplus query

I am getting "Invalid Number"
Can anyone please guide me what I am doing wrong here ???
select TO_CHAR(a.START_TIME,'YY-MON-DD HH24') as START_TIME,
count(*) AS NbOperations,
SUM(CASE WHEN OPERATION_RESULT=0 THEN 1 ELSE 0 END) AS Success,
SUM(CASE WHEN OPERATION_RESULT=200 THEN 1 ELSE 0 END) AS Cancel,
SUM(CASE WHEN OPERATION_RESULT=203 THEN 1 ELSE 0 END) AS AppletInternalError,
SUM(CASE WHEN OPERATION_RESULT=406 THEN 1 ELSE 0 END) AS TimeoutWaitForCard,
SUM(CASE WHEN OPERATION_RESULT=413 THEN 1 ELSE 0 END) AS BillingError,
SUM(CASE WHEN OPERATION_RESULT=1000 THEN 1 ELSE 0 END) AS ABANDONNED,
SUM(CASE WHEN OPERATION_RESULT=1004 THEN 1 ELSE 0 END) AS ABANDON_FOR_NEW_OPERATION,
SUM(CASE WHEN OPERATION_RESULT NOT IN (0,200,203,406,413,1000,1004) THEN 1 ELSE 0 END) AS NbOthers
from MyTable1 a,
MyTable2 b
where OPERATION_TYPE in (2,3,4,5)
and a.OPERATION_TYPE=b.OPERATION_ID
and a.START_TIME >= to_timestamp('&1', 'YYYY-MM')
and a.START_TIME < to_timestamp('&2', 'YYYY-MM') + interval '1' month
group by TO_CHAR(a.START_TIME,'YY-MON-DD HH24')
order by TO_CHAR(START_TIME,'YY-MON-DD HH24');
check the format of your inputs &1 and &2 maybe it doesn't match with your date mask: 'YYYY-MM'. Another possible reason is the contents of the table: OPERATION_TYPE and OPERATION_RESULT are both number datatypes? Possible there are storing things different from a number.
I am able to solve the issue using below SQL code:
select TO_CHAR(a.START_TIME,'YY-MON-DD HH24') as Hourly, count(*) AS NbOperations,
SUM(CASE WHEN OPERATION_RESULT=0 THEN 1 ELSE 0 END) AS Success,
SUM(CASE WHEN OPERATION_RESULT=200 THEN 1 ELSE 0 END) AS Cancel,
SUM(CASE WHEN OPERATION_RESULT=203 THEN 1 ELSE 0 END) AS AppletInternalError,
SUM(CASE WHEN OPERATION_RESULT=406 THEN 1 ELSE 0 END) AS TimeoutWaitForCard,
SUM(CASE WHEN OPERATION_RESULT=413 THEN 1 ELSE 0 END) AS BillingError,
SUM(CASE WHEN OPERATION_RESULT=1000 THEN 1 ELSE 0 END) AS ABANDONNED,
SUM(CASE WHEN OPERATION_RESULT=1004 THEN 1 ELSE 0 END) AS ABANDON_FOR_NEW_OPERATION,
SUM(CASE WHEN OPERATION_RESULT NOT IN (0,200,203,406,413,1000,1004) THEN 1 ELSE 0 END) AS NbOthers
from gsg3_invocation_history a, GSG3_OPERATION_TYPE b where START_TIME >= to_timestamp('&1', 'YYYY-MM')
and START_TIME < to_timestamp('&2', 'YYYY-MM') + interval '1' month and OPERATION_TYPE in (2,3,4,5) and a.OPERATION_TYPE=b.OPERATION_ID
group by TO_CHAR(a.START_TIME,'YY-MON-DD HH24') order by TO_CHAR(START_TIME,'YY-MON-DD HH24');

oracle SQL procedure updating table

I need to update the the following columns in a table. males,females,infants and children using SQL procedure.from this column paxtype which has f,i,m,c.which is females,infants,males and children repectively.but am not able error ORA-00907: missing right parenthesis
update xxxx a set (a.INFANTS,a.MALES,a.CHILDREN,a.FEMALES)=
(SELECT b.PAXTYPE, COUNT (b.PAXTYPE) FROM xxxx b
(case ( count (b.PAXTYPE))
when ( count (b.PAXTYPE))='M'then 'a.males'
when ( count (b.PAXTYPE))='F' then 'a.females'
when ( count (b.PAXTYPE))='I' then 'a.infants'
when ( count (b.PAXTYPE))='C' then 'a.children'
END)
WHERE a.date_key = TO_CHAR (b.FLIGHTDATE, 'RRRRMMDD')
AND a.FLTNUM_KEY = TRIM (SUBSTR (b.flightnumber, 3))
AND a.origin = b.frm
AND a.destination = b.too
--and a.date_key=20170801
--and fightnumber = '100'
AND TRIM (a.cancelled) IS NULL
-- and rownum = 1
GROUP BY b.PAXTYPE;
)
It looks like you want a total count of each type, right? I think this is what you want to do.
update xxxx a set (a.INFANTS,a.MALES,a.CHILDREN,a.FEMALES)=
(SELECT
sum(case when b.PAXTYPE = 'I' then 1 else 0 end) as infants_count,
sum(case when b.PAXTYPE = 'M' then 1 else 0 end) as males_count,
sum(case when b.PAXTYPE = 'C' then 1 else 0 end) as children_count,
sum(case when b.PAXTYPE = 'F' then 1 else 0 end) as females_count
FROM xxxx b
WHERE a.date_key = TO_CHAR (b.FLIGHTDATE, 'RRRRMMDD')
AND a.FLTNUM_KEY = TRIM (SUBSTR (b.flightnumber, 3))
AND a.origin = b.frm
AND a.destination = b.too
--and a.date_key=20170801
--and fightnumber = '100'
AND TRIM (a.cancelled) IS NULL
-- and rownum = 1
GROUP BY b.PAXTYPE);

Oracle/SQL Query issue

I am sort of a newbie to oracle/sql. I am trying to pull from the same column different values and add them with some other information. The other information is not the issue it is trying to count and add here the problem comes in.
I am connecting to an oracle database. Here is what i have
SELECT
EV.PUBLIC_DESCRIPTION,
EV.EVENT_DATE,
ES.PRICE,
BT.BUYER_TYPE_CODE,
PCA.ADDR1,
PCA.ADDR2,
PCA.CITY,
PCA.POSTAL_CODE,
PCE.EMAIL,
PC.FORMATTED_NAME,
PCP.PHONE_NUMBER,
PCP.SECONDARY,
SUM(COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRADLT' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRADTE' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GSTDTE' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GROUDI' THEN 1 ELSE 0 END)) AS "Adults",
SUM(COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRCHILD' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRCHTE' THEN 1 ELSE 0 END)) AS 'Paid Child',
SUM(COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRPCH' THEN 1 ELSE 0 END)) AS 'Free Child',
SUM(COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRCOMP' THEN 1 ELSE 0 END)) AS 'Comps'
FROM EVENT EV
INNER JOIN EVENT_SEAT ES ON EV.EVENT_ID = ES.EVENT_ID
INNER JOIN BUYER_TYPE BT ON ES.BUYER_TYPE_ID = BT.BUYER_TYPE_ID
INNER JOIN PATRON_ORDER PO ON ES.ORDER_ID = PO.ORDER_ID
INNER JOIN PATRON_ACCOUNT PA ON ES.ATTENDING_PATRON_ACCOUNT_ID = PA.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT PC ON PA.PATRON_ACCOUNT_ID = PC.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_ADDRESS PCA ON PC.PATRON_ACCOUNT_ID = PCA.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_EMAIL PCE ON PCA.PATRON_ACCOUNT_ID = PCE.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_PHONE PCP ON PCE.PATRON_ACCOUNT_ID = PCP.PATRON_ACCOUNT_ID
GROUP BY EV.PUBLIC_DESCRIPTION, EV.EVENT_DATE
ORDER BY ES.TRANSACTION_ID DESC, PCP.SECONDARY DESC, PCP.PHONE_NUMBER DESC, PC.FORMATTED_NAME DESC, PCE.EMAIL DESC, PCA.POSTAL_CODE DESC, PCA.CITY DESC, PCA.ADDR2 DESC, PCA.ADDR1 DESC, BT.BUYER_TYPE_CODE DESC, ES.PRICE DESC;
any help would be greatly appreciated
You need to decide which technique you want to use, currently you are using 2 techniques and they are colliding.
For this you must know: COUNT() will increment by one for every NON-NULL value
So, to use COUNT() with a case expression do this
COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 END)
or
COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 ELSE NULL END)
OR, don't use COUNT(), use SUM() instead
SUM(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 ELSE 0 END)
To add conditions together, I suggest you use the case expression better
Instead of something like this:
, COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRADLT' THEN 1 END)
+ COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRADTE' THEN 1 END)
+ COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 END)
+ COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GSTDTE' THEN 1 END)
+ COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GROUDI' THEN 1 END) AS "Adults"
Use this:
COUNT(CASE WHEN BT.BUYER_TYPE_CODE IN ('GRADLT','GRADTE','GRSTND','GSTDTE','GROUDI') THEN 1 ELSE NULL END)
There is also an issue with your GROUP BY, which MUST contain ALL non-aggregating columns. I think your query should look more like this:
SELECT
EV.PUBLIC_DESCRIPTION
, EV.EVENT_DATE
, ES.PRICE
/* , BT.BUYER_TYPE_CODE */
, PCA.ADDR1
, PCA.ADDR2
, PCA.CITY
, PCA.POSTAL_CODE
, PCE.EMAIL
, PC.FORMATTED_NAME
, PCP.PHONE_NUMBER
, PCP.SECONDARY
, COUNT(CASE WHEN BT.BUYER_TYPE_CODE IN ('GRADLT', 'GRADTE', 'GRSTND', 'GSTDTE', 'GROUDI') THEN 1 ELSE NULL END) AS "Adults"
, COUNT(CASE WHEN BT.BUYER_TYPE_CODE IN ('GRCHILD', 'GRCHTE', 'GRPCH', 'GRCOMP') THEN 1 ELSE NULL END) AS "Free Child"
, COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRCOMP' THEN 1 ELSE NULL END) AS "Comps"
FROM EVENT EV
INNER JOIN EVENT_SEAT ES
ON EV.EVENT_ID = ES.EVENT_ID
INNER JOIN BUYER_TYPE BT
ON ES.BUYER_TYPE_ID = BT.BUYER_TYPE_ID
INNER JOIN PATRON_ORDER PO
ON ES.ORDER_ID = PO.ORDER_ID
INNER JOIN PATRON_ACCOUNT PA
ON ES.ATTENDING_PATRON_ACCOUNT_ID = PA.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT PC
ON PA.PATRON_ACCOUNT_ID = PC.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_ADDRESS PCA
ON PC.PATRON_ACCOUNT_ID = PCA.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_EMAIL PCE
ON PCA.PATRON_ACCOUNT_ID = PCE.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_PHONE PCP
ON PCE.PATRON_ACCOUNT_ID = PCP.PATRON_ACCOUNT_ID
GROUP BY
EV.PUBLIC_DESCRIPTION
, EV.EVENT_DATE
, ES.PRICE
/* , BT.BUYER_TYPE_CODE */
, PCA.ADDR1
, PCA.ADDR2
, PCA.CITY
, PCA.POSTAL_CODE
, PCE.EMAIL
, PC.FORMATTED_NAME
, PCP.PHONE_NUMBER
, PCP.SECONDARY
/* check all these columns exist in the select clause
ORDER BY
ES.TRANSACTION_ID DESC
, PCP.SECONDARY DESC
, PCP.PHONE_NUMBER DESC
, PC.FORMATTED_NAME DESC
, PCE.EMAIL DESC
, PCA.POSTAL_CODE DESC
, PCA.CITY DESC
, PCA.ADDR2 DESC
, PCA.ADDR1 DESC
, BT.BUYER_TYPE_CODE DESC
, ES.PRICE DESC
*/
When you come the the final clause: ORDER BY you can ONLY reference columns that exist in the select clause. This example would FAIL
select column1 from table1 group by column1 order by fred
but this would work:
select column1 from table1 group by column1 order by column1
Your column aliases 'Paid Child', 'Free Child', 'Comps' should not be wrapped in single quotes. You should be using double quotes like you are already for "Adults".
So they should instead be:
"Paid Child"
"Free Child"
"Comps"
Or better yet, consider naming your aliases without any spaces, so you don't have to worry about wrapping the aliases in anything, like this:
paid_child
free_child
comps
Documentation on Database Object Names and Qualifiers:
Database Object Naming Rules
Every database object has a name. In a SQL statement, you represent the name of an object with a quoted identifier or a nonquoted identifier.
A quoted identifier begins and ends with double quotation marks ("). If you name a schema object using a quoted identifier, then you must use the double quotation marks whenever you refer to that object.
A nonquoted identifier is not surrounded by any punctuation.
...
Although column aliases, table aliases, usernames, and passwords are not objects or parts of objects, they must also follow these naming rules unless otherwise specified in the rules themselves.

Resources