Query to aggregate across multiple tables - sql

I'm new to SQL, and I'm trying to create a database to manage a small inventory. This is the structure of the db:
DatabaseStructure
I need to create a query that returns the total inventory per material. So, the first step would be to look up for all the batches associated with the material. Second, look up for all the movements associated with each batch. Then, sum the quantity associated with each movement, but depending on the movement type (If it is a good receipt is addition (+), but if it is an inventory withdrawal is subtraction (-)).
Here is an example of the tables with sample data and the desired result.
Table Material
MaterialID
MaterialDescription
1
Bottle
2
Box
Table Batch
BatchID
MaterialID
VendorMaterial
VendorBatch
ExpirationDate
1000
1
2096027
00123456
12/12/2025
1001
1
2096027
00987654
11/11/2026
1002
2
102400
202400E
10/10/2023
Table Movement
MovementID
BatchID
MovementType
Quantity
CreatedBy
CreatedOnDate
1
1000
Good receipt
100
user1#email.com
4/10/2022
2
1000
Inventory withdrawal
20
user2#email.com
4/15/2022
3
1000
Inventory withdrawal
25
user3#email.com
4/17/2022
4
1001
Good receipt
100
user1#email.com
4/20/2022
5
1001
Inventory withdrawal
10
user4#email.com
4/26/2022
6
1002
Good receipt
50
user1#email.com
2/26/2022
Expected query result - total inventory per material:
MaterialDescription
TotalInventory
Bottle
145
Box
50
TotalInventory calculation: for Bottle there are two good receipts movements of 100 and three withdrawals of 20, 25 and 10. So, total inventory will be (100+100)-(20+25+10)=145.
Thanks for your help!

select
mat.MaterialDescription,
sum(
case mov.MovementType
when 'Good receipt' then 1
when 'Inventory withdrawal' then -1
else 0 /* don't know what to do for other MovementTypes */
end * mov.Quantity
) as TotalInventory
from
Material as mat
left join Batch as bat on bat.MaterialID = mat.MaterialID
left join Movement as mov on mov.BatchID = bat.BatchID
group by
mat.MaterialDescription
;

Related

Creating Dupe records in SQL Server

Background: We are in process of migrating our ETL from an old database to a new database which record data differently. In order to be as close to our old system (which creates 1 record for sale and another if the sale was cancelled and the NEW system creating only the cancelled record), I need to create another record for these sales so that I match 1:1 between the old and new database.
What would be an effective way to achieve this. For example, I can get 10k records that need duplication. These are based on specific conditions in the database.
Here is the sample of data :
AcctNum CancelFlag SaleDate SalePrice
-----------------------------------------------------------------------
1 Y 2022-06-27 100.00
2 Y 2022-05-10 156.00
Desired output:
AcctNum CancelFlag SaleDate SalePrice
---------------------------------------------------------------------
1 Y 2022-06-27 100.00
1 2022-06-27 -100.00
2 Y 2022-05-10 156.00
2 2022-05-10 -156.00
Please note that on the duped row, I won't have the CancelFlag = Y and the SalePrice will be flipped to a negative sign to indicate the cancellation of the sale.
Thanks for taking the time to read.
So if I understand correctly, you basically want to create new rows for each of those rows selected , with the CheckFlag off, and the amount negated - right?
So try something like this:
INSERT INTO dbo.YourTable (AcctNum, CancelFlag, SaleDate, SalePrice)
SELECT
AcctNum, NULL, SaleDate, SalePrice * -1.0
FROM
dbo.YourTable
WHERE
CancelFlag = 'Y'
AND SalePrice > 0.0;
Does that do what you're looking for? You might need to tweak your WHERE clause to get precisely those existing rows from the table that you're really interested in.

Join with three tables in Hive

