Related
I'm having a table Storing planned_production below Format.I need to get the working_dates for current week.
+------------+-------------+---------+----------+---------+------+--------------+---------------------------------------------+
| planned_id | per_quarter | per_day | per_week | quarter | year | working_days | working_dates | |
+------------+-------------+---------+----------+---------+------+--------------+---------------------------------------------+
| 1 | 860 | 14.10 | 70.50 | 2 | 2018 | 61 | 02-04-2018,03-04-2018,04-04-2018,05-04-2018 |
| 06-04-2018,09-04-2018,12-04-2018,15-04-2018
+------------+-------------+---------+----------+---------+------+--------------+---------------------------------------------+
I'm trying to get the working dates for current week.
the query i'm using is below.
i'm not able to figure it out where i'm going wrong.
SELECT Value from ( SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(working_dates, ',', n.n), ',', -1) Value
FROM planned_production t CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
ORDER BY n
) n
WHERE quarter = quarter(curdate()) and n.n <= 1 + (LENGTH(t.working_dates) - LENGTH(REPLACE(t.working_dates, ',', '')))
) as TEMP_TABLE
WHERE Value between CURRENT_DATE() - INTERVAL
WEEKDAY(CURRENT_DATE()) + 7 DAY
AND
(CURRENT_DATE() - INTERVAL WEEKDAY(CURRENT_DATE()) DAY) - INTERVAL 1
SECOND ORDER BY Value asc;
I need something like this as output
+------------+
| value |
+------------+
| 17-05-2018 |
| 18-05-2018 |
+------------+
Any suggestions welcomed and Thanks in advance.
Table Structure:
create table test (planned_id int, per_quarter float, per_day float, per_week float,
quarter int, year int, working_days int, working_dates varchar(1000));
Table Values:
insert into test values ( 1,860,14.10,70.50,2,2018,61,'02-04-2018,03-04-2018,04-04-2018,05-04-2018,06-04-2018,09-04-2018,12-04-2018,15-04-2018,17-05-2018,18-05-2018');
insert into test values ( 2,800,12.10,60.50,2,2018,50,'02-04-2018,03-04-2018,04-04-2018,05-04-2018,06-04-2018,09-04-2018,12-04-2018,15-04-2018,14-05-2018,15-05-2018') ;
SQL Query:
set #startdate := date_add(SUBDATE(current_date, WEEKDAY(current_date)), interval -1 day);
select test.planned_id, checkdate
from
(
select #startdate := date_add(#startdate, interval 1 day) as checkdate
from ( SELECT 0 singles
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9)
a cross join (
SELECT 0 singles
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
cross join (
SELECT 0 singles
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) c
) as dim_date
join test
where
checkdate between SUBDATE(current_date, WEEKDAY(current_date))
and date_add(SUBDATE(current_date, WEEKDAY(current_date)) , interval 7 day)
and find_in_set(DATE_FORMAT(checkdate, '%d-%m-%Y'),working_dates) != 0
order by test.planned_id
Output:
planned_id checkdate
1 2018-05-17
1 2018-05-18
2 2018-05-14
2 2018-05-15
Logic:
1) First create table dim_date. You can create it using query as shown in above query but I would suggest you to create one table in your database.
2) Use filter to select only one week
checkdate between SUBDATE(current_date, WEEKDAY(current_date))
and date_add(SUBDATE(current_date, WEEKDAY(current_date)) , interval 7 day)
3) Use cross join between dim_table and test
4) Use find_in_set function to check date is present or not.
and find_in_set(DATE_FORMAT(checkdate, '%d-%m-%Y'),working_dates) != 0
How to calculate the number of unique days per month from a table with two date columns, in which the periods can have gaps and overlaps?
I rather not use a calendar table to get the unique days, because it generates a temporary table with thousands of records, and resources are limited.
Example table:
+---------+------------+------------+
| mygroup | alpha | omega |
+---------+------------+------------+
| 1 | 2017-02-04 | 2017-04-14 |
| 1 | 2017-03-25 | 2017-03-28 |
| 1 | 2017-01-23 | 2017-01-25 |
| 2 | 2017-02-05 | 2017-02-20 |
| 1 | 2017-04-28 | 2017-05-12 |
| etc. | etc. | etc. |
+---------+------------+------------+
Is it what you need?
select count(distinct selected_date),te.mygroup, MONTHNAME(selected_date)from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
cross join test te
where selected_date between te.alpha and te.omega
group by mygroup, MONTHNAME(selected_date)
Оutput for your example:
'17','1','April'
'25','1','February'
'3','1','January'
'31','1','March'
'12','1','May'
'16','2','February'
Count may be greater than number of the days in month because such overlap exists in few rows - it's not а mistake.
There is another way to do this roughly 10 times faster than a calendar table.
The biggest resource spoiler is the calendar table itself, it is used to filter unique days.
But instead of using a whole table record for that, it can be done using 31 bits in an UINT.
Recepe:
Create a calendar table with months only
Cut periods in months, and join them with the calendar table
Translate periods to bits of UINTs
OR the UINTs per month for uniqueness
Count their bits as unique days per month
Output:
+--------------+---------+---------+-------+
| Period | Group 1 | Group 2 | Total |
+--------------+---------+---------+-------+
| 2017 month 5 | 11 | 0 | 11 |
| 2017 month 4 | 15 | 0 | 15 |
| 2017 month 3 | 30 | 0 | 30 |
| 2017 month 2 | 24 | 15 | 39 |
| 2017 month 1 | 2 | 0 | 2 |
+--------------+---------+---------+-------+
MySQL query:
SELECT
`tabulate`.`period` AS `Period`,
SUM(IF(`tabulate`.`mygroup` = 1,
`tabulate`.`days`, 0)) AS `Group 1`,
SUM(IF(`tabulate`.`mygroup` = 2,
`tabulate`.`days`, 0)) AS `Group 2`,
SUM(`tabulate`.`days`) AS `Total`
FROM
( SELECT
`unique`.`period`,
BIT_COUNT(BIT_OR(CONV(CONCAT(
REPEAT("1", DAYOFMONTH(`unique`.`omega`) - DAYOFMONTH(`unique`.`alpha`)),
REPEAT("0", DAYOFMONTH(`unique`.`alpha`) - 1)
), 2, 10))) AS `days`,
`unique`.`mygroup`
FROM
( SELECT
DATE_FORMAT(`permonth`.`period_alpha`, "%Y month %c") AS `period`,
GREATEST(`permonth`.`period_alpha`, `permonth`.`example_alpha`) AS `alpha`,
LEAST(`permonth`.`period_omega`, `permonth`.`example_omega`) AS `omega`,
`permonth`.`mygroup`
FROM
( SELECT
`period`.`alpha` AS `period_alpha`,
DATE_SUB(`period`.`omega`, INTERVAL 1 DAY) AS `period_omega`,
`example`.`mygroup`,
IFNULL(`example`.`alpha`, `period`.`alpha`) AS `example_alpha`,
IFNULL(`example`.`omega`, CURDATE()) AS `example_omega`
FROM
( SELECT
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL `season`.`n` + (`month`.`n` << 2) MONTH
) AS `alpha`,
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL 1 + `season`.`n` + (`month`.`n` << 2) MONTH
) AS `omega`
FROM
( SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
) AS `month`
CROSS JOIN (SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
) AS `season`
) AS `period`
INNER JOIN
( SELECT 1 AS `mygroup`, "2017-02-04" AS `alpha`, "2017-04-14" AS `omega`
UNION ALL SELECT 1, "2017-03-25", "2017-03-28"
UNION ALL SELECT 1, "2017-01-23", "2017-01-25"
UNION ALL SELECT 2, "2017-02-05", "2017-02-20"
UNION ALL SELECT 1, "2017-04-28", "2017-05-12"
) AS `example` ON (
(`example`.`alpha` < `period`.`omega` OR `example`.`alpha` IS NULL)
AND IFNULL(`example`.`omega`, CURDATE()) >= `period`.`alpha`
)
) AS `permonth`
) AS `unique`
GROUP BY
`unique`.`period`,
`unique`.`mygroup`
) AS `tabulate`
GROUP BY `tabulate`.`period`
ORDER BY `tabulate`.`period` DESC
Need a help on how to get opening balances and closing balances of a non-working date in my transaction table in my database.
transaction table
+------------------+----------+-----------+
| id | trans_date | debit | credit |
+----+-------------+----------+-----------+
| 1 | 2016-05-09 | 200.00 | 0.00 |
| 2 | 2016-05-11 | 0.00 | 50.00 |
+---------------+-------------+-----------+
Want a result like below. You will realize there were no transaction on "2016-05-10" but the result shows both opening balance and closing balance. Thanks
+-------------+--------------+-----------+-----------+------------+
| trans_date | open_bal | debit | credit |closing_bal |
+-------------+--------------+-----------+-----------+------------+
| 2016-05-09 | 0.00 | 200.00 | 0.00 | 200.00 |
| 2016-05-10 | 200.00 | 0.00 | 0.00 | 200.00 |
| 2016-05-11 | 200.00 | 0.00 | 50.00 | 150.00 |
+-------------+-------------+--------------+------------+---------+
I tried this but "2016-05-10" does not show in the result.
SELECT trans_date,open_balance
FROM(SELECT s.gen_id, s.trans_id, s.trans_date,
s.narations, s.account_code,
s.op_balance as open_balance,
s.debit, s.credit, s.closing_balance
from ( select t.gen_id, t.trans_id,
t.narations, t.account_code,
t.trans_date, t.credit, t.debit,
#tot_debit := if(#prev_client = t.account_code, #tot_debit + t.debit,t.debit) as tot_cred,
#tot_credit := if(#prev_client = t.account_code,#tot_credit + t.credit,t.credit) as tot_deb,
#cur_bal := if(#prev_client = t.account_code, #tot_debit - #tot_credit,t.debit-t.credit) as closing_balance,
(#cur_bal + t.credit) - t.debit as op_balance, #prev_client := t.account_code
from (select * from journal WHERE account_code = 41003
GROUP BY trans_date order by trans_date,account_code,trans_id)t, (select #prev_client:=0,#cur_bal:=0,#tot_debit:=0,#tot_credit:= 0,#open_balance:=0)r )s) g where trans_date <= '2016-05-11'
You can use this to select list of dates in MySQL.
SELECT DATE_ADD(CURDATE() , INTERVAL -(a.n + 10 * b.n + 100 * c.n) DAY) AS `date`
FROM (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS a
CROSS JOIN (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS b
CROSS JOIN (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS c
If you want to include in result dates not presented at table transactions, it can be done with LEFT JOIN
SELECT * FROM
(SELECT DATE_ADD(CURDATE() , INTERVAL -(a.n + 10 * b.n + 100 * c.n) DAY) AS `date`
FROM (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS a
CROSS JOIN (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS b
CROSS JOIN (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS c
) AS dates
LEFT JOIN transactions ON trans_date = dates.date
Note, that this will select dates from current for 999 past days. If you want more days ago, another CROSS JOIN can be added and 9 999 days will be returned.
well, this might be easy but I can't seem to find any solution
I have this working query
SELECT
count(*), TIMESTAMPDIFF(DAY, buydate, NOW()) daydif
FROM
order_item
WHERE
buydate > NOW() - INTERVAL 4 DAY
GROUP BY daydif
ORDER BY daydif ASC
but the result is skipping the days with zero item
+----------+--------+
| count(*) | daydif |
+==========+========+
| 5 | 0 |
+----------+--------+
| 9 | 1 |
+----------+--------+
| 2 | 3 |
+----------+--------+
I want to get this result
+----------+--------+
| count(*) | daydif |
+==========+========+
| 5 | 0 |
+----------+--------+
| 9 | 1 |
+----------+--------+
| 0 | 2 | //I want this row in the returned results
+----------+--------+
| 2 | 3 |
+----------+--------+
Update:
From the answers and further search it seems I'm forced to create a helper table and join with it. It's so frustrating that I can't find a sequence producing function like that of MariaDB as mentioned in the comments. Any hint for a more elegant way?
Probably you do not have any items, where the difference is 2 days. SQL can't create something, that is not there.
What you can do is to have a table with a single field of long list of numbers from 0 to up to whatever the difference can be (in this case 4), let's call it tbl_diff, and the field is diff. Left join tbl_diff on your existing query to fill in the gaps. Note, that I also changed the count() to count those records only, where buydate is set.
SELECT
count(buydate), TIMESTAMPDIFF(DAY, buydate, NOW()) daydif
FROM
tbl_diff
LEFT JOIN
order_item on tbl_diff.diff=TIMESTAMPDIFF(DAY, buydate, NOW())
WHERE
diff<=4
GROUP BY daydif
ORDER BY daydif ASC
If you do not want to create a helper table, then you can operate with a series of unions because you only want 5 number (0 to 4):
SELECT
count(buydate), TIMESTAMPDIFF(DAY, buydate, NOW()) daydif
FROM
(SELECT 0 as diff FROM dual
UNION
SELECT 1 FROM dual
UNION
SELECT 2 FROM dual
UNION
SELECT 3 FROM dual
UNION
SELECT 4 FROM dual) tbl_diff
LEFT JOIN
order_item on tbl_diff.diff=TIMESTAMPDIFF(DAY, buydate, NOW())
GROUP BY daydif
ORDER BY daydif ASC
And as suggested by Paul Spiegel I ended up using some helper tables,
for future reference and better help here's the code to I used to generate the tables
create table `stat_helper_calendar` (
`date` DATE NOT NULL,
PRIMARY KEY (`date`)
)
select date_add('2015-01-01', INTERVAL t3.c*1000 + t2.c*100 + t1.c*10 + t0.c DAY) as `date`
from
(select 0 c union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t0,
(select 0 c union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t1,
(select 0 c union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t2,
(select 0 c union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t3
And this one for integers
CREATE TABLE `stat_helper_num` (
`num` INT NOT NULL,
PRIMARY KEY (`num`)
) SELECT (t4.c * 10000 + t3.c * 1000 + t2.c * 100 + t1.c * 10 + t0.c) AS `num` FROM
(SELECT 0 c UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t0,
(SELECT 0 c UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t1,
(SELECT 0 c UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2,
(SELECT 0 c UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t3,
(SELECT 0 c UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t4
I have searched similar problems here on stackoverflow but I could not understand how to make this work, what I'm trying to do...
So, I want to get last 7 days transactions from database and get total sales amount and also include empty rows if there is no data for some day.
What I have so far:
http://sqlfiddle.com/#!2/f4eda/6
This outputs:
| PURCHASE_DATE | AMOUNT |
|---------------|--------|
| 2014-04-25 | 19 |
| 2014-04-24 | 38 |
| 2014-04-22 | 19 |
| 2014-04-19 | 19 |
What I want:
| PURCHASE_DATE | AMOUNT |
|---------------|--------|
| 2014-04-25 | 19 |
| 2014-04-24 | 38 |
| 2014-04-23 | 0 |
| 2014-04-22 | 19 |
| 2014-04-21 | 0 |
| 2014-04-20 | 0 |
| 2014-04-19 | 19 |
Any help appreciated :)
This is not easy. I took help from this thread generate days from date range and combined it with your query.
So the idea was to get the list of dates from last 7 days then left join these dates with a static amount 0 to the query you have and then finally sum them. This could be used for any date range, just need to change them in both the queries
select
t1.purchase_date,
coalesce(SUM(t1.amount+t2.amount), 0) AS amount
from
(
select DATE_FORMAT(a.Date,'%Y-%m-%d') as purchase_date,
'0' as amount
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) a
where a.Date BETWEEN NOW() - INTERVAL 7 DAY AND NOW()
)t1
left join
(
SELECT DATE_FORMAT(purchase_date, '%Y-%m-%d') as purchase_date,
coalesce(SUM(amount), 0) AS amount
FROM transactions
WHERE purchase_date BETWEEN NOW() - INTERVAL 7 DAY AND NOW()
AND vendor_id = 0
GROUP BY purchase_date
)t2
on t2.purchase_date = t1.purchase_date
group by t1.purchase_date
order by t1.purchase_date desc
DEMO
Simply put together a subquery with the dates you want and use left outer join:
select d.thedate, coalesce(SUM(amount), 0) AS amount
from (select date('2014-04-25') as thedate union all
select date('2014-04-24') union all
select date('2014-04-23') union all
select date('2014-04-22') union all
select date('2014-04-21') union all
select date('2014-04-20') union all
select date('2014-04-19')
) d left outer join
transactions t
on t.purchase_date = d.thedate and vendor_id = 0
GROUP BY d.thedate
ORDER BY d.thedate DESC;
This is for last 7 days;
select d.thedate, coalesce(SUM(amount), 0) AS amount
from (select DATE(NOW()) as thedate union all
select DATE(DATE_SUB( NOW(), INTERVAL 1 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 2 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 3 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 4 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 5 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 6 DAY))) d left outer join
transactions t
on t.purchase_date = d.thedate and vendor_id = 0
GROUP BY d.thedate
ORDER BY d.thedate DESC;
with recursive all_dates(dt) as (
select '2014-04-19' as dt
union all
select dt + interval 1 day
from all_dates
where dt + interval 1 day <= '2014-04-25'
)
select d.dt as purchase_date, coalesce(m.amount, 0) as purchased
from all_dates as d
left join mytable m
on d.dt = m.purchase_date
order by purchase_date desc;