GROUP BY on column, aggregation and transform results - mysql

i have the following table:
date | data_type | data_value
01-01-2023 | max | 4
02-01-2023 | min | 7
03-01-2023 | avg | 54
04-01-2023 | max | 8
05-01-2023 | min | 98
06-01-2023 | avg | 23
01-02-2023 | max | 65
02-02-2023 | min | 2
03-02-2023 | avg | 45
04-02-2023 | max | 22
05-02-2023 | min | 56
06-02-2023 | avg | 65
01-03-2023 | max | 7
02-03-2023 | min | 5
03-03-2023 | avg | 23
04-03-2023 | max | 65
05-03-2023 | min | 51
06-03-2023 | avg | 33
from the following table I would need to group by current month and from it take the max of its values ​​with data_type 'max', the lesser of those of type 'min' and finally the average of the types 'avg', as follows:
mounth | max | min | avg
1 | 8 | 7 | 38,5
2 | 65 | 2 | 55
3 | 65 | 5 | 28
I therefore wanted to ask you which is the most performing SQL query in order to resolve the request.
Thank you all.

Use conditional aggregation as the following:
select year(date_) as 'yaer',
month(date_) as 'month',
min(case when data_type = 'min' then data_value end) as 'min',
max(case when data_type = 'max' then data_value end) as 'max',
avg(case when data_type = 'avg' then data_value end) as 'avg'
from tbl
group by year(date_), month(date_)
Or, if you want to aggregate for a specific year:
select month(date_) as 'month',
min(case when data_type = 'min' then data_value end) as 'min',
max(case when data_type = 'max' then data_value end) as 'max',
avg(case when data_type = 'avg' then data_value end) as 'avg'
from tbl
where year(date_) = 2023
group by month(date_)
See demo.

Related

Mysql Query numbering groups rows

I looked for days for a way to show a compact continuous numbering for group rows.
The products can be single type in the carton or mix together. Some of the carton markings are already printed so I cannot rearrange carton markings.
I have this table:
+-----+------------+--------+-----------+
| qty | product_id | Type | carton_no |
+-----+------------+--------+-----------+
| 18 | 111 | single | 1 |
| 18 | 111 | single | 2 |
| 18 | 111 | single | 3 |
| 48 | 115 | single | 4 |
| 48 | 115 | single | 5 |
| 48 | 115 | single | 6 |
| 36 | 119 | single | 7 |
| 36 | 119 | single | 8 |
| 18 | 111 | single | 9 |
| 36 | 119 | single | 10 |
| 16 | 199 | single | 11 |
| 16 | 199 | single | 12 |
| 4 | 111 | mix | 13 |
| 4 | 115 | mix | 13 |
| 4 | 119 | mix | 13 |
| 4 | 199 | mix | 13 |
+-----+------------+--------+-----------+
The documents processor needs a view like this:
+-----------+-----+------------+--------+
| Numbering | QTY | product_id | Type |
+-----------+-----+------------+--------+
| 1-4 | 72 | 111 | single |
| 5-7 | 144 | 115 | single |
| 8-10 | 108 | 119 | single |
| 11-12 | 32 | 199 | single |
| 13 | 4 | 111 | mix |
| 13 | 4 | 115 | mix |
| 13 | 4 | 119 | mix |
| 13 | 4 | 199 | mix |
+-----------+-----+------------+--------+
The numbering are actually counting of total cartons for each product_id order by type, product_id ASC.
Any ideas?
WITH
cte1 AS (
SELECT qty,
product_id,
Type,
carton_no,
CASE WHEN product_id = LAG(product_id) OVER (ORDER BY carton_no)
THEN 0
ELSE 1
END new_group
FROM src ),
cte2 AS (
SELECT qty,
product_id,
Type,
carton_no,
SUM(new_group) OVER (ORDER BY carton_no) group_num
FROM cte1
)
SELECT CASE WHEN MAX(carton_no) > MIN(carton_no)
THEN CONCAT(MIN(carton_no), '-', MAX(carton_no))
ELSE MIN(carton_no)
END Numbering ,
SUM(qty) QTY,
product_id,
ANY_VALUE(Type) Type
FROM cte2
GROUP BY group_num, product_id;
fiddle
WITH
cte1 AS (
SELECT qty,
product_id,
Type,
carton_no,
CASE WHEN product_id = LAG(product_id) OVER (ORDER BY type desc, product_id)
THEN 0
ELSE 1
END new_group
FROM src order by type desc, product_id ),
cte2 AS (
SELECT qty,
product_id,
Type,
carton_no,
SUM(new_group) OVER (ORDER BY type desc, product_id) group_num
FROM cte1 ),
cte3 AS (
SELECT SUM(qty) QTY,
product_id,
Type,
group_num,
carton_no,
count(group_num) sum,
LAG(count(group_num)) OVER () prevsum
FROM cte2 group by group_num order by type desc, carton_no
)
SELECT CASE WHEN group_num = 1 THEN CONCAT(group_num,'-', sum)
WHEN group_num <> 1 and Type = "mix" and LAG(carton_no) OVER (ORDER BY carton_no) <> carton_no THEN CONCAT(SUM(prevsum) OVER (ORDER BY type desc, product_id) + 1)
WHEN group_num <> 1 and Type = "mix" and LAG(carton_no) OVER (ORDER BY carton_no) = carton_no THEN CONCAT(LAG(carton_no) OVER (ORDER BY carton_no))
WHEN group_num <> 1 and Type = "single" THEN CONCAT(SUM(prevsum) OVER (ORDER BY type desc, product_id) + 1,'-', SUM(prevsum) OVER (ORDER BY type desc, product_id) + sum)
END numbering,
qty,
product_id,
type
FROM cte3
I think I solved the problem, but the code is working in Workbench, but not in fiddle. Any idea how to compress it more and not working in fiddle?

