Mysql Counting different column with group by - mysql

I have a table where each row represent a correction with an id, date and a causes
id | date | causes
___________________________________
1 | 2018-01-29 08:49:19 | crash
2 | 2018-08-08 10:03:37 | timeout
3 | 2018-06-26 07:48:12 | other
I use this sql request to get the number of correction by month
SELECT YEAR(date) Year, MONTH(date) Month, count(*) Total FROM correction group by YEAR(date), MONTH(date)
It gives me a result like this
Year | Month | Total
____________________
2018 | 1 | 42
2018 | 2 | 69
2018 | 3 | 50
Is it possible to modify the request to also get the number of result for each causes and have a result like :
Year | Month | Total | crash | timeout | other
____________________________________________
2018 | 1 | 42 | 10 | 12 | 20
2018 | 2 | 69 | 9 | 50 | 10
2018 | 3 | 50 | 10 | 20 | 20

You need conditional aggregation :
SELECT YEAR(date) Year, MONTH(date) Month, COUNT(*) Total,
SUM(causes = 'crash') crash,
SUM(causes = 'timeout') timeout,
SUM(causes = 'other') other
FROM correction c
GROUP BY YEAR(date), MONTH(date);

Related

How to count the number of first transactions of each user per day?

I have a table named transactions which contains all transactions. Something like this:
// transactions
+--------+-------------+---------+-------------+---------------------+
| id | business_id | user_id | amount | created_at |
+--------+-------------+---------+-------------+---------------------+
| 1 | 503 | 12 | 4500 | 2021-04-15 04:22:09 |
| 2 | 33 | 12 | 1200 | 2021-04-17 12:22:10 |
| 3 | 771 | 13 | 400 | 2021-04-18 13:02:18 |
| 4 | 86 | 14 | 7500 | 2021-04-18 16:07:12 |
| 5 | 772 | 13 | 3400 | 2021-04-23 07:11:04 |
| 6 | 652 | 14 | 900 | 2021-04-24 10:35:08 |
| 7 | 567 | 15 | 1000 | 2021-04-24 14:55:11 |
+--------+-------------+---------+-------------+---------------------+
I need to count the number of users that have had their first transaction per day. So here is the expected result:
// The expected result
+------+-------+-------+--------------------------+
| year | month | day | first_transactions_num |
+------+-------+-------+--------------------------+
| 2021 | 04 | 15 | 1 |
| 2021 | 04 | 18 | 2 |
| 2021 | 04 | 24 | 1 |
+------+-------+-------+--------------------------+
Any idea how can I do that?
Here's what I've tried:
SELECT year(created_at) year,
month(created_at) month,
day(created_at) day,
count(1) num
FROM transactions
GROUP BY year, month, day
But my query doesn't care about "the first transaction of each user".
You should aggregate on the results of this query:
SELECT MIN(created_at) created_at
FROM transactions
GROUP BY user_id
which returns the 1st transaction of each user.
So do it like this:
SELECT YEAR(created_at) year,
MONTH(created_at) month,
DAY(created_at) day,
COUNT(*) num
FROM (
SELECT MIN(created_at) created_at
FROM transactions
GROUP BY user_id
) t
GROUP BY year, month, day
Or better:
SELECT YEAR(created_at) year,
MONTH(created_at) month,
DAY(created_at) day,
COUNT(*) num
FROM (
SELECT DATE(MIN(created_at)) created_at
FROM transactions
GROUP BY user_id
) t
GROUP BY created_at
Or if your version of MySql is 8.0+ use COUNT() window function:
SELECT DISTINCT
YEAR(MIN(created_at)) year,
MONTH(MIN(created_at)) month,
DAY(MIN(created_at)) day,
COUNT(*) OVER (PARTITION BY DATE(MIN(created_at))) num
FROM transactions
GROUP BY user_id
See the demo.

How to group data by invoicing dates from 26th of the previous month until 25th of the present month?

