SQL - GROUP BY and SUM using joined table fields - mysql

Let's assume there are two different tables:
PACKAGE
id
--
1
2
3
PRODUCT
id | package_id | currency | total
----------------------------------
1 | 1 | USD | 100
2 | 1 | EUR | 200
3 | 2 | USD | 300
4 | 2 | USD | 400
5 | 3 | GBP | 500
And the desired result is to get concatenated total amounts for each package (from its products) by each DISTINCT currency, something like:
id | total_amount
----------------------
1 | USD 100, EUR 200
2 | USD 700
3 | GBP 500
Tried with this query:
SELECT
packages.id,
(
SELECT
GROUP_CONCAT(CONCAT(currency,' ',total))
FROM
(
SELECT
products.currency AS currency,
SUM(products.total) AS total
FROM products
WHERE products.package_id = [[ packages.id ]] -- double brackets just for marking
GROUP BY products.currency
) T
) AS total_amount
FROM packages
LEFT JOIN products
ON products.package_id = packages.id
GROUP BY packages.id;
But there is an error that package.id (in double brackets above) is not visible, probably because of the subquery depth.
Is there any way that it can be achieved? Thanks.

You may try rewriting your query as a left join instead of a subquery which may be more efficient or faster as shown below:
SELECT
p.id,
ct.currency_totals
FROM
packages p
LEFT JOIN (
SELECT
package_id,
GROUP_CONCAT(
CONCAT(currency,' ',total)
) as currency_totals
FROM (
SELECT
package_id,
currency,
SUM(total) as total
FROM
products
GROUP BY
package_id,
currency
) t
GROUP BY
package_id
) ct on ct.package_id=p.id;
id
currency_totals
1
USD 100,EUR 200
2
USD 700
3
GBP 500
Moreover, if you only require the package id of currently used packages and no other details , using your subquery may be enough for this task.
SELECT
package_id,
GROUP_CONCAT(
CONCAT(currency,' ',total)
) as currency_totals
FROM (
SELECT
package_id,
currency,
SUM(total) as total
FROM
products
GROUP BY
package_id,
currency
) t
GROUP BY
package_id;
package_id
currency_totals
1
USD 100,EUR 200
2
USD 700
3
GBP 500
View working demo on DB Fiddle

you can use :
select package_id , group_concat(concat(currency,' ',grandtotal))
from (
select package_id,currency, sum(total) grandtotal
from products
group by package_id,currency
) t group by package_id
db<>fiddle here

Try use alias for tables names to identify...
SELECT
pkg.id,
(
SELECT
GROUP_CONCAT(CONCAT(currency,' ',total))
FROM
(
SELECT
products.currency AS currency,
SUM(products.total) AS total
FROM products
WHERE products.package_id = pkg.id
GROUP BY products.currency
) T
) AS total_amount
FROM packages as pkg
LEFT JOIN products
ON products.package_id = pkg.id
GROUP BY pkg.id;

Related

MySQL SUM over a virtual subquery field (with another SUM) with GROUP BY

