I am working with T-SQL in SQL Server 2000 and I have a table TRANSACTIONS which has a date column TRANDATE defined as DateTime, among many other columns which are irrelevant for this question..
The table is populated with transactions spanning many years. I ran into code, test, that has me confused. There is a simple SELECT, like this:
SELECT TRANDATE, RECEIPTNUMBER FROM TRANSACTIONS WHERE TRANDATE BETWEEN '12/01/2010' and '12/31/2010' ORDER BY TRANDATE
and its not returning two rows of data that I know are in that table.
With the statement above, the last row its returning, in order, has a TRANDATE of:
2010-12-31 00:00:00.000
When I modify the statement like below, I get the additional two rows for December 2010 that are in that table:
SELECT TRANDATE, RECEIPTNUMBER FROM TRANSACTIONS WHERE TRANDATE BETWEEN '12/01/2010 00:00:00' and '12/31/2010 23:59:59' ORDER BY TRANDATE
I have tried to find out why the BETWEEN operator doesnt include ALL rows for the 24 period in 12/31/2010 when using the first SELECT, above. And why does it need to have the explicit hours added to the SELECT statement as in the second, modified, statement to get it to pull the correct number of rows out?
Is it because of the way TRANDATE is defined as "DATETIME"?
Based on this finding, I think that am going to have to go through all of this old code because these BETWEEN operators are littered throughout this old system and it seems like its not pulling all of the data properly. I just wanted clarification from some folks first. Thanks!
A date is a point in time, not a time span.
'12/31/2010' is a point, too. Namely, it's the midnight of the 31st of December.
Everything that happened after this point is ignored.
That's exactly the behaviour you want (even if you haven't realised that yet).
Do not think that when you choose to omit the time part, it is magically assumed to be "any". It's going to be "all zeroes", that is, the midnight.
If you want to include the entire day in your query without having to specify 23:59:59 (which, by the way, excludes the last second of the day, between the moment 23:59:59 of the current day and the moment 00:00:00 of the next day), you can do that either by using strict inequalities (>, <) bounded by the first points of time you don't want:
WHERE TRANDATE >='12/01/2010 00:00:00' and TRANDATE < '01/01/2011'
or by comparing date values casted to DATE:
WHERE CAST(TRANDATE AS DATE) between '12/01/2010' and '12/31/2010'
(it is okay to put this type of cast in a WHERE clause, it is sargable).
As you have discovered, if you don't specify a time when entering a date, it defaults to midnight in the morning of the date. So 12/31/2010 stops at midnight when that day begins.
To get all dates for 12/31/2010, you can either specify the time, as you have done, or add one day to the ending date. Without a time, 1/1/2011 ends at the stroke of midnight on 12/31/2010. So, you could do BETWEEN 12/1/2010 AND 1/1/2011. You can use DATEADD to add the day in your SQL if that makes it easier.
There is some risk in that second approach of adding a day. You will get any records for 1/1/2011 that carry the time of 00:00:00.
Here's one way to perform the DATEADD:
DECLARE #FromDate datetime, #ToDate datetime
// These might be stored procedure input parameters
SET #FromDate = '12/1/2010'
SET #ToDate = '12/31/2010'
SET #ToDate = DATEADD(d, 1, #ToDate)
Then you use #ToDate in your WHERE clause in the BETWEEN phrase in the usual way.
'12/01/2010' means '12/01/2010 00:00:00' and '12/31/2010' means '12/31/2010 00:00:00'. This is why datetime values that fall later on the day on 12/31/2010 are excluded from your query results.
What would be your expected result if I would do this
Insert "12/31/2010" into your datetime column?
Exactly: 12-31-2010 00:00:00
So why would you expect it to be different as argument for a query?
You have kind of answered your own question already. What you have observed is the way SQL Server works.
If it is confirmation you need, this MSDN document has following to say about it
When the time part is unspecified, it
defaults to 12:00 A.M. Note that a row
that contains a time part that is
after 12:00 A.M. on 1998-0105 would
not be returned by this query because
it falls outside the range.
Edit
As for your comment, a datetime essentially is a floating point value.
Following script shows what numbers SQL Server works with.
40541.9749 (12/31/2010 23:23:59) can't be included when your upper bound is 40541 (12/31/2010)
DECLARE #ADateTime1 DATETIME
DECLARE #ADateTime2 DATETIME
DECLARE #ADateTime1AsFloat FLOAT
DECLARE #ADateTime2AsFloat FLOAT
SET #ADateTime1 = '12/31/2010'
SET #ADateTime2 = '12/31/2010 23:23:59'
SET #ADateTime1AsFloat = CAST(#ADateTime1 AS FLOAT)
SET #ADateTime2AsFloat = CAST(#ADateTime2 AS FLOAT)
SELECT #ADateTime1AsFloat, #ADateTime2AsFloat
Related
I want to get the whole day of the week depend on the date, my query is working and getting the result that I want but when the date value is Sunday, result changes.
I'm starting the result from Mon to Sunday.
Examples below:
My Code:
SELECT UserID,Scdl_TkIN as TimeIn, Scdl_TkOut as [TimeOut]
FROM EmployeeTimekeeping
WHERE CONVERT(DATE,Scdl_TkIN) >= dateadd(day, 2-datepart(dw, '2022-04-23'),CONVERT(date,'2022-04-23'))
AND CONVERT(DATE,Scdl_TkIN) < dateadd(day, 9-datepart(dw, '2022-04-23'), CONVERT(date,'2022-04-23'))AND UserID ='15020009'
ORDER BY CONVERT(DATE,Scdl_TkIN)
1st display is correct, but when I change the value into '2022-04-24' , the result is now the second pic but I want the result still 1st pic.
If I got it right you want the whole week of data given a single date.
I'm not 100% sure about your date logic and I'd rather use the WEEK as a filter as it seems clearer, that said the issue you have is the value of SELECT ##DATEFIRST.
By default its value is 7, meaning that Sunday is considered the first day of the week, that's why you get that "unexpected" result.
here is my solution, but just setting SET DATEFIRST 1; should give you the expected result.
SET DATEFIRST 1;
SELECT
UserID
,Scdl_TkIN as TimeIn
,Scdl_TkOut as TimeOut
FROM EmployeeTimekeeping
WHERE
DATEPART(WEEK,Scdl_TkIN) = DATEPART(WEEK,'2022-04-23')
AND YEAR(Scdl_TkIN) = YEAR('2022-04-23')
AND UserID ='15020009'
ORDER BY
Scdl_TkIN
Note: if you decide to use WEEK for filtering you will have to choose between WEEK and ISO_WEEK
Edit: when using week you must also consider the year in the filter
Morning experts. I have a very simple query that I can not seem to get working as it should.
I have a table that has invoiceNumber, CustNum, Datetime, and Grand_total
I want to pull all transactions between a set of dates in this example all transactions between 4/10/2014 and 4/23/2014. I was using:
SELECT Invoice_number
,CustNum
,DATETIME
,Grand_Total
FROM invoice_totals
WHERE custnum = '10014877'
AND DATETIME BETWEEN '2014-04-10'
AND '2014-04-23'
ORDER BY DATETIME DESC
I just realized if there are any dates on 4/23 that this does not show them.. I have tried to substuite using:
WHERE custnum = '10014877'
AND DATETIME >= '2014-04-10' AND DATETIME <='2014-04-23'
But it is still giving me the same results (ignoring any transactions that occured on 4/23)
the last record pulling up has a datetime stamp of 2014-04-22 12:26:08.000. There ARE 2 transactions on the 23'ed I am trying to include.
Thank you very much.
The part:
AND DATETIME <='2014-04-23'
is actually(according to TSQL):
AND DATETIME <='2014-04-23 00:00:000'
So you're quering from midnight and missing all the transactions from 00:01 to 23:59 on the 23rd.
Try:
AND DATETIME <='2014-04-23 23:59:999'
Or
AND DATETIME < '2014-04-24'
Both should include all the transactions for the day of the 23rd.
If you're working with continuous data, such as datetimes, it's usually better to switch to using a semi-open interval - an inclusive start date and an exclusive end date. Exclusive end dates are usually easier to calculate:
WHERE custnum = '10014877'
AND DATETIME >= '20140410' AND DATETIME <'20140424'
I've also switched to a safe, unambiguous date format.
The alternative, using <= or BETWEEN (which is just shorthand for a pair of >= and <= comparisons, so your two queries were identical) requires an inclusive end date. Which depending on the exact data type you're using may be 2014-04-23T23:59:59.997 or 2014-04-23T23:59:59.9999 or any number of other possibilities - if you get it wrong and overspecify the value, it'll get rounded to be 20140424 and then an inclusive comparison is incorrect.
And even if you get it right today, it's a pain to find all usage of this pattern if the data type of the column changes later.
BETWEEN is inclusive with respect to the boundaries.
However, I'm guessing that the type of your (poorly named) DATETIME field is .. DATETIME, so in the comparison, the date '2014-04-23' gets converted to a DATETIME as follows: '2014-04-23:00:000'. Thus, all records with DATETIME greater than that first moment on 04-23 are rejected.
Date calculations are not my strong point and I need a little help.
I'm trying to check if a date (which is a timestamp) from a selected field is before 12pm of the current day. Thanks in advance.
Scenario: if an order is placed before 12pm that day, it will qualify for x otherwise it gets y. So my create date (including time) of that order is what I get in my select statement.
The DATE type doesn't carry time information, so it is up to you to define at what point in time the date is. You should use TIMESTAMP type if the time information is also important.
Anyway, lets say that the field stores the date at 12pm time, then you use
WHERE date_field <= CURRENT_DATE;
CURRENT_DATE is so called context variable which returns, obviously, current date. CURRENT_TIMESTAMP and CURRENT_TIME are also available. You can use DATEADD and DATEDIFF builtin functions to do some date calculations.
So if the field is actually timestamp, you could do it like
WHERE date_field < DateAdd(12 HOUR to cast(CURRENT_DATE as timestamp));
Is there a way to update only Hour part in a DateTime field?? If not, how do I update the time part in Oracle? I tried this->
update tab_name
set C_Name=to_date('04/03/2012 00:31:00','MM/DD/YYYY HH:MI:SS AM')
where C_Name1=10484;
didn't work as I'm updating '00' in Hour part.
If I knew I wanted to update just one part of the time I'd probably convert to a string with the value I want in the appropriate place, then convert back to a date. Say I wanted the minutes to be "31":
update tab_name
set C_Name=
to_date(
to_char(C_Name, 'MM/DD/YYYY HH24:"31":SS'),
'MM/DD/YYYY HH24:MI:SS'
)
where C_Name1=10484;
If you want to modify the time portion relative to its current value (to add 2 hours or subtract 3 seconds, for example) then there are some choices for date arithemtic. The Oracle documentation is very good for these things.
I am converting an Unix script with a SQL transact command to a PostgreSQL command.
I have a table with records that have a field last_update_time(xtime) and I want to select every record in the table that has been updated within a selected period.
Say, the current time it 05/01/2012 10:00:00 and the selected time is 04/01/2012 23:55:00. How do I select all the records from a table that have been updated between these dates. I have converted the 2 times to seconds in the Unix script prior to issuing the psql command, and have calculated the interval in seconds between the 2 periods.
I thought something like
SELECT A,B,C FROM table
WHERE xtime BETWEEN now() - interval '$selectedtimeParm(in secs)' AND now();
I am having trouble evaluating the Parm for the selectedtimeParm - it doesn't resolve properly.
Editor's note: I did not change the inaccurate use of the terms period, time frame, time and date for the datetime type timestamp because I discuss that in my answer.
What's wrong with:
SELECT a,b,c
FROM table
WHERE xtime BETWEEN '2012-04-01 23:55:00'::timestamp
AND now()::timestamp;
If you want to operate with a count of seconds as interval:
...
WHERE xtime BETWEEN now()::timestamp - (interval '1s') * $selectedtimeParm
AND now()::timestamp;
Note the standard ISO 8601 date format YYYY-MM-DD h24:mi:ss which is unambiguous with any locale or DateStyle setting.
The first value for the BETWEEN construct must be the smaller one. If you don't know which value is smaller use BETWEEN SYMMETRIC instead.
In your question you refer to the datetime type timestamp as "date", "time" and "period". In the title you used the term "time frames", which I changed to "timestamps". All of these terms are wrong. Freely interchanging them makes the question even harder to understand.
That, and the fact that you only tagged the question psql (the problem hardly concerns the command line terminal) might help to explain why nobody answered for days. Normally, it's a matter of minutes around here. I had a hard time understanding your question.
Understand the data types date, interval, time and timestamp - with or without time zone. Start by reading the chapter "Date/Time Types" in the manual.
Error message would have gone a long way, too.
For anyone who is looking for the fix to this. You need to remove timestamp from the where clause and use BETWEEN!
TABLENAME.COL-NAME-FOR-TIMESTAMP BETWEEN '2020-01-29 04:18:00-06' AND CURRENT_TIMESTAMP