The invoicing process groups the services made from the 26th of the past month until the 25th of the present month. So I want to group by client_id how much they have to pay for each month.
+-----------+-----------+------------+-------+--------------+
| person_id | client_id | service_id | price | service_date |
+-----------+-----------+------------+-------+--------------+
| 101 | 1001 | 301 | 1000 | 2019-11-20 |
+-----------+-----------+------------+-------+--------------+
| 106 | 1001 | 301 | 1000 | 2019-11-24 |
+-----------+-----------+------------+-------+--------------+
| 102 | 1002 | 301 | 1000 | 2019-11-25 |
+-----------+-----------+------------+-------+--------------+
| 105 | 1001 | 301 | 1000 | 2019-11-26 |
+-----------+-----------+------------+-------+--------------+
| 103 | 1002 | 301 | 1000 | 2019-12-02 |
+-----------+-----------+------------+-------+--------------+
| 111 | 1002 | 301 | 1000 | 2019-12-05 |
+-----------+-----------+------------+-------+--------------+
From the above data I would expect the following:
+-----------+-----------+------------+
| client_id | total | month |
+-----------+-----------+------------+
| 1001 | 2000 | november |
+-----------+-----------+------------+
| 1002 | 1000 | november |
+-----------+-----------+------------+
| 1001 | 1000 | december |
+-----------+-----------+------------+
| 1002 | 2000 | december |
+-----------+-----------+------------+
EDIT: service_date is date in %d/%m/%Y format
EDIT2: I changed the date format for service_date to %Y-%m-%d
Db-fiddle : https://www.db-fiddle.com/f/v1Xty9c1SAp2PfaWCC4WvK/0
To get your desired results you need to group by the adjusted month (and year). The easiest way to compute these is by taking advantage of the fact that MySQL treats a boolean value as 1 or 0 in a numeric context, thus we can use:
(MONTH(service_date) + (DAY(service_date) >= 26) - 1) % 12 + 1 AS month_num
YEAR(service_date) + (MONTH(service_date) = 12 AND DAY(service_date) >= 26) AS year
Since we have to use these values to generate the month name (but we need the numeric values for ordering), it's easiest to compute them in a subquery (or a CTE if you are using MySQL 8+):
SELECT client_id,
total,
year,
MONTHNAME(CONCAT_WS('-', year, month_num, '01')) AS month
FROM (SELECT client_id,
SUM(price) AS total,
(MONTH(service_date) + (DAY(service_date) >= 26) - 1) % 12 + 1 AS month_num,
YEAR(service_date) + (MONTH(service_date) = 12 AND DAY(service_date) >= 26) AS year
FROM mytable
GROUP BY client_id, month_num, year
) t
ORDER BY client_id, year, month_num
Output (for my expanded demo)
client_id total year month
1001 2000 2019 November
1001 1000 2019 December
1001 2000 2020 January
1002 1000 2019 November
1002 2000 2019 December
1002 1000 2020 January
Demo on dbfiddle

Displaying groups having max number of occurence

t_table looks like:
+-----------+---------+--------------+------------------+-----------------------+----------------------------------+
| pk_IdLoan | fk_IdCar| fk_IdCustomer| fk_Source_Agency | fk_Destination_Agency | RentalDate | DeliveryDate | Cost |
+-----------+---------+--------------+------------------+-----------------------+----------------------------------+
I wrote a query:
(SELECT fk_IdCustomer, MONTHNAME(RentalDate) AS Month, YEAR(RentalDate) As Year, COUNT(*)
FROM t_loan
GROUP BY fk_IdCustomer, Month, Year);
which results in
+---------------+-------------+------+----------+
| fk_IdCustomer | Month | Year | COUNT(*) |
+---------------+-------------+------+----------+
| 1 | July | 2016 | 3 |
| 1 | November | 2017 | 1 |
| 1 | September | 2016 | 7 |
| 5 | May | 2016 | 1 |
| 6 | January | 2016 | 1 |
| 6 | September | 2017 | 2 |
+---------------+-------------+------+----------+
Now I want to get these months and years for each customer which result in highest COUNT(*), f.e.:
+---------------+-------------+------+----------+
| fk_IdCustomer | Month | Year | COUNT(*) |
+---------------+-------------+------+----------+
| 1 | September | 2016 | 7 |
| 5 | May | 2016 | 1 |
| 6 | September | 2017 | 2 |
+---------------+-------------+------+----------+
How to achieve this?
This is a bit painful in MySQL, which doesn't support CTEs or window functions. One method is:
SELECT fk_IdCustomer, MONTHNAME(RentalDate) AS Month,
YEAR(RentalDate) As Year, COUNT(*) as cnt
FROM t_loan l
GROUP BY fk_IdCustomer, Month, Year
HAVING cnt = (SELECT COUNT(*)
FROM t_loan l2
WHERE l2.fk_IdCustomer = l.fk_IdCustomer
GROUP BY MONTHNAME(RentalDate), YEAR(RentalDate)
ORDER BY COUNT(*) DESC
LIMIT 1
);
Note: If there are duplicates, you will get all matching values.

Calculating month over month numbers

