Assistance with MySQL query & timestamps - php

I am trying to create a query which returns offers for all rows that belong to a club_id that are within a start_date & end_date, however the query should also return results for any that match the club_id AND the end_date is 0 - any ideas of how to do this?
My current query is below...
SELECT
* ,
UNIX_TIMESTAMP( start_date ) AS start_dateStamp,
UNIX_TIMESTAMP( end_date ) AS end_dateStamp
FROM
(`offers`)
WHERE
UNIX_TIMESTAMP( `start_date` ) <1329308797
AND
UNIX_TIMESTAMP( `end_date` ) >1329308797
AND
`club_id` =23

SELECT
`offers`.* ,
UNIX_TIMESTAMP( start_date ) AS start_dateStamp,
UNIX_TIMESTAMP( end_date ) AS end_dateStamp
FROM `offers`
WHERE `club_id` =23
AND (
(
`start_date`<FROM_UNIXTIME(1329308797)
AND `end_date`>FROM_UNIXTIME(1329308797)
)
OR `end_date`=FROM_UNIXTIME(0)
)
Please note, that I moved the convertion from unix-timestamp to MySQL-date from the field to the constant - this way it has to be converted only once, and not for all rows. Additionally this way an index can be used.
Edit
With "date zero" not being Unix-Zero but MySQL-Zeor the last line should be
OR `end_date`='0000-00-00'
additionally, if the data type of start_date and end_date is not DATETIME but DATE you need
DATE(FROM_UNIXTIME(...))
instead of
FROM_UNIXTIME(...)

you didn't specify if end_date = 0 also reqires start_date > $timestamp, so I assumed start_date still has to meet its criteria.
SELECT
please_name,
the_columns,
you_want_to_select_seperately,
for_reasons,
UNIX_TIMESTAMP(start_date) AS start_dateStamp,
UNIX_TIMESTAMP(end_date) AS end_dateStamp
FROM `offers`
WHERE `club_id` = 23
AND `start_date`) < UNIX_TIMESTAMP(1329308797)
AND (`end_date` > UNIX_TIMESTAMP(1329308797) OR `end_date` = "0000-00-00")
Some notes on your SQL:
don't use * unless you have reasons. which, judging from your original question, you really really don't
try to format your code so it's readable
sort your conditions so the condition selecting the fewest records (read: has the highest specificity) comes first. This has become more a mental thing than hinting the MySQL optimizer, it's still a convention I'd stick to
if you compare (date) ranges, consider using col BETWEEN min_val and max_val (boundaries are inclusive)
don't use functions in the WHERE and GROUP BY clause. MySQL cannot cache/index these and must thus run the value of every row through that function. UNIX_TIMESTAMP() has a friend named FROM_UNIXTIME() - doing pretty much the exact opposite. This is nothing you can't do yourself in PHP with date(), though.
consider setting fields to NULL if they have an unknown value. You either have a date (2012-02-15) or you don't (NULL). That would allow your query to simply check OR end_date IS NULL

Is this what you want?
SELECT *
, UNIX_TIMESTAMP(start_date) AS start_dateStamp
, UNIX_TIMESTAMP(end_date) AS end_dateStamp
FROM
`offers`
WHERE
(UNIX_TIMESTAMP(`start_date`) < 1329308797 AND UNIX_TIMESTAMP(`end_date`) > 1329308797)
OR
(`club_id` = 23 AND `end_date` = '0000-00-00 00:00:00')

Related

Between Operator - Mysqli [duplicate]

I have saved the dates of a user's registration as a datetime, so that's for instance 2011-12-06 10:45:36. I have run this query and I expected this item - 2011-12-06 10:45:36 - will be selected:
SELECT `users`.* FROM `users` WHERE created_at >= '2011-12-01' AND
created_at <= '2011-12-06'
But is not. Exist any elegant way, how to select this item? As a first idea that I got was like 2011-12-06 + 1, but this doesn't looks very nice.
Your problem is that the short version of dates uses midnight as the default. So your query is actually:
SELECT users.* FROM users
WHERE created_at >= '2011-12-01 00:00:00'
AND created_at <= '2011-12-06 00:00:00'
This is why you aren't seeing the record for 10:45.
Change it to:
SELECT users.* FROM users
WHERE created_at >= '2011-12-01'
AND created_at <= '2011-12-07'
You can also use:
SELECT users.* from users
WHERE created_at >= '2011-12-01'
AND created_at <= date_add('2011-12-01', INTERVAL 7 DAY)
Which will select all users in the same interval you are looking for.
You might also find the BETWEEN operator more readable:
SELECT users.* from users
WHERE created_at BETWEEN('2011-12-01', date_add('2011-12-01', INTERVAL 7 DAY));
SELECT users.* FROM users WHERE created_at BETWEEN '2011-12-01' AND '2011-12-07';
You need to use '2011-12-07' as the end point as a date without a time default to time 00:00:00.
So what you have actually written is interpreted as:
SELECT users.*
FROM users
WHERE created_at >= '2011-12-01 00:00:00'
AND created_at <= '2011-12-06 00:00:00'
And your time stamp is: 2011-12-06 10:45:36 which is not between those points.
Change this too:
SELECT users.*
FROM users
WHERE created_at >= '2011-12-01' -- Implied 00:00:00
AND created_at < '2011-12-07' -- Implied 00:00:00 and smaller than
-- thus any time on 06
Another alternative is to use DATE() function on the left hand operand as shown below
SELECT users.* FROM users WHERE DATE(created_at) BETWEEN '2011-12-01' AND '2011-12-06'
Have you tried before and after rather than >= and <=? Also, is this a date or a timestamp?
Searching for created_at <= '2011-12-06' will search for any records that where created at or before midnight on 2011-12-06
. You want to search for created_at < '2011-12-07'.
Maybe use in between better. It worked for me to get range then filter it
You can use MySQL DATE function like below
For instance, if you want results between 2017-09-05 till 2017-09-09
SELECT DATE(timestamp_field) as date FROM stocks_annc WHERE DATE(timestamp_field) >= '2017-09-05' AND DATE(timestamp_field) <= '2017-09-09'
Make sure to wrap the dates within single quotation ''
Edit:
A better solution would be this. It would make sure that it uses the index if any exists.
select date(timestamp_field) as date from stocks_annc where time_stamp_field >= '2022-01-01 00:00:00' and time_stamp_field <= '2022-01-10 00:00:00'
Hope this helps.

