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

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

Related

sum a value that appears on multiple rows ones per id in microsoft sql

I have a table that looks like below. In my query I'm sum:ing the order item value for each order item type, but I'm not sure how to summarize the providerfee per order id when the providerfee appears on multiple rows. Sum(providerfee) would give me 60 instead of 30.
Raw table (sample):
order id
order item type
order item value
providerfee
1
Product
300
30
1
Shipping
40
30
1
Invoicefee
30
30
Aggregated table if I would query above example table with the query I'm using now:
noOfOrders
productAmount
ShippingAmount
InvoiceAmount
providerfee
1
300
40
30
60
What value I wish to get in the providerfee column in my aggregated table instead:
noOfOrders
productAmount
ShippingAmount
InvoiceAmount
providerfee
1
300
40
30
30
Does anyone now best practice of fetching the right sum per order id from the providerfee-column? I think it won't make any sense to publish the actual query I'm writing but basically what I'm doing to fetch the amounts for order item types is:
with amounts as (
select
case when orderItemType= 'Product' then orderItemValue else 0 end as productAmount
,case when orderItemType = 'Shipping' then orderItemvalue else 0 end as shippingAmount
case when orderItemType = 'Fee' then orderItemvalue else 0 end as InvoicefeeAmount
,providerfee
from exampleTable
)
select
sum(amounts.productAmount) as productAmount
,sum(amounts.shippingAmount) as shippingAmount
,sum(amounts.invoicefeeAmount) as invoicefeeAmount
,sum(amounts.providerfee) as providerfeeAmount <---- This one gives me too high values since I'm summing every rows and providerfee appears on every row
from amounts
Here's a picture of how my actual table look like. Providerfee is supposed to be 638 just like invoiceAmount
One thing I also tried was a logic with row number function. But this gave me a result where three providerfees where missing. Instead of 638 I got 551. Can't see why since I counted in the raw table and got it to 638 myself. Not sure if I'm missing something logical with row number function that can have an impact on the SUM. Should I try another function to be able to pull out only one providerfee-row per order id?
with amounts as (
select
row_number() over (partition by orderId order by orderItemTimestamp DESC) as rn
,case when orderItemType= 'Product' then orderItemValue else 0 end as productAmount
,case when orderItemType = 'Shipping' then orderItemvalue else 0 end as shippingAmount
,case when orderItemType = 'Fee' then orderItemvalue else 0 end as InvoicefeeAmount
,providerfee
from exampleTable
)
select
sum(amounts.productAmount) as productAmount
,sum(amounts.shippingAmount) as shippingAmount
,sum(amounts.invoicefeeAmount) as invoicefeeAmount
,sum(amounts.providerfee) as providerfeeAmount <---- This one gives me too high values since I'm summing every rows and providerfee appears on every row
,sum(case when (amounts.providerfee is not null and amounts.rn = 1) then amounts.providerfee else 0 end) as providerfee2
from amounts
Grand total can be done using conditional aggregation. (Note that I here expect every Orderto have exactly one Invoicefee.)
select COUNT(distinct OrderId) as noOfOrders,
SUM(case when orderItemType = 'Product' then orderItemValue else 0 end) as productAmount,
SUM(case when orderItemType = 'Shipping' then orderItemvalue else 0 end) as shippingAmount,
SUM(case when orderItemType = 'Fee' then orderItemvalue else 0 end) as InvoicefeeAmount,
SUM(case when orderItemType = 'Fee' then providerfee else 0 end) as providerfee
from amounts
You can also add a GROUP BY, to get aggregation per order:
select OrderId,
SUM(case when orderItemType = 'Product' then orderItemValue else 0 end) as productAmount,
SUM(case when orderItemType = 'Shipping' then orderItemvalue else 0 end) as shippingAmount,
SUM(case when orderItemType = 'Fee' then orderItemvalue else 0 end) as InvoicefeeAmount,
SUM(case when orderItemType = 'Fee' then providerfee else 0 end) as providerfee
from amounts
group by OrderId

How to exclude 0 from count()? in sql?

