I have a table in my db, which contains following data:
————————————————————————————————————————————————————————————————————————
Id startDate availabilityStatus Hotel_Id
————————————————————————————————————————————————————————————————————————
1 2016-07-01 available 2
2 2016-07-02 available 2
3 2016-07-03 unavailable 2
4 2016-07-04 available 3
5 2016-07-05 available 3
6 2016-07-06 available 3
7 2016-07-07 unavailable 4
8 2016-07-08 available 4
9 2016-07-09 available 4
10 2016-07-10 available 4
Now, user wants to see all the Hotels which have 3 continuous days availability in July’16.
I am able to make the query to get the availability, but not sure how to fetch the Continuous date availability.
As per the above data, in July only Hotel Id 3, 4 have the continuous available dates, but as 2 also have the dates available. so how should we remove 2 and show just 3, 4 via MySQL query.
Please advise?
You can use the following query:
SELECT DISTINCT t1.hotel_id
FROM mytable AS t1
JOIN mytable AS t2
ON t1.hotel_id = t2.hotel_id AND
DATEDIFF(t1.startDate, t2.startDate) = 2 AND
t1.availabilityStatus = 'available' AND
t2.availabilityStatus = 'available'
LEFT JOIN mytable AS t3
ON t1.hotel_id = t3.hotel_id AND
t3.startDate < t2.startDate AND t3.startDate > t1.startDate AND
t3.availabilityStatus = 'unavailable'
WHERE t3.hotel_id IS NULL
The query is written in such a way, so that it can easily be adjusted in order to accommodate longer availability periods.
Edit:
Here's a solution using variables:
SELECT DISTINCT hotel_id
FROM (
SELECT hotel_id,
#seq := IF(#hid = hotel_id,
IF(availabilityStatus = 'available', #seq + 1, 0),
IF(#hid := hotel_id,
IF(availabilityStatus = 'available', 1, 0),
IF(availabilityStatus = 'available', 1, 0))) AS seq
FROM mytable
CROSS JOIN (SELECT #seq := 0, #hid := 0) AS vars
ORDER BY hotel_id, startDate) AS t
WHERE t.seq >= 3
You can test it with your actual data set and tell us how it compares with the first solution.
Try something like that. It works for any number of days. Replace N with 3.
SELECT DISTINCT A.Hotel_Id FROM table A
WHERE
A.availabilityStatus = 'available' AND
N-1 = (
SELECT count(DISTINCT startDate) FROM table B
WHERE B.availabilityStatus = 'available'
AND A.Hotel_Id = B.Hotel_Id
AND B.startDate
BETWEEN DATE_ADD(A.startDate, INTERVAL 1 DAY)
AND DATE_ADD(A.startDate, INTERVAL N-1 DAY)
)
It works like that: for each available date, count available dates in N-1 next days. If their count is N-1, add hotel_id to results.
Try this. I didn't get chance to test it as sqlfiddle is not working, but the general idea is to take 2 more instance of table by adding 1 and 2 days to the start date respectively.
Then join them based on derived dates and hotel id.
select t1.hotelid from
(select * from Table1 where availabilityStatus='available' ) t1
inner join
(select a.*, DATE_ADD(startDate,INTERVAL 1 DAY) as date_plus_one
from Table1 where availabilityStatus='available' ) t2
on t1.start_date=t2.date_plus_one and t1.hotelid=t2.hotelid
inner join
(select a.*, DATE_ADD(startDate,INTERVAL 2 DAY) as date_plus_two
from Table1 where availabilityStatus='available' ) t3
on t1.start_date=t3.date_plus_two and t1.hotelid=t3.hotelid
This query uses double self-join to find the same hotel available at day a, b and c, split by a day (function ADDDATE).
SELECT DISTINCT a.Hotel_Id
FROM table a
INNER JOIN table b ON a.Hotel_Id=b.Hotel_Id
INNER JOIN table c ON a.Hotel_Id=c.Hotel_Id
WHERE ADDDATE(a.startDate , INTERVAL 1 DAY) = b.startDate
AND ADDDATE(a.startDate , INTERVAL 2 DAY) = c.startDate
AND a.availabilityStatus = 'available'
AND b.availabilityStatus = 'available'
AND c.availabilityStatus = 'available'
Its working fine...
SELECT a.hotel_id FROM `mytable` as a WHERE
(select COUNT(id) from mytable as a1 where
DATE(a1.startDate)=DATE_ADD(a.startDate,INTERVAL 1 DAY) and
a1.hotel_id=a.hotel_id and
a1.availabilityStatus="Available"
) >0
and
(select COUNT(id) from mytable as a1 where
DATE(a1.startDate)=DATE_ADD(a.startDate,INTERVAL -1 DAY) and
a1.hotel_id=a.hotel_id and
a1.availabilityStatus="Available"
) >0
and
(select COUNT(id) from mytable as a1 where
DATE(a1.startDate)=DATE(a1.startDate) and
a1.hotel_id=a.hotel_id and
a1.availabilityStatus="Available"
) >0
Related
This question already has answers here:
MySQL how to fill missing dates in range?
(6 answers)
MySQL: Select All Dates In a Range Even If No Records Present
(6 answers)
Closed 2 years ago.
I have the following query that returns the dates first_visit starting from today and 7 days back, as well as the visitors hash per day:
SET time_zone= '{$company_timezone}';
SELECT DATE( first_visit ) AS day , COUNT( DISTINCT hash ) AS total
FROM table
WHERE company = 1 and first_visit > SUBDATE( CURDATE( ) , 7 )
GROUP BY day
The flaw with this is that if company = 1 have visitors only today and three days ago, I will get this:
day --------- total
2020-03-08 ----- 30
2020-03-05 ----- 40
leaving out all other dates inbetween.
What I want is to get all the past 7 days, even there are no visitors at all. If there are no visitors, then it should just show 0.
How to edit my query in order to achieve this?
Thank you
Perform an outer join with a derived table that contains desired dates:
select b.date as day, count(distinct hash) as total
from table
right join (select #now := #now - interval 1 day as date from (select #now := curdate()) a, table limit 7) b
on b.date = date(first_visit) and company = 1
group by b.date
This assumes that table has at least 7 rows.
Note: there are two occurrences of table.
If you have data for each day -- but not for that company -- then conditional aggregation is a pretty simply approach:
SELECT DATE( first_visit ) AS day ,
COUNT( DISTINCT CASE WHEN company = 1 THEN hash END ) AS total
FROM table
WHERE first_visit > SUBDATE( CURDATE( ) , 7 )
GROUP BY day;
This only works if all days are represented in your table for some company.
Some solutions involve a table of numbers.
Here is one way with a recursive query, available in MySQL 8.0:
with d as (select 0 n union all select n + 1 where n < 6)
select
current_date - interval n day myday,
count(distinct t.hash) total
from d
left join mytable t
on t.company = 1
and t.first_visit >= current_date - inteval n day
and t.first_visit < current_date - interval (n - 1) day
group by d.n
In earlier version, you can enumerate the numbers as a derived table:
select
current_date - interval n day myday,
count(distinct t.hash) total
from (
select 0 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
) d
left join mytable t
on t.compatny = 1
and t.first_visit >= current_date - inteval n day
and t.first_visit < current_date - interval (n - 1) day
group by d.n
I am using this query for getting monthly record of employees those are present and absent.
However i am getting the result for one employee by using this query but for all employees it doesn't seems to work.
SELECT
m.emp_id AS `Empid`,
d.dt AS `AbsentDate`,
(CASE
WHEN p.punch_status IS NULL THEN 'A'
ELSE p.punch_status
END) s
FROM
(SELECT
DATE(t.added_date) AS dt
FROM
pmc_attendance t
WHERE
DATE(t.added_date) >= '2018-08-01'
AND DATE(t.added_date) < DATE_ADD('2018-08-31', INTERVAL 1 DAY)
GROUP BY DATE(t.added_date)
ORDER BY DATE(t.added_date)) d
CROSS JOIN
tbl_admin_users m
LEFT JOIN
pmc_attendance p ON DATE(p.added_date) >= d.dt
AND DATE(p.added_date) < d.dt + INTERVAL 1 DAY
AND p.emp_id = m.emp_id
WHERE
p.emp_id IS NULL AND m.emp_id = '000838'
GROUP BY d.dt
ORDER BY m.emp_id , d.dt
I am using two tables 1. tbl_admin_users- employee data stored 2. pmc_attendance- present records of employees.
in query if i have passed the and m.emp_id='000838' it works fines but i want to show all records for all employees. any suggestions how i can optimize this query.
There are a couple of ways to structure this query. I can see what yuo are doing, and I think the only issue is with your group by clauses. You dont need them as everything should be distinct. Your status will always be 'A' as you are only getting rows where there is no punch for the employee for the day, so you can also take out the case statement.
SELECT
m.emp_id AS Empid,
d.dt AS AbsentDate,
'A' s
FROM
(
SELECT distinct DATE(t.added_date) AS dt
FROM pmc_attendance t
WHERE t.added_date >= '2018-08-01' AND DATE(t.added_date) < DATE_ADD('2018-08-31', INTERVAL 1 DAY)
) d
CROSS JOIN tbl_admin_users m
LEFT JOIN pmc_attendance p ON p.emp_id = m.emp_id and DATE(p.added_date) >= d.dt AND DATE(p.added_date) < d.dt + INTERVAL 1 DAY
WHERE p.emp_id IS NULL
ORDER BY m.emp_id , d.dt
If you want to include both present and absent, you would need to put your case statement back in, and remove your check WHERE p.emp_id IS NULL
If you have multiple punchs for the day, then you need to resolve it down to a single entry with a MIN/MAX so you only get one row per person per day, and add back in your group by Emp_ID, d.dt
I have a table with 2 columns, date and score. It has at most 30 entries, for each of the last 30 days one.
date score
-----------------
1.8.2010 19
2.8.2010 21
4.8.2010 14
7.8.2010 10
10.8.2010 14
My problem is that some dates are missing - I want to see:
date score
-----------------
1.8.2010 19
2.8.2010 21
3.8.2010 0
4.8.2010 14
5.8.2010 0
6.8.2010 0
7.8.2010 10
...
What I need from the single query is to get: 19,21,9,14,0,0,10,0,0,14... That means that the missing dates are filled with 0.
I know how to get all the values and in server side language iterating through dates and missing the blanks. But is this possible to do in mysql, so that I sort the result by date and get the missing pieces.
EDIT: In this table there is another column named UserID, so I have 30.000 users and some of them have the score in this table. I delete the dates every day if date < 30 days ago because I need last 30 days score for each user. The reason is I am making a graph of the user activity over the last 30 days and to plot a chart I need the 30 values separated by comma. So I can say in query get me the USERID=10203 activity and the query would get me the 30 scores, one for each of the last 30 days. I hope I am more clear now.
MySQL doesn't have recursive functionality, so you're left with using the NUMBERS table trick -
Create a table that only holds incrementing numbers - easy to do using an auto_increment:
DROP TABLE IF EXISTS `example`.`numbers`;
CREATE TABLE `example`.`numbers` (
`id` int(10) unsigned NOT NULL auto_increment,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Populate the table using:
INSERT INTO `example`.`numbers`
( `id` )
VALUES
( NULL )
...for as many values as you need.
Use DATE_ADD to construct a list of dates, increasing the days based on the NUMBERS.id value. Replace "2010-06-06" and "2010-06-14" with your respective start and end dates (but use the same format, YYYY-MM-DD) -
SELECT `x`.*
FROM (SELECT DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY)
FROM `numbers` `n`
WHERE DATE_ADD('2010-06-06', INTERVAL `n`.`id` -1 DAY) <= '2010-06-14' ) x
LEFT JOIN onto your table of data based on the time portion:
SELECT `x`.`ts` AS `timestamp`,
COALESCE(`y`.`score`, 0) AS `cnt`
FROM (SELECT DATE_FORMAT(DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY), '%m/%d/%Y') AS `ts`
FROM `numbers` `n`
WHERE DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY) <= '2010-06-14') x
LEFT JOIN TABLE `y` ON STR_TO_DATE(`y`.`date`, '%d.%m.%Y') = `x`.`ts`
If you want to maintain the date format, use the DATE_FORMAT function:
DATE_FORMAT(`x`.`ts`, '%d.%m.%Y') AS `timestamp`
I'm not a fan of the other answers, requiring tables to be created and such. This query does it efficiently without helper tables.
SELECT
IF(score IS NULL, 0, score) AS score,
b.Days AS date
FROM
(SELECT a.Days
FROM (
SELECT curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY AS Days
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.Days >= curdate() - INTERVAL 30 DAY) b
LEFT JOIN your_table
ON date = b.Days
ORDER BY b.Days;
So lets dissect this.
SELECT
IF(score IS NULL, 0, score) AS score,
b.Days AS date
The if will detect days that had no score and set them to 0. b.Days is the configured amount of days you chose to get from the current date, up to 1000.
(SELECT a.Days
FROM (
SELECT curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY AS Days
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.Days >= curdate() - INTERVAL 30 DAY) b
This subquery is something I saw on stackoverflow. It efficiently generates a list of the past 1000 days from the current date. The interval (currently 30) in the WHERE clause at the end determines which days are returned; the maximum is 1000. This query could be easily modified to return 100s of years worth of dates, but 1000 should be good for most things.
LEFT JOIN your_table
ON date = b.Days
ORDER BY b.Days;
This is the part that brings your table that contains the score into it. You compare to the selected date range from the date generator query to be able to fill in 0s where needed (the score will be set to NULL initially, because it is a LEFT JOIN; this is fixed in the select statement). I also order it by the dates, just because. This is preference, you could also order by score.
Before the ORDER BY you could easily join with your table about user info you mentioned with your edit, to add that last requirement.
I hope this version of the query helps someone. Thanks for reading.
Time went by since this question was asked. MySQL 8.0 was released in 2018 and added support for recursive common table expressions, which provide an elegant, state-of-the-art solution to this question.
The following query can be used to generate a list of dates, say for the first 15 days of August 2010:
with recursive all_dates(dt) as (
-- anchor
select '2010-08-01' dt
union all
-- recursion with stop condition
select dt + interval 1 day from all_dates where dt < '2010-08-15'
)
select * from all_dates order by dt
You can then left join this resultset with your table to generate the expected output:
with recursive all_dates(dt) as (
select '2010-08-01' dt
union all
select dt + interval 1 day from all_dates where dt < '2010-08-15'
)
select d.dt date, coalesce(t.score, 0) score
from all_dates d
left join mytable t on t.date = d.dt
order by d.dt
Demo on DB Fiddle:
date | score
:--------- | ----:
2010-08-01 | 19
2010-08-02 | 21
2010-08-03 | 0
2010-08-04 | 14
2010-08-05 | 0
2010-08-06 | 0
2010-08-07 | 10
2010-08-08 | 0
2010-08-09 | 0
2010-08-10 | 14
2010-08-11 | 0
2010-08-12 | 0
2010-08-13 | 0
2010-08-14 | 0
2010-08-15 | 0
Note that it is very easy to adapt the recursive CTE for other intervals or periods. As an example, say we want a row every 15 minutes from 4 AM to 8 AM on August 1st, 2010 ; we can do :
with recursive all_dates(dt) as (
select '2010-08-01 04:00:00' dt
union all
select dt + interval 15 minute from all_dates where dt < '2010-08-01 08:00:00'
)
...
You can accomplish this by using a Calendar Table. That's a table which you create once and fill with a date range (e.g. one dataset for each day 2000-2050; that depends on your data). Then you can make an outer join of your table against the calendar table. If a date is missing in your table, you return 0 for the score.
Michael Conard answer is great but I needed intervals of 15 minutes where the time must always start at the top of every 15th minute:
SELECT a.Days
FROM (
SELECT FROM_UNIXTIME( FLOOR( UNIX_TIMESTAMP() / (15 * 60) ) * (15 * 60)) - INTERVAL 15 * (a.a + (10 * b.a) + (100 * c.a)) MINUTE AS Days
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.Days >= curdate() - INTERVAL 30 DAY
This will set the current time to the previous round 15th minute:
FROM_UNIXTIME( FLOOR( UNIX_TIMESTAMP() / (15 * 60) ) * (15 * 60))
And this will remove time with a 15 minute step:
- INTERVAL 15 * (a.a + (10 * b.a) + (100 * c.a)) MINUTE
If there's a simpler way to do it, please let me know.
you can user direct from start date up to today with insertion
with recursive all_dates(dt) as (
-- anchor
select '2021-01-01' dt
union all
-- recursion with stop condition
INSERT IGNORE INTO mytable (date,score) VALUES (dt + interval 1 day ,0 ) where dt + interval 1 day <= curdate()
)
select * from all_dates
I have a MySQL-Table
id mydate content
----------------------------------
1 2015-06-20 some content
2 2015-06-20 some content
3 2015-06-22 some content
Now I want to count the entries for each day:
SELECT DATE(mydate) Date, COUNT(DISTINCT id) dayCount FROM mytable
GROUP BY DATE(mydate) HAVING dayCount > -1 ORDER BY DATE(mydate) DESC
This works for me, result:
2015-06-20 = 2
2015-06-22 = 1
How can I fetch days without any entries? In my example the result should be:
2015-06-19 = 0
2015-06-20 = 2
2015-06-21 = 0
2015-06-22 = 1
2015-06-23 = 0
Based on this:
<?php
$today = date("Y-m-d");
$mystartdate = date_create($today);
date_sub($mystartdate, date_interval_create_from_date_string('14 days'));
$mystartdate = date_format($mystartdate, 'Y-m-d');
?>
Finaly I want to output the counts of the last 14 days, also with "0-days". Hope you understand my problem.
For this you can create new table that holds the increment numbers, but it's not a great idea. However, if doing it this way, use this table to construct a list of dates using DATE_ADD.
LEFT JOIN onto your table of data based on the time portion to achieve your list of dates
for more info go through the link
MySQL how to fill missing dates in range?
try below-
SELECT a.date_field, COUNT(DISTINCT b.id) dayCount FROM
(SELECT date_field FROM
(
SELECT
MAKEDATE(YEAR(NOW()),1) +
INTERVAL (MONTH(NOW())-1) MONTH +
INTERVAL daynum DAY date_field
FROM
(
SELECT t*10+u daynum
FROM
(SELECT 0 t UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) A,
(SELECT 0 u 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) B
ORDER BY daynum
) AA
) AAA
WHERE MONTH(date_field) = MONTH(NOW()) ) a
LEFT JOIN mytable b ON a.date_field=DATE(b.mydate)
GROUP BY a.date_field HAVING dayCount > -1 ORDER BY a.date_field DESC;
This has me a stummped...
If I have a this MySQL table:
UserId | Commission | Date Of Commission
1 | 200.00 | 2014-02-12
1 | 50.00 | 2014-04-01
2 | 10.00 | 2014-04-05
and I would like to display the Total Commission for a specific user per week starting from his/her first record, and display 0 for that range if there's no record.
how would I go about it?
Sample Output
UserId | Date Range | Total Commission
1 | 02/10/14 - 02/16/14 | 200.00
1 | 02/17/14 - 02/23/14 | 0.00
...
1 | 03/31/14 - 04/06/14 | 50.00
I'm not a seasoned coder so any help will be much appreciated.
Thanks!
Edit:
I have tried this:
SELECT IFNULL(SUM(Commisssion),0) Total ,DATE_SUB(`DateOfCommission`,INTERVAL 7 DAY)
AS RangStart,DATE_SUB(`DateOfCommission`,INTERVAL 1 DAY) AS RangeEnd
FROM `comms` WHERE `UserId` = '$UserID' GROUP BY DATE(`DateOfCommission`) DESC
but it starts the week with whatever date the first record was entered..
This is very tricky to accomplish. Here is what I managed to do with small modifications it should work they way it needs to be. I have done it for userid = 1 and this could be done for other users as well.
In the query I have 2 lines
where a.Date BETWEEN (select min(date) from transactions where UserId = 1) AND NOW()
and
WHERE date BETWEEN (select min(date) from transactions where UserId = 1) AND NOW()
The query will try to generate the list of dates using the min() date of transaction for the user till today. Instead of now() this could be used as max() date of transaction for the user as well.
select
t1.date_range,
coalesce(SUM(t1.Commission+t2.Commission), 0) AS Commission
from
(
select
a.Date as date,
concat(
DATE_ADD(a.Date, INTERVAL(1-DAYOFWEEK(a.Date)) +1 DAY),
' - ',
DATE_ADD(a.Date, INTERVAL(7- DAYOFWEEK(a.Date)) +1 DAY)
) as date_range,
'0' as Commission
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 (select min(date) from transactions where UserId = 1) AND NOW()
)t1
left join
(
SELECT date ,
coalesce(SUM(Commission), 0) AS Commission
FROM transactions
WHERE date BETWEEN (select min(date) from transactions where UserId = 1) AND NOW()
AND UserId = 1
GROUP BY date
)t2
on t2.date = t1.date
group by t1.date_range
order by t1.date_range asc
DEMO
So, this is sort of an algorithm you could use:
$Result = select distinct userid from table(this will fetch all userids from table)
while(There are rows in $Result)
{
$Userid = $Result['userid']
$StartDateRes = mysql_query(select Date, WEEKOFYEAR(Date) as week from table where userid = Userid order by date asc limit 1)
$StartDateRow = mysql_fetch_assoc($StartDateRes)
$StartDate = $StartDateRes['Date']
$StartWeekNumber = $StartDateRes['week']
$EndDateRes = mysql_query(select Date, WEEKOFYEAR(Date) as week from table where userid = Userid order by date desc limit 1)
$EndDateRow = mysql_fetch_assoc($EndDateRes)
$EndDate = $EndDateRes['Date']
$EndWeekNumber = $EndWeekRes['week']
for($i=$StartWeekNumber; $i<=$EndWeekNumber; $i++)
{
$StartDateOfWeek = FuncToFindStartDateOfWeek($i)
$EndDateOfWeek = FuncToFindEndDateOfWeek($i)
$Result2 = mysql_query(select sum(commission) as sum from table where date between StartDateOfWeek and EndDateOfWeek group by userid)
$Row2= mysql_fetch_assoc($Result2)
$Sum = $Row2['sum']
mysql_query("insert into OutputTable values($UserId, $StartDateOfWeek. '-'. $EndDateOfWeek ,$Sum");
}
}
SELECT UserId, COALESCE(SUM(Commission),0), YEARWEEK(DateOfCommission) AS TheWeek
GROUP BY UserId, TheWeek
ORDER BY UserId, TheWeek;
This will not print the nice date range, but should get you started in a SQL-only direction where the sum is broken down by the week of the year. I think you could take it from this point to add the nicer formatting of the Year/Week column. YEARWEEK() should give you pretty fast results.
The easiest way I can think of doing this as follows
Step 1: Get the date of the first record
"SELECT dateofcommission FROM comissionstable WHERE id='userid' ORDER BY dateofcommission ASC LIMIT 1"
The above query will return the first date of commission only
Step 2: Create a loop which starts from the date you got in Step 1 and continue the loop till the date is greater than or equal to today's date. Increment this date using PHP date function.
date('Y-m-d', strtotime($dateofcommission. ' + 7 days'));
Step 3: In this loop you can get the commission with-in the starting date and ending date. Starting date will be the date before adding 7 days and ending date will be the one after you have added 7 days.
SELECT SUM(commission) FROM commissiontable WHERE dateofcommission>= startingdate AND dateofcomission < endingdate AND id='userid'
The above logic should work. If you end up having some issues with this logic feel free to post in comments. I would be happy to help
The following is another solution
function getStartAndEndDate($week, $year) {
$time = strtotime("1 January $year", time());
$day = date('w', $time);
$time += ((7*$week)+1-$day)*24*3600;
$return[0] = date('Y-n-j', $time);
$time += 6*24*3600;
$return[1] = date('Y-n-j', $time);
return $return;
}
$query = mysqli_query($con, "SELECT userid, COALESCE( SUM( commission ) , 0 ) AS thecommission , YEARWEEK( doc ) AS TheWeek FROM commission GROUP BY userid, TheWeek ORDER BY userid, TheWeek");
while ($array = mysqli_fetch_array($query)) {
$test = $array['TheWeek'];
$store_array = getStartAndEndDate(substr($test,4,2), substr($test,0,4));
echo $array['userid']."-".$array['thecommission']."-".$store_array[0]."/".$store_array[1]."<br>";
}