select timestamp from mysql

I have a problem with query where the conditions are related to TIMESTAMP column. So I have a table named table1 which has two columns start_time and end_time. Both of them store TIMESTAMP. I want to query my databse for a particular rows where end_time is higher than given timestamp value and lower than given timestamp value. Basically I want to get all the rows where end_time is between given TIMESTAMP range.
I have found that in my case following query works:
SELECT * FROM table1
WHERE UNIX_TIMESTAMP(end_time) >= FROM_UNIXTIME('2015-11-30 20:14:00')
AND UNIX_TIMESTAMP(end_time) <= FROM_UNIXTIME('2015-11-30 20:14:05')
But it is not what I want to. I want to give TIMESTAMP value instead of DATE.
What is also strange is that this query returns values that have end_time of earlier date than given timestamp:
SELECT * FROM table1 WHERE end_time > FROM_UNIXTIME( '2015-11-30 20:20:05' )
Query above returns rows where end_timestamp is 2015-11-30 20:18:05
I feel that I am missing something here. Should I use INT instead of TIMESTAMP for my end_time? I believe that the following query should return expected values but it gives me nothing:
SELECT * FROM table1
WHERE end_time >= FROM_UNIXTIME( 1448914740 )
AND end_time <= FROM_UNIXTIME( 1448914810 )
You don't need to convert your WHERE clause values using FROM_UNIXTIME. FROM_UNIXTIME converts a TIMESTAMP into a date. If you're sending values to the SQL statement that are already in TIMESTAMP format, your SQL will look like this:
SELECT * FROM table1 WHERE end_time >= 1448914740 AND end_time <= 1448914810
If you are sending date strings (e.g. '2015-11-30 20:14:00' and '2015-11-30 20:14:05') then your SQL would look like this:
SELECT * FROM table1 WHERE FROM_UNIXTIME(end_time) >= '2015-11-30 20:14:00' AND FROM_UNIXTIME(end_time) <= '2015-11-30 20:14:05'
Since you're storing your values in your table as TIMESTAMP I would recommend you use the first SQL statement.

Converting date inside loop breaks loop

