Displaying groups having max number of occurence - mysql

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.

Related

MySQL Query to get the monthly data difference

select * from new_joiner;
+------+--------------+
| id | date_of_join |
+------+--------------+
| 1 | 2020-01-10 |
| 2 | 2020-01-02 |
| 3 | 2020-01-05 |
| 4 | 2020-02-10 |
| 5 | 2020-02-11 |
| 6 | 2020-07-11 |
| 7 | 2020-07-11 |
| 8 | 2020-07-11 |
| 9 | 2020-07-11 |
| 10 | 2020-07-11 |
| 11 | 2020-05-01 |
| 12 | 2020-05-02 |
| 13 | 2020-05-03 |
| 14 | 2020-05-04 |
| 15 | 2020-05-05 |
| 16 | 2020-05-05 |
| 17 | 2020-05-06 |
+------+--------------+
select MONTHNAME(date_of_join) as MONTHNAME,
count(id) as JOINEE
from new_joiner
where MONTH(date_of_join)>=1
group by MONTH(date_of_join);
+-----------+--------+
| MONTHNAME | JOINEE |
+-----------+--------+
| January | 3 |
| February | 2 |
| May | 7 |
| July | 5 |
+-----------+--------+
I want a query that gives me the monthly data change compare to previous month.
For example: new joinee in Jan was 3, and in Feb it was 2, so compare to Jan in Feb month -1 joined, so the query should output me:
+-----------+-------------+
| MONTHNAME | JOINEE_DIFF |
+-----------+-------------+
| February | -1 |
| Mar | -2 |
| April | 0 |
| May | 7 |
| June | -7 |
| July | 5 |
| Aug | -5 |
| Sep | 0 |
| Oct | 0 |
| Nov | 0 |
| Dec | 0 |
+-----------+-------------+
Ignore Jan as it doesn't have a previous month and assume we have data only for a given year say 2020. Require data for all months from Feb to Dec.
Assuming you have data for every month, you can use lag():
select MONTHNAME(date_of_join) as MONTHNAME,
count(id) as JOINEE,
(count(*) - lag(count(*)) over (order by min(date_of_join)) as diff
from new_joiner
where MONTH(date_of_join) >= 1
group by MONTH(date_of_join);
Note that using months without years if fraught with peril. Also, the month() of any well-formed date should be larger than 1.
All this suggests a query more like:
select *
from (select MONTHNAME(date_of_join) as MONTHNAME,
count(id) as JOINEE,
(count(*) - lag(count(*)) over (order by min(date_of_join)) as diff,
min(date_of_join) as min_date_of_join
from new_joiner
where date_of_join >= '2020-01-01' and date_of_join < '2021-01-01'
group by MONTH(date_of_join)
) t
where diff is not null
order by min_date_of_join;
Use a correlated subquery to get the number of joinees of previous month and subtract it:
SELECT
t.monthname,
joinee - (SELECT COUNT(*) FROM new_joiner WHERE MONTH(date_of_join) = t.month - 1) JOINEE_DIFF
FROM (
SELECT MONTH(date_of_join) month, MONTHNAME(date_of_join) monthname,
COUNT(id) joinee
FROM new_joiner
GROUP BY month, monthname
) t
WHERE t.month > 1;

MySQL: Tagging 1 to each unique occurence during SELECT query

Sample table tbl_name:
| ID | Name | Month | Quarter | Year |
| 1 | A | Jan | 1 | 2019 |
| 1 | A | Feb | 1 | 2019 |
| 2 | B | May | 2 | 2019 |
| 3 | C | May | 2 | 2018 |
Hi, this is the table I extract using SELECT query. I can find the distinct name per year using SELECT distinct name, year FROM tbl_name; But I'm trying to add a column during SELECT query to identify or count the unique occurrence per year of the name.
Expected:
| ID | Name | Month | Quarter | Year | Unique Count |
| 1 | A | Jan | 1 | 2019 | 1 |
| 1 | A | Feb | 1 | 2019 | 0 |
| 2 | B | May | 2 | 2019 | 1 |
| 3 | C | May | 2 | 2018 | 1 |
I tried splitting into two queries - one select everything; the other select just distinct and join them together but that will introduce duplicates. Is there a way to do this using SQL?
If you are running MySQL 8.0, you can use row_number() to flag the appearance of a name in a year:
select
t.*,
(row_number() over(
partition by name, year
order by str_to_date(concat(year, '-', month), '%Y-%b')
) = 1) unique_count
from mytable t
Note: do consider fixing the storage strategy of your date columns. Rather than splitting the information over several columns, you would better have a unique column in the relevant DATE datatype to store that information. That would save you the pain of recomposing the date when you need it.
Demo on DB Fiddle:
ID | Name | Month | Quarter | Year | unique_count
-: | :--- | :---- | ------: | ---: | -----------:
1 | A | Feb | 1 | 2019 | 1
1 | A | Jan | 1 | 2019 | 0
2 | B | May | 2 | 2019 | 1
3 | C | May | 2 | 2018 | 1
You can try this below logic-
DEMO HERE
WITH your_table(ID,Name,Month,Quarter,Year)
AS
(
SELECT 1,'A','Jan',1,2019 UNION ALL
SELECT 1,'A','Feb',1,2019 UNION ALL
SELECT 2,'B','May',2,2019 UNION ALL
SELECT 3,'C','May',2,2018
)
,CTE AS
(
SELECT *,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Quarter,Year) RN
FROM your_table
)
SELECT ID,Name,Month,Quarter,Year,
CASE WHEN RN = 1 THEN 1 ELSE 0 END Unique_Count
FROM CTE
Output is-
ID Name Month Quarter Year Unique_Count
1 A Jan 1 2019 1
1 A Feb 1 2019 0
2 B May 2 2019 1
3 C May 2 2018 1

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

get last record per category filter by date

I have this tables SQL Fiddle
items table:
+----+----------+
| id | name |
+----+----------+
| 1 | Facebook |
| 2 | Twitter |
| 3 | Amazon |
+----+----------+
prices table:
+----+-----------+---------+-----------------------------+
| id | buy | item_id | created_at |
+----+-----------+---------+-----------------------------+
| 1 | 43000 | 1 | June, 18 2014 17:31:04+0000 |
| 2 | 44000 | 1 | June, 19 2014 17:31:04+0000 |
| 3 | 30000 | 2 | June, 20 2014 17:31:04+0000 |
| 4 | 33000 | 2 | June, 21 2014 17:31:04+0000 |
| 5 | 20000 | 3 | June, 22 2014 17:31:04+0000 |
| 6 | 21000 | 3 | June, 23 2014 17:31:04+0000 |
+----+-----------+---------+-----------------------------+
I want to get last prices per item and one before last price's buy field based on a price date
Desired output:
+----+---------+-----------------+---------+
| id | buy | last_before_buy | item_id |
+----+---------+-----------------+---------+
| 10 | 45000 | 43000 | 3 |
| 7 | 33000 | 31000 | 2 |
| 4 | 23000 | 23000 | 1 |
+----+---------+-----------------+---------+
Here's another way to do it:
select a.id, a.buy, b.buy last_before_buy, a.item_id
from (select * from prices WHERE (created_at <= NOW() - INTERVAL 5 DAY) order by id desc) a
join (select * from prices order by id desc) b on a.item_id = b.item_id and a.id > b.id
group by a.item_id;
fiddle
You can do this with the substring_index()/group_concat() trick:
select max(id) as id,
substring_index(group_concat(buy order by created_at desc), ',', 1) as buy,
substring_index(substring_index(group_concat(buy order by created_at desc), ',', 2), ',', -1) as lastbuy,
item_id
from prices p
group by item_id;

Fetching Total Sale of each day and month from database

I have table with column c_date as datetime, total as int type in mysql, and i want to print out sale of each day, and total sale of each month, and total sale annually including day, month, year where there was no sale.
Currently for daily sale, I am running below query :
mysql> select date(c_date) as date, sum(total) as total_sale from sale group by date;
+------------+------------+
| date | total_sale |
+------------+------------+
| 2013-10-3 | 798 |
| 2013-10-6 | 114 |
+------------+------------+
but, i want something like this :
mysql> select date(c_date) as date, sum(total) as total_sale from sale group by date;
+------------+------------+
| date | total_sale |
+------------+------------+
| 2013-10-1 | 0 |
| 2013-10-2 | 0 |
| 2013-10-3 | 798 |
| 2013-10-4 | 0 |
| 2013-10-5 | 0 |
| 2013-10-6 | 114 |
+------------+------------+
and for Monthly, I am getting this :
mysql> select c_date, month(c_date) as month, year(c_date) as year, sum(total) as total from sale group by c_date order by c_date;
+---------------------+-------+------+-------+
| c_date | month | year | total |
+---------------------+-------+------+-------+
| 2013-10-3 02:40:06 | 10 | 2013 | 228 |
| 2013-10-3 02:41:58 | 10 | 2013 | 114 |
| 2013-10-3 02:44:36 | 10 | 2013 | 114 |
| 2013-10-3 02:46:40 | 10 | 2013 | 114 |
| 2013-10-3 02:49:15 | 10 | 2013 | 114 |
| 2013-10-3 02:53:36 | 10 | 2013 | 114 |
| 2013-10-6 07:43:27 | 10 | 2013 | 114 |
+---------------------+-------+------+-------+
But i want something like this :
mysql> select c_date, month(c_date) as month, year(c_date) as year, sum(total) as total from sale group by c_date order by c_date;
+---------------------+-------+------+-------+
| c_date | month | year | total |
+---------------------+-------+------+-------+
| 2013-1-3 02:40:06 | 1 | 2013 | 0 |
| 2013-2-3 02:41:58 | 2 | 2013 | 0 |
| 2013-3-3 02:44:36 | 3 | 2013 | 0 |
| 2013-4-3 02:46:40 | 4 | 2013 | 0 |
| 2013-5-3 02:49:15 | 5 | 2013 | 0 |
| 2013-6-3 02:53:36 | 6 | 2013 | 0 |
| 2013-7-6 07:43:27 | 7 | 2013 | 0 |
| 2013-8-3 02:44:36 | 8 | 2013 | 0 |
| 2013-9-3 02:46:40 | 9 | 2013 | 0 |
| 2013-10-3 02:49:15 | 10 | 2013 | 912 |
| 2013-11-3 02:53:36 | 11 | 2013 | 0 |
| 2013-12-6 07:43:27 | 12 | 2013 | 0 |
+---------------------+-------+------+-------+
Is this possible with MysqL ?
Since it's impossible to use sequences in MySQL (actually, they simply do not exist there), you'll have to create your dates range table first. That will be like:
CREATE TABLE dates_range (record_date DATE)
and then fill this table with dates, starting from minimum among dates, that exist in your sale table and till maximum.
After this, using SQL LEFT JOIN operator, you'll be able to aggregate your data like this:
SELECT
YEAR(dates_range.record_date),
MONTH(dates_range.record_date),
DAY(dates_range.record_date),
COALESCE(SUM(sale.total), 0) AS total_sum
FROM
dates_range
LEFT JOIN sale
ON dates_range.record_date=DATE(sale.c_date)
GROUP BY
YEAR(dates_range.record_date),
MONTH(dates_range.record_date),
DAY(dates_range.record_date)
it looks to me that you need an outer join with a calendar table.
Imagine a calendar table populated like:
Calendar
Year Month Day
2013 201310 2013-10-1
2013 201310 2013-10-2
...
Then you can write a query like
select date(c_day) as date,
sum(total) as total_sale
from calendar c
left outer join sale s
on c.day = s.c_date
where c.month = 201310
group by c_day
having c_day <= max(s.c_date); -- this is to avoid to show all
-- days for October