How to get all dates in a month - php

CREATE TABLE `tbl_atn` (
`atn_id` int(15) NOT NULL AUTO_INCREMENT,
`eng_id` int(15) DEFAULT NULL,
`visit` varchar(50) DEFAULT NULL,
`travel` varchar(50) DEFAULT NULL,
`start_time` varchar(50) DEFAULT NULL,
`mile` varchar(50) DEFAULT NULL,
`end_time` varchar(50) DEFAULT NULL,
`comments` varchar(100) DEFAULT NULL,
`actual` varchar(50) DEFAULT NULL,
`total_job` varchar(50) DEFAULT NULL,
`regular` varchar(50) DEFAULT NULL,
`over` varchar(50) DEFAULT NULL,
`total_hrs` varchar(50) DEFAULT NULL,
`pay` varchar(50) DEFAULT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`atn_date` date DEFAULT NULL,
PRIMARY KEY (`atn_id`)
)
What I want to do is create is month report for each month. All specific dates are displayed and records from above table if no record then empty
The above is my table. I am trying to create this application, but due to this sheet I am stuck. First of all, can this only be achieved using a MySQL query? If not, what I have to do is generate all dates first, then for each date I have to fetch a record from the database then run another query to sum them up. I am unable to create a query for that.
Any help?
$now = date('Y-m-d');
$month = date("m",strtotime($now));
$year = date("Y",strtotime($now));
$first = date('Y-m-d', mktime(0, 0, 0, $month, 1, $year));
$last = date('Y-m-t', mktime(0, 0, 0, $month, 1, $year));
$thisTime = strtotime($first);
$endTime = strtotime($last);
while($thisTime <= $endTime)
{
$thisDate = date('Y-m-d', $thisTime);
echo $thisDate."<br>";
$thisTime = strtotime('+1 day', $thisTime); // increment for loop
}
Made this code now dynamic now i can get all the dates of any month given month and year is given now what i will do now is make a function that will loop thru all dates and send query to database to find data if found it will set values other wise zero is it right approach ?

This will get all records where atn_date is in this month:
SELECT * FROM `tbl_atn` WHERE `atn_date` BETWEEN "2012-06-01" AND "2012-06-30"
This PHP will loop through every day in this month:
$thisTime = strtotime("2012-06-01");
$endTime = strtotime("2012-06-31");
while($thisTime <= $endTime)
{
$thisDate = date('Y-m-d', $thisTime);
echo $thisDate;
$thisTime = strtotime('+1 day', $thisTime); // increment for loop
}

A common way of displaying a contiguous sequence when your table may have none or only some of the records in your range of interest, is to use an integer table. An integer table contains integers from 0 to 9 in sequence. When you need a set of sequential numbers you self join it to get what you want. So for a range from 5 to 25 do
SELECT i.n + j.n*10 as num
FROM myints i CROSS JOIN myints j
WHERE (i.n + j.n*10) BETWEEN 5 AND 25
ORDER BY (i.n + j.n*10);
In your case you want sequential dates. You know that any particular month can have at most 31 days, so you do a subquery for a set of integers from 0 to 31 and express them as dates starting on your beginning of month and finishing on your end of month. Like so:
SELECT DATE_ADD('2012-06-01', INTERVAL n.num DAY) AS mydate, o.*
FROM
(SELECT i.n + j.n*10 as num
FROM myints i CROSS JOIN myints j
WHERE (i.n + j.n*10) BETWEEN 0 AND 31
ORDER BY (i.n + j.n*10)) AS n
LEFT JOIN other o ON ( DATE_ADD('2012-06-01', INTERVAL n.num DAY) = o.atn_date)
WHERE mydate BETWEEN '2012-06-01 '2012-06-30';
or
SELECT datelist.mydate, o.* FROM
(SELECT DATE_ADD( '2012-01-06', INTERVAL i.n + j.n*10 DAY) as mydate
FROM myints i CROSS JOIN myints j
WHERE mydate BETWEEN '2012-01-06' AND '2012-01-30'
ORDER BY (i.n + j.n*10)) datelist
LEFT JOIN othertable o ON (datelist.mydate=o.atn_date);