So I have a MySQL query feeding into an array to sanitize it to meet the needs of a graphing library. I tried to convert the hours, currently in 24h format, to a slightly prettier 12h format. But when I do, it kills the loop after one iteration, and so only one value gets fed to the graph (instead of values for the whole day). I'm very new to PHP, so I'm not sure what is breaking this. What is the proper way to accomplish what I need to do?
$data = $conn->query('SELECT HOUR( TIMESTAMP ) AS HOUR , COUNT( DISTINCT detected_key ) AS num_rows
FROM Visitors
WHERE TIMESTAMP >= NOW( ) - INTERVAL 1 DAY
GROUP BY HOUR( TIMESTAMP )
ORDER BY `id` ASC
LIMIT 0 , 24');
while($row = $data->fetch(PDO::FETCH_ASSOC)) {
$prettytime = DATE("g:i a", STRTOTIME($row['HOUR']));
$visit[$prettytime]= $row['num_rows'];
}
EDIT: I tried using DATE_FORMAT already, but that still just outputs as the 24h format. The only documentation I found on the MySQL website always had it right after SELECT.
SELECT DATE_FORMAT( TIMESTAMP, "%l:%i %a" ) , HOUR( TIMESTAMP ) AS HOUR , COUNT( DISTINCT detected_key ) AS num_rows
FROM Visitors
WHERE TIMESTAMP >= NOW( ) - INTERVAL 1 DAY
GROUP BY HOUR( TIMESTAMP )
ORDER BY `id` ASC
LIMIT 0 , 24
Here is the website where you can see the graph. The one using the query above is the top left graph.
This would seem to work rather nicely from a database field defined as
`thetime` timestamp NULL DEFAULT CURRENT_TIMESTAMP
Then using the MySQL DATE_FORMAT() and HOUR() functions :-
SELECT thetime, DATE_FORMAT(thetime, '%r') from test_table
Gives the results :-
2014-01-20 10:57:45, 10:57:45 AM
2014-01-20 23:59:31, 11:59:31 PM
Or
SELECT thetime, HOUR(DATE_FORMAT(thetime, '%r')) from test_table
Gives :-
2014-01-20 10:57:45, 10
2014-01-20 23:59:31, 11

PHP & SQL - compare unix timestamp with MySQL CURDATE()

i'm trying to do the following thing:
In my Database, I have rows with time in each one (using time() function).
For example: 1380300397.
Now, I'd like to build a query that gets the rows from the current day.
I've tried this query with no success:
SELECT `id`,DATE_FORMAT(`time`, '%Y-%m-%d') FROM `facts`
WHERE `app` = 1 AND DATE(`time`) = CURDATE()
What am I doing wrong?
Thanks!
AND DATE(FROM_UNIXTIME(time)) = CURDATE()
Well you can try use NOW() instead of CURDATE
In my opinion this is the best way to do it. I know it seems to be an overhead at the first glance, but this way you are not applying any function to your column, therefore your query can use index, if you have one on your time column.
SELECT
`id`, DATE_FORMAT(`time`, '%Y-%m-%d')
FROM
`facts`
WHERE
`app` = 1
AND `time` >= UNIX_TIMESTAMP(CONCAT(CURDATE(), ' 00:00:00'))
AND `time` < UNIX_TIMESTAMP(CONCAT(DATE_ADD(CURDATE(), INTERVAL 1 DAY),
' 00:00:00'))

How to minimize the load in queries that need grouping with different invervals?

I'm looking for a best practice advice how to speed up queries and at the same time to minimize the overhead needed to invoke date/mktime functions. To trivialize the problem I'm dealing with the following table layout:
CREATE TABLE my_table(
id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
important_data INTEGER,
date INTEGER);
The user can choose to show 1) all entries between two dates:
SELECT * FROM my_table
WHERE date >= ? AND date <= ?
ORDER BY date DESC;
Output:
10-21-2009 12:12:12, 10002
10-21-2009 14:12:12, 15002
10-22-2009 14:05:01, 20030
10-23-2009 15:23:35, 300
....
I don't think there is much to improve in this case.
2) Summarize/group the output by day, week, month, year:
SELECT COUNT(*) AS count, SUM(important_data) AS important_data
FROM my_table
WHERE date >= ? AND date <= ?
ORDER BY date DESC;
Example output by month:
10-2009, 100002
11-2009, 200030
12-2009, 3000
01-2010, 0 /* <- very important to show empty dates, with no entries in the table! */
....
To accomplish option 2) I'm currently running a very costly for-loop with mktime/date like the following:
for(...){ /* example for group by day */
$span_from = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i, date("Y", $time_min));
$span_to = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i+1, date("Y", $time_min));
$query = "..";
$output = date("m-d-y", ..);
}
What are my ideas so far? Add additional/ redundant columns (INTEGER) for day (20091212), month (200912), week (200942) and year (2009). This way I can get rid of all the unnecessary queries in the for loop. However I'm still facing the problem to very fastly calculate all dates that doesn't have any equivalent in database. One way to simply move the problem could be to let MySQL do the job and simply use one big query (calculate all the dates/use MySQL date functions) with a left join (the data). Would it be wise to let MySQL take the extra load? Anyway I'm reluctant to use all these mktime/date in the for loop. Since I have complete control over the table layout and code even suggestions with major changes are welcome!
Update
Thanks to Greg I came up with the following SQL query. However it still bugs me to use 50 lines of sql statements - build up with php - that maybe could be done faster and more elegantly otherwise:
SELECT * FROM (
SELECT DATE_ADD('2009-01-30', INTERVAL 0 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 1 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 2 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 3 DAY) AS day UNION ALL
......
SELECT DATE_ADD('2009-01-30', INTERVAL 50 DAY) AS day ) AS dates
LEFT JOIN (
SELECT DATE_FORMAT(date, '%Y-%m-%d') AS date, SUM(data) AS data
FROM test
GROUP BY date
) AS results
ON DATE_FORMAT(dates.day, '%Y-%m-%d') = results.date;
You definitely shouldn't be doing a query inside a loop.
You can group like this:
SELECT COUNT(*) AS count, SUM(important_data) AS important_data, DATE_FORMAT('%Y-%m', date) AS month
FROM my_table
WHERE date BETWEEN ? AND ? -- This should be the min and max of the whole range
GROUP BY DATE_FORMAT('%Y-%m', date)
ORDER BY date DESC;
Then pull these into an array keyed by date and loop over your data range as you are doing (that loop should be pretty light on CPU).
Another idea is not to use string inside the query. Transform the string parameter to datetime, on mysql.
STR_TO_DATE(str,format)
http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html

Categories