SQL query for combining data from two sources in specified logic - sql

I have three tables in my SQL server setup.
Two of the tables are given below:
Product Substitute
XXX ABC
YYY XYZ
Table two is similar to table one but lists alternates.
Product Alternate
XXX OPPP
YYY FTTT
And I have a master table with the quantities of all the products (replacements and alternates included):
Product Quantity
XXX 52
YYY 84
ABC 180
XYZ 220
OPPP 590
FTTT 760
Now I need an SQL query to list the total parts available for each main part number. For example, the output query should look like:
Product Quantity
XXX 822
YYY 1064
That is the total amount of XXX = total of XXX + total of ABC + total of OPPP.

You first get all the data you need (inner query with union all).
Then you group the whole set of data by Product.
Select Product, Sum(Quantity)
From (
Select Product, Quantity From master
Union All
Select a.Product, m.Quantity From master as m
Inner join alternates as a on A.alternate = m.product
Union All
Select s.Product, m.Quantity From master as m
Inner join Substitute as s on s.Substitute = m.product
) as p
Group By Product

Related

How to avoid aggregate functions in recursive query's recursive term

I am having trouble getting around a sum() in the recursive term. Basically my problem is this.
Lets say 3 different finish products. 'ABC1', 'ABC2', 'ABC3' every one of them is made from 'ABC'. Every 'ABC' is made from 'AB'. Every 'AB' is made from 'A'. I went out and sold 10 of each 'ABC1', 'ABC2', 'ABC3'
I am trying to make a query give me a list of each item and how much I need of that item based on how much I have sold.
This is an example of the return that I am looking for
Item
Level
Sold
On Hand
Required
A
0
0
0
15
AB
1
0
10
25
ABC
2
10
0
25
ABC1
3
10
5
10
ABC2
3
10
5
10
ABC3
3
10
5
10
For a general table structure you would have
Item
item_id
item_onhand
AND
BOM
bom_product_id
bom_material_id
AND
Sales
sale_id
sale_item_id
sale_qty
I cant start at the top and go down in my case. because the dataset takes too long to process. So I have to start with all the sales and work up the tree from there.
My idea was to create a result for each level.
And then recursively go up the material tree. Something along the lines of
WITH RECURSIVE sales_req AS(
SELECT item_id,
SUM(sale_qty) AS sales_req_sold,
item_onhand AS sales_req_qoh
FROM sales JOIN item ON sales_item_id = item_id
GROUP BY item_id
UNION
SELECT
item_id,
SUM(sales_req_sold - sales_req_qoh),
item_onhand
FROM
bom
JOIN sales_req ON bom_product_id = sales_req.item_id
JOIN item mat ON bom_material_id = mat.item_id
WHERE sales_req_sold > sales_req_qoh
The first Query Returning Something Like this
Item
Required
ABC
10
ABC1
10
ABC2
10
ABC3
10
And The recursive portion returning something like this
Item
Required
Notes
ABC
15
( The sum of sales for "ABC1,ABC2,ABC3" minus the inventory for each one)
AB
25
( The sum of ABC requirements from 1,2 and 3 Plus the requirement for the sale of ABC)
A
15
( AB Minus the inventory on hand for AB)
I need some sort of alternate solution to sum function. However there are a few constraints. I have to start with the sales table. I cannot put a limit on the levels. In this example I have 4 levels and only one level has multiple parts on it. But there could be 7 levels and each level could have 3 parts on it. I can assume the top level to be 1 single item.
try this :
WITH RECURSIVE req AS(
SELECT item_id, item_onhand, SUM(sale_qty) AS item_sales
FROM sales INNER JOIN item ON sale_item_id = item_id
GROUP BY item_id, item_onhand
), accum (item_id, item_onhand, item_sales, item_req, level) AS (
SELECT item_id, item_onhand, item_sales, item_sales, 0
FROM req
UNION ALL
SELECT b.bom_product_id, a.item_onhand, a.item_sales, a.item_sales - a.item_onhand, a.level - 1
FROM accum AS a
INNER JOIN bom AS b ON b.bom_material_id = a.item_id
)
SELECT r.item_id, min(a.level) AS level, r.item_onhand AS on_hand, r.item_sales AS sold, sum(item_req) AS required
FROM accum AS a
INNER JOIN req AS r ON r.item_id = a.item_id
GROUP BY r.item_id, r.item_onhand, r.item_sales
ORDER BY level
see test result in https://dbfiddle.uk/J7PMY1fZ[enter link description here]1

how to apply where condition for only one element inside the IN statement

I have a SQL query which will return product list and it's respective sales. Below is the query
SELECT *
FROM sales
where sales.name IN ('product_1', 'product_2')
AND sales.product_number IN (SELECT number FROM products) where sales.name = 'product_2'
There are some unnecessary rows in product_2 which i want to filter out with the help of prod_number.
For example the output of above query is
cust_id name product_number
1 product_1 11
2 product_2 22
3 product_2 23
4 product_2 34
Now i want to filter out the product_2 rows based on it's product_number. I want product_2 only with product_number 22 and 23. I tried the above query but it's returning an error.
Use an OR condition to deal with the two cases.
SELECT *
FROM sales
WHERE (name = 'product_1' AND product_number IN (SELECT number FROM products))
OR (name = 'product_2' AND product_number IN ('22', '23'))
Since MySQL often optimizes OR poorly, you may get better results if you split this into two queries that you combine with UNION.
SELECT s.*
FROM sales AS s
JOIN products AS p ON s.product_number = p.number
WHERE s.name = 'product_1'
UNION ALL
SELECT *
FROM sales
WHERE name = 'product_2' AND product_number IN ('22', '23')

How to create a query using the difference of two sums in seperate tables?

I'm trying to calculate the quantity left in each water container, based on the refills it gets, and how much is extracted.
At the moment I have created my tables as:
CONTAINERS
----------
ID NUMBER
1 14F
2 12A
3 55Y
REFILLS
-------
ID CONTAINERID QUANTITY
1 14F 100
2 14F 10
3 12A 65
EXTRACTIONS
-----------
ID CONTAINERID QUANTITY
1 14F 20
So I need a query that will return each container with the amount that is left in them, i.e. in this case:
CONTAINERID CURRENTQUANTITY
14F 90
12A 65
55Y 0
Where 90 is the result from the two refills and one extraction in that case (100+10-20).
I have managed to calculate the sum of all refills/extractions:
SELECT CONTAINERS.ID, SUM(REFILLS.QUANTITY) AS REFILLSQUANTITY
FROM CONTAINERS INNER JOIN REFILLS ON CONTAINERS.ID = REFILLS.CONTAINERID
GROUP BY CONTAINERS.ID;
And the same way for extractions, but I'm a bit stuck how to combine them and get the difference in one query. Any help would be much appreciated!
In MS Access, use left join and aggregations:
select c.id, c.number,
nz(refill, 0) - nz(extraction, 0) as net
from (containers as c left join
(select containerid, sum(quantity) as refill
from refills
group by containerid
) as r
on c.number = r.containerid
) left join
(select containerid, sum(quantity) as extraction
from extractions
group by containerid
) as e
on c.number = e.containerid;

SQL select statement similar to vertical search approximate match in Excel

Having a table with columns 'price' and 'quantity'.
e.g:
rec price qty
1. 10,00 1
2. 7,50 5
3. 5,00 25
4. 3,00 100
I need to select the price for a quantity of 65. This is the price of record 3. Qty 65 is between qty 25 and 100. How to solve this in a sql query?
You can solve that with an inner SQL statement which tries to find the highest quantity lower than or equal to your requested quantity of 65:
select pce.price
from prices pce
join ( select max(qty) qty
from prices
where qty <= 65
) pce2
on pce.qty = pce2.qty
Here pce2 is the join to match the prices line. The pce table is joined to have access to all fields joined. This will only work correctly if there are no duplicates in prices for qty.

Merge output as one

Select Category, Books.ISBN,
Orderitems.Quantity * (Books.Retail - Books.Cost) AS Category_Profit
From BOoks
INNER JOIN Orderitems
ON BOOKS.ISBN=ORDERITEMS.ISBN
Example output:
Category ISBN Category_Profit
Family life 1234 50
Family Life 1234 50
Family Life 1234 100
Fitness 4321 10
Fitness 4321 20
So forth and so forth,
How can I make the output calculate all the values for each category into one line?
I.e
Family Life 1234 200
Fitness 4321 30
Because you already have this as a starting point, use exactly what you have as a temp table, and pull data from that:
Select Category, ISBN, Sum(Category_Profit) From
(
select Category, Books.ISBN as ISBN,
Orderitems.Quantity * (Books.Retail - Books.Cost) AS Category_Profit
From Books
INNER JOIN Orderitems
ON BOOKS.ISBN=ORDERITEMS.ISBN
) temp
group by Category, ISBN
You organized the data really well, so implementing a sum on the Profit is easy. You group by Category and ISBN to get all unique pairs of those columns with the corresponding Profit.
If you do not want to use a sub-query you can sum in your query (but I feel it's something helpful to use my existing query as a sub-query before altering it, just to make sure it works:
select Category, Books.ISBN,
SUM(Orderitems.Quantity * (Books.Retail - Books.Cost)) AS Category_Profit
From Books
INNER JOIN Orderitems
ON BOOKS.ISBN=ORDERITEMS.ISBN
temp
group by Category, Books.ISBN
Group by can be used to solve your problem.
Note: In Group by clause , a set of table rows can be grouped based on certain columns and in the select clause either the group by columns or aggregate function(SUM,MIN,MAX,Count etc) on other columns can be shown.
Reference :
http://www.dofactory.com/sql/group-by
Use group by as is done below. Hope this solves the issue.
Select Category, Books.ISBN,
SUM(Orderitems.Quantity * (Books.Retail - Books.Cost)) AS Category_Profit
From BOoks
INNER JOIN Orderitems
ON BOOKS.ISBN=ORDERITEMS.ISBN
Group by Category,ISBN
Use GROUP_BY & SUM, Syntax :
SELECT column_name, SUM(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name;
Refer: SQL GROUP_BY
On you table you may run :
Select Category, ISBN, Sum(Category_Profit) From Table1
group by Category, ISBN;
Table1:
Category ISBN Category_Profit
Family life 1234 50
Family Life 1234 50
Family Life 1234 100
Fitness 4321 10
Fitness 4321 20
Output:
| Category | ISBN | Sum(Category_Profit) |
|-------------|------|----------------------|
| Family life | 1234 | 200 |
| Fitness | 4321 | 30 |
Fiddle