How can we pivot in this scenario in MySQL

I have this table:
+----------+--------+-----+--------+--------+
| personId | Name | Age | height | weight |
+----------+--------+-----+--------+--------+
| 1 | Aritra | 20 | 5.6 | 56 |
| 1 | Aritra | 30 | 5.6 | 76 |
+----------+--------+-----+--------+--------+
But require the output to be like this:
+---------------+----------+--------------+----------+
| AttributeName | personId | Presentvalue | OldValue |
+---------------+----------+--------------+----------+
| Height | 1 | 5.6 | 5.6 |
| weight | 1 | 76 | 56 |
+---------------+----------+--------------+----------+
I tried to get this using pivot but am facing a problem. How can I do this, with or without using pivot?
Thank you.
union all is probably the simplest method:
select 'Height' as attributeName, personId,
(case when age = 30 then height end) as newValue,
(case when age = 20 then height end) as oldValue
from t
union all
select 'Weight' as attributeName, personId,
(case when age = 30 then weight end) as newValue,
(case when age = 20 then weight end) as oldValue;

Mysql Join Union Query By Date

I have 2 tables
and i want the result as this using join query. I don't have any Idea
Join Query By
TBL_SUCCESS_ORDER
------------------------
id | date | amount
-------------------------
1 | 2017-01-01 | 1000
2 | 2017-01-06 | 300
3 | 2017-01-29 | 50
4 | 2017-02-02 | 100
5 | 2017-02-16 | 400
6 | 2017-03-01 | 500
7 | 2017-04-03 | 1200
TBL_FAIL_ORDER
------------------------
id | date | amount
-------------------------
1 | 2017-01-03 | 400
2 | 2017-01-07 | 300
3 | 2017-01-24 | 50
4 | 2017-02-02 | 100
5 | 2017-04-07 | 200
RESULT
------------------------------------------------------------------
year | month | sum_of_succes_amount | sum_of_fail_amount | total
------------------------------------------------------------------
2017 | January | 1350 | 750 | 2100
2017 | Febuary | 500 | 100 | 600
2017 | March | 500 | 0 | 500
2017 | April | 1200 | 200 | 1400
I been stack for a whole week i did not get it . When i connect api json
TBL_PENDING_ORDER
------------------------
id | date | amount
-------------------------
1 | 2017-04-03 | 600
2 | 2017-05-07 | 600
RESULT
-----------------------------------------------------------------------------------------
year | month | sum_of_succes_amount | sum_of_fail_amount | sum_of_pending_amount |total
-----------------------------------------------------------------------------------------
2017 | January | 1350 | 750 | 0 | 2100
2017 | Febuary | 500 | 100 | 0 | 600
2017 | March | 500 | 0 | 0 | 500
2017 | April | 1200 | 200 | 600 | 2000
2017 | May | 0 | 0 | 600 | 600
What if I add The third table ? TBL_PENDING_ORDER
You can use the following solution using UNION ALL AND GROUP BY:
SELECT
YEAR(x.date),
MONTH(x.date),
SUM(CASE WHEN x.type = 'S' THEN amount ELSE 0 END) AS sum_of_succes_amount,
SUM(CASE WHEN x.type = 'F' THEN amount ELSE 0 END) AS sum_of_fail_amount,
SUM(amount) AS total
FROM (
SELECT id, date, amount, 'S' AS type FROM TBL_SUCCESS_ORDER
UNION ALL
SELECT id, date, amount, 'F' AS type FROM TBL_FAIL_ORDER
)x GROUP BY YEAR(x.date), MONTH(x.date)
You want to add the third table TBL_PENDING_ORDER?
SELECT
YEAR(x.date),
MONTH(x.date),
SUM(CASE WHEN x.type = 'S' THEN amount ELSE 0 END) AS sum_of_succes_amount,
SUM(CASE WHEN x.type = 'F' THEN amount ELSE 0 END) AS sum_of_fail_amount,
SUM(CASE WHEN x.type = 'P' THEN amount ELSE 0 END) AS sum_of_pending_amount,
SUM(amount) AS total
FROM (
SELECT id, date, amount, 'S' AS type FROM TBL_SUCCESS_ORDER
UNION ALL
SELECT id, date, amount, 'F' AS type FROM TBL_FAIL_ORDER
UNION ALL
SELECT id, date, amount, 'P' AS type FROM TBL_PENDING_ORDER
)x GROUP BY YEAR(x.date), MONTH(x.date)

