How to get yesterday date starts at 00:00:00 in Vertica SQL - vertica

How to get yesterday date starts at 00:00:00 in Vertica SQL?
Normally in SQL Server it will be like this
SELECT DATEADD(Day, -1, DATEDIFF(Day, 0, GetDate()))
2021-01-31 00:00:00.000
How about in Vertica?

I would clearly go closer to the ANSI standard. There is an ANSI reserved word CURRENT_DATE, which is an expression that returns today's date (without the time). Many DBMSs support it (I actually think SQL Server, too); and I would stick to that - or to CURRENT_TIMESTAMP if I need the timestamp. "Never use DBMS specifics if you can avoid them".
In Vertica, you can subtract integers from or add integers to dates. You can't use SQL-Server's peculiar DATEADD(), but you can use the more standard (in other DBMSs) TIMESTAMPADD() which works like DATEADD() and returns a timestamp.
For completeness's sake: If you have a result timestamp with something else than '00:00:00' as the time element, use TRUNC(<timestamp>) to truncate the value and get a timestamp with the time at zero.
SELECT
CURRENT_DATE AS ANSI_CURRENT_DATE
, CURRENT_DATE-1 AS yesterday_midnight_minus
, TIMESTAMPADD(DAY,-1,CURRENT_DATE) AS tsadd
;
-- out ANSI_CURRENT_DATE | yesterday_midnight_minus | tsadd
-- out -------------------+--------------------------+---------------------
-- out 2021-02-01 | 2021-01-31 | 2021-01-31 00:00:00

Related

Date range comparison off-by-one

There is a table in Oracle 19c with a DATE abc column. A row with value '2000-01-01' does not get picked up with query
select abc from t where abc <= DATE '2000-01-01'
The row does surface if I modify the query as
select abc from t where abc < DATE '2000-01-01' + interval '1' day
The displayed value (in DBeaver, VSCode + Oracle Dev Tools, Oracle SQL Developer) is always '2000-01-01'.
Issue
Oracle stores also time portion of inserted/copied data and while it doesn't always show, it affects queries regardless of output data.
You can notice it with query:
select to_char(abc, 'YYYY-MM-DD hh24:MI:ss') from t
where trunc(abc) <= date '2000-01-01'
Mitigation
use the comparable date (you're searching for) as variable and add + interval '1' day to it. Notice to adjust your comparison limits, use < instead of <= to not accidentally find items from next day with 00:00:00 time.
don't use trunc(abc) or to_char(abc ..), it will likely wreck the index performance`
add ALTER SESSION SET nls_date_format='YYYY-MM-DD HH24:MI:SS'; to your sql editor startup script.
In Oracle, a DATE is a binary data type that consists of 7 bytes representing: century, year-of-century, month, day, hour, minute and second. It ALWAYS has those 7 bytes and it is NEVER stored in any particular (human-readable) format.
The displayed value (in DBeaver, VSCode + Oracle Dev Tools, Oracle SQL Developer) is always '2000-01-01'.
What you are seeing is the client application receiving the binary DATE value and trying to be helpful and displaying the binary value as a string; however, the client application's default format is YYYY-MM-DD and it is not showing the time component of the date. This does not mean that the time component does not exist, it only means that it is not displayed.
What you need to do is to go into the preferences and change how your client application formats dates.
In SQL Developer, you can use:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
Which will set the format for your current session. Or you can go into Tool > Settings > Database > NLS and in set the "Date Format" field to YYYY-MM-DD HH24:MI:SS which will set the preference for the current session and any future session that you create from that client.
You can do something similar and change the settings in most (all) client applications.
select abc from t where abc <= DATE '2000-01-01'
Is the equivalent of:
select abc from t where abc <= TIMESTAMP '2000-01-01 00:00:00'
It will not match rows where abc is between 2000-01-01 00:00:01 and 2000-01-01 23:59:59 but the client application is not displaying the time component so you cannot see that the rows have a non-midnight time component and should not be matched.
If you do:
select abc from t where abc < DATE '2000-01-01' + interval '1' day
Then it will match those rows because it will match the entire 24-hour period for the day.

Alter a timestamp column in an oracle table with default value as '0000-00-00 00:00:00'

Can we assign a default value '0000-00-00 00:00:00' in oracle as we do in mysql as shown below ?
Sample query in mysql:
ALTER TABLE . MODIFY COLUMN TIMESTAMP DEFAULT '0000-00-00 00:00:00';
No - in Oracle neither year, month, or day can be set to zero. I suggest using NULL instead.
dbfiddle here
EDIT
Of course, having now gone back through one of my old questions and crawled down a wormhole or two I see that you can get a year of zero to be accepted - but it still appears that month and day cannot be zero. To get the year of zero in you have to use an ANSI date literal - e.g. DATE '0000-01-01' is considered acceptable. I don't know if the various date routines and date calculations will like this - for example, TO_CHAR(DATE '0000-01-01', 'DD-MON-YYYY') produces a result of '00-000-0000', which is certainly not what I'd expect, but perhaps it's good enough for your purposes. Note that you can't go the other way with this - TO_DATE('00-000-0000', 'DD-MON-YYYY') produces the expected ORA-01847 - day of month must be between 1 and last day of month error.
Does anybody really know what time it is..? :-)

