MySQL UNION ALL SELECT Issue on getting all the SUM per column - mysql

I wish to get the SUM of the product per column, not sure if the UNION ALL SELECT can handle the desired result considering that the first column is concatenated.
Here is the code:
SUM(product.product_id = 1) AS Soda,
SUM(product.product_id = 2) AS Liquor,
SUM(product.product_id = 3) AS Lemon,
SUM(product.product_id = 4) AS Mango,
SUM(product.product_id = 5) AS Inhaler,
SUM(1) AS Count
FROM line_item
JOIN product USING (product_id)
JOIN ( SELECT 0 lowest, 500 highest UNION
SELECT 501 , 1000 UNION
SELECT 1001 , 1500 UNION
SELECT 1501 , 2000 UNION
SELECT 2001 , 2500 ) ranges ON product.price * line_item.quantity BETWEEN ranges.lowest AND ranges.highest
GROUP BY ranges.lowest, ranges.highest
UNION ALL SELECT '','','','','','',
(
SELECT
COUNT(product.price * line_item.quantity)
FROM (line_item
INNER JOIN product ON line_item.product_id = product.product_id)
);
**The output:**
+-------------+------+--------+-------+-------+---------+-------+
| Revenue | Soda | Liquor | Lemon | Mango | Inhaler | Count |
+-------------+------+--------+-------+-------+---------+-------+
| 0 - 500 | 4 | 0 | 4 | 0 | 1 | 9 |
| 501 - 1000 | 0 | 0 | 0 | 2 | 0 | 2 |
| 1001 - 1500 | 0 | 1 | 0 | 2 | 2 | 5 |
| 1501 - 2000 | 0 | 2 | 0 | 0 | 1 | 3 |
| 2001 - 2500 | 0 | 1 | 0 | 0 | 0 | 1 |
| | | | | | | 20 |
+-------------+------+--------+-------+-------+---------+-------+
Thank for your help.

Have you tried ROLL UP operator? Seems like there's a similar problem to yours: Add a summary row with totals

I'm confused. You are defining the ranges already. If you want a range that encompasses all the values, just add it in:
FROM line_item JOIn
product
USING (product_id) JOIN
( SELECT 0 lowest, 500 highest UNION ALL
SELECT 501 , 1000 UNION ALL
SELECT 1001 , 1500 UNION ALL
SELECT 1501 , 2000 UNION ALL
SELECT 2001 , 2500 UNION ALL
SELECT 0 , 2500
-------^ all encompassing range
) ranges
ON product.price * line_item.quantity BETWEEN ranges.lowest AND ranges.highest
Voila! This also has the summary row.

Related

MySQL inner join limit 1 row from second table