Select rows where total elapsed value is less than or equal to

id | name | elapsed
1 James 0
2 John 60
3 Kerry 60
4 Janet 60
5 Katie 60
Based on the results above , how can I select names where the 'elapsed' value total is less than or equal to 120? For those results, that would involve selecting the names 'James', 'John' and 'Kerry'. If I changed the total elapsed to 180 it would also select 'Janet'.
How can I construct a query that returns this?
This should work:
SELECT
t1.id,
t1.name,
t1.elapsed
FROM your_table t1
INNER JOIN your_table t2 ON (t1.id >= t2.id)
GROUP BY t1.id, t1.name, t1.elapsed
HAVING SUM(t2.elapsed) <= 120
Here's something to think about...
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT i,#x:=#x+i running FROM ints i,(SELECT #x:=0)var ORDER BY i;
+---+----------+
| i | running |
+---+----------+
| 0 | 0 |
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
| 6 | 21 |
| 7 | 28 |
| 8 | 36 |
| 9 | 45 |
+---+----------+
SELECT
*
FROM (
SELECT '1' AS ID, 'James' AS Name, 0 AS Elapsed
UNION ALL SELECT '2', 'John', 60
UNION ALL SELECT '3', 'Kerry', 60
UNION ALL SELECT '4', 'Janet', 60
UNION ALL SELECT '5', 'Katie', 60
) AS X
WHERE 1=1
AND (
SELECT SUM(Elapsed)
FROM (
SELECT '1' AS ID, 'James' AS Name, 0 AS Elapsed
UNION ALL SELECT '2', 'John', 60
UNION ALL SELECT '3', 'Kerry', 60
UNION ALL SELECT '4', 'Janet', 60
UNION ALL SELECT '5', 'Katie', 60
) AS Y
WHERE Y.ID <= X.ID
) <= 180
ORDER BY ID

Group by historical date for each date in the provided range

Am stuck with this for days. wanna group a sql result set by historical dates. so want a result to be grouped by date from each date in the date specified range all the way back in time. Here is my sql so far, but it groups the result by date instead of historical date.
Please help!
SELECT ledger.transdate,
sum(case when transcodes.dtcr = 'C' then ledger.amount else 0 end) Credit,
sum(case when transcodes.dtcr = 'D' then ledger.amount else 0 end) Debit,
sum(case when transcodes.dtcr = 'C' then ledger.amount else 0 end) -
sum(case when transcodes.dtcr = 'D' then ledger.amount else 0 end) Balance
FROM
LEDGER
INNER JOIN TRANSCODES ON (LEDGER.TRANSCODE = TRANSCODES.TRANSCODE)
where ledger.transdate >= '2013-02-28' and ledger.transdate <= '2013-03-01'
group by ledger.transdate
Maybe consider this example...
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT x.i, SUM(y.i) running FROM ints x JOIN ints y ON y.i <= x.i GROUP BY i;
+---+---------+
| i | running |
+---+---------+
| 0 | 0 |
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
| 6 | 21 |
| 7 | 28 |
| 8 | 36 |
| 9 | 45 |
+---+---------+
If I understood you correctly, and you want to group by date-only part of transdate field, you can use Date() function, i.e. ... group by Date(transdate)
Your answer can be found here:
http://www.codeproject.com/Articles/300785/Calculating-simple-running-totals-in-SQL-Server