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.
Related
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!
I am currently writing a SQL query in SQL Server Management Studio that will be used in PHP
The query is meant to get all of the records between 8AM and 8 PM yesterday and count how many of the records have the fffffff0 as the CardID currently this just spits out 355 records with the column CardCOUNT just having the number one in them not quite what I want, how should I do this?
SELECT
COUNT(CardID) AS CardCOUNT,
ReaderTime,
controllerID,
dtReading
FROM
myMachineMonitor2.dbo.ReaderData
WHERE
(controllerID = 31)
AND (ReaderTime BETWEEN '08:00:00' AND '20:00:00')
AND (CardID = 'fffffff0')
AND (DATEDIFF(DAY, DATEADD(DAY, - 1, CURRENT_TIMESTAMP), dtReading) = 0)
GROUP BY
ReaderTime, controllerID, dtReading
After playing around with it I kinda figured it out here is what I came up with
SELECT
COUNT(CardID) AS CardCOUNT
FROM
myMachineMonitor2.dbo.ReaderData
WHERE
(controllerID = 31)
AND (ReaderTime BETWEEN '08:00:00' AND '20:00:00')
AND (CardID = 'fffffff0')
AND (DATEDIFF(DAY, DATEADD(DAY, - 0, CURRENT_TIMESTAMP), dtReading) = 0)
This changes the logic to just working with the current date:
SELECT COUNT(CardID) AS CardCOUNT, ReaderTime, controllerID, dtReading
FROM myMachineMonitor2.dbo.ReaderData
WHERE (controllerID = 31) AND
(CardID = 'fffffff0') AND
dtReading >= cast(cast(getdate() as date) as datetime) - 16.0/24 and
dtReading >= cast(cast(getdate() as date) as datetime) - 4.0/24
GROUP BY ReaderTime, controllerID, dtReading ;
The cast to date puts the value at midnight. Then it subtracts 16 and 4 hours for the range.
This expression also does all the work on the current date.
I know there are some debates about the sargability of datetime columns but I would approach this more like this. I find this a lot easier to understand by separating the two conditions.
SELECT COUNT(CardID) AS CardCOUNT
, ReaderTime
, controllerID
, dtReading
FROM myMachineMonitor2.dbo.ReaderData
WHERE controllerID = 31
AND CardID = 'fffffff0'
AND ReaderTime >= dateadd(hour, 8, dateadd(day, datediff(day, 0, CURRENT_TIMESTAMP) - 1, 0)) --8am yesterday
AND ReaderTime < dateadd(hour, 19, dateadd(day, datediff(day, 0, CURRENT_TIMESTAMP) - 1, 0)) --9pm yesterday
GROUP BY ReaderTime
, controllerID
, dtReading;
The problem is by making time a grouping condition you are forcing one line per distinct time, not allowing an aggregate over a range of times. Just drop the times from your GROUP BY and you're fine.
SELECT controllerID, COUNT(CardID) AS CardCOUNT
FROM myMachineMonitor2.dbo.ReaderData
WHERE (controllerID = 31)
AND (ReaderTime BETWEEN '08:00:00' AND '20:00:00')
AND (CardID = 'fffffff0')
AND (DATEDIFF(DAY, DATEADD(DAY, - 1, CURRENT_TIMESTAMP), dtReading) = 0)
GROUP BY controllerID
I am trying to get registerd users during the last 7 days so I have the users table
+-----+------------+--------------+
| ID | USERNAME | ADDED |
+-----+------------+--------------+
| 1 | Vlad | 1347386878 |
+-----+------------+--------------+
| 2 | Test | 1347386578 |
+-----+------------+--------------+
I tried below sql but the output is empty, no errors... and I need something descending from today to 7 days ago
SELECT date(added), COUNT(id) AS num_registered
FROM users
WHERE added < CURDATE()
AND added > CURDATE() - INTERVAL 7 DAYS
GROUP BY date(added) LIMIT 1, 7
Any suggestions how to do this?
EDIT:
$mysql_query = mysql_query('SELECT added, DATE(added), COUNT(id) AS num_reg FROM users_test WHERE added < (UNIX_TIMESTAMP() - (7 * 24 * 60 * 60)) GROUP BY DATE(added) LIMIT 1, 7') or die(mysql_error());
while($row = mysql_fetch_array($mysql_query))
{
$month = date('F', $row['added']);
$day = date('j', $row['added']);
$textbuilder .= '
<li>
<a href="#" title="'.$month.' '.$day.', '.$row['num_reg'].' registered">
<span class="label">'.$day.'</span>
<span class="count" style="height: 20%">('.$row['num_reg'].')</span>
</a>
</li>';
}
TABLE:
CREATE TABLE IF NOT EXISTS `users_test` (
`id` int(10) NOT NULL,
`username` varchar(60) NOT NULL,
`added` int(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `users_test` (`id`, `username`, `added`) VALUES
(1, 'Test', 1347303641),
(2, 'Test1', 1347217241),
(3, 'Test2', 1347130841),
(4, 'Test3', 1347044441);
ADDED is a UNIX timestamp value, so just do this:
SELECT
DATE(Added), Count()
FROM
Users
WHERE
Added > ( UNIX_TIMESTAMP() - ( 7 * 24 * 60 * 60 ) )
GROUP BY
DATE(Added)
LIMIT
1, 7
Note that this won't work in cases where you're dealing with leap seconds because it uses the approximate definition of a day as 24*60*60, but I imagine you can live with that.
You need to convert your timestamp into a datetime using FROM_UNIXTIME(). Since there are a large amount of conversions taking place, it would be easier to work with a derived table:
SELECT DATE(Added), COUNT(id) AS num_registered FROM
(SELECT id, username, FROM_UNIXTIME(added) AS added FROM users) u
WHERE added < CURDATE()
AND added > CURDATE() - INTERVAL 7 DAYS
GROUP BY DATE(added) LIMIT 1, 7
SELECT FROM_UNIXTIMESTAMP(added, , '%b %D, %Y'), COUNT(id) AS num_registered
FROM users
WHERE added > unix_timestamp() - ( 7 - 86400 )
GROUP BY FROM_UNIXTIMESTAMP(added, , '%b %D, %Y')
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 .. :)
I have a table of users which has a date field for their birthday:
buddy_auto_id int(11) PK
user_id varchar(128)
buddy_user_id varchar(128)
buddy_name varchar(128)
buddy_bday date
buddyuser_id varchar(20)
active enum('Yes','No')
requestsentby int(11)
whenrequested timestamp
I'm trying to find the 3 users whose birthdays fall soonest compared to todays date and then display the number of days until their birthday ordered by soonest first.
Is this possible within a SQL query or do I have to pull it out and let PHP do the equation?
Many thanks
We first need to calculate the next birthday, then order by that value:
select *,
buddy_bday + interval
if(
month(buddy_bday) < month(now()) or
(month(buddy_bday) = month(now()) and day(buddy_bday) < day(now())),
year(now())+1,
year(now())
) - year(buddy_bday) year as next_bday
from buddies order by next_bday - date(now());
The long if statement figures out whether the buddy already had his/her birthday this year.
This should be possible with SQL. You need to compare the current date with a birthday expressed as from this year or next year, depending on whether we've past it in the current year. Once you have this "next birthday" date, use DATEDIFF function to determine number of days distant from current date.
SELECT *,
DATEDIFF(
# determine date of next birthday by adding age in years to birthday year
DATE_ADD(buddy_bday, INTERVAL
YEAR(CURDATE())-YEAR(buddy_bday)
# add a year if we celebrated birthday already this year
+(MONTH(buddy_bday)<MONTH(CURDATE()) OR (MONTH(buddy_bday)=MONTH(CURDATE()) AND DAY(buddy_bday) < DAY(CURDATE())))
YEAR),
CURDATE())
AS days_to_next_bday
FROM user_table
ORDER BY days_to_next_bday
LIMIT 3;
You should use a DAYOFYEAR function. Try this query -
SELECT buddy_auto_id , buddy_bday FROM table_name
WHERE DAYOFYEAR(buddy_bday) - DAYOFYEAR(NOW()) > 0
ORDER BY DAYOFYEAR(buddy_bday) - DAYOFYEAR(NOW())
LIMIT 3;
So, this query works only for current year.
EDITED query 2:
This one works for all dates.
CREATE TABLE birtdays(
buddy_auto_id INT(11) NOT NULL AUTO_INCREMENT,
buddy_bday DATE DEFAULT NULL,
PRIMARY KEY (buddy_auto_id)
);
INSERT INTO birtdays VALUES
(1, '2011-10-04'),
(2, '2011-03-01'),
(3, '2011-11-29'),
(4, '2011-11-10'),
(5, '2011-12-29'),
(6, '2011-11-30'),
(7, '2011-12-08'),
(8, '2011-09-17'),
(9, '2011-12-01'),
(10, '2011-12-11');
SELECT buddy_auto_id, buddy_bday
FROM
birtdays, (SELECT #day_of_year:=DAYOFYEAR(NOW())) t
ORDER BY
DAYOFYEAR(buddy_bday + INTERVAL YEAR(NOW()) - YEAR(buddy_bday) YEAR) - #day_of_year
+ IF (DAYOFYEAR(buddy_bday + INTERVAL YEAR(NOW()) - YEAR(buddy_bday) YEAR) - #day_of_year > 0, 0, DAYOFYEAR(STR_TO_DATE(CONCAT(YEAR(NOW()), '-12-31'), '%Y-%m-%d')))
LIMIT 3;
+---------------+------------+
| buddy_auto_id | buddy_bday |
+---------------+------------+
| 6 | 2011-11-30 |
| 7 | 2011-12-08 |
| 10 | 2012-02-11 |
+---------------+------------+