I have 2 (MySQL) tables , exchange table can have 1--n rows in exchitems, when an exchange record has multiple rows, I would like to display the word "multi", but when there is only 1 row, I would like to display the row's details:
First table (exchange):
xid (PK) | cusid | xdate | xref | xtotal
1 | 1 | 2021-10-01 | 345667 | 500
2 | 1 | 2021-10-01 | 345668 | 200
3 | 1 | 2021-10-02 | 345669 | 450
4 | 1 | 2021-10-03 | 345670 | 1200
And the second table (exchitems):
chid (PK) | xid | cusid | xcur| xsell| xbuy
1 | 1 | 1 | USD | 300 | 0
2 | 1 | 1 | EUR | 0 | 400
3 | 2 | 1 | USD | 200 | 0
4 | 3 | 1 | EUR | 0 | 500
5 | 4 | 1 | EUR | 0 | 800
6 | 4 | 1 | USD | 300 | 0
The exchange table must have at least 1 row in exchtiems table, and this is what I would like to get:
xid | cusid | xdate | xref | xcur | xsell | xbuy | xtotal
1 | 1 | 2021-10-01 | 345667 | multi | 0 | 0 | 500
2 | 1 | 2021-10-01 | 345668 | USD | 200 | 0 | 200
3 | 1 | 2021-10-02 | 345669 | EUR | 0 | 500 | 450
4 | 1 | 2021-10-03 | 345670 | multi | 0 | 0 | 1200
Using the following query, i am able to get the all records, but I would like to limit the exchitems table to one row "any row" when there are multiple rows, the count is used to display the word "multi" when it is > 1:
SELECT a.xid,a.xdate,a.xref,a.xtotal,b.xcur,b.xsell,b.xbuy,
(SELECT COUNT(*) FROM exchitems c WHERE c.xid= a.xid) AS tRec
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
ORDER BY a.xdate DESC,a.xid DESC
I have tried many different queries but couldn't achieve what I want.
Any help is highly appreciated.
Untested, but this should work.
SELECT
a.xid,
a.cusid,
a.xdate,
a.xref,
-- if distinct currency in the group is > 1 then the word 'multi', else currency.
IF(COUNT(DISTINCT b.xcur) > 1, 'multi', b.xcur) AS `xcur`,
b.xsell,
b.xbuy,
a.xtotal
FROM exchange a
JOIN exchitems b ON a.xid = b.xid AND a.cusid = b.cusid
WHERE a.cusid = 1
GROUP BY xid -- will let you have exchange rows with groups of exchitems 1:n
ORDER BY a.xdate DESC, a.xid DESC
You can modify your current query to the following:
SELECT a.xid, a.cusid, a.xdate,
a.xref,
GROUP_CONCAT(b.xcur),
MIN(b.xsell),
MIN(b.xbuy),
MAX(a.xtotal)
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
GROUP BY a.xid,a.cusid,a.xdate,a.xref
ORDER BY a.xid;
The result will look like this:
xid
cusid
xdate
xref
GROUP_CONCAT(b.xcur)
MIN(b.xsell)
MIN(b.xbuy)
MAX(a.xtotal)
1
1
2021-10-01
345667
USD,EUR
0
0
500
2
1
2021-10-01
345668
USD
200
0
200
3
1
2021-10-02
345669
EUR
0
500
450
4
1
2021-10-03
345670
EUR,USD
0
0
1200
The part where I use MIN and MAX is according to your expected result. You may want to clarify which value to show there is you have multiple value. If I change that to GROUP_CONCAT:
SELECT a.xid, a.cusid, a.xdate,
a.xref,
GROUP_CONCAT(b.xcur),
GROUP_CONCAT(b.xsell),
GROUP_CONCAT(b.xbuy),
GROUP_CONCAT(a.xtotal)
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
GROUP BY a.xid,a.cusid,a.xdate,a.xref
ORDER BY a.xid;
Then you'll see a more elaborate result:
xid
cusid
xdate
xref
GROUP_CONCAT(b.xcur)
GROUP_CONCAT(b.xsell)
GROUP_CONCAT(b.xbuy)
GROUP_CONCAT(a.xtotal)
1
1
2021-10-01
345667
USD,EUR
300,0
0,400
500,500
2
1
2021-10-01
345668
USD
200
0
200
3
1
2021-10-02
345669
EUR
0
500
450
4
1
2021-10-03
345670
EUR,USD
0,300
800,0
1200,1200
To make the xcur value show multi, you probably can do something like:
SELECT a.xid, a.cusid, a.xdate,
CASE WHEN COUNT(b.xcur) > 1 THEN 'multi' ELSE MAX(b.xcur) END AS xcur,
MIN(b.xsell),
MIN(b.xbuy),
MAX(a.xtotal)
FROM (exchange a
INNER JOIN exchitems b ON a.xid= b.xid AND a.cusid= b.cusid)
WHERE a.cusid = 1
GROUP BY a.xid,a.cusid,a.xdate,a.xref
ORDER BY a.xid;
Demo fiddle

Updating table with sum of selected values from that table itself

I have a table tmp_addit which looks like this
addition_id allowance_id sal_id amount
--------------------------------------------
1 4 1 300
2 5 1 400
3 6 1 200
4 4 2 300
5 5 2 250
6 6 2 150
I want to update the table such that the amount with allowance_id in (5,6) must be added to allowance_id = 4. Eg: I want to get 900 as amount for (sal_id = 1 and allowance_id = 4).
I tried this code but it's not working
update tmp_addit set amount =(select sum(amount) from tmp_addit
where allowance_id in(5,6)
group by salary_id) where allowance_id=4;
Any help?
With this query:
select salary_id, sum(amount) amount
from tmp_addit
where allowance_id in (5, 6)
group by salary_id
you get the sums that you want to add to the column amount.
Then you need to join this query to the table in an UPDATE statement:
update tmp_addit t
inner join (
select salary_id, sum(amount) amount
from tmp_addit
where allowance_id in (5, 6)
group by salary_id
) g on g.salary_id = t.salary_id
set t.amount = t.amount + g.amount
where t.allowance_id = 4;
See the demo.
Result:
| addition_id | allowance_id | salary_id | amount |
| ----------- | ------------ | --------- | ------ |
| 1 | 4 | 1 | 900 |
| 2 | 5 | 1 | 400 |
| 3 | 6 | 1 | 200 |
| 4 | 4 | 2 | 700 |
| 5 | 5 | 2 | 250 |
| 6 | 6 | 2 | 150 |
I'd use a join on the aggregated query by the sal_id:
UPDATE tmp_addit t
JOIN (SELECT sal_id, SUM(amount) AS sum_amount
FROM tmp_addit
WHERE allowance_id IN (5, 6)) s ON t.sal_id = s.sal_id
SET t.amount = t.amount + s.sum_amount
WHERE allowance_id = 4