I searched around and found solutions, but they didn't work with MySQL because they used functions from other software.
I'm trying to show month-over-month growth for the current year (starting January), though knowing how to check within the past year might come in handy in the future as well.
What the "orders" table might look like:
+-----------+-------+
| Month | Sales |
+-----------+-------+
| 1-1-2017 | 3 |
| 1-5-2017 | 9 |
| 2-16-2017 | 10 |
| 2-16-2017 | 13 |
| 3-7-2017 | 25 |
| 4-29-2017 | 22 |
+-----------+-------+
What I want the query result to look like:
+----------+-------+--------+
| Month | Sales | Growth |
+----------+-------+--------+
| January | 12 | |
| February | 23 | 91.66% |
| March | 25 | 8.69% |
| April | 22 | -12% |
+----------+-------+--------+
Is there a simple way to do this?
You can do something like that:
SELECT
thisMonth.MonthOnly,
SUM(thisMonth.Sales) AS ThisMonthSales,
(SUM(thisMonth.Sales) / SUM(lastMonth.Sales) - 1) * 100 AS Growth
FROM
(
SELECT STR_TO_DATE(DATE_FORMAT(Month, '%Y%m01'), '%Y%m%d') AS MonthOnly,
SUM(Sales) AS Sales
FROM orders
GROUP BY DATE_FORMAT(Month, '%Y%m01')
) thisMonth
LEFT OUTER JOIN
(
SELECT STR_TO_DATE(DATE_FORMAT(DATE_ADD(Month, INTERVAL 1 MONTH), '%Y%m01'), '%Y%m%d') AS MonthOnly,
SUM(Sales) AS Sales
FROM orders
GROUP BY DATE_FORMAT(Month, '%Y%m01')
) lastMonth
ON thisMonth.MonthOnly = lastMonth.MonthOnly
GROUP BY thisMonth.MonthOnly

How to Count the number of rows within each distinct group?

Given the following MySQL table:
| id | category | Hour | quantity|
| 0 | Sunday | 10 | 32 |
| 0 | Sunday | 11 | 19 |
| 0 | Sunday | 12 | 48 |
| 0 | Sunday | 19 | 7 |
| 1 | Monday | 09 | 45 |
| 1 | Monday | 10 | 17 |
| 1 | Monday | 12 | 18 |
| 2 | Tuesday | 08 | 16 |
| 2 | Tuesday | 09 | 39 |
| 2 | Tuesday | 10 | 24 |
| 2 | Tuesday | 11 | 37 |
| 2 | Tuesday | 12 | 40 |
I need to compute a fifth column which must be the division of "quantity" by the number of rows of id: for 0 there are 4 rows, for 1 3 rows, for 2 5 rows.
| id | category | Hour | quantity| avg |
| 0 | Sunday | 10 | 32 | 8 |
| 0 | Sunday | 11 | 19 | 4.75 |
| 0 | Sunday | 12 | 48 | 12 |
| 0 | Sunday | 19 | 7 | 1.75 |
| 1 | Monday | 09 | 45 | 15 |
| 1 | Monday | 10 | 17 | 5.7 |
| 1 | Monday | 12 | 18 | 6 |
| 2 | Tuesday | 08 | 16 | 3.2 |
| 2 | Tuesday | 09 | 39 | 7.8 |
| 2 | Tuesday | 10 | 24 | 4.8 |
| 2 | Tuesday | 11 | 37 | 7.4 |
| 2 | Tuesday | 12 | 40 | 8 |
How can I get the result in a MySQL query?
The first table, is the result of this query:
select id, category, Hour, count(*) as quantity
FROM table_1
GROUP by id, Hour ORDER by id, Hour;
This what I tried, in order to get the number of rows for the occurrence of each id, however I get a large number, the count of id=0 occurrences instead of id=0 rows in the previous query:
select id, Hour, count(id) as q
FROM table_1
GROUP by id
This is mySql 5.6.
This is really quite ugly and cumbersome, but it was the only way to get the results without having a primary key to work with:
SELECT
t.id,
t.category,
t.hour,
quantity,
ROUND(quantity/count,2) AS avg
FROM table_1 t
JOIN (SELECT
id, Hour, count(*) as quantity
FROM table_1
GROUP by id, category, Hour) AS qty
ON t.id = qty.id AND t.hour = qty.hour
JOIN (SELECT
id, count(distinct hour) as count
FROM table_1
GROUP BY id) as counts
ON t.id = counts.id
GROUP BY id, hour;
It seems to be working locally for me, at least, with guessing what your original dataset looks like.
There may well be a simpler way, however.
Edit: On second check, the 'quantity' subquery doesn't really add much that I can see, so this can be replaced with a 'count(*)', making a more optimal query:
SELECT
t.id,
t.category,
t.hour,
count(*) as quantity,
ROUND(count(*)/count,2) AS avg
FROM table_1 t
JOIN (SELECT
id, count(distinct hour) as count
FROM table_1
GROUP BY id) as counts
ON t.id = counts.id
GROUP BY id, hour;
You need to do the counting in a subquery that just groups by id. Join the subquery to the main query and do the division.
SELECT id, category, hour, COUNT(*) AS quantity, COUNT(*)/count AS avg
FROM table_1
JOIN (SELECT id, COUNT(DISTINCT hour) AS count
FROM table_1
GROUP BY id) AS counts
ON table_1.id = counts.id
GROUP BY table_1.id, table_1.hour
ORDER BY table_1.id, table_1.hour