I have to write Hive SQL.
I want to find the lowest price for each catalog. I want to get one row for each catalog with the catalog ID, the product ID, the price (lowest price), the image url, the large_ctgr_id, the small_ctgr_id, the large_ctgr_name, and the small_ctgr_name columns.
In the example below, we finally need two rows.
In addition, there are multiple products in a catalog, and each product is classified into a large category and a small category.
Eventually we have to join the three tables.
There is a similar issue with the link I asked.
(Finding the lowest price for each category using join sql in Hive) In this case, we joined only two tables, and the names of the columns changed slightly.
Help me. Thank you.
"catalog_product_match" table
catalog_id product_id
1001 500001
1001 500002
1002 500101
1002 500102
1002 500103
"product_info" table
prd_id price img_url large_ctgr_id small_ctgr_id
500001 29000 /app/url/img/500001.jpg 200 34543
500002 29500 /app/url/img/500002.jpg 201 2345234
500101 8100 /app/url/img/500101.jpg 1020 23252
500102 8100 /app/url/img/500102.jpg 42133 2349823
500103 8500 /app/url/img/500103.jpg 3435 342514
"category_info" table
l_ctgr_id s_ctgr_id l_ctgr_name s_ctgr_name
200 34543 computer notebook
200 3423984 computer tablet
201 2345234 phone smartphone
1020 23252 clothes top
42133 2349823 cup cup
3435 342514 pen ink

SQL Server query related to grouping and counting for inventory analysis

My table is concerned with inventory items, stocking levels and required numbers. An example is given below:
Part Sub Part Pieces Required Pieces In Stock
Barbie Legs 2 1000
Barbie Arms 2 5000
Barbie Head 1 20
Barbie Torso 1 40000
Dora Legs 2 1000
Dora Arms 2 5000
Dora Head 1 0
Dora Torso 1 40000
I want my end result to look like:
Part No: of dolls that can be built
Barbie 20
Dora 0
So the logic is we need a minimum number of each part to make a complete doll. If even one of the required parts is not in stock, then no doll can be made. The complexity comes when we need 2 of certain parts and only 1 of other parts.
How do I achieve this using SQL Server?
Thank you in advance for your help and time.
SELECT part, MIN([Pieces In Stock]/[Pieces Required]) AS PossibleCompleteDolls
FROM tblName
WHERE [Pieces Required] <> 0
GROUP BY part;
The WHERE clause here is just to prevent division by zero.
First add a column of how many can be build with each piece
SELECT *, CAST([Pieces In Stock]/[Pieces Required] as INTEGER) AS canmake
FROM TABLE
Then group that by the part and take the min
SELECT Part, min(canmake) as [No: of dolls that can be built]
FROM (
SELECT *, CAST([Pieces In Stock]/[Pieces Required] as INTEGER) AS canmake
FROM TABLE
WHERE [Pieces Required] != 0
) T
GROUP BY Part

Joining a table to two one-to-many relationship tables in SQL Server