I have a code as below where I want to count number of first purchases for a given period of time. I have a column in my sales table where if the buyer is not a first time buyer, then is_first_purchase = 0
For example:
buyer_id = 456391 is already an existing buyer who made purchases on 2 different dates.
Hence is_first_purchase column will show as 0 as per below.
If i do a count() on is_first_purchase for this buyer_id = 456391 then it should return 0 instead of 2.
My query is as follows:
with first_purchases as
(select *,
case when is_first_purchase = 1 then 'Yes' else 'No' end as first_purchase
from sales)
select
count(case when first_purchase = 'Yes' then 1 else 0 end) as no_of_first_purchases
from first_purchases
where buyer_id = 456391
and date_id between '2021-02-01' and '2021-03-01'
order by 1 desc;
It returned the below which is not an intended output
Appreciate if someone can help explain how to exclude is_first_purchase = 0 from the count, thanks.
Because COUNT function count when the value isn't NULL (include 0), if you don't want to count, need to let CASE WHEN return NULL
There are two ways you can count as your expectation, one is SUM other is COUNT but remove the part of else 0
SUM(case when first_purchase = 'Yes' then 1 else 0 end) as no_of_first_purchases
COUNT(case when first_purchase = 'Yes' then 1 end) as no_of_first_purchases
From your question, I would combine CTE and main query as below
select
COUNT(case when is_first_purchase = 1 then 1 end) as no_of_first_purchases
from sales
where buyer_id = 456391
and date_id between '2021-02-01' and '2021-03-01'
order by 1 desc;
I think that you are using COUNT() when you want SUM().
with first_purchases as
(select *,
case when is_first_purchase = 1 then 'Yes' else 'No' end as first_purchase
from sales)
select
SUM(case when first_purchase = 'Yes' then 1 else 0 end) as no_of_first_purchases
from first_purchases
where buyer_id = 456391
and date_id between '2021-02-01' and '2021-03-01'
order by 1 desc;
You could simplify your query as:
SELECT COUNT(*) AS
FROM sales no_of_first_purchases
WHERE is_first_purchase = 1
AND buyer_id = 456391
AND date_id BETWEEN '2021-02-01' AND '2021-03-01'
ORDER BY 1 DESC;
It is better to avoid the use of functions like IF and CASE when it can be done with WHERE.
The simplest approach for Trino (f.k.a. Presto SQL) is to use an aggregate with a filter:
count(name) FILTER (WHERE first_purchase = 'Yes') AS no_of_first_purchases

tsql - pivot sum of employee

I have here a sample table data which i want to use pivot and make my DedCode data into column.
the AMount example is already computed to its total but how can i count the number of employee if it is same with branch,deptcode and emptype.
my sample table data.
expected output:
You can use conditional aggregation:
select branch, deptcode, emptype, sum(empcount) as empcount,
sum(case when dedcode = 'PHIC' then amount else 0 end) as phic,
sum(case when dedcode = 'SLOAN' then amount else 0 end) as sloan,
sum(case when dedcode = 'VLOAN' then amount else 0 end) as vloan
from t
group by branch, deptcode, emptype;

SQL server Calculate with COUNT [duplicate]

This question already has an answer here:
How to use an Alias in a Calculation for Another Field
(1 answer)
Closed 4 years ago.
select
category, count(category) as 'TotalCounts',
COUNT(case kind when 'avail'then 1 else null end) as 'avail',
Count(case kind when 'offers' then 1 else null end) as 'offers',
COUNT(CASE contactMethod WHEN 'SMS' then 1 else null END) as 'SMS',
COUNT(case contactMethod when 'call' then 1 else null end) as 'call',
CONVERT(varchar(254),COUNT (case when max_biz_status='A' OR
max_biz_status ='B' then 1 else null end) * 100 / count(category)) +'%'
as 'Percetange'
from reports
group by category
order by TotalCounts
Instead of calculating again in Convert method i want to use avail* 100 / TotalCounts like i did in order by when i used TotalCounts.
i tried:
CONVERT(varchar(254),avail * 100 / TotalCounts) +'%' as 'Percetange'
but i get 'invalid column name' for avail and TotalCounts
You can't do that because your TotalCounts column is made from your result set.
you can try to use a subquery to contain it then calculation.
if your mssql version support CONCAT function you can use it let the SQL clearer.
SELECT t1.*,CONCAT((max_biz_statusCnt * 100 /TotalCounts),'%')as 'Percetange'
FROM
(
select
category,
count(*) as 'TotalCounts',
COUNT(case kind when 'avail'then 1 else null end) as 'avail',
Count(case kind when 'offers' then 1 else null end) as 'offers',
COUNT(CASE contactMethod WHEN 'SMS' then 1 else null END) as 'SMS',
COUNT(case contactMethod when 'call' then 1 else null end) as 'call',
COUNT (case when max_biz_status='A' OR max_biz_status ='B' then 1 else null end) 'max_biz_statusCnt'
from reports
group by category
) t1
order by TotalCounts
You can't use avail or TotalCounts as you just created them, so they aren't in scope, using a common-table expression is one way to fix this:
WITH cte AS (
SELECT
category,
COUNT(category) AS TotalCounts,
COUNT(case kind WHEN 'avail' THEN 1 ELSE NULL END) AS avail,
COUNT(case kind WHEN 'offers' THEN 1 ELSE NULL END) AS offers,
COUNT(CASE contactMethod WHEN 'SMS' THEN 1 ELSE NULL END) AS SMS,
COUNT(case contactMethod WHEN 'call' THEN 1 ELSE NULL END) AS [call]
FROM
reports
GROUP BY
category)
SELECT
*,
CONVERT(varchar(254),avail * 100 / TotalCounts) +'%' AS Percetange --(sic)
FROM
cte
ORDER BY
TotalCounts;

Oracle SQL Count with join

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.