Mysql Query with multiple subqueries with group by distinct condition

Hi I am a PHP Developer weak in MySQL Medium complex queries make my head fired.
The below is the table vulnerability.
+-----------------------------------------------------------------------------------------------+
| id | webisite_id | low_count| high_count | medium_count | date_time | vul_date |
+-----------------------------------------------------------------------------------------------+
| 20 | 6 | 1 | 1 | 1 | 2018-07-04 09:14:04 | 2018-02-01 |
| 19 | 6 | 30 | 30 | 30 | 2018-07-04 09:13:38 | 2018-01-30 |
| 18 | 6 | 1 | 1 | 1 | 2018-07-04 09:13:16 | 2018-01-01 |
+-----------------------------------------------------------------------------------------------+
This table represent count of low, medium, high - vulnerability count for each website in database. We can enter as many entries for each websites. But the only relevant entry for a website is the latest entry in each month (based on vul_date).
Here I need help I want query which fetch sum of counts low, high, medium of each websites of each month of last 1 year, for example if -> website with id 1 has 1, 2, 3 low, high, medium number of vulnerabilities, on month June and
-> that of with id 2 has 7, 8, 9 respectively the result should be for June 8, 10, 12. And like last 1 year's each month result should be get. If no entry it should be 0.
Note that the entries should be the maximum value of vul_date and if a site has multiple entries on the same vul_date get the latest date_time entry.
I tried to write question as much as simple. hope the question is understood.
Please help me on this
Thanks in advance.
I think below query will work for you.
SELECT
SUM(low_count),
SUM(medium_count),
SUM(high_count),
MONTH(vul_date)
FROM
(SELECT
low_count, medium_count, high_count, vul_date, date_time
FROM
test
WHERE
(website_id , vul_date) IN (SELECT website_id, MAX(vul_date)
FROM test GROUP BY website_id , MONTH(vul_date))) t
WHERE
date_time IN (SELECT MAX(date_time) FROM test GROUP BY website_id , vul_date)
GROUP BY MONTH(vul_date);
What it does is, first finds the latest entry month wise for each website id which is your max vul_date.
SELECT website_id, MAX(vul_date)
FROM test GROUP BY website_id , MONTH(vul_date)
If there are more than one entry for a vul_date, it uses date_time to select maximum value from them. Finally it sums all website date after grouping it month wise.
You can change the above query to get 0 value for those months where there is no entry for any websites.
DROP TABLE IF EXISTS T;
CREATE TABLE T(id INT, website_id INT, low_count INT, high_count INT, medium_count INT, date_time DATETIME, vul_date DATE);
INSERT INTO T VALUES
( 20 , 6 , 1 , 1 , 1, '2018-07-04 09:14:04' , '2018-02-01'),
( 19 , 6 , 30, 30, 30, '2018-07-04 09:13:38' , '2018-01-30'),
( 18 , 6 , 2 , 2 ,2 , '2018-07-04 09:13:16' , '2018-01-01'),
( 17 , 6 , 2 , 2 ,2 , '2018-07-04 09:12:01' , '2018-01-01'),
( 90 , 1,1,2,3,'2017-07-05 01:00:00',' 2017-07-06'),
( 90 , 2,8,9,10,'2017-07-05 01:00:00',' 2017-07-06');
select coalesce(c.yyyymm,d.yyyymm) yyyymm,
coalesce(c.lo,0) lo,
coalesce(c.hi,0) hi,
coalesce(c.med,0) med
from
(
SELECT concat(year(a.vul_date),'-',month(a.vul_date)) yyyymm,
SUM(LOW_COUNT) lo,SUM(HIGH_COUNT) hi,sum(medium_count) med
from
(
select website_id,date_time,vul_date
from t
where date_time = (select max(date_time) from t t1 where t1.website_id = t.website_id and t1.vul_date = t.vul_date)
) a
join
(select website_id, date_time,vul_date,
LOW_COUNT,HIGH_COUNT,medium_count
from t) b
on b.website_id = a.website_id and b.date_time = a.date_time
group by concat(year(a.vul_date),'-',month(a.vul_date))
) c
right join
(select distinct concat(year(dte),'-',month(dte)) yyyymm from dates d
where dte between date_sub(now(), interval 1 year) and now()
) d on d.yyyymm = c.yyyymm
;
Sub query a get the vul_date with the most recent data_time which is then self joined, aggregated and then infilled with missing dates using a right join to a dates/calender table. If you don't have a dates/calender it would be useful for this kind of exercise nut there are alternatives which you can find in SO.
Result
+---------+------+------+------+
| yyyymm | lo | hi | med |
+---------+------+------+------+
| 2017-7 | 9 | 11 | 13 |
| 2017-8 | 0 | 0 | 0 |
| 2017-9 | 0 | 0 | 0 |
| 2017-10 | 0 | 0 | 0 |
| 2017-11 | 0 | 0 | 0 |
| 2017-12 | 0 | 0 | 0 |
| 2018-1 | 32 | 32 | 32 |
| 2018-2 | 1 | 1 | 1 |
| 2018-3 | 0 | 0 | 0 |
| 2018-4 | 0 | 0 | 0 |
| 2018-5 | 0 | 0 | 0 |
| 2018-6 | 0 | 0 | 0 |
| 2018-7 | 0 | 0 | 0 |
+---------+------+------+------+
13 rows in set (0.04 sec)