Happy Friday folks,
I'm trying to write an SSRS report displaying data from three (actually about 12, but only three relevant) tables that have akward relationships and the SQL query behind the data is proving difficult.
There are three entities involved - a Purchase Order, a Sales Order, and a Delivery. The problem is the a Purchase Order can have many sales orders, and also many deliveries which are NOT linked to the sales orders...that would be too easy.
Both the Sales Order and Delivery tables can be linked to the Purchase Order table by foreign keys and an intermediate table each.
I need to basically list Purchase Orders, a list of sales orders and a list of deliveries next to them, with NULLs for any fields that aren't valid so that'll give the required output in SSRS/when read by a human, ie, for a purchase order with 2 sales orders and 4 delivery dates;
PO SO Delivery
1234 ABC 05/10
1234 DEF 09/10
1234 NULL 10/12
1234 NULL 14/12
The above (when grouped by PO) will tell the users there are two sales orders and four (unlinked) delivery dates.
Likewise if there are more SOs than deliveries, we need NULLs in the Delivery column;
PO SO Delivery
1234 ABC 03/08
1234 DEF NULL
1234 GHI NULL
1234 JKL NULL
Above would be the case with 4 SOs and one delivery date.
Using Left Outer joins alone gives too much duplication - in this case 8 rows, as it gives 4 delivery dates for each match on the sales order;
PO SO Delivery
1234 ABC 05/10
1234 ABC 09/10
1234 ABC 10/12
1234 ABC 14/12
1234 DEF 05/10
1234 DEF 09/10
1234 DEF 10/12
1234 DEF 14/12
It's fine that the PO column is duplicated as SSRS can visually group that - but the SO/Delivery fields can't be allowed to duplicate as this can't be got rid of in the report - if I group the column in SSRS by SO then it still spits out 4 delivery dates for each one.
The only situation our query works nice is when there is just one SO per PO. In that case the single PO and SO numbers are duplicated together for x deliveries and can both be neatly grouped in SSRS. Unfortunately this is a rare occurence in the data.
I've thought of trying to use some sort of windowing function or CROSS APPLY but both fall down as they will repeat for every PO number listed and end up spitting out too much data.
At the point of thinking this just isn't set-based enough to be doable in SQL, I know the data is horrible..
Any help much appreciated.
EDIT - basical sqlfiddle link to the table schemas. Omitted many columns which aren't relevant. http://sqlfiddle.com/#!2/5ba16
Example data...
Purchase Order
PO_Number Style
1001 Black work boots
1002 Green hat
1006 Red Scarf
Sales Order
Sales_order_number PO_number Qty Retailer
A100-21 1001 15 Walmart
A100-22 1001 29 Walmart
A200-31 1006 1000 Asda
Delivery
Delivery_ID Delivery_Date PO_number
1543285 10/05/2014 1001
1543286 12/05/2014 1001
1543287 17/05/2014 1001
1543288 21/05/2014 1002
If you assign row numbers to the elements in salesorders and deliveries, you can link on that.
Something like this
declare #salesorders table (po int, so varchar(10))
declare #deliveries table (po int, delivery date)
declare #purchaseorders table (po int)
insert #purchaseorders values (123),(456)
insert #salesorders values (123,'a'),(123,'b'),(456,'c')
insert #deliveries values (123,'2014-1-1'),(456,'2014-2-1'),(456,'2014-2-1')
select *
from
(
select numbers.number, p.po, so.so, d.delivery from #purchaseorders p
cross join (Select number from master..spt_values where type='p') numbers
left join (select *,ROW_NUMBER() over (partition by po order by so) sor from #salesorders ) so
on p.po = so.po and numbers.number = so.sor
left join (select * , ROW_NUMBER() over (partition by po order by delivery) dor from #deliveries) d
on p.po = d.po and numbers.number = d.dor
) v
where so is not null or delivery is not null
order by po,number

How to update a column when the same column in another table is changed ?(postgresql)

I need A Function or a Trigger to solve this Problem??
customer_details :::
custid name creditid
----------------------------
2 a 1
3 b 2
4 c 3
balance_amount :::
creditid credit_type balance
-----------------------------------
1 rent 1000
1 transport 2000
1 food 1000
1 fruits 1500
2 rent 1500
2 transport 1020
2 food 1200
2 fruits 1000
3 transport 1600
3 rent 2000
3 food 1540
3 fruits 1560
Pay_the_loan :::
creditid credit_type Pay status
---------------------------------------------
1 rent 500 null
2 fruits 600 null
3 transport 400 null
1 fruits 500 null
once i update the status column in pay_the_loan table to ok for a particular creditid i.e..,
(update pay_the_loan set status='ok' where creditid=2)
then it should deduct the amount from the balance column in balance_amount table and it should be updated i.e..,(1000-600=400 in balance_amount table where balance_amount.credit_type=fruits and creditid=2 from balance amount table)
Possible post me a Function or a Trigger to solve this problem ?
You're probably better off restructuring a little, creating a loan_amount table with the original loans, and just make balance_amount a view that shows the current balance with all payments deducted. That way, for example, a correction in a payment amount will not make your system show the wrong balance.
A view that calculates the current balance for you could look something like;
CREATE VIEW balance_amount AS
SELECT la.creditid, la.credit_type, amount
- COALESCE(SUM(pay),0) balance
FROM loan_amount la
LEFT JOIN pay_the_loan pl
ON la.creditid = pl.creditid
AND la.credit_type = pl.credit_type
AND pl.status = 'OK'
GROUP BY la.creditid, la.credit_type, la.amount;
An SQLfiddle with the whole setup.
You can update both tables in the same query:
update
pay_the_loan
set
pay_the_loan.status='ok',
balance_amount.balance=balance_amount.balance-pay_the_loan.Pay
from balance_amount
where
pay_the_loan.creditid=balance_amount.creditid
and
pay_the_loan.creditid=2
and
balance_amount.credit_type='fruits';
Update. I've read the documentation of postgresql update statement. Apparently, I was wrong and it is not possible to update two tables in a single query. However, I still think that you do not need a trigger here. Just use two update queries one after another:
update
pay_the_loan
set
status='ok'
where
pay_the_loan.creditid=2;
update
balance_amount
set
amount=balance_amount.amount-pay_the_loan.Pay
from pay_the_loan
where
pay_the_loan.creditid=balance_amount.creditid
and
pay_the_loan.creditid=2
and
balance_amount.credit_type='fruits';