Fetching Total Sale of each day and month from database - mysql

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

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;

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.

MYSQL: Left JOIN from two SELECT to "fill gaps" in dates

Let's say I have a table "calendar"
+------------+
| day_date |
+------------+
| 2015-01-01 |
| 2015-01-02 |
| 2015-01-03 |
| .......... |
| 2015-07-14 |
| 2015-07-15 |
+------------+
With this query I can select the WEEK (that I need)
SELECT WEEK(day_date,1) AS NUM_WEEK,
YEAR(day_date) AS YEAR,
STR_TO_DATE(CONCAT(YEAR(day_date),WEEK(day_date,1),' Monday'), '%X%V %W') AS date_start
FROM calendar
GROUP BY NUM_WEEK
And this is the result:
+----------+------+------------+
| NUM_WEEK | YEAR | date_start |
+----------+------+------------+
| 29 | 2015 | 2015-07-20 |
| 30 | 2015 | 2015-07-27 |
| 31 | 2015 | 2015-08-03 |
| 32 | 2015 | 2015-08-10 |
| 33 | 2015 | 2015-08-17 |
| 34 | 2015 | 2015-08-24 |
| 35 | 2015 | 2015-08-31 |
| 36 | 2015 | 2015-09-07 |
| 37 | 2015 | 2015-09-14 |
| 38 | 2015 | 2015-09-21 |
| 39 | 2015 | 2015-09-28 |
| 40 | 2015 | 2015-10-05 |
| 41 | 2015 | 2015-10-12 |
| 42 | 2015 | 2015-10-19 |
| 43 | 2015 | 2015-10-26 |
+----------+------+------------+
Now I have another table:
+----+------------+--------+---------------------+
| id | id_account | amount | date_transaction |
+----+------------+--------+---------------------+
| 1 | 283 | 150 | 2015-06-21 15:50:47 |
| 2 | 283 | 47.74 | 2015-07-23 15:55:44 |
| 3 | 281 | 21.55 | 2015-08-24 12:27:11 |
| 4 | 283 | 11.22 | 2015-08-25 10:00:54 |
+----+------------+--------+---------------------+
They are gaps in date.
With a similar query:
SELECT WEEK(date_transaction,1) AS NUM_WEEK,
YEAR(date_transaction) AS YEAR,
STR_TO_DATE(CONCAT(YEAR(date_transaction),WEEK(date_transaction,1),' Monday'), '%X%V %W')
AS date_start,
transaction.id_account,
SUM(amount) as total FROM transaction
INNER JOIN account ON account.id_account = transaction.id_account
WHERE amount > 0 AND transaction.id_account
IN ( SELECT id_account FROM account WHERE id_customer = 12 )
GROUP BY id_account, WEEK(date_transaction,1)
I obtain this result (probably data are not accurate, referring to previous tables, just to explain).
+----------+------+------------+-----------+----------+
| NUM_WEEK | YEAR | date_start | idAccount | total |
+----------+------+------------+-----------+----------+
| 29 | 2015 | 2015-07-20 | 281 | 22377.00 |
| 30 | 2015 | 2015-07-27 | 281 | 11550.00 |
| 32 | 2015 | 2015-08-04 | 281 | 4500.00 |
| 30 | 2015 | 2015-07-27 | 283 | 1500 |
+----------+------+------------+-----------+----------+
What I would, RIGHT (or LEFT) JOINING the two tables?
The min (and max) WEEK, so I can... (see 2)
Fill the gaps with missing WEEKS with NULL VALUES.
E.g., in a more complicated resultset:
+----------+------+------------+-----------+----------+
| NUM_WEEK | YEAR | date_start | idAccount | total |
+----------+------+------------+-----------+----------+
| 29 | 2015 | 2015-07-20 | 281 | 22377.00 |
| 30 | 2015 | 2015-07-27 | 281 | 11550.00 |
| 31 | 2015 | 2015-07-02 | 281 | NULL |
| 32 | 2015 | 2015-08-09 | 281 | 4500.00 |
| 29 | 2015 | 2015-08-09 | 283 | NULL |
| 30 | 2015 | 2015-07-16 | 283 | 1500 |
| 31 | 2015 | 2015-07-16 | 283 | NULL |
| 32 | 2015 | 2015-07-16 | 283 | NULL |
+----------+------+------------+-----------+----------+
Note, for example, that id=283 now has NULL at WEEK 29, 31 and 32, for example, like id=281 has NULL in WEEK 31.
I prepared also SQLFiddle here: http://sqlfiddle.com/#!9/a8fdc/3
Thank you very much.
I take a look on your question and i came up with this solution. Here is how your query could look like:
SELECT t1.NUM_WEEK, t1.`YEAR`, t1.date_start, t1.id_account, t2.total
FROM (SELECT c.NUM_WEEK, c.`YEAR`, c.date_start, a.id_account
FROM (SELECT WEEK(day_date,1) AS NUM_WEEK,
YEAR(day_date) AS `YEAR`,
STR_TO_DATE(CONCAT(YEAR(day_date),WEEK(day_date,1),' Monday'), '%X%V %W') AS date_start,
(SELECT GROUP_CONCAT(id_account) FROM account WHERE id_customer=12) AS accounts_id
FROM calendar
GROUP BY NUM_WEEK) c
INNER JOIN account a
ON FIND_IN_SET(a.id_account, c.accounts_id)
ORDER BY a.id_account, c.NUM_WEEK) t1
LEFT JOIN
(SELECT WEEK(t.date_transaction,1) AS NUM_WEEK,
YEAR(t.date_transaction) AS `YEAR`,
STR_TO_DATE(CONCAT(YEAR(t.date_transaction),WEEK(t.date_transaction,1),' Monday'), '%X%V %W') AS date_start,
t.id_account, SUM(t.amount) AS total
FROM `transaction` t
INNER JOIN account a
ON a.id_account = t.id_account
WHERE t.amount > 0 AND
t.id_account IN (SELECT id_account FROM account WHERE id_customer = 12)
GROUP BY id_account, WEEK(date_transaction,1)) t2
ON t1.NUM_WEEK = t2.NUM_WEEK AND t1.YEAR = t2.YEAR AND t1.id_account = t2.id_account;
Here is SQL Fiddle for that so you can check up result. Hope that is what are you looking for.
Little explanation:
First think i done is that I little modified your first query where you extract data from table calendar and add there one new column called accounts_id. That query now look's like this:
SELECT WEEK(day_date,1) AS NUM_WEEK,
YEAR(day_date) AS `YEAR`,
STR_TO_DATE(CONCAT(YEAR(day_date),WEEK(day_date,1),' Monday'), '%X%V %W') AS date_start,
(SELECT GROUP_CONCAT(id_account) FROM account WHERE id_customer=12) AS accounts_id
FROM calendar
GROUP BY NUM_WEEK
Please pay attention on this line in SELECT statement
(SELECT GROUP_CONCAT(id_account) FROM account WHERE id_customer=12) AS accounts_id
Note that when you select for specific customer you need to change customer ID in this line too!!!
Here is Fiddle so you can check result that this query produce.
This is necessary because we need to connect each week with each account to get desired result.
Next step is to extend previous query so we could separate accounts_id column (look result of previous query) so we could get row for each value in that column. Extended query look like this:
SELECT c.NUM_WEEK, c.`YEAR`, c.date_start, a.id_account
FROM (SELECT WEEK(day_date,1) AS NUM_WEEK,
YEAR(day_date) AS `YEAR`,
STR_TO_DATE(CONCAT(YEAR(day_date),WEEK(day_date,1),' Monday'), '%X%V %W') AS date_start,
(SELECT GROUP_CONCAT(id_account) FROM account WHERE id_customer=12) AS accounts_id
FROM calendar
GROUP BY NUM_WEEK) c
INNER JOIN account a
ON FIND_IN_SET(a.id_account, c.accounts_id)
ORDER BY a.id_account, c.NUM_WEEK
and output you can see in this Fiddle
After that all we need to do is to make left join between this query and query you already wrote in your question (last query).
There might be a better solution or even this one maybe can be improved a little, but I don't have much time now to deal with that and this is the first think that cross my mind...
GL!
P. S. pay attention when you use reserved word in MySQL like YEAR, TRANSACTION etc for column name (as column_name).. that can cause you a treble if have to use them in name of column or table use backquote () to mark them (asyear`)...

How do I select the population of a User table as an increasing value by DATE?

Consider this excerpt of our Users table:
+-------------+---------------------+---------------------+
| id | last_login | created |
+-------------+---------------------+---------------------+
| 14551578822 | 2014-02-22 17:38:39 | 2013-03-26 23:30:50 |
| 18442388426 | 0000-00-00 00:00:00 | 2013-11-07 15:51:11 |
| 49983341634 | 2014-03-06 22:28:47 | 2013-03-23 16:00:05 |
| 9527246957 | 2014-01-17 02:37:53 | 2013-05-14 02:14:49 |
| 58667409337 | 2014-03-08 06:54:01 | 2013-05-15 01:52:23 |
| 1907780002 | 2014-03-01 03:24:04 | 2013-05-01 07:57:56 |
| 65319490251 | 2014-03-19 05:49:41 | 2013-03-23 08:53:43 |
| 23896465717 | 0000-00-00 00:00:00 | 2012-10-21 10:52:23 |
| 19147401900 | 0000-00-00 00:00:00 | 2013-05-01 17:43:28 |
| 28598429318 | 0000-00-00 00:00:00 | 2014-03-14 14:44:15 |
+-------------+---------------------+---------------------+
We have many, many users - and we would like to generate a report that will display the total number of users we have as the date increases. We would like output similar to this:
+---------+---------------+
| DATE | User Count |
+---------+---------------+
| 2012-08 | 122 |
| 2012-09 | 1746 |
| 2012-10 | 3847 |
| 2012-11 | 5826 |
...
| 2014-03 | 472647 |
| 2014-04 | 497286 |
+---------+---------------+
There must be some way to do it without subselects and all kinds of messiness like that. I have a table already that displays the number of joins per period by the following query:
SELECT DATE(users.created) as JOIN_DATE , COUNT(users.id) AS JOIN_COUNT from users
WHERE users.created > '2012-07-01 00:00:00'
GROUP BY JOIN_DATE
ORDER BY JOIN_DATE ASC
Just wondered if there was a way to do it something like that.
Thanks!
You can use a variable to sum up the population foreach iteration
SELECT t.date ,
#population := #population+t.per_time population
FROM (
SELECT
DATE_FORMAT(`last_login` ,'%Y-%m') `date`,
COUNT(*) per_time
FROM Table1
WHERE created > '2012-07-01 00:00:00'
GROUP BY `date` ) t ,
(SELECT #population:=0) p
Fiddle Demo

Getting the missing period through mmysql query or procedure..?

I have a table customer_order as follows
mysql> select * from customer_order;
+---------+---------+-----------+------------------+----------------+
| cust_id | orderno | region_cd | order_start_date |order_del_date |
+---------+---------+-----------+------------------+----------------+
| CU_082 | ONO_001 | reg1 | 2012-04-25 | 2012-08-25 |
| CU_082 | ONO_002 | reg1 | 2012-04-28 | 2012-11-28 |
| CU_083 | ONO_002 | reg2 | 2012-04-28 | 2012-11-28 |
| CU_082 | ONO_003 | reg1 | 2012-04-25 | 2012-08-25 |
| CU_084 | ONO_004 | reg4 | 2012-04-25 | 2012-10-25 |
I need a table like this...which i get....as follows
mysql> select order_start_date,order_del_date,orderno,cust_id from customer_order wh
ere order_start_date >= '2012-04-25' AND order_del_date <='2012-12-28' and cust_i
d IN ('36082','36088') order by cust_id ;
+------------------+----------------+---------+---------+
| order_start_date | order_del_date | pid | emp_id |
+------------------+----------------+---------+---------+
| 2012-04-25 | 2012-05-25 | ONO_001 | CU_082 |
| 2012-08-22 | 2012-12-28 | ONO_004 | CU_082 |
| 2012-06-22 | 2012-08-28 | ONO_003 | CU_082 |
| 2012-05-27 | 2012-06-25 | ONO_002 | CU_082 |
| 2012-04-25 | 2012-05-25 | ONO_001 | CU_082 |
| 2012-05-27 | 2012-06-25 | ONO_001 | CU_082 |
| 2012-04-30 | 2012-06-25 | ONO_001 | CU_088 |
| 2012-06-28 | 2012-07-15 | ONO_002 | CU_088 |
| 2012-07-28 | 2012-08-25 | ONO_003 | CU_088 |
| 2012-07-16 | 2012-09-25 | ONO_004 | CU_088 |
+------------------+----------------+---------+---------+
now i need to query on this table ...
to get
for each customer here we get data for the period from wat date to wat date his order processing details in the above table..
now for each customer i shld find the period for which thr is no order processing...
eg cust_id =CU_088
he has his order processed from 30 apr to 25 june
den from 28th june to 15 july
(here thr is a diff that is thr is no order taken or any processing done from 26th to 27th june..this is wat is the required result)
**one more imp consideration is...
in the next entry we find thr is a order process from 28th july to 25th aug
w.r.t previous entry i.e, 28th june to 15july we find that for this customer thr is no order taken or processed from 16th to 27th july..
but with the last entry tat is 16th july to 25th sept he has an order with different order_no thrfore the gap 16th july to 27th july is filled here so this kind of a condition also needs to be checked...
I need to get the output as something like this..
+------------------+----------------+---------+---------+
| order_start_date | order_del_date | pid | emp_id |
+------------------+----------------+---------+---------+
| 2012-06-26 | 2012-06-27 | ONO_001 | CU_088 |
+------------------+----------------+---------+---------+
that is either the query or procedure which is more efficient should give me the period wer in thr was no action done for customer...
help me write the query which does the all the above things.
I m new to db queries..so please help me out..
To fetch every 'gap' between orders, you can use a self-join:
SELECT o1.cust_id,
o1.order_del_date + INTERVAL 1 DAY AS gap_begin,
MIN(o2.order_start_date) - INTERVAL 1 DAY AS gap_end
FROM customer_order o1
JOIN customer_order o2 ON o1.cust_id = o2.cust_id
AND o1.order_del_date <= o2.order_start_date
GROUP BY o1.cust_id, o1.order_del_date
HAVING gap_begin < gap_end