Oracle SQL Count with join - sql

I am trying to count Orders and Order lines dependent upon order type. So far i have counted the Order headers per order type.
SELECT lifnr,
COUNT(CASE WHEN e1.bsart = 'NB' THEN 1 ELSE NULL END) Schedule_Orders,
COUNT(CASE WHEN e1.bsart = 'ZNBS' THEN 1 ELSE NULL END) Store_Orders,
COUNT(CASE WHEN e1.bsart = 'ZCSO' THEN 1 ELSE NULL END) Third_Party,
COUNT(CASE WHEN e1.bsart NOT IN ('ZCSO', 'NB', 'ZNBS' ) THEN 1 ELSE NULL END) Other_Orders,
COUNT(*) Total_Orders
FROM saprpe.ekko e1
JOIN saprpe.ekpo p1
ON e1.ebeln = p1.ebeln
WHERE e1.aedat BETWEEN '20130701' AND '20140701'
GROUP BY lifnr
Order Lines are stored in table EKPO so i have joined EKKO to EKPO using the document number. How do i go about counting order lines for each of the order types on the case above? I've only managed to count ALL order lines, not specifically for each order type.
Extra Info: An Order header is stored in EKKO which contains a Vendor, an OrderNumber and an OrderType. EKKO has a 1-to-many relationship with table EKPO which contains Order lines. Order lines relates to Order Header through the Order Number.
What i am trying to do is get the total order headers for the three order types NB, ZNBS and ZCSO, and the total lines for each order type. All into their own columns
For example I would want to see;
Vendor1, 10 NB orders, 100 NB Order Lines, 0 ZNBS Orders, 0 ZNBS Order Lines, 5 ZCSO Orders, 15 ZCSO Order Lines, 15 Total Orders, 115 Total Order Lines
Please point out if i need to clarify anything. I should note i would prefer this in a single SQL query, i can manage it on an individual query basis for each Order Type using the WHERE clause.

After some trial and error, googling etc, I eventually got there.
SELECT
lifnr,
COUNT(DISTINCT (CASE WHEN e1.bsart = 'NB' THEN e1.ebeln ELSE NULL END)) Schedule_Orders,
COUNT(CASE WHEN e1.bsart = 'NB' THEN e1.ebeln ELSE NULL END) Schedule_Order_Lines,
COUNT(DISTINCT (CASE WHEN e1.bsart = 'ZNBS' THEN e1.ebeln ELSE NULL END)) Store_Orders,
COUNT(CASE WHEN e1.bsart = 'ZNBS' THEN e1.ebeln ELSE NULL END) Store_Order_Lines,
COUNT(DISTINCT (CASE WHEN e1.bsart = 'ZCSO' THEN e1.ebeln ELSE NULL END)) Third_Party,
COUNT(CASE WHEN e1.bsart = 'ZCSO' THEN e1.ebeln ELSE NULL END) Third_Party_Lines,
COUNT(DISTINCT (CASE WHEN e1.bsart NOT IN ('ZCSO', 'NB', 'ZNBS' ) THEN e1.ebeln ELSE NULL END)) Other_Orders,
COUNT(CASE WHEN e1.bsart NOT IN ('ZCSO', 'NB', 'ZNBS') THEN e1.ebeln ELSE NULL END) Other_Order_Lines,
COUNT(DISTINCT(e1.ebeln)) Total_Orders,
COUNT(e1.ebeln) Total_Order_Lines
FROM saprpe.ekko e1
LEFT JOIN saprpe.ekpo p1
ON e1.ebeln = p1.ebeln
WHERE e1.aedat BETWEEN '20130701' AND '20140701'
GROUP BY lifnr
The JOIN originally was making my header count the same count as the lines because of the relationship between the tables... Adding DISTINCT to my header counts and using a case for the line counts enabled me to count for those order types.

Related

Use a calculated column value to look up a specific value from the same source table

