I have tried to search this forum for an answer but can't seem to find one.
My problem: I have 3 tables. One with items, one with in-going deliveries and one with outgoing deliveries.
Table called items:
id | item_name
----------------
1 | Bike
2 | Helmet
3 | Pedal
4 | Light
Table called ingoing:
id | item_id | quantity
-----------------------
1 | 2 | 5
2 | 3 | 2
3 | 4 | 1
4 | 1 | 5
5 | 2 | 4
6 | 1 | 6
7 | 3 | 5
Table called outgoing:
id | item_id | quantity
-----------------------
1 | 3 | 2
2 | 1 | 1
3 | 2 | 3
4 | 3 | 4
5 | 1 | 2
6 | 2 | 1
7 | 4 | 1
What I want to do is get the total amount in stock by subtracting the outgoing deliveries from the in-going and order it by the item with the least amount.
Maybe there is a better way to do this?
This is the query that I got, but the SUM amounts isn't correct. Can someone help me and explain why the SUM amounts isn't correct and how should I solve this with the best way?
SELECT items.id AS ID,
items.item_name,
Sum(ingoing.quantity) - Sum(outgoing.quantity) AS InStock
FROM items
LEFT JOIN ingoing
ON ingoing.item_id = items.id
LEFT JOIN outgoing
ON outgoing.item_id = items.id
GROUP BY ID
ORDER BY InStock ASC
This is the result I want from this:
ID | item_name | InStock
---------------------------
4 | Light | 0
3 | Pedal | 1
2 | Helmet | 5
1 | Bike | 8
What I Get:
ID | item_name | InStock
---------------------------
4 | Light | 0
3 | Pedal | 2
2 | Helmet | 10
1 | Bike | 16
It can be easy to forget the multiplicative effect of a join. When you encounter problems like this, check the result of the join, prior to filtering/grouping:
SELECT items.id,
items.item_name,
ingoing.id AS ingoing,
outgoing.id AS outgoing
FROM items
LEFT JOIN ingoing
ON ingoing.item_id = items.id
LEFT JOIN outgoing
ON outgoing.item_id = items.id
See it on sqlfiddle.
As you can see, the resultset contains multiple records with the same ingoing.id values, and multiple records with the same outgoing.id values. This is because each ingoing record for a particular item has joined with every outgoing record for that same item: thus there are 4 pedals in the resultset (2 incoming x 2 outgoing), etc.
Aggregating each table by item prior to joining (and thus ensuring that there is only 1 record per item on each side of the join) will achieve what you're after:
SELECT items.id AS ID,
items.item_name,
ingoing.quantity - outgoing.quantity AS InStock
FROM items
LEFT JOIN (
SELECT item_id AS id, SUM(quantity) AS quantity
FROM ingoing
GROUP BY item_id
) AS ingoing USING (id)
LEFT JOIN (
SELECT item_id AS id, SUM(quantity) AS quantity
FROM outgoing
GROUP BY item_id
) AS outgoing USING (id)
ORDER BY InStock ASC
See it on sqlfiddle.
Note that your problem would have been greatly simplified by only having a single underlying table of stock movements, with positive quantities indicating movements in one direction and negative quantities indicating movements in the opposite direction: then a simple groupwise summation of the whole table would yield your desired results.
Try this query:
SELECT items.id AS ID, items.item_name, (SELECT SUM(quantity) from ingoing
WHERE ingoing.item_id = items.id) - (SELECT SUM(quantity) from outgoing WHERE
outgoing.item_id = items.id) AS InStock FROM items ORDER BY InStock ASC;
Related
I have 2 tables. sales and payment.
sales table (has around 900.000 records)
item_id | sale_id
------------------
1 | 1
2 | 1
3 | 1
1 | 2
1 | 3
3 | 3
payment table (also has around 900.000 records)
sale_id | payment_method
------------------------
1 | CASH
2 | CREDIT
3 | CASH
I'd like to know top 5 best seller items grouped by payment method. Something like this
item_id | total_sales | payment_method
--------------------------------------
1 | 2 | CASH
3 | 2 | CASH
So far I have
SELECT
a.item_id as item_id,
COUNT(a.item_id) as total_sales,
b.payment_method as payment_method
FROM sales a
LEFT JOIN payment b ON a.sale_id = b.sale_id
GROUP BY a.item_id
ORDER BY COUNT(a.item_id) DESC
However, the result is not correct.
How to get something like this?
I have a question about FULL JOIN at MySql. I know that alternative is UNION but I have dificulties to combine them all.
I guess it would be already enough to get answer on 4 tables as 5th and 6th are same as in first 4.
Tables: Bill, Service, BS, Item, BI, Buyer
BS connect more Services to Bill and BI connect more Items to Bill. Buyer is 1:1 relation with Bill.
Tables example:
Bill:
----------------------
id | number | Buyer_id
1 | 12014 | 3
2 | 22014 | 2
3 | 32014 | 5
Services:
----------------------
id | cost
1 | 2
2 | 7
3 | 1
4 | 12
BS:
----------------------
id | Bill_id | Services_id
1 | 1 | 3
2 | 1 | 4
3 | 2 | 2
4 | 3 | 1
5 | 3 | 2
6 | 3 | 3
7 | 3 | 4
Item:
----------------------
id | cost
1 | 34
2 | 77
3 | 2
4 | 15
5 | 13
BI:
----------------------
id | Bill_id | Items_id
1 | 1 | 5
2 | 2 | 3
3 | 3 | 2
Buyer:
----------------------
id | name
1 | John
2 | Mary
3 | Dave
4 | Carl
5 | Jack
So far the closest I got to was that I used following SQL:
SELECT *
FROM Bill b
LEFT JOIN BI ON BI.Bill_id = b.id
LEFT JOIN BS ON BS.Bill_id = b.id
LEFT JOIN Item i ON i.id = BI.Item_id
LEFT JOIN Services s ON s.id = BS.Services_id
LEFT JOIN Buyer ON Buyer.id = b.Buyer_id
WHERE b.number = '12014'
Result gives me 2 Services and 1 duplicated Item but I want one Item and one NULL item (as only one item is attached to that bill.
Result that I get (just ids as it's shorter):
b.id | s.id | BS.id | i.id | BI.id | Buyer.id
1 | 3 | 1 | 1 | 5 | 3
1 | 4 | 2 | 1 | 5 | 3
And desired result in table:
b.id | s.id | BS.id | i.id | BI.id | Buyer.id
1 | 3 | 1 | 1 | 5 | 3
1 | 4 | 2 | NULL | NULL | 3 (or NULL, doesn't really matter)
I tried with others as well but I got even more rows than 2 (note that two are expected or if Bill.number=32014 then 4 rows).
Thank you!
You are taking the wrong approach.
The result of a query to a relational database is a relation - you may treat it as a rectangular matrix with rows and columns. Every row represents something (at least it should), every column represents an attribute and every cell represents the thing's attribute's value.
| [attr 1] | [attr 2]
-----------+---------------+-------------------------
[thing 1] | value | some value
[thing 2] | value | other value
[thing 3] | a value | yeah, a value
Now here is what you are trying to produce:
| [attr 1] | [attr 2] | | [other attr 3]
-----------+---------------+------------------+---------------------+-------------------
[thing 1] | value | some value | [other thing 1] | a value
[thing 2] | value | other value |
See? Attempting to return two relations with a single query. Not rectangular anymore, huh? Items and services are independent here, but you are trying to put them into a single row. Don't go this way, here are three queries for you:
-- Get bill/buyer details (1 row)
SELECT b.id, Buyer.id
FROM Bill b
LEFT JOIN Buyer ON Buyer.id = b.Buyer_id
WHERE b.number = '12014';
-- Get billed items (1 row per item)
SELECT BI.id, i.id
FROM Bill b
JOIN BI ON BI.Bill_id = b.id
JOIN Item i ON i.id = BI.Item_id
WHERE b.number = '12014';
-- Get billed services (1 row per service)
SELECT BS.id, s.id
FROM Bill b
JOIN BS ON BS.Bill_id = b.id
JOIN Services s ON s.id = BS.Services_id
WHERE b.number = '12014';
Note that item and services queries don't use left joins. You would like to return 0 rows if there are no items/services on the bill.
Then handle the results of them one by one in your application.
Edit:
Sometimes two (or more) entities share some common characteristics, for example in your application you could treat services and items as bill lines. In this case, this could be valid to retrieve all of them in a single query, but only this way using union:
-- Get bill lines (items and services)
SELECT BI.id AS bill_item_id, i.id AS item_id, NULL as bill_service_id, NULL as service_id
FROM Bill b
JOIN BI ON BI.Bill_id = b.id
JOIN Item i ON i.id = BI.Item_id
WHERE b.number = '12014';
UNION ALL
SELECT NULL AS bill_item_id, NULL AS item_id, BS.id as bill_service_id, s.id as service_id
FROM Bill b
JOIN BS ON BS.Bill_id = b.id
JOIN Services s ON s.id = BS.Services_id
WHERE b.number = '12014';
Which will return a result similar to what you originally expected:
BI.id | i.id | BS.id | s.id
5 | 1 | NULL | NULL
NULL | NULL | 1 | 3
NULL | NULL | 2 | 4
Note that:
each item and service is an individual bill line represented by a record. Don't try to artificially "compress" data across rows or columns
it's not the case in your schema, but most often there are also some shared attributes, like line id, quantity ordered or amount paid.
I have the following data structure:
Table 1(groups):
ID | Group
=============
1 | Sample
2 | Data
Table 2(items):
ID | GroupID | Cost | Amount
==============================
1 | 1 | 1 | 12
2 | 1 | 7 | 15
3 | 1 | 3 | 8
4 | 2 | 2 | 12
And would like the following (query) results:
groups.ID | groups.Name | total
1 | Sample | 141
2 | Data | 24
total is the sum over the products of cost and amount of all items in the group i.e. for group 1: 1*12+7*15+3*8=141
Im guessing I have to something with
SELECT g.ID, g.Group, SUM(Products)
FROM groups AS g, items AS i
WHERE g.ID=i.GroupID
GROUP BY i.GroupID
But don't know what exactly.
Doing iit in clientsoftware with loops is no problem, but I am curious (and certain) that this can be done in (my)Sql
SELECT g.ID as ID, g.Group as Name, SUM(i.Cost * i.Amount) as total
FROM groups g
INNER JOIN items i ON i.GroupID = g.ID
GROUP BY g.Group, g.ID
Having a field named "Group" is quite a bad idea in SQL (reserved keyword)
I have a table containing inventory
ID | Product ID | In_Transit | Quantity | Cumulative Quantity
=====+================+==============+==============+====================
1 | 1 | 0 | 1000 | 1000
2 | 1 | 0 | 1 | 1001
3 | 1 | 1 | 54 | 1055
4 | 1 | 1 | 1 | 1056
So the total inventory for product id 1 is '1056' I get this using a SELECT MAX(ID) subquery join with the table to get its cumulative quantity which is 1056.
I would like to get the Inventory total (subtracting all the amounts in transit)
So 1056 - 54 - 1 = 1001
How would I get this in one query so i get
Product ID | Total Inventory | Inventory on Hand (Excluding in Transit |
===========+=================+=========================================
1 | 1056 | 1001
Also i need to use the cumulative inventory to get the total as opposed to 'SUM', except for summing those in transit because (those not in transit) have a large number of records and they take ages to SUM. I can use it to sum those in transit because there are far fewer records
SELECT
a.product_id
, a.cumulative as total_inventory
, a.cumulative - COALESCE(b.quantity,0) AS inventory_on_hand
FROM table1 a
JOIN
( SELECT MAX(id) AS max_id
FROM table1
GROUP BY product_id
) m ON (m.max_id = a.id)
LEFT JOIN
( SELECT product_id, SUM(quantity)
FROM table1
WHERE in_transit = 1
GROUP BY product_id
) b ON (a.product_id = b.product_id)
gurus! I am stuck. Catalog items have prices dependent to its quantity. Here example of tables:
items: just item definitions
-------------------------
item_id | item_title
-------------------------
1 | "sample item"
2 | "another item"
items_prices: prices dependent to item quantity.
Less price taken for more quantity of item
----------------------------
item_id | quantity | price
----------------------------
1 | 1 | 100
1 | 5 | 80
1 | 10 | 60
2 | 1 | 120
2 | 3 | 100
cart
-------------------
item_id | quantity
-------------------
1 | 20
2 | 2
Is it possible to get current cart cost with single query?
select sum(x.totalcost)
from (
select c.item_id, c.quantity * ip.price as totalcost
from cart c
join items_prices ip
on c.item_id = ip.item_id
left join items_prices ip2
on ip2.quantity > ip.quantity
and c.quantity >= ip2.quantity
where c.quantity >= ip.quantity
and ip2.quantity is null
) x
The join back onto items_price again lets us filter out any cases where there is a greater quantity which still meets our criteria. This should be getting close to what we're after