If you want to get specific days in a month then query for them, you can use the built in PHP function cal_days_in_month (http://php.net/manual/en/function.cal-days-in-month.php). You can write a real simple function to handle this such as the following:
function getDateTime($month, $year){
$month = intval($month);
$year = intval($year);
$day = cal_days_in_month(CAL_GREGORIAN, $month, $year);
//Now build your query here since you will have the days of the month
$query = SELECT * FROM `tbl_atn` WHERE `atn_date` BETWEEN $year."-".$month."-1" AND $year."-".$month."-".$day;
}
Note, the dates piece is however you have it configured in your database. I just used the above query example from Scott Saunders for simplicity sake.
If you do not have the calendar plugin built for your PHP stack, you can also do a custom function with date() - http://php.net/manual/en/function.date.php.

It is possible to retrieve a list of dates in a particular month and year using mysql
You can try this:
SELECT
ldays.`day` as 'Date',
atn.`regular` as RegularHours,
atn.`over` as OT1,
atn.`over` as OT2,
atn.`total_hrs` as TotalHrsPerDay,
atn.`comments` as Comments
FROM(
SELECT DATE_FORMAT(ADDDATE(LAST_DAY(SUBDATE(DATE_FORMAT('{$DateParamHere}','%Y-%m-%d'), INTERVAL 1 MONTH)), 1) + INTERVAL a + b DAY,'%Y-%m-%d') as 'day'
FROM
(SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9 ) d,
(SELECT 0 b UNION SELECT 10 UNION SELECT 20
UNION SELECT 30 UNION SELECT 40) m
WHERE ADDDATE(LAST_DAY(SUBDATE(DATE_FORMAT('{$DateParamHere}','%Y-%m-%d'), INTERVAL 1 MONTH)), 1) + INTERVAL a + b DAY <= LAST_DAY('{$DateParamHere}')
ORDER BY a + b
) ldays
LEFT JOIN `tbl_atn` atn ON (atn.`eng_id`='{$EngIDParamHere}' AND DATE(atn.`atn_date`) = ldays.`day`)
$DateParamHere = you can set here a particular year, month, and current day and concat it by the format of '%Y-%m-%d' in mysql but you can change its format anyways
$EngIDParamHere = put the id of a particular engineer here
after that you are good to go .. :)

Related

MySQL incorrect result on group by

I am counting unique ip visits for user account. I am getting different result on checking total visits vs grouped user account visits
My table structure is like this
id userid userip status date
1 xxxx 11111 1 unix timestamp
2 yyyy 11122 1 unix timestamp
3 zzzz 11133 1 unix timestamp
4 cccc 11144 1 unix timestamp
I am doing query like this
$date1 = strtotime("yesterday midnight");
$date2 = strtotime("today midnight");
SELECT `userid`, COUNT(DISTINCT `userip`) AS `total` FROM `stats`
WHERE (`date` >= $date1 AND `date` < $date2) AND `status`=1
This gives result as 5644
But when I group by userid result is different
$date1 = strtotime("yesterday midnight");
$date2 = strtotime("today midnight");
SELECT `userid`, COUNT(DISTINCT `userip`) AS `total` FROM `stats`
WHERE (`date` >= $date1 AND `date` < $date2) AND `status`=1 GROUP BY `userid`
while($row=mysqli_fetch_assoc($result)){
$total=$total+$row['total'];
}
This gives result as 6312
Please see why there is different result on group by
Thanks
EDIT
Result is correct if I don't count DISTINCT
its obvious that the count wont match as in your first query you have used distinct with count now the rows fetched is 5644.
Which may have count with the values like 2,3,4,5 so if you need the same count as group by you need to add all the count column which will be same as your group by query
if you need unique users the use below query
SELECT DISTINCT `userip` AS `userip` FROM `stats`
WHERE (`date` >= $date1 AND `date` < $date2) AND `status`=1
For user with unique ip
SELECT userid,userip from stats GROUP BY userip HAVING COUNT(*) >=1