I have a table in SQL Server called ShippingDocSummary which contains all historical records for shipping documents. For a given document number, there can be any number of historical records/transactions. What I am trying to do is create a view that summarizes that table and calculates various column values. My issue comes when trying to get a specific value from the source table based on a calculated column value from the same source table.
Here is a simplified example of what the source table looks like:
DocNum DocHistID StatusCode StatusQty StatusDate
12345 10000001 AS1 2 2/16/2020
12345 10000002 D6T 2 4/20/2020
12345 10000003 COR 2 4/20/2020
12345 10000004 AS1 2 5/5/2020
My code to create my summary so far is as follows:
SELECT
DocNum,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_' THEN StatusQty ELSE 0 END) AS AS_QTY,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'D6T' THEN StatusQty ELSE 0 END) AS D6T_QTY,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'COR' THEN StatusQty ELSE 0 END) AS COR_QTY,
MAX(CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_' THEN DocHistID ELSE null END) AS LastAS_DHID
FROM ShippingDocSummary
GROUP BY DocNum
This gives me the following, which is correct:
DocNum AS_QTY D6T_QTY COR_QTY LastAS_DHID
12345 4 2 2 10000004
What I am trying to get next, as a column, is the StatusDate that correlates to the LastAS_DHID value that was calculated from the select (i.e. I need the next column to show '5/5/2020' since that date ties to the DocHistID '10000004').
In Power Bi, I'd use a lookupvalue function, but as I transfer my logic to the SQL server, I think I need to use a derived CASE WHEN type of query, but unsure of how to structure the syntax. Any help is greatly appreciated!
Maybe you have to create your on table and then do a left join. This being said, Try this:
SELECT A.DocNum,
A.AS_QTY,
A.D6T_QTY,
A.COR_QTY,
A.LastAS_DHID,
B.StatusDate
FROM (
SELECT DocNum,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_' THEN StatusQty ELSE 0 END) AS AS_QTY,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'D6T' THEN StatusQty ELSE 0 END) AS D6T_QTY,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'COR' THEN StatusQty ELSE 0 END) AS COR_QTY,
MAX(CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_' THEN DocHistID ELSE null END) AS LastAS_DHID
FROM ShippingDocSummary
GROUP BY DocNum) A
LEFT JOIN TABLE_WITH_DOCHIST_10000004 B
ON A.LastAS_DHID = B.DocHistID
SELECT DISTINCT
DocNum,
SUM(CASE WHEN StatusCode LIKE 'AS_' THEN StatusQty ELSE 0 END) OVER (PARTITION BY DocNum) AS AS_QTY,
SUM(CASE WHEN StatusCode LIKE 'D6T' THEN StatusQty ELSE 0 END) OVER (PARTITION BY DocNum) AS D6T_QTY,
SUM(CASE WHEN StatusCode LIKE 'COR' THEN StatusQty ELSE 0 END) OVER (PARTITION BY DocNum) AS COR_QTY,
FIRST_VALUE(StatusDate) OVER (PARTITION BY DocNum ORDER BY CASE WHEN StatusCode LIKE 'AS_' THEN DocHistID ELSE null END DESC) AS LastAS_DHID
FROM ShippingDocSummary
I've converted your aggregate functions to windowed versions with partitioning on the original grouping column. The select distinct is then necessary to actually collapse the rows to a single aggregated row. If first_value() isn't available on your platform there are other alternatives.
Here's one that worked for me as a scalar subquery:
(
SELECT StatusDate FROM ShippingDocSummary sds2
WHERE sds2.DocHistId =
MAX(
CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_'
THEN ShippingDocSummary.DocHistID ELSE null END
)
) AS LastAS_DHID

SUM value when another column value is DISTINCT

I was wondering how I can SUM the values of a column based on another column's values being distinct like below. I tried the following two ways, each giving errors due to the aggregate function. I am trying to get NonDistinctTotals with the queries below.
SELECT SUM(InvoiceSaleAmt) AS NonDistinctTotals, SUM(case when count(*) over (partition by InvoiceNo) = 1 then InvoiceSaleAmt else 0 END) as DistinctTotals, SUM(CASE WHEN PaymentType= 'CASH' THEN CashTotal ELSE 0 END) AS CashTotal
FROM #InvoiceTable a
group by LocationId, InvoiceNo
Error: Windowed functions cannot be used in the context of another windowed function or aggregate.
SELECT SUM(InvoiceSaleAmt) AS NonDistinctTotals, SUM(CASE WHEN InvoiceNoin (SELECT DISTINCT InvoiceNofrom #InvoiceTable) THEN InvoiceSaleAmt else 0 END) as DistinctTotals, SUM(CASE WHEN PaymentType= 'CASH' THEN CashTotal ELSE 0 END) AS CashTotal
FROM #InvoiceTable a
group by LocationId, InvoiceNo
Error: Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
Use a subquery:
SELECT SUM(InvoiceSaleAmt) AS NonDistinctTotals,
SUM(case when cnt = 1 then InvoiceSaleAmt else 0 END) as DistinctTotals,
SUM(CASE WHEN PaymentType = 'CASH' THEN CashTotal ELSE 0 END) AS CashTotal
FROM (SELECT it.*, COUNT(*) over (partition by InvoiceNo) as cnt
FROM #InvoiceTable it
) it
GROUP BY LocationId, InvoiceNo

Case Statement having no effect on output

I am trying to get sums of donations based on the bank approval status and grouped by gift kind. However, it the script outputs RR and NR donations on separate lines (see below the script). It seems like the case statements aren't working at all.
select gift_kind,
case
when c.bank_approval_status = 'AP' then
sum(c.charge_amount) end approved,
case
when c.bank_approval_status in ('RR','NR') then
sum(c.charge_amount) end rejected,
case
when c.bank_approval_status = 'AR' then
sum(c.charge_amount)*-1 end refunded,
case
when c.bank_approval_status not in ('AR','AP','RR','NR') then
sum(c.charge_amount) end other_status
from charge_log c, transactions t
where c.account_id=t.account_id
and c.process_id= 'CHG - 02532'
and c.gift_date=t.gift_date
and c.gift_seq=t.gift_seq
and C.PLEDGE_NUMBER=t.pledge_number
and t.sts='A'
group by t.fund_type, t.gift_kind, c.bank_approval_status
order by gift_kind asc
I believe you are looking for this logic:
select gift_kind,
sum(case when c.bank_approval_status = 'AP' then c.charge_amount
end) as approved,
sum(case when c.bank_approval_status in ('RR', 'NR') then c.charge_amount
end) as rejected,
sum(case when c.bank_approval_status in ('AR') then c.charge_amount*-1
end) as refunded,
sum(case when c.bank_approval_status not in ('AR','AP','RR','NR') then c.charge_amount
end) other_status
from charge_log c join
transactions t
on c.account_id = t.account_id and
c.gift_date = t.gift_date and
c.gift_seq = t.gift_seq and
C.PLEDGE_NUMBER = t.pledge_number
where c.process_id = 'CHG - 02532' and t.sts = 'A'
group by t.fund_type, t.gift_kind
order by gift_kind asc
Notes:
Learn to use proper JOIN syntax. Never use commas in the FROM clause.
The JOIN conditions should all be in the ON clause, not the WHERE clause.
The CASE is an argument to the SUM().
Remove the bank_approval_status from the GROUP BY.
I don't know why fund_type is in the GROUP BY. You may have a reason for that so I left it.
You need to put the case clauses inside the sums, and add else 0 to make sure you don't get null as a result:
sum(case
when c.bank_approval_status = 'AP' then
c.charge_amount else 0 end) approved,
sum(case
when c.bank_approval_status in ('RR','NR') then
c.charge_amount else 0 end) rejected,
sum(case
when c.bank_approval_status = 'AR' then
-c.charge_amount else 0 end) refunded,
sum(case
when c.bank_approval_status not in ('AR','AP','RR','NR') then
c.charge_amount else 0 end) other_status
And your group by should rarely be on columns you use in aggregations (here bank_approval_status in the sum). Change to:
group by gift_kind
Depending on your scheme, other fields might need to be added to the group by clause but then it would make sense to also put them in the select and order by clauses.

Trying to group several rows based on donatedmoney status

I'm really struggling with this. I just can't seem to figure it out. I've got the concept in my head but don't exactly know how to put my plain language understanding of how to solve the problem into the correct Syntax.
Here is the question.
Give me a list of all donors and their addresses categorized by whether they donated art, money, or both.
Here is the set up for the tables.
CareTakers: CareTakerID, CareTakerName
Donations: DonationID, DonorID, DonatedMoney, ArtName, ArtType, ArtAppraisedPrice, ArtLocationBuilding, ArtLocationRoom, CareTakerID
Donors: DonorID, DonorName, DonorAddress
Here is what I have for my code so far.
SELECT
DISTINCT(DonorName), DonorAddress
FROM
Donors JOIN Donations ON Donors.DonorID = Donations.DonorID
GROUP BY
DonatedMoney
HAVING
DonatedMoney = 'Y' OR DonatedMoney = 'N' OR DonatedMoney = 'Y' AND ArtName IS NOT NULL
Any help would be highly appreciated!
Why would you use a having clause? The question specifies no filtering. The following summarizes the donations to get what you need and then joins the results back to the donors table:
select d.*, don.DonationType
from donors d join
(select don.donorid,
(case when sum(case when donatedmoney = 'Y' then 1 else 0 end) > 0 and
sum(case when artname is not null then 1 else 0 end) > 0
then 'Both'
when sum(case when donatedmoney = 'Y' then 1 else 0 end) > 0
then 'Money'
when sum(case when artname is not null then 1 else 0 end)
then 'Art'
else 'Neither'
end) as DonationType
from donations don
group by don.donorid
) don
on d.donorid = don.donorid

Merging data SQL Query

I have a query request where I have to show one customer activity for each web-site but it has to be only one row each, instead of one customer showing multiple times for each activity.
Following is the query I tried but brings lot more rows. please help me as how I can avoid duplicates and show only one customer by each row for each activity.
SELECT i.customer_id, i.SEGMENT AS Pistachio_segment,
(CASE when S.SUBSCRIPTION_TYPE = '5' then 'Y' else 'N' end ) PB_SUBS
(CASE WHEN S.SUBSCRIPTION_TYPE ='12' THEN 'Y' ELSE 'N' END) Daily_test,
(CASE when S.SUBSCRIPTION_TYPE ='8' then 'Y' else 'N' end) COOK_4_2
FROM IDEN_WITH_MAIL_ID i JOIN CUSTOMER_SUBSCRIPTION_FCT S
ON I.IDENTITY_ID = S.IDENTITY_ID and I.CUSTOMER_ID = S.CUSTOMER_ID
WHERE s.site_code ='PB' and s.subscription_end_date is null
Sounds like you need to group by customer_id and perform aggregations for the other columns you are selecting. For example:
sum(case when s.subscription_type = '5' then 1 else 0 end) as pb_subs_count
You could try one of two things:
Use a GROUP BY statement to combine all records with the same id, e.g.,
...
WHERE s.site_code ='PB' and s.subscription_end_date is null
GROUP BY i.customer_id
Use the DISTINCT command in your SELECT, e.g.,
SELECT DISTINCT i.customer_id, i.SEGMENT, ...
you could use a aggregation (SUM) on customer_id, but what do you expect to happen on the other fields? for example, if you have SUBSCRIPTION_TYPE 5 and 13 for the same customer (2 rows), which value do you want?
Perhaps you are looking for something like this:
SELECT i.customer_id, i.SEGMENT AS Pistachio_segment,
MAX(CASE when S.SUBSCRIPTION_TYPE = '5' then 'Y' else 'N' end ) PB_SUBS
MAX(CASE WHEN S.SUBSCRIPTION_TYPE ='12' THEN 'Y' ELSE 'N' END) Daily_test,
MAX(CASE when S.SUBSCRIPTION_TYPE ='8' then 'Y' else 'N' end) COOK_4_2
FROM IDEN_WITH_MAIL_ID i JOIN CUSTOMER_SUBSCRIPTION_FCT S
ON I.IDENTITY_ID = S.IDENTITY_ID and I.CUSTOMER_ID = S.CUSTOMER_ID
WHERE s.site_code ='PB' and s.subscription_end_date is null
GROUP BY i.customer_id, i.SEGMENT
I can't be sure, though, without knowing more about the tables involved.