How to use Mysql SUM with JOIN

I have the following tables:
purchase_tbl
id | productId | purchaseQuantity
---+-----------+-----------------
1 | 1 | 30
2 | 2 | 30
3 | 1 | 10
4 | 2 | 10
sale_tbl
id | productId | saleQuantity
---+-----------+-------------
1 | 1 | 10
2 | 2 | 10
3 | 1 | 10
4 | 2 | 10
5 | 1 | 10
6 | 2 | 10
I need to get the output as this one:
productId | totalPurchasedQuantity| totalSaleQuantity
----------+-----------------------+------------------
1 | 40 | 30
2 | 40 | 30
I'm using this query and how to get the desired result?
SELECT purchase_tbl.productId
, SUM(purchase_tbl.purchaseQuantity) AS totalPurchaseQuantity
, SUM(sale_tbl.saleQuantity) AS totalSaleQuantity
FROM purchase_tbl
JOIN sale_tbl
ON purchase_tbl.productId = sale_tbl.productId
GROUP BY purchase_tbl.productId
Current output
productId | totalPurchaseQuantity | totalSaleQuantity
----------+-----------------------+------------------
1 | 120 | 60
2 | 120 | 60
You better group then in separate query, as table have multiple records for each product, which getting cross product.
SELECT purchase.productId, totalPurchaseQuantity, totalSaleQuantity
FROM
(SELECT purchase_tbl.productId
, SUM(purchase_tbl.purchaseQuantity) AS totalPurchaseQuantity
FROM purchase_tbl
GROUP BY purchase_tbl.productId) purchase
INNER JOIN
(SELECT sale_tbl.productId
, SUM(sale_tbl.saleQuantity) AS totalSaleQuantity
FROM sale_tbl
GROUP BY sale_tbl.productId
) sale ON sale.productId= purchase.productId;
To obtain your expected result you have to do the aggregation on the individual table before joining them. Your query with be like:
SELECT A.productId, A.totalpurchaseQuantity, B.totalsaleQuantity
FROM
(SELECT productId, SUM(purchaseQuantity)
totalpurchaseQuantity FROM purchase_tbl
GROUP BY productId) A JOIN
(SELECT productId, SUM(saleQuantity)
totalsaleQuantity FROM sale_tbl
GROUP BY productId) B ON
A.productId=B.productId;

Two records limit when the same column

I have column:
id | id_contract | price
I'd like to select all with the limit 2 the cheapest offer from one contract.
I use kochana ORM.
Thanks.
For example
1 | 1 | 100 *
2 | 1 | 500
3 | 1 | 300 *
4 | 1 | 900
5 | 2 | 1000
6 | 2 | 100 *
7 | 2 | 200 *
8 | 3 | 10000 *
This is what I want to select.
You can do this in MySQL with the following query:
select t.*
from table t
where (select count(*)
from table t2
where t2.id_contract = t.id_contract and
t2.price <= t.price
) <= 2;