I have two tables: invoices and items.
invoices
id | timest
items
id | invoice_id | price | qty
It is apparent an invoice may have several items - items.invoice_id = invoices.id.
I have the following query that selects all invoices with the total sum of theirs items:
SELECT id, DATE_FORMAT(FROM_UNIXTIME(inv.time), "%Y-%m" ) AS _period,
(SELECT SUM(it.price*it.quantity) FROM items AS it WHERE it.invoice_id=inv.id) as total
FROM `invoices` `inv`
This generates something like:
id| _period | total
-------------------
1 | 2014-06 | 100
4 | 2014-06 | 200
5 | 2014-07 | 660
6 | 2014-07 | 300
7 | 2014-07 | 30
9 | 2015-02 | 225
Now I want to group it by the period to have output as:
_period | qty | total_price
---------------------------
2014-06 | 2 | 300
2014-07 | 3 | 990
2015-02 | 1 | 224
I can easily do it for the quantity field as
SELECT DATE_FORMAT(FROM_UNIXTIME(inv.time), "%Y-%m" ) AS _period,
COUNT(inv.id) as qty
FROM `invoices` `inv`
GROUP BY _period
But I can't figure out how the similar thing could be done for the total_price field, which results from a subquery virtual field? Does anyone have any idea?
Thank you!
You should do this using a LEFT JOIN and GROUP BY:
SELECT DATE_FORMAT(FROM_UNIXTIME(i.time, '%Y-%m') AS _period,
COUNT(DISTINCT i.id) as num_invoices
SUM(i.price * it.quantity) as total
FROM invoices i LEFT JOIN
items it
ON it.invoice_id = i.id
GROUP BY _period
ORDER BY _period;
try this
SELECT InnerTable._period, Count(InnerTable.id) as id, Sum(InnerTable.total) as total FROM
(SELECT id, DATE_FORMAT(FROM_UNIXTIME(inv.time), "%Y-%m" ) AS _period,
(SELECT SUM(it.price*it.quantity) FROM items AS it WHERE it.invoice_id=inv.id) as total
FROM `invoices` `inv`) as InnerTable FROM GROUP BY InnerTable._period.
Making sub table from the query and then put group by on it.

Mysql Select with two SUM from two tables

I'm trying to select SUMed data from two tables.
This is how they look.
Table1:
products | revenue|
------------------|
product1 | 10 |
product2 | 20 |
product1 | 20 |
Table2:
products | revenue|
------------------|
product1 | 40 |
product2 | 30 |
product2 | 40 |
So the query should sum them like this:
products | revenue|
------------------|
product1 | 70 |
product2 | 90 |
I've tried this and some other queries but they are incorrect.
SELECT Table1.products, Table1.SUM(`revenue`), Table2.SUM(`revenue`)
FROM Table1
JOIN Table2
ON Table1.products = Table2.products
group by Table1.products;
Could you help me, what is the right query in this case? Thanks.
I would recommend using union all and then group by:
select product, sum(revenue)
from ((select product, revenue from table1) union all
(select product, revenue from table2)
) tt
group by product;
This will ensure that all products are in the result set, even products that are only in one table.
Use UNION ALL and SUM aggregate function :
SELECT products , SUM(revenue) revenue
FROM
(
SELECT products , revenue
FROM table1
UNION ALL
SELECT products , revenue
FROM table2
) A
GROUP BY products

Joining multiple select sql statements (4 tables)

I need to join together 2 SQL statements and both of those statements work on their own. But I don't know how to combine both into 1 SQL statement.
I have two tables in 1st statement, TR120 and TR1201.
The SQL is this:
select
PRODUCT, PRICE, QUANTITY, INVOICE.DATE
from
TR1201
left join
(select
DATE, ID as INVOICE_ID, INVOICE
from TR120) as INVOICE on INVOICE.INVOICE_ID = ID
where
INVOICE.DATE >= '2016-06-01' and INVOICE.DATE <= '2016-06-30'
This returns a list of all the products I sold, with price, quantity and date of sales in a specific time frame from 01-06-16 till 30-06-16.
Now I need to find out the latest price that I bought product for in different two tables TR100 and TR1001 based on the product and date of sale from the 1st SQL statement.
select
PRODUCT, PRICE, SUP.DATE
from
TR1001
left join
(select
DATE, ID as SUP_ID, SUP_INVOICE
from TR100) as SUP on SUP.SUP_ID = ID
This returns a list of all the products that I have bought with a price and a date. I only need last record from this query based on product and date of purchased.
TR120
ID | INVOICE | DATE
1 | 000001 |2016-06-05
2 | 000002 |2016-06-15
3 | 000003 |2016-06-25
TR1201
ID | PRODUCT | PRICE A | QUANTITY
1 | A | 2,00 | 5
2 | A | 2,00 | 2
3 | A | 2,00 | 1
TR100
ID | SUP_INVOICE | DATE
1 | 160001 | 2016-05-30
2 | 160002 | 2016-06-16
TR1001
ID | PRODUCT | PRICE B
1 | A | 0,5
2 | A | 0,7
The result I am trying to get is this:
PRODUCT | PRICE A (tr1201) | QUANTITY | DATE (tr100) | PRICE B (tr1001)
A | 2 | 5 | 2016-05-30 | 0,5
A | 2 | 2 | 2016-05-15 | 0,5
A | 2 | 1 | 2016-05-16 | 0,7
That is all I want to do :(
Have you tried first_value?
FIRST_VALUE ( [scalar_expression ] )
OVER ( [ partition_by_clause ] order_by_clause [ rows_range_clause ] )
it works like this:
select distinct id,
first_value(price) over (partition by id (,sup) order by date DESC (latest, ASC for oldest)) as last_price
from table;
Documentation can be found here: https://msdn.microsoft.com/en-us/library/hh213018.aspx
I don't have your tables so cannot test and therefore am providing advice only.
I think what you need is an Outer apply like this instead of joins
select
T1.Product
, T1.Price
, T2.DATE -- Alias this
, T2.Price -- Alias this
, T3.DATE -- Alias this
, T3.Price -- Alias this
from T1
OUTER APPLY (
select top 1
Date
,Price
from table2
WHERE ID = T1.Id AND product = T1.Product-- plus any other joins
ORDER BY Date desc
) as T2
OUTER APPLY (
select top 1
Date
,Price
from table3
WHERE ID = T1.Id AND product = T1.Product-- plus any other joins
ORDER BY Date desc
) as T3

MySQL query resulting in the substraction of the sum of 2 columns in different tables

I have a MySQL DB with the following Tables:
Products:
Product_ID | Product_Name
1 | Blaster
2 | Faser
3 | BFG
Orders:
Order_ID | Product_ID | Order_Product_Qnt
1 | 1 | 10
2 | 2 | 5
3 | 3 | 7
4 | 2 | 10
Sells:
Sell_ID | Product_ID | Sel_Product_Qnt
1 | 2 | 5
2 | 1 | 1
3 | 3 | 2
What I want to do is a query that lists all the products followed by their amount.
The result should be:
Product_Name | Quantity
BFG | 5
Blaster | 9
Faser | 10
Following Barnar's suggestion I got to this piece of code:
SELECT
Products.Product_Name,
COALESCE (SUM(Orders.Order_Product_Qnt), 0) - COALESCE (SUM(Sells.Sells_Product_Qnt), 0) AS Quantity
FROM
Products
LEFT JOIN
Orders ON Products.Product_ID = Orders.Product_ID
LEFT JOIN
Sells ON Products.Product_ID = Sells.Product_ID
GROUP BY
Products.Product_Name
The query works but it returns wrong values.
For example, I have a product that has 6 orders, and 1 sell, logic dictates that 6-1=5, but that query gives me 4 as a result.
Or another one with 18 Orders and 6 Sells, returns 60 (should be 12).
Any advise is appreciated.
Maybe something like this?
SELECT
product_name,
orders_cnt - sales_cnt AS Quantity
FROM (
SELECT product_name,
SUM(orders) AS orders_cnt,
SUM(sales) AS sales_cnt
FROM (
SELECT products.product_name,
ifnull(orders.order_product_qnt, 0) orders,
ifnull(sells.sells_product_qnt,0) sales
FROM products
LEFT JOIN orders ON products.product_id = orders.product_id
LEFT JOIN sells ON products.product_id = sells.product_id
) t1
GROUP BY product_name ) t2
Finally got it working, forgot to post my solution here:
SELECT
Products.Product_ID,
Products.Product_Name,
IFNULL(b.SB - c.SC, 0) AS Quantity,
FROM Produtos_Table
LEFT JOIN (
SELECT
Product_ID,
SUM(Quantity) AS SB
FROM
Orders
GROUP BY Product_ID
) b
ON Products.Product_ID = b.Product_ID
LEFT JOIN (
SELECT
Product_ID,
SUM(Sell_Product_Qnt) AS SC
FROM
Sells
GROUP BY
Product_ID
) c
ON Products.Product_ID = c.Product_ID
GROUP BY
Products.Product_Name

MAX() and SUM() in one query

I have this table
dept | amount | price
1 | 2 | 20
3 | 2 | 50
4 | 3 | 10
2 | 5 | 20
1 | 1 | 15
4 | 1 | 30
4 | 6 | 5
2 | 7 | 7
1 | 1 | 24
2 | 5 | 12
dept is de department number
amount is how many of a product is sold.
price is how much the price of the product is
How can I found the dept, that has got the most money from selling their products.
I have this:
SELECT dept, SUM( amount * price ) AS total
FROM table
GROUP BY dept
I need it to return the dept with the highest total.
I can't use MAX( SUM( amount * price ) ), so how do I do this?
Oh yeah. It's a school assignment and I may not use LIMIT or ORDER BY
Without using LIMIT you can try using HAVING:
SELECT dept,SUM(amount * price) AS total
FROM tab1
GROUP BY dept
HAVING SUM(amount * price) = (
SELECT MAX(total)
FROM (
SELECT SUM(amount * price) AS total
FROM tab1
GROUP BY dept
) a
)
sqlfiddle demo
If you do not want to use ORDER and LIMIT. This is a solution ( Tested)
SELECT dept, SUM( amount * price ) AS total
FROM table
GROUP BY dept
HAVING SUM( amount * price ) = ( SELECT MAX(A.total)
FROM
(
SELECT dept, SUM( amount * price ) AS total
FROM table
GROUP BY dept
) A
)
This will give you the department with the highest total:
select top 1 dept, sum(amount * price)
from table
group by dept
order by sum(amount * price) desc
You can sort by the total descending and take the first entry
SELECT dept, SUM( amount * price ) AS total
FROM table
GROUP BY dept
order by total desc
limit 1