select statement for averages based on different date ranges in one MySQL query

Basically I am attempting to make a chart with this data. I am able to put my query into a while loop in PHP to get each average, but I would prefer this was done with one query producing one result table.
<?php
date_default_timezone_set('America/Los_Angeles');
include('../connect.php');
$subcategory = 'T-Shirts';
$date = date('Y-m-d', strtotime('-29 days'));
$today = date("Y-m-d");
$subcategory = mysqli_real_escape_string($conp, $subcategory);
echo "<table border=\"1\">";
echo "<tr>";
echo "<th>date</th>";
echo "<th>average</th>";
echo "</tr>";
while (strtotime($date) <= strtotime($today)) {
$from_date = date ("Y-m-d", strtotime("-29 day", strtotime($date)));
$query = $conp->query("SELECT ROUND(SUM(OutCount)/30) AS 'average' FROM inventory
LEFT JOIN item
ON inventory.itemcode = item.itemcode
WHERE item.subcategory = '$subcategory'
AND TrDateTime BETWEEN '$from_date' AND '$date' AND transactiontype like 'OUT_%'");
if($query->num_rows){
while($row = mysqli_fetch_array($query, MYSQL_ASSOC)){
if(!empty($row['average'])){
$average = $row['average'];
}else{
$average = "N/A";
}
}
mysqli_free_result($query);
}else{
$average = "N/A";
}
$date = date ("Y-m-d", strtotime("+1 day", strtotime($date)));
echo "<tr>";
echo "<td>" . $date . "</td>";
echo "<td>" . $average . "</td>";
echo "</tr>";
}
echo "</table>";
?>
I get all the dates in the past 30 days (including today) and the average sales from a range of 29 days prior until that date.
+------------+----------+
| date | average |
+------------+----------+
| 2015-04-09 | 222 |
| 2015-04-10 | 225 |
| 2015-04-11 | 219 |
| ... | ... |
+------------+----------+
I am able to get everything I need this way, but it is running 29 queries in this situation and MySQL would be substantially quicker. I started to come up with a MySQL procedure, but I am not sure how well this will work when I try and call it with PHP.
DELIMITER //
CREATE PROCEDURE average_daily_sales()
BEGIN
SET #today = CURDATE();
SET #date_var = CURDATE() - INTERVAL 29 DAY;
SET #from_date = #date_var - INTERVAL 29 DAY;
SET #to_date = #from_date + INTERVAL 29 DAY;
label1: WHILE #date_var < #today DO
SELECT DATE_FORMAT(trdatetime, '%Y-%m-%d') as 'date', ROUND(SUM(OutCount)/30) AS 'average'
FROM inventory
LEFT JOIN item
ON inventory.itemcode = item.itemcode
WHERE item.subcategory = 'T-Shirts'
AND trdatetime BETWEEN #from_date - INTERVAL 29 DAY AND #to_date
AND transactiontype like 'OUT_%';
SET #date_var = #date_var + INTERVAL 1 DAY;
END WHILE label1;
END; //
DELIMITER ;
Ultimately, I would prefer a regular MySQL statement that I can use to produce the desired result table in one shot. Any help would be greatly appreciated.
Do you have data on each distinct day in the range? If so, this is a slightly complex join operation, but very doable.
You can get the date ranges you need as follows:
SELECT DISTINCT
DATE(trdatetime)- INTERVAL 30 DAY AS startdate,
DATE(trdatetime) AS enddateplus1
FROM inventory
WHERE trdatetime >= NOW() - INTERVAL 31 DAY
Debug this query. Take a look to make sure you get each date range you want.
Then you can join this to your business query like so
SELECT dates.startdate,
ROUND(SUM(OutCount)/30) AS 'average'
FROM (
SELECT DISTINCT
DATE(trdatetime)- INTERVAL 30 DAY AS startdate,
DATE(trdatetime) AS enddateplus1
FROM inventory
WHERE trdatetime >= NOW() - INTERVAL 31 DAY
) dates
LEFT JOIN inventory ON i.trdatetime >= dates.startdate
AND i.trdatetime < dates.enddateplus1
LEFT JOIN item ON i.itemcode = item.itemcode
WHERE item.subcategory = 'T-Shirts'
AND transactiontype like 'OUT_%'
GROUP BY dates.startdate
If your inventory data is sparse, that is, you don't have transactions on all days, then your dates query will be missing some rows.
There's a way to fill in those missing rows. But it's a pain in the s. Read this for more info. http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
Notice that BETWEEN works very badly indeed for filtering DATETIME or TIMESTAMP values.
If you create a calender table and populate that with a range of date values, e.g.
CREATE TABLE cal (dt DATE NOT NULL PRIMARY KEY) ;
INSERT INTO cal VALUES ('2015-04-01'),('2015-04-02'),('2015-04-03'), ... ;
you could use that as a row source, in a query like this:
SELECT cal.dt
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foo'
AND n.TrDateTime >= cal.dt + INTERVAL -28 DAY
AND n.TrDateTime < cal.dt + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM cal
WHERE cal.dt >= '2015-04-01'
AND cal.dt < '2015-05-01'
ORDER BY cal.dt
It's not mandatory to create a cal calendar table. We could use an inline view and give it an alias of cal. For example, in the query above, we could replace this line:
FROM cal
with this:
FROM ( SELECT DATE('2015-04-01') AS dt
UNION ALL SELECT DATE('2015-04-02')
UNION ALL SELECT DATE('2015-04-03')
UNION ALL SELECT DATE('2015-04-04')
UNION ALL SELECT DATE('2015-04-05')
) cal
Or, if you have a rowsource that can give you a contiguous series of integers, starting at zero up t you could manufacture your date values from a base date, for example
FROM ( SELECT '2014-04-01' + INTERVAL i.n DAY
FROM source_of_integers i
WHERE i.n >= 0
AND i.n < 31
ORDER BY i.n
) cal
Some notes:
The original query shows an outer (LEFT) join, but the equality predicate in the WHERE clause negates the "outerness" of the join, it's equivalent to an inner join.
Some of the column references in the query are not qualified. Best practice is to qualify all column references, then the reader can understand which columns are coming from which tables, without requiring the reader to be familiar with which columns are in which tables. This also protects the statement from breaking in the future (with an "ambiguous column" error) when a column that has the same name is added to another table referenced in the query.)
FOLLOWUP
Personally, for a limited number of date values, I'd go with the inline view that doesn't reference a table. I'd have the PHP code generate that query for me.
With a starting date, say it's '2015-04-10', I'd take that date value and format it into a query, equivalent doing this:
$cal = "SELECT DATE('2015-04-10') AS dt" ;
Then I'd spin through a loop, and increment that date value by 1 day. Each time through the loop, I'd appending to $cal a select of the next date, the net effect of running through the loop three times would be equivalent to doing this:
$cal .= " UNION ALL SELECT DATE('2015-04-11')";
$cal .= " UNION ALL SELECT DATE('2015-04-12')";
$cal .= " UNION ALL SELECT DATE('2015-04-13')";
As a less attractive alternative, we could keep repeating the same value of the start date, and just increment an integer value, and let MySQL do the date math for us.
$cal .= " UNION ALL SELECT '2015-04-10' + INTERVAL 1 DAY";
$cal .= " UNION ALL SELECT '2015-04-10' + INTERVAL 2 DAY";
$cal .= " UNION ALL SELECT '2015-04-10' + INTERVAL 3 DAY";
Then, I'd just slide the $cal query into the SQL text as an inline view query. Something like this:
$sql = "SELECT cal.dt
, ( SELECT IFNULL(ROUND(SUM
,0) AS average_
FROM ( " . $cal . " ) cal
LEFT
JOIN item ON ... ";
Anyway, that's the approach I'd take if this was for a limited number of date values (a couple dozen or so), and if I was only going to be running this query occasionally, not hammering the database server with this query repeatedly, for every request.) If I was going to pound the server, I'd create and maintain a real cal table, rather than incur the overhead of materializing a derived table on every query.
The suggestions from #OllieJones and #spencer7593 either required a 'transaction' to take place every day in order to utilize SELECT DISTINCT DATE(trdatetime), you needed to create another table, or you needed to generate a derived table.
SELECT DISTINCT DATE(trdatetime) wasn't an option for me because I did not have transactions for everyday.
The hybrid PHP and MySQL example that #spencer7593 suggested would generate a derived table very well. In the end it took the static version about 1.8 seconds to get a result. The issue being that you would need additional PHP to generate this... (see #spencer7593 answer)
SELECT cal.dt
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foo'
AND n.TrDateTime >= cal.dt + INTERVAL -28 DAY
AND n.TrDateTime < cal.dt + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM ( SELECT DATE('2015-04-01') AS dt
UNION ALL SELECT DATE('2015-04-02')
UNION ALL SELECT DATE('2015-04-03')
UNION ALL SELECT DATE('2015-04-04')
UNION ALL SELECT DATE('2015-04-05')
UNION ALL SELECT DATE('2015-04-06')
etc...
) cal
WHERE cal.dt >= '2015-04-01'
AND cal.dt < '2015-05-01'
ORDER BY cal.dt
I am attempted to use another one of #spencer7593 answers. I created a "source of integers" table with the numbers 0-31 as he suggested. This method took a little over 1.8 seconds.
SELECT cal.sd, cal.ed
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foobar'
AND n.TrDateTime >= cal.ed + INTERVAL -30 DAY
AND n.TrDateTime < cal.ed + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM ( SELECT (CURDATE() + INTERVAL -30 DAY) + INTERVAL i.n DAY as `ed`, (((CURDATE() + INTERVAL -30 DAY) + INTERVAL i.n DAY) + INTERVAL - 30 DAY) as `sd`
FROM source_of_integers i
WHERE i.n >= 0
AND i.n < 31
ORDER BY i.n
) cal
WHERE cal.ed >= CURDATE() + INTERVAL -29 DAY
AND cal.ed <= CURDATE()
ORDER BY cal.ed;
You need a rowsource for these dates, there isn't really a way around that. In the end I made a cal table..
CREATE TABLE cal (
dt DATE NOT NULL PRIMARY KEY
);
CREATE TABLE ints ( i tinyint );
INSERT INTO ints VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
INSERT INTO cal (dt)
SELECT DATE('2010-01-01') + INTERVAL a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i DAY
FROM ints a JOIN ints b JOIN ints c JOIN ints d JOIN ints e
WHERE (a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i) <= 3651
ORDER BY 1;
And then ran a slightly modified version of #spencer7593 answer on it..
SELECT cal.dt
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foo'
AND n.TrDateTime >= cal.dt + INTERVAL -28 DAY
AND n.TrDateTime < cal.dt + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM cal
WHERE cal.dt >= CURDATE() + INTERVAL -30 DAY
AND cal.dt < CURDATE()
ORDER BY cal.dt;
In my opinion, I believe this is the cleanest (less PHP) and highest performing answer.
Here is how I indexed the inventory table to speed it up substantially:
ALTER TABLE inventory ADD KEY (ItemCode, TrDateTime, TransactionType);
Thank you #OllieJones and #spencer7593 for all of your help!

mysql, use a result of if condition in a following if condition

Is it possible to use a resulting "column" from an if-condition in another if-condition inside the same query? I'd like to get all my documents related data once.
SELECT d.id, d.filename,
IF(d.document_valid_until_further_notice = 0,
IF(d.document_valid_until = "0000-00-00",
DATE_ADD(d.document_date, INTERVAL 12 MONTH),
d.document_valid_until
), "0000-00-00"
) AS calculated_valid_until_date,
IF(calculated_valid_until_date != "0000-00-00",
DATE_SUB(calculated_valid_until_date, INTERVAL 8 WEEK,
"0000-00-00"
) AS calculated_alert_expiring_date,
IF(calculated_valid_until_date > CURDATE() AND calculated_valid_until_date != "0000-00-00", 1, 0) AS expired FROM documents AS d WHERE 1 ORDER BY d.document_date DESC';
Now, "calculated_valid_until_date" comes out correctly, e.g. 2015-10-20, but I can't use that value in the following if-statements. with or without # -sign. (#calculated_valid_until_date). Is there even a way or do I have to do this all with separated queries or in client side?
Thanks for any ideas!
Wrap the calculated fields of the into a derived table, then reference these calculations in an outer select from this derived table to continue your calculations:
SELECT x.id, x.filename,
x.calculated_valid_until_date,
x.calculated_alert_expiring_date,
-- Use the derived table to do the second round of calculations
IF(x.calculated_valid_until_date > CURDATE()
AND x.calculated_valid_until_date != "0000-00-00", 1, 0) AS expired
FROM
-- Project the first round of calculations into a derived table
(SELECT
d.id, d.filename, d.document_date,
IF(d.document_valid_until = '0000-00-00', DATE_ADD(d.document_date, INTERVAL 12 MONTH),
d.document_valid_until) AS calculated_valid_until_date,
IF(d.calculated_valid_until_date != '0000-00-00',
DATE_SUB(d.calculated_valid_until_date, INTERVAL 8 WEEK),
'0000-00-00') AS calculated_alert_expiring_date
FROM documents AS d
) x
WHERE 1 = 1
ORDER BY x.document_date DESC;
SqlFiddle here
SELECT x.id, x.filename,
x.calculated_valid_until_date,
IF(x.calculated_valid_until_date < CURDATE() AND x.calculated_valid_until_date != "0000-00-00", 1, 0) AS expired, IF(x.calculated_valid_until_date != '0000-00-00',
DATE_SUB(x.calculated_valid_until_date, INTERVAL 8 WEEK), '0000-00-00') AS calculated_alert_expiring_date
FROM
(SELECT
d.id, d.filename, d.document_date, d.calculated_alert_expiring_date,
IF(d.document_valid_until = '0000-00-00', DATE_ADD(d.document_date, INTERVAL 12 MONTH),
d.document_valid_until) AS calculated_valid_until_date
FROM documents AS d
) x
WHERE 1 = 1
ORDER BY x.document_date DESC
CREATE TABLE documents
(
id INT,
filename VARCHAR(50),
document_valid_until_further_notice TINYINT(1),
document_valid_until_date DATE,
document_date DATE,
document_valid_until DATE,
calculated_valid_until_date DATE,
calculated_alert_expiring_date DATE
);
This is how I got it working. I didn't look at the fiddle the first time so I didn't quite realize I'd have to create the table columns for "calculated_valid_until_date" and "calculated_alert_expiring_date".
But anyhow, it is working just perfect now. THANK YOU!

Find next time business is open; mysql hours calculation

Iā€™m trying to figure out if a shop is currently within its opening hours, if not then select the next time its open.
Finally I need to be able to put the opening day as a specific date.
Can someone possible give me a tip how to construct this query?
Thanks in advance
CREATE TABLE `shop_hours` (
`id` int(11) NOT NULL,
`shop_id` int(11) unsigned NOT NULL,
`day_of_week` int(11) unsigned NOT NULL,
`open_time` time NOT NULL,
`close_time` time NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `shop_hours` (`id`, `shop_id`, `day_of_week`, `open_time`, `close_time`)
VALUES
(1, 1, 0, '08:00:00', '24:00:00'),
(2, 1, 1, '08:00:00', '24:00:00'),
(3, 1, 2, '08:00:00', '24:00:00'),
(4, 1, 3, '08:00:00', '24:00:00'),
(5, 1, 4, '08:00:00', '24:00:00'),
(6, 1, 5, '08:00:00', '24:00:00'),
(7, 1, 6, '08:00:00', '24:00:00');
Edit:
To clarify a little I'm not looking to find open shops, but only open hours for ONE specific shop. Based on the opening/closing hour, and what time it is now. I will generate some selectable timestamps incremented by 15 minutes.
E.g. if a shop has just closed (1PM), I will need to use the next open day's open/closing time instead. (the shop isn't necessarily open every day, could be closed Sundays).
To find out shop_id's, that is open for NOW()
SELECT *
FROM `shop_hours`
WHERE `day_of_week` = DATE_FORMAT(NOW(), '%w')
AND CURTIME() BETWEEN `open_time` AND `close_time`
Obsolete:
To find tomorrow's available open_times:
SELECT *
FROM `shop_hours`
WHERE `day_of_week` = DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 1 DAY), '%w')
Edit 2:
To find next available open_times:
SELECT `shop_id`,
MIN(CAST(CONCAT(DATE(DATE_ADD(NOW(), INTERVAL ((7 + DATE_FORMAT(NOW(), '%w') - `day_of_week`) % 7) DAY)), ' ', `open_time`) AS DATETIME)) AS `next_open_datetime`
FROM `shop_hours`
GROUP BY `shop_id`
Edit:
DATE_FORMAT(*DATE*, '%w') uses the format 0 = Sunday ... 6 = Saturday
If you want to use the ISO format 1 = Monday ... 7 = Sunday in your day_of_week field, you should bind php's date('N') to your query (or use Mysql's if function IF(DATE_FORMAT(NOW(), '%w') = 0, 7, DATE_FORMAT(NOW(), '%w')), but that's ugly)
1) Check if shop is open. Result is empty, if shop is closed:
select * from shop_hours
where shop_id = $id
and dayofweek(curdate()) = day_of_week
and curtime() between open_time and close_time;
2) Find next open time:
(select open_time from shop_hours
where shop_id = $id and curtime() < open_time
and day_of_week >= dayofweek(curdate()))
union
(select open_time from shop_hours
where shop_id = $id and curtime() < open_time
order by day_of_week)
union
(select open_time from shop_hours
where shop_id = $id and curtime() > close_time
and day_of_week >= dayofweek(curdate()))
union
(select open_time from shop_hours
where shop_id = $id and curtime() > close_time
order by day_of_week)
limit 1;
Untested, but this should respect weekend wraparound and holes in the week (i.e. closed days).
Keep in mind, that dayofweek() numbers 1 = Sunday, 2 = Monday, ... If your table stores the weekdays in a different format, you must adjust the query accordingly.

