Supply chain SQL query - sql

I am having issues with my SQL code.
I want to display the total count of stores that correspond with a certain Distribution Center/ WH. I want both to be tied to a certain item.
For example: I have one WH that gave a certain item to 50 stores. I want the query to tell me if I give it the item# and WH# it will give me the amount of stores that received that item.
SELECT
COUNT(*) AS TOTAL_STORES
FROM
(SELECT
a.WH_i, a.STORE_i
FROM
WH a, STORES b
WHERE
a.item_i = 2201
AND a.WH_i IN (10)
GROUP BY
a.WH_i, B.STORE_i
HAVING
COUNT(a.item_i) = 1) a;
Table WH has the warehouse numbers and item numbers and store has the store numbers.
I am new to SQL so I am not 100% confident with joins just yet. Any help is greatly appreciated though!
EDIT: I tried joining the two tables without actually using the JOIN clause and it is still not giving proper results.
SELECT COUNT(*) AS TOTAL_STORES
FROM(
SELECT a.WH_i, b.STORE_i
FROM WH a, STORES b
WHERE a.item_i = b.sku_i
AND a.stock_i = b.stock_i
AND a.item_i = 2201
AND a.WH_i IN (10)
GROUP BY a.WH_i, B.STORE_i
HAVING COUNT(a.item_i) = 1
)a;

You only want the count of stores so just pull stores in your subquery.
SELECT COUNT(STORE_i) AS TOTAL_STORES
FROM(
SELECT b.STORE_i
FROM WH a
JOIN STORES b
ON a.item_i = b.sku_i
AND a.stock_i = b.stock_i
WHERE
a.item_i = 2201
AND a.WH_i = 10
GROUP BY b.STORE_i
HAVING COUNT(a.item_i) = 1
) a;

SELECT a.WH_i, a.item_i, count(DISTINCT b.store_i)
FROM WH a
JOIN STORES b
ON a.item_i = b.sku_i
GROUP BY a.WH_i, a.item_i

Related

Joining multiple CTEs

