select
start_date,stop_date_original
from dates
where
start_date is not null
and stop_date_original is not null
and start_date > str_to_date('10/10/2009','%d/%m/%Y')
/*and stop_date_original < str_to_date('01/24/2013','%d/%m/%Y')*/
this query works fine but when i uncomment the last line or use it to replace the one before the result doesnt get affected or i get an empty result set.
are there issues with this approach that might cause this behaviour?
also, are the null checks intrinsically necesarry across different database systems?
Stop date has to be 24/01/2013, not 01/24/2013:
select
start_date,
stop_date_original
from
dates
where
start_date is not null
and stop_date_original is not null
and start_date > str_to_date('10/10/2009','%d/%m/%Y')
and stop_date_original < str_to_date('24/01/2013','%d/%m/%Y')
or you have to invert day and month on your function str_to_date('01/24/2013','%m/%d/%Y').
Also, if start_date is null, or if stop_date_original is null, the condition would be evaluated as null anyway so you don't need to check if they are not null, although this make things more readable.
Related
I have a table which record things happen time as below
each thing will go though all this 5 steps, start->first try->second try->third try->end., and I need to test if the time is reasonable.
Rules as below:
if one thing have gone to a step then every record before should not be empty.(intergrity)
date should be bigger step by step
Now, I can choose to wirte the rules in SQL or dax, but the methods I've come up with are complicated, just like:
case when
end_time is not null and (first_time is null or second_time is null or third_time is null or start_time is null )
then 'things end, some before date missing'
when third_time is not null and (first_time is null or second_time is null or start_time is null )
then 'third try done, some before date missing'
...
end check_integrity
second rule seems more complex. Is there a easy way to check if (date1, date2,...,date-n) follow the sequences?
Possible table structure (MySQL 8+ needed):
CREATE TABLE things (
things INT PRIMARY KEY,
start_time DATE,
first_try_time DATE,
second_try_time DATE,
third_try_time DATE,
end_time DATE,
CHECK (first_try_time IS NULL OR (first_try_time > start_time AND start_time IS NOT NULL)),
CHECK (second_try_time IS NULL OR (second_try_time > first_try_time AND first_try_time IS NOT NULL)),
CHECK (third_try_time IS NULL OR (third_try_time > second_try_time AND second_try_time IS NOT NULL)),
CHECK (end_time IS NULL OR (end_time > third_try_time AND third_try_time IS NOT NULL))
);
DEMO fiddle
I have a date table, which has a column date (PK). The CREATE script is here:
CREATE TABLE date_table (
date DATE
,year INT(4)
,month INT(2)
,day INT(2)
,month_pad VARCHAR(2)
,day_pad VARCHAR(2)
,month_name VARCHAR(10)
,year_month_index INT(6)
,year_month_hypname VARCHAR(7)
,year_month_name VARCHAR(15)
,week_day_index INT(1)
,day_name VARCHAR(9)
,week INT(2)
,week_interval VARCHAR(13)
,weekend_fl INT(1)
,quarter_num INT(1)
,quarter_num_pad VARCHAR(2)
,quarter_name VARCHAR(2)
,year_quarter_index INT(6)
,year_quarter_name VARCHAR(7)
,PRIMARY KEY (date)
);
Now I would like select rows from this table with dynamic values, using such as LAST_DAY() or DATE_SUB(DATE_FORMAT(SYSDATE(),'%Y-01-01'), INTERVAL X YEAR), etc.
When one of my queries failed and didn't execute in 30 secs, I knew something was fishy, and it looks like the reason is that the index on the primary key column is not used. Here are my results (sorry for using an image instead of copying the queries, but I thought it's concise enough for this purpose, and the queries are short/simple enough):
First of all, it's strange that the BETWEEN works differently than using >= and <=. Secondly, it looks like the index is only used for constant values. If you look closely, you can see that on the right side (where >= and <= is used), it shows ~9K rows, which is half of the rows in the table (the table has about ~18k rows, dates from 2000-01-01 to `2050-12-31).
SYSDATE() returns the time at which it executes. This differs from the behavior for NOW(), which returns a constant time that indicates the time at which the statement began to execute. (Within a stored function or trigger, NOW() returns the time at which the function or triggering statement began to execute.)
-- https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_sysdate
That is, the Optimizer does not see this as a "constant". Otherwise, the Optimizer eagerly evaluates any "constant expressions", then tries to take advantage of knowing the value.
See also the sysdate_is_now option.
Bottom line: Don't use SYSDATE() for normal datetime usage; use NOW() or CURDATE().
Looks like if I use CURRENT_DATE() (or NOW()) instead of SYSDATE(), it's working. Both of these queries:
SELECT *
FROM date_table t
WHERE 1 = 1
AND t.ddate >= LAST_DAY(CURRENT_DATE()) AND t.ddate <= LAST_DAY(CURRENT_DATE());
SELECT *
FROM date_table t
WHERE 1 = 1
AND t.ddate >= LAST_DAY(NOW()) AND t.ddate <= LAST_DAY(NOW());
Give the same result, which is this:
I will accept my answer as a solution, but I'm still looking for an explanation. I thought it might has to do something with SYSDATE() not being a DATE, but NOW() is also not a DATE...
EDIT: Forgot to add, BETWEEN is also working as I see.
I have found many way to search a mysql record by DATE
Method 1:
SELECT id FROM table WHERE datetime LIKE '2015-01-01%' LIMIT 1
Method 2 (same as method 1 + ORDER BY):
SELECT id FROM table WHERE datetime LIKE '2015-01-01%' ORDER BY datetime DESC LIMIT 1
Method 3:
SELECT id FROM table WHERE datetime BETWEEN '2015-01-01' AND '2015-01-01 23:59:59' LIMIT 1
Method 4:
SELECT id FROM table WHERE DATE_FORMAT( datetime, '%y.%m.%d' ) = DATE_FORMAT( '2015-01-01', '%y.%m.%d' )
Method 5 (I think is the slowest):
SELECT id FROM table WHERE DATE(`datetime`) = '2015-01-01' LIMIT 1
What is the fastest?
In my case the table has 1 million rows, and the date to search is always recent.
The fastest of the methods you've mentioned is
SELECT id
FROM table
WHERE datetime BETWEEN '2015-01-01' AND '2015-01-01 23:59:59'
LIMIT 1
This is made fast when you create an index on the datetime column. The index can be random-accessed to find the first matching row, and then scanned until the last matching row. So it's not necessary to read the whole table, or even the whole index. And, when you use LIMIT 1, it just reads the single row. Very fast, even on an enormous table.
Your other means of search apply a function to each row:
datetime LIKE '2011-01-01%' casts datetime as a string for each row.
Methods 3,4, and 5 all use explicit functions like DATE() on the contents of each row.
The use of these functions defeats the use of indexes to find your data.
Pro tip: Don't use BETWEEN for date arithmetic because it handles the ending condition poorly. Instead use
WHERE datetime >= '2015-01-01'
AND datetime < '2015-01-02'
This performs just as well as BETWEEN and gets you out of having to write the last moment of 2015-01-01 explicitly as 23:59:59. That isn't correct with higher precision timestamps anyway.
The fastest way, assuming there's in index on the datetime column, is a variant of method 3 except both range values are datetime literals:
SELECT id FROM table
WHERE datetime BETWEEN '2015-01-01 00:00:00' AND '2015-01-01 23:59:59'
LIMIT 1
Using literal of the same type as the column means there won't be any casting of the column to perform comparison, giving the best chance of using an index on the column. I have used this in production to great effect.
Clearly, I am missing the forest for the trees...I am missing something obvious here!
Scenario:
I've a typical table asset_locator with multiple fields:
id, int(11) PRIMARY
logref, int(11)
unitno, int(11)
tunits, int(11)
operator, varchar(24)
lineid, varchar(24)
uniqueid, varchar(64)
timestamp, timestamp
My current challenge is to SELECT records from this table based on a date range. More specifically, a date range using the MAX(timestamp) field.
So...when selecting I need to start with the latest timestamp value and go back 3 days.
EX: I select all records WHERE the lineid = 'xyz' and going back 3 days from the latest timestamp. Below is an actual example (of the dozens) I've been trying to run.
MySQL returns a single row with all NULL values for the following:
SELECT id, logref, unitno, tunits, operator, lineid,
uniqueid, timestamp, MAX( timestamp ) AS maxdate
FROM asset_locator
WHERE 'maxdate' < DATE_ADD('maxdate',INTERVAL -3 DAY)
ORDER BY uniqueid DESC
There MUST be something obvious I am missing. If anyone has any ideas, please share.
Many thanks!
MAX() is an aggregated function, which means your SELECT will always return one row containing the maximum value. Unless you use GROUP BY, but it looks that's not what you need.
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_max
If you need all the entries between MAX(timestamp) and 3 days before, then you need to do a subselect to obtain the max date, and after that use it in the search condition. Like this:
SELECT id, logref, unitno, tunits, operator, lineid, uniqueid, timestamp
FROM asset_locator
WHERE timestamp >= DATE_ADD( (SELECT MAX(timestamp) FROM asset_locator), INTERVAL -3 DAY)
It will still run efficiently as long as you have an index defined on timestamp column.
Note: In your example
WHERE 'maxdate' < DATE_ADD('maxdate',INTERVAL -3 DAY)
Here you were are actually using the string "maxdate" because of the quotes causing the condition to return false. That's why you were seeing NULL for all fields.
Edit: Oops, forgot the "FROM asset_locator" in query. It got lost at some point when writing the answer :)
table:
--duedate timestamp
--submissiondate timestamp
--blocksreq numeric
--file clob
--email varchar2(60)
Each entry is a file which will take blocksreq to accomplish. There are 8 blocks allotted per day (but could be modified later). before i insert into the table, i want to make sure there are enough blocks to accomplish it in the timeframe of NOW() and #duedate
I was thinking of the following, but i think i am doing it wrong:
R1 = select DAY(), #blocksperday - sum(blocksreq) as free
from table
where #duedate between NOW() and #duedate
group by DAY()
order by DAY() desc
R2 = select sum(a.free) from R1 as a;
if(R2[0] <= #blocksreq){ insert into table; }
pardon the partial pseudocode.
SQL FIDDLE: http://sqlfiddle.com/#!2/5bda5
warning: My sql fiddle has garbage code... as i dont know how to make a lot of test cases. nor set the duedate to NOW()+5 days
Something like this? (wasn't sure how partial days were handled so ignored that part)
CREATE TABLE `DatTable` (
`duedate` datetime DEFAULT NULL,
`submissiondate` datetime DEFAULT NULL,
`blocksreq` smallint(6) DEFAULT NULL
)
SET #duedate:='2012-10-15';
SET #submissiondate:=CURRENT_TIMESTAMP;
SET #blocksreq:=5;
INSERT INTO DatTable(duedate,submissiondate,blocksreq)
SELECT #duedate,#submissiondate,#blocksreq
FROM DatTable AS b
WHERE duedate > #submissiondate
HAVING COALESCE(SUM(blocksreq),0) <= DATEDIFF(#duedate,#submissiondate)*8-#blocksreq;