MySQL queries and intervals

This is the toughest query I ever made:
http://robertr.pastebin.com/X4bG4pFp
"SELECT `user`.`id` , `user`.`fname` , `user`.`lname` ,
YEAR( `user`.`bday` ) AS `bday_year` , `user`.`class_id` ,
( SELECT `class`.`class_name`
FROM `wp_class_classes` `class`
WHERE `user`.`class_id` = `class`.`id`) AS `class_name`
FROM `wp_class_users` `user`
WHERE MONTH( `bday` ) = $month AND DAY( `bday` ) = $day
OR `user`.`fname` =
( SELECT `name`.`names`
FROM `wp_class_namedays` `name`
WHERE `name`.`day` = '$month.$day'
AND `user`.`fname` = `name`.`names` )
This query grabs data from three different database tables to check if there is someone in the database, who has a party today. And in Latvia we have Name Days too. Anyway, this query works well, and does its job well, but now I want to make it a bit cooler.
I want it to show, who will be having a party next week. You've probably noticed these emails that Facebook sends to you every weekend showing who has a birthday coming up.
But I just can't understand how to get at least that interval?
I remember that PHP has some good functions with which you can find on which day starts month and so on, but maybe here are some bright heart, and willing to help me kick me a bit faster forward.
SELECT
`user`.`id`,
`user`.`fname`,
`user`.`lname` ,
YEAR(`user`.`bday`) AS `bday_year`,
`user`.`class_id`,
(
SELECT
`class`.`class_name`
FROM `wp_class_classes` `class`
WHERE `user`.`class_id` = `class`.`id`
) AS `class_name`,
CASE
WHEN MONTH(`week`.`Date`) = MONTH(`user`.`bday`) AND
DAY(`week`.`Date`) = DAY(`user`.`bday`) THEN 1
ELSE 2
END AS `event_type`
FROM `wp_class_users` `user`
LEFT JOIN `wp_class_namedays` `name` ON `user`.`fname` = `name`.`names`
LEFT JOIN (
SELECT CURDATE() + INTERVAL (1 - DAYOFWEEK(CURDATE())) DAY AS `Date` UNION ALL
SELECT CURDATE() + INTERVAL (2 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (3 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (4 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (5 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (6 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (7 - DAYOFWEEK(CURDATE())) DAY
) `week`
ON CONCAT(MONTH(`week`.`Date`), '.', DAY(`week`.`Date`)) IN (
CONCAT(MONTH(`user`.`bday`), '.', DAY(`user`.`bday`)),
`name`.`day`
)
WHERE `week`.`Date` IS NOT NULL
The user table is joined with the name day table, and the result set is then compared against the dates of the current week. The final result set lists only those users whose birthdays or name days happen during the week.
If you want to know about the events of, for example, the next week, you can simply change the intervals in the week.Date definitions as 8 - DAYOFWEEK..., 9 - DAYOFWEEK... etc.
One last thing is, instead of the correlated subquery in the select list you could use INNER JOIN, like this:
SELECT
`user`.`id`,
`user`.`fname`,
`user`.`lname` ,
YEAR(`user`.`bday`) AS `bday_year`,
`user`.`class_id`,
`class`.`class_name`
FROM `wp_class_users` `user`
INNER JOIN `wp_class_classes` `class` ON `user`.`class_id` = `class`.`id`
LEFT JOIN `wp_class_namedays` `name` ON ... /* the rest of the above script */
The event_type column as defined above can tell you whether the event is a birthday or not, but it doesn't let you know whether it's both the Birthday and a Name Day for that particular person.
In case you would like to have that distinction, you could change the event_type definition like this:
CASE
WHEN MONTH(`week`.`Date`) = MONTH(`user`.`bday`) AND
DAY(`week`.`Date`) = DAY(`user`.`bday`) THEN 1
ELSE 0
END +
CASE CONCAT(MONTH(`week`.`Date`), '.', DAY(`week`.`Date`))
WHEN `name`.`day` THEN 2
ELSE 0
END AS `event_type`
Now the result of the column would be:
1 ā€“ a birthday
2 ā€“ a name day
3 ā€“ both
Additionally, you could have 'B' instead of 1 and 'N' instead of 2 (and '' instead of 0). The results would be then 'B', or 'N', or 'BN'. Not sure whether + can be used for concatenation, though. If not, put both CASEs into CONCAT().
I'm not sure if I get your query right, but the command SYSDATE() is mentioned in the MySQL docs. You might want to try something like:
... where date = SYSDATE() + 7
(check the syntax, I come from Oracle ;) )
This will get the parties for the next 7 days.

Categories