I am working on a database of a large retail store.
I have to query data from multiple tables to get numbers such as revenue, raw proceeds and compare different time periods.
Most of it is quite easy but I was struggling to work out a way of joining multiple CTEs.
I made a fiddle so you know what I am talking about.
I simplified the structure a lot and left out quite a few columns in the subqueries because they do not matter in this case.
As you can see every row in every table has country and brand in it.
The final query has to be grouped by those.
What I first tried was to FULL JOIN all the tables, but that didn't work in some cases as you can see here: SQLfiddle #1. Note the two last rows which did not group correctly.
Select Coalesce(incoming.country, revenue.country, revcompare.country,
openord.country) As country,
Coalesce(incoming.brand, revenue.brand, revcompare.brand,
openord.brand) As brand,
incoming.OrdersNet,
openord.OpenOrdersNet,
revenue.Revenue,
revenue.RawProceeds,
revcompare.RevenueCompare,
revcompare.RawProceedsCompare
From incoming
Full Join openord On openord.country = incoming.country And
openord.brand = incoming.brand
Full Join revenue On revenue.country = incoming.country And
revenue.brand = incoming.brand
Full Join revcompare On revcompare.country = incoming.country And
revcompare.brand = incoming.brand
Group By incoming.OrdersNet,
openord.OpenOrdersNet,
revenue.Revenue,
revenue.RawProceeds,
revcompare.RevenueCompare,
revcompare.RawProceedsCompare,
incoming.country,
revenue.country,
openord.country,
revcompare.country,
incoming.brand,
revenue.brand,
revcompare.brand,
openord.brand
Order By country,
brand
I then rewrote the query keeping all the CTEs. I added another CTE (basis) which UNIONs all the possible country and brand combinations and left joined on that one.
Now it works fine (check it out here -> SQLfiddle #2) but it just seems so complicated. Isn't there an easier way to achieve this? The only thing I probably won't be able to change are the CTEs as in real life they are way more complex.
WITH basis AS (
SELECT Country, Brand FROM incoming
UNION
SELECT Country, Brand FROM openord
UNION
SELECT Country, Brand FROM revenue
UNION
SELECT Country, Brand FROM revcompare
)
SELECT
basis.Country,
basis.Brand,
incoming.OrdersNet,
openord.OpenOrdersNet,
revenue.Revenue,
revenue.RawProceeds,
revcompare.RevenueCompare,
revcompare.RawProceedsCompare
FROM basis
LEFT JOIN incoming On incoming.Country = basis.Country AND incoming.Brand = basis.Brand
LEFT JOIN openord On openord.Country = basis.Country AND openord.Brand = basis.Brand
LEFT JOIN revenue On revenue.Country = basis.Country AND revenue.Brand = basis.Brand
LEFT JOIN revcompare On revcompare.Country = basis.Country AND revcompare.Brand = basis.Brand
Thank you all for your help!
Since you only work with two tables, orders and rev, consider conditional aggregation by moving WHERE conditions to CASE logic for single aggregate query. Also, consider only one CTE for all possible country/brand pairs for LEFT JOIN on the two tables.
WITH cb AS (
SELECT Country, Brand FROM orders
UNION
SELECT Country, Brand FROM rev
)
SELECT cb.Country
, cb.Brand
, SUM(o.netprice) AS OrdersNet
, SUM(CASE
WHEN o.isopen = 1
THEN o.netprice
END) AS OpenOrdersNet
, SUM(CASE
WHEN r.bdate BETWEEN '2020-12-01' AND '2020-12-31'
THEN r.netprice
END) AS Revenue
, SUM(CASE
WHEN r.bdate BETWEEN '2020-12-01' AND '2020-12-31'
THEN r.rpro
END) AS RawProceeds
, SUM(CASE
WHEN r.bdate BETWEEN '2020-11-01' AND '2020-11-30'
THEN r.netprice
END) AS RevenueCompare
, SUM(CASE
WHEN r.bdate BETWEEN '2020-11-01' AND '2020-11-30'
THEN r.rpro
END) AS RawProceedsCompare
FROM cb
LEFT JOIN orders o
ON cb.Country = o.Country
AND cb.Brand = o.Brand
LEFT JOIN rev r
ON cb.Country = r.Country
AND cb.Brand = r.Brand
GROUP BY cb.Country
, cb.Brand
SQL Fiddle

How to use 1 SQL query related to date and time in order to compare value difference

SELECT c.treatment_category, a.treatment_id, MAX(a.counts - b.counts) AS ReviewDifference
FROM
(SELECT treatment_id, COUNT(treatment_id) AS counts
FROM review
WHERE DATE(review.created) BETWEEN DATE(TIMESTAMP'2016-01-01 00:00:00.0') AND DATE(TIMESTAMP'2016-12-31 23:59:59.999')
GROUP BY treatment_id) a
LEFT JOIN
(SELECT treatment_id, COUNT(treatment_id)
FROM review
WHERE DATE(review.created) BETWEEN DATE(TIMESTAMP'2015-01-01 00:00:00.0') AND DATE(TIMESTAMP'2015-12-31 23:59:59.999')
GROUP BY treatment_id) b
ON a = b
LEFT JOIN
(SELECT t.treatment_category AS category, r.treatment_id AS number
FROM treatment t
LEFT JOIN review r
ON t.treatment_id = r.treatment_id
GROUP BY category, number) c
ON b.treatment_id = c.number
GROUP BY a.treatment_id, c.treatment_category
ORDER BY ReviewDifference DESC
LIMIT 1;
I need some hints or simpler query on how to do this question since it is related to date and time. Thank you.
What treatment category has seen the biggest increase in reviews from 2015 to 2016?
Please see below for the tables.
I have provided my code snippet and I would like to find a simpler and cleaner way on writing the code.
SELECT t.treatment_id, t.treatment_name,
COUNT( CASE WHEN YEAR(created) = 2016 THEN r.review_id END)
- COUNT( CASE WHEN YEAR(created) = 2015 THEN r.review_id END) as review_count
FROM treatments t
JOIN reviews r
ON t.treatment_id = r.treatment_id
GROUP BY t.treatment_id, t.treatment_name,
ORDER BY review_count DESC

By store (number), list the maximum number of store visits made by a customer

The three tables that I'm linking are item_scan_fact, member_dimension and store_dimension. So far this is what I have:
SELECT
store_dimension.store_number,
member_dimension.member_number
COUNT (item_scan_fact.visit_number) AS NumVisits
FROM
member_dimension,
item_scan_fact
INNER JOIN store_dimension
ON item_scan_fact.member_key = member_dimension.member_key
AND item_scan_fact.store_key = store_dimension.store_key
GROUP BY
store_dimension.store_number,
member_dimension.member_number, NumVisits;
On the surface it appears solvable with a couple Common Table Expressions
Does this help point you in the right direction?
WITH s1 -- JJAUSSI: Find the visit_number_count by member_key and store_key
AS
(SELECT isf.member_key
,isf.store_key
-- JJAUSSI: DISTINCT resolves a potential 1:N (one to many) relationship here
,COUNT( DISTINCT isf.visit_number) AS visit_number_count
FROM item_scan_fact isf
GROUP BY isf.member_key
,isf.store_key),
s2 -- JJAUSSI: Find the visit_number_count_max by member_key
AS
(SELECT s1.member_key
,MAX(s1.visit_number_count) AS visit_number_count_max
FROM s1
GROUP BY s1.member_key)
-- JJAUSSI: Use this version to see the list of store_key values
-- that have the visit_number_count_max value. This has the potential
-- to be a 1:N relationship.
SELECT s1.member_key
,md.member_number
,s1.store_key
,sd.store_number
,s1.visit_number_count
FROM s2 INNER JOIN s1
ON s2.member_key = s1.member_key
AND s2.visit_number_count_max = s1.visit_number_count
INNER JOIN store_dimension sd
ON sd.store_key = s1.store_key
INNER JOIN member_dimension md
ON md.member_key = s1.member_key;
If this is what you were going for...congratulations! On to the next query!
If you ultimately are after a single store_key response for each member_key (basically you want to determine the member_key's "primary" store_key) then an additional step is probably needed (depending on your data).
Here are some ideas:
Evaluate the member_key based on some other summable facet of
item_scan_fact (like total price paid?)
If you consider all store_key values of equal merit that have the same visit_number_count_max value for a given member_key, just choose a store_key with MAX or MIN
You would seem to want:
SELECT member_number, MAX(NumVisits)
FROM (SELECT sd.store_number, md.member_number
COUNT(*) AS NumVisits
FROM member_dimension md JOIN
item_scan_fact isf
ON md.member_key = isf.member_key JOIN
store_dimension sd
ON isf.store_key = sd. store_key
GROUP BY sd.store_number, md.member_number
) sm
GROUP BY member_number;
If you want to return both the max and the matching customer number you can apply a Teradata SQL extension, qualify:
SELECT sd.store_number, md.member_number
COUNT(*) AS NumVisits
FROM member_dimension md JOIN
item_scan_fact isf
ON md.member_key = isf.member_key JOIN
store_dimension sd
ON isf.store_key = sd. store_key
GROUP BY sd.store_number, md.member_number
QUALIFY
rank() -- might return multiple rows with the same max, ROW_NUMBER a single row
over (partition by sd.store_number
order by NumVisits desc) = 1

Select only rows mith minimum cost

I need to pull back a set of data for a view where only rows with the the minimum cost are returned. I am joining 3 tables and they are big tables (225 million records a piece give or take) so performance is essential.
SELECT RIC.CarrierName, L.LoadGuid, RIC.RateIQCarrierid, RIRD.Cost
FROM tblLoads L
INNER JOIN RateIQRecord RIR ON L.LoadGuid = RIR.LoadGuId
INNER JOIN RateIQCarrier RIC ON RIR.RateIQRecordID = RIC.RateIQRecordID
INNER JOIN RateIQRateDetail RIRD ON RIC.RateIQRecordID = RIRD.RateIQRecordID
AND CAST(L.CreatedDate AS Datetime) Between '03/3/2014' and '03/3/2014 23:59:59.997'
Here is an example of the data set based of the code above
CarrierName LoadGuid Carrierid Cost
Carrier a FF98010A-90CE-4541-AB88-683645352712 210677951 192.51
Carrier a FF98010A-90CE-4541-AB88-683645352712 210677921 153.17
Carrier b FF98010A-90CE-4541-AB88-683645352712 210677925 196.28
Carrier b FF98010A-90CE-4541-AB88-683645352712 210677947 280.65
Carrier b FF98010A-90CE-4541-AB88-683645352712 210677949 241.71
Here is what I need:
CarrierName LoadGuid Carrierid Cost
Carrier a FF98010A-90CE-4541-AB88-683645352712 210677921 153.17
Carrier b FF98010A-90CE-4541-AB88-683645352712 210677925 196.28
Try this out:
Note:I'm assuming you have SQL Server 2008 or above. ROW_NUMBER() won;t work otherwise.
SELECT *
FROM
(
SELECT RIC.CarrierName,
L.LoadGuid,
RIC.RateIQCarrierid,
RIRD.Cost,
--Partition says look at each carrier as a group, then number them in order of cost lowest to highest.
ROW_NUMBER() OVER (PARTITION BY RIC.CarrierName ORDER BY Cost) rank_num
FROM tblLoads L
INNER JOIN RateIQRecord RIR
ON L.LoadGuid = RIR.LoadGuId
INNER JOIN RateIQCarrier RIC
ON RIR.RateIQRecordID = RIC.RateIQRecordID
INNER JOIN RateIQRateDetail RIRD
ON RIC.RateIQRecordID = RIRD.RateIQRecordID
--Don't do it this way
--AND CAST(L.CreatedDate AS Datetime) Between '03/3/2014' and '03/3/2014 23:59:59.997'
--Try this instead
AND CAST(L.CreatedDate AS DATE) = '03/03/2014'
) A
--Only grab the lowest number aka first of row number
WHERE A.rank_num = 1

Group on two columns and calculate net total SQL Query

I am finding it difficult to modify an existing SQL query to group records on two columns. Following is the query which retrieves records from multiple tables by joining:
SELECT
InM.invm_No AS DocNo,
InM.docs_DocCode,
D.doctyp_Desc AS DocType,
L.loc_Desc AS Site,
InM.sup_Code AS Supplier,
InD.invd_Qty,
InD.invd_Rate
FROM
[SI_InventoryMaster] InM
INNER JOIN
[SI_DocType] AS D ON InM.doctyp_Code = D.doctyp_Code
INNER JOIN
SI_Supplier S ON InM.sup_Code = S.sup_Code
INNER JOIN
[SI_InventoryDetail] AS InD ON InD.invm_No = InM.invm_No
AND InM.invm_verified = 1
AND InM.docs_DocCode = 'GRN'
AND MONTH (InM.invm_Date) = 12
AND YEAR(InM.invm_date) = 2013
INNER JOIN
SI_Location L ON InD.loc_code = L.loc_Code
ORDER BY
InM.invm_No
Which results the following:
I need to group records "by DocNo by Site" and find total amount for each site. Total amount will be last column in this resultset and it is net of product of InD.invd_Qty and InD.invd_Rate for each site.
For DocNo: 00000030, there are 4 records of CWS Store Site, so its total amount would be calculated as: 7684.999+7684.999+3000+3000=21369.998
In this particular example, modified query should return following resultset:
Please help.
Thanks.
Try grouping on all the columns associated with those two columns. Something like:
SELECT InM.invm_No AS DocNo, InM.docs_DocCode, D.doctyp_Desc AS DocType,
L.loc_Desc AS Site, InM.sup_Code AS Supplier,
sum(InD.invd_Qty),
sum(InD.invd_Rate)
FROM [SI_InventoryMaster] InM INNER JOIN
[SI_DocType] AS D
ON InM.doctyp_Code = D.doctyp_Code INNER JOIN
SI_Supplier S
ON InM.sup_Code = S.sup_Code INNER JOIN
[SI_InventoryDetail] AS InD
ON InD.invm_No = InM.invm_No AND
InM.invm_verified = 1 AND
InM.docs_DocCode = 'GRN' AND
MONTH (InM.invm_Date) = 12 AND
YEAR(InM.invm_date) = 2013 INNER JOIN
SI_Location L
ON InD.loc_code = L.loc_Code
GROUP BY InM.invm_No AS DocNo, InM.docs_DocCode, D.doctyp_Desc AS DocType,
L.loc_Desc AS Site, InM.sup_Code AS Supplier;