Date operations in Oracle

I'm trying to run this queries (Oracle 12c):
SELECT trunc(sysdate) - '25-SEP-18' FROM dual;
SELECT 1 FROM dual WHERE trunc(sysdate) = '04-SEP-19';
CREATE TABLE my_table (order_date date);
INSERT INTO my_table (order_date) VALUES ('04-SEP-19');
I expect implicit conversion and everything is good with the 2 last queries, but for the first i get error ORA-01722: invalid number. NLS_DATE_FORMAT = 'DD-MON-RR'. What is the problem?
The question is WHY is does not work? I didn't find any explanations in documentation.
The documentation has a section on Datetime/Interval Arithmetic which explains what is allowed. The table shows that arithmetic is only allowed between dates, timestamp, intervals and numbers. When you do:
SELECT trunc(sysdate) - '25-SEP-18'
you are trying to subtract a string from a date, which isn't possible. Oracle 'helpfully' tries anyway and interprets the string as a number, effectively doing:
SELECT trunc(sysdate) - to_number('25-SEP-18')
which understandably throws the error you see, "ORA-01722: invalid number". As already said, you should explicitly convert your string to a date:
SELECT trunc(sysdate) - to_number('25-SEP-18', 'DD-MON-RR')
or preferably with a four-digit year, and since you're using a month name it's safer to specify the language that is in:
SELECT trunc(sysdate) - to_number('25-SEP-2018', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=ENGLISH')
or more simply, if it's a fixed value, with a date literal:
SELECT trunc(sysdate) - DATE '2018-09-25'
I expect implicit conversion
You should not rely on implicit conversion, particularly where that is influenced by session NLS settins. As well as the date language I already mentioned, someone else running your statement could have a different NLS_DATE_FORMAT setting which could lead to errors or more subtle data mismatches or corruption; e.g.
alter session set nls_date_format = 'DD-MON-YYYY';
SELECT trunc(sysdate) - DATE '2018-09-25' FROM dual;
TRUNC(SYSDATE)-DATE'2018-09-25'
-------------------------------
344
SELECT trunc(sysdate) - to_date('25-SEP-18') FROM dual;
TRUNC(SYSDATE)-TO_DATE('25-SEP-18')
-----------------------------------
730831
SELECT 1 FROM dual WHERE trunc(sysdate) = '04-SEP-19';
no rows selected
CREATE TABLE my_table (order_date date);
INSERT INTO my_table (order_date) VALUES ('04-SEP-19');
The second query gets a much bigger value than expected; and the third gets no rows back from dual.
Looking at the implicitly converted date shows you why:
SELECT to_char(order_date, 'SYYYY-MM-DD HH24:MI:SS') FROM my_table;
TO_CHAR(ORDER_DATE,'
--------------------
0019-09-04 00:00:00
With a YYYY mask (and no FX modifier) a 2-digit year value like 19 is converted as 0019, not 2019. That sort of problem could go unnoticed for some time, giving you incorrect results in the meantime.
If the session's format mask had RRRR or - as you have - RR then it would be interpreted as 2019; but the point is that you usually have no control over the settings in another session that runs your code later.
You can also cause performance issues or errors by creating implicit conversions where you didn't expect, or where they behave in a way you didn't expect. Not in this example - "When comparing a character value with a DATE value, Oracle converts the character data to DATE" - but it still comes up. It's better to avoid the possibility.
When dealing with strings with dates in them you should use the to TO_DATE command, otherwise Oracle may not always figure out that the string contains a date.
SELECT trunc(sysdate) - TO_DATE('25-SEP-18') FROM dual;
Even better is to indicate the format of the date within the string
SELECT trunc(sysdate) - TO_DATE('25-SEP-18','DD-MON-RR') FROM dual;

Convert Varchar to ANY time format

I have a varchar column called begin_time that stores the 24-hour time as a varchar with no time formatting, ie 1330
I need to convert this varchar to a usable timestamp, datetime, etc. where I can easily convert the varchar to a standard time format (1:30 PM)
The end format type doesn't matter as long as I can convert the varchar into a format that I can manipulate to a standard format.
I've tried looking into Cognos-specific format tricks (These functions are for Metric Designer, and I'm using Report Studio) to no avail. The methods I found when looking for oracle-specific tricks seemed to be way too convoluted (using insanely long regex rules) for what I need.
If I need to have a date involved, I can use the column start_date and append the varchar time.
Note: start_date is in the date format
Example
select
to_date('08/27/2018','MM/DD/YYYY') as start_date
, '1300' as begin_time
from dual
What I ultimately need is just to be able to output the time as 1:00 PM
Any help would be appreciated. I'm beating my head against the wall on this... I'm used to using proprietary codes for periods of time and don't have a lot of experience with the true datetime formats.
Updates answering questions
Alex Poole, I make no claims that this system is the best... It's vendor-provided. :)
The BEGIN_TIME is always 4 characters
It does look like I was overthinking it quite a bit... Littlefoot may have nailed it on the head, but I unfortunately won't have a chance to test that until tomorrow.
Thank you all for the fast responses. I might have hair left when this request is over now :)
Final Thought
My lesson learned from this is simple: If you're dealing with time formats, don't throw out the idea of using a Date format function.
Looking for this?
SQL> with test (col) as
2 (select '1330' from dual)
3 select to_char(to_date(col, 'hh24mi'), 'hh:mi am') result
4 from test;
RESULT
--------
01:30 PM
SQL>
What does it do?
TO_DATE converts string you have (such as 1330) into a valid DATE value. By default, it'll be a date value truncated to the first of current month:
SQL> alter session set nls_Date_format = 'dd.mm.yyyy hh24:Mi';
Session altered.
SQL> select to_date('1330', 'hh24mi') res1 from dual;
RES1
----------------
01.04.2019 13:30
SQL>
applying TO_CHAR to it, again with the appropriate format mask, returns the desired result

SQL Query with date that does not exist

I was recently working on some SQL queries where I had to separate the values into various months (e.g. December, January, Feb...) and make some comparisons. However I was at a loss for wondering about what to use for the ending day of each month. So I was wondering what happens when you define a date that does not technically exist. For example.
WHERE myDate BETWEEN '2016-01-01' AND '2016-02-31' //note Feb 31 does not exist.
My assumption (based on my current query seeming to return the proper results) is that it simply ignores the extra dates that do not exist (e.g. when counting the dates, it simply has no dates for the range outside of the regular dates).
Is this undefined behavior that I may run into trouble with in the future? Or is there a better way to do this to cover all basis?
Why don't you want to use LAST_DAY() function:
SELECT SYSDATE, trunc(LAST_DAY(SYSDATE)) last,
LAST_DAY(SYSDATE) - SYSDATE days_left FROM DUAL;
Output:
SYSDATE LAST DAYS_LEFT
----------------- ----------------- ----------
03.02.16 18:38:26 29.02.16 00:00:00 26
1 row selected.

Resources