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 |
+---------------+------------+
Related
I am currently developing an application that displays documents and allows the members to search for these documents by a number of different parameters, one of them being date range.
The problem I am having is that the database schema was not developed by myself and the creator of the database has created a 'date' table with fields for 'day','month','year'.
I would like to know how I can select a specific day, month, year from the table and create a date object in SQL so that I can compare dates input by the user using BETWEEN.
Below is the structure of the date table:
CREATE TABLE IF NOT EXISTS `date` (
`deposition_id` varchar(11) NOT NULL default '',
`day` int(2) default NULL,
`month` int(2) default NULL,
`year` int(4) default NULL,
PRIMARY KEY (`deposition_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
When you have integer values for year, month and day you can make a DATETIME by combining MAKEDATE() and DATE_ADD(). MAKEDATE() with a constant day of 1 will give you a DATETIME for the first day of the given year, and then you can add to it the month and day with DATE_ADD():
mysql> SELECT MAKEDATE(2013, 1);
+-------------------+
| MAKEDATE(2013, 1) |
+-------------------+
| 2013-01-01 |
+-------------------+
mysql> SELECT DATE_ADD(MAKEDATE(2013, 1), INTERVAL (3)-1 MONTH);
+---------------------------------------------------+
| DATE_ADD(MAKEDATE(2013, 1), INTERVAL (3)-1 MONTH) |
+---------------------------------------------------+
| 2013-03-01 |
+---------------------------------------------------+
mysql> SELECT DATE_ADD(DATE_ADD(MAKEDATE(2013, 1), INTERVAL (3)-1 MONTH), INTERVAL (11)-1 DAY);
| DATE_ADD(DATE_ADD(MAKEDATE(2013, 1), INTERVAL (3)-1 MONTH), INTERVAL (11)-1 DAY) |
+----------------------------------------------------------------------------------+
| 2013-03-11 |
+----------------------------------------------------------------------------------+
So to answer the OP's question:
SELECT * FROM `date`
WHERE DATE_ADD(DATE_ADD(MAKEDATE(year, 1), INTERVAL (month)-1 MONTH), INTERVAL (day)-1 DAY)
BETWEEN '2013-01-01' AND '2014-01-01';
You can use STR_TO_DATE() function.
The simplest way to do this is:
DATE(CONCAT_WS('-', year, month, day))
LPAD is not necessary as #pbarney pointed out earlier. If you are comparing with another date object, it's not strictly necessary to wrap it with DATE as MySQL will cast it automatically:
some_date_field > CONCAT_WS('-', year, month, day)
To build a sortable date string from that, you'll need CONCAT to join the bits together and LPAD to make sure the month and day fields are two digits long. Something like this:
CONCAT(`year`,'-',LPAD(`month`,2,'00'),'-',LPAD(`day`,2,'00'))
Once you have that, you should be able to use BETWEEN, as they'll be in a sortable format. However if you still need to convert them to actual datetime fields, you can wrap the whole thing in UNIX_TIMESTAMP() to get a timestamp value.
So you'd end up with something like this:
SELECT UNIX_TIMESTAMP(CONCAT(`year`,'-',LPAD(`month`,2,'00'),'-',LPAD(`day`,2,'00'))) as u_date
WHERE u_date BETWEEN timestamp_1 and timestamp_2
However, be aware that this will be massively slower than if the field was just a simple timestamp in the first place. And you should definitely make sure you have an index on the year, month and day fields.
Expanding this answer, here's my take on it:
DELIMITER $$
CREATE FUNCTION fn_year_month_to_date(var_year INTEGER,
var_month enum('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12')
)
RETURNS DATE
BEGIN
RETURN (MAKEDATE(var_year, 1) + INTERVAL (var_month - 1) MONTH);
END $$
DELIMITER ;
SELECT fn_year_month_to_date(2020, 12)
;
Try to use CONCAT() and make it one field and compare .
Am not sure you can compare it as date after concatenation.
You can compare as integer.
concatinate year month day and make an integer like this 20101017 and compare.
Hopefully :)
I have the following query:
SELECT `date_created`, COUNT(`ID`) AS no_of_registration
FROM (
SELECT `id`,
DATE_SUB(DATE_ADD(MAKEDATE(from_unixtime(`created_on`,'%Y'), 7),
INTERVAL EXTRACT(WEEK FROM from_unixtime(`created_on`,'%Y-%m-%d')) WEEK),
INTERVAL WEEKDAY(DATE_ADD(MAKEDATE(from_unixtime(`created_on`,'%Y'), 1),
INTERVAL EXTRACT(WEEK FROM from_unixtime(`created_on`,'%Y-%m-%d')) WEEK)) -1 DAY)
as `date_created`
FROM `users` WHERE 1) x
GROUP BY `date_created`
HAVING `date_created` BETWEEN CURDATE() - INTERVAL 12 WEEK AND CURDATE()
This displays the data from the last 12 weeks using the date today. However, it only shows date_created as the Monday date. I'd like to show every week as a date range. Example: 2017-11-27 - 2017-12-04.
Is this possible? I need it to display in my graph using chart.js.
Thanks for you help. -Eli
Hello not sure that i have understood your problem but if you want only the monday, you can add in your where statement :
AND DAYOFWEEK(a.Date)=2
On my database table I have 2 columns, start_date and end_date.
Sample data would be:
-------------------------------
start_date | end_date
-------------------------------
2017-11-01 2017-11-02
2017-11-03 2017-11-07
2017-11-20 2017-11-28
2017-11-13 2017-12-02
-------------------------------
I need to find if there are 5 consecutive days that are not yet used, which in this case, there is:
(2017-11-08 to 2017-11-13)
I'm using PHP and MySQL.
Thanks in advance!
You'd need to check for edge cases depending on your actual data and if there were no overlap dates, but this is a good start for the provided data.
Assuming table and data as defined as below:
CREATE TABLE
`appointments`
(
`appointment_id` INT PRIMARY KEY AUTO_INCREMENT,
`start_date` DATE,
`end_date` DATE
);
INSERT INTO
`appointments`
(`start_date`, `end_date`)
VALUES
('2017-11-01', '2017-11-02'),
('2017-11-03', '2017-11-07'),
('2017-11-20', '2017-11-28'),
('2017-11-13', '2017-12-02');
If you order the rows, and take the lag from the end date before it, and take any gaps of 5 or more. In SQL Server there are LAG functions, but here's a way of doing the same. Then once you have a table of all rows and their corresponding gaps, you take the start date of that period, and create the gap period from the number of days between. Since TIMESTAMPDIFF is inclusive, you need to subtract a day.
SET #end_date = NULL;
SELECT
DATE_ADD(`start_date`, INTERVAL -(`gap_from_last`-1) DAY) AS `start_date`,
`start_date` AS `end_date`
FROM
(
SELECT
`appointment_id`,
CASE
WHEN #end_date IS NULL THEN NULL
ELSE TIMESTAMPDIFF(DAY, #end_date, `start_date`)
END AS `gap_from_last`,
`start_date`,
#end_date := `end_date` AS `end_date` -- Save the lag date from the row before
FROM
`appointments`
ORDER BY
`start_date`,
`end_date`
) AS `date_gap` -- Build table that has the dates and the number of days between
WHERE
`gap_from_last` > 5;
Provides:
start_date | end_date
------------------------
2017-11-08 | 2017-11-13
Edit: Oops! Forgot the SQLFiddle (http://sqlfiddle.com/#!9/09cfce/16)
SELECT x.end_date + INTERVAL 1 DAY unused_start
, MIN(y.start_date) unused_end
FROM appointments x
JOIN appointments y
ON y.start_date >= x.end_date
GROUP
BY x.start_date
HAVING DATEDIFF(MIN(y.start_date),unused_start) >= 5;
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.
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')