How do I combine YEAR and MONTH into a DATE? - sql

I'm looking for an easier way to do this. My current code is written as below:
CASE WHEN LENGTH(CAST (MTH AS VARCHAR(4))) = 1
THEN CAST(CAST (YR AS VARCHAR(4))||'-0'||CAST (MTH AS VARCHAR(4))||'-01' AS DATE)
ELSE CAST(CAST (YR AS VARCHAR(4))||'-' ||CAST (MTH AS VARCHAR(4))||'-01' AS DATE)
END AS RPT_MTH
The table has a field called YR with the 4 digit Year. MTH field is just a number 1 through 12, with no leading 0 for 1-9.
Is there an easier way to get to the RPT_MTH than what I'm currently doing?

Based on Teradata's internal date:
cast((yr-1900) * 10000 + mth * 100 + 1 as date)

This is a little shorter:
cast(cast(YR*10000+MTH*100+1 as char(8)) as date format 'YYYYMMDD') AS RPT_MTH
Or if you need to stick to character operations:
cast(cast(YR as char(4))||right('0'||cast(MTH as varchar(2)),2) as date format 'YYYYMM')

Consider CONCAT, LPAD, TO_CHAR and TO_DATE which may vary in support depending on your version. Default format for TO_DATE is ISO date format at: 'YYYY-MM-DD'.
TO_DATE(CONCAT(YR, '-', LPAD(TO_CHAR(MTH), 2, '0'), '-01'))

Related

convert int YYYYMMDD to date AS400

I need to convert an integer to a date format in AS400.
I have the field called ivdat8, which is integer in the formatted YYYYMMDD.
I need to use a WHERE clause to select data between two dates, today and 3 days ago.
I am using the below line to do this:
Where
ivdat8 Between (Select current date - 3 days from sysibm.sysdummy1) And (Select current date from sysibm.sysdummy1)
The current date from sysibm is a true date format, but ivdat is integer.
How can i cast ivdat8 to be a date format i can use within the WHERE clause ?
I have tried the below to convert the int to date:
cast(cast(A.ivdat8 as varchar(50)) as date)
&
TIMESTAMP_FORMAT(ivdat8,'YYYYMMDD')
Actually it's better for performance not to convert the ivdat8 column data, but do this with parameters like below.
select ivdat8
from table (values
int(to_char(current date - 2 days, 'YYYYMMDD'))
, int(to_char(current date - 5 days, 'YYYYMMDD'))
) t (ivdat8)
where ivdat8 between
int(to_char(current date - 3 days, 'YYYYMMDD'))
and int(to_char(current date, 'YYYYMMDD'));
Easiest way to do it without causing a complicated conversion in the query is to use this:
cast(digits(cast(A.ivdat8 as dec(8))) || '000000' as date)
The full where clause doesn't need to select from sysibm.dummy1 either.
where cast(digits(cast(A.ivdat8 as dec(8))) || '000000' as date) between current date - 3 days and current date
If you have indexes built on ivdat8 though, the fastest selection will be:
where A.ivdat8 between cast(Current date - 3 days as dec(8)) and cast(Current Date as dec(8))
Managed to convert the int to a date, first i had to cast it as a string, then use TO_DATE
To_Date(cast(A.ivdat8 as varchar(50)),"YYYYMMDD")
try this
Where cast(TIMESTAMP_FORMAT(cast(ivdat8 as varchar(8)), 'YYYYMMDD') as date)
Between (current date - 3 days) and current date

How to convert a date format YYYY-MM-DD into integer YYYYMMDD in Presto/Hive?

How to CONVERT a date in format YYYY-MM-DD into integer YYYYMMDD in Presto/Hive?
I am trying to convert the below list into YYYYMMDD integers
WITH all_dates as (SELECT
CAST(date_column AS DATE) date_column
FROM
(VALUES
(SEQUENCE(FROM_ISO8601_DATE('2017-07-01'),
FROM_ISO8601_DATE('2017-11-15'),
INTERVAL '1' DAY)
)
) AS t1(date_array)
CROSS JOIN
UNNEST(date_array) AS t2(date_column)
)
I tried something like this but it doesn't work
SELECT
CAST(
CAST(year(date_column) AS VARCHAR(4)) +
right('0' + CAST(month(date_column) AS VARCHAR(2)), 2) +
right('0' + CAST(day(date_column) AS VARCHAR(2)), 2)
AS DATETIME)
FROM all_dates
Also you can use date_format function:
hive> select cast(date_format('2017-07-01','yyyyMMdd') as int);
OK
20170701
If you just need to transform your date YYYY-MM-DD into an integer YYYYMMDD why don't you try to first remove all the occurrences of "-" from the string representation of your date before casting the result to int by using something like this?
cast(regexp_replace(str_column,'-','') as int)
Simply REPLACE the '-' with Empty string and CAST it into INT.
Try the following:
SELECT CAST(REPLACE(Date_Column,'-','') AS INT)
Are you sure you want to use YYYYMMDD ?
Hive follows Java convention for Date Formatting and as per https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
you can see that YYYY represents week year which means you may get into trouble for end of the year dates. 2019 may appear as 2020.
Use yyyyMMdd instead.
#leftjoin has probably the correct respone.
beeline> select cast(date_format('2019-07-01','yyyyMMdd') as int);
OK
20190701

Find date in a specific week

I would like to select an item where the date is in a specific week in the year.
For example :
SELECT item
FROM table
WHERE my_date in week 44
Where week 44 is from monday 31/10 to sunday 06/11.
I know to get the week number with
to_number(to_char(to_date(my_date,'MM/DD/YYYY'),'IW'))
How can I do that ?
Thanks.
Try:
SELECT item
FROM table
WHERE to_char( my_date, 'IW ) = '44'
See this link for details:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34924
IW ---> Week of year (1-52 or 1-53) based on the ISO standard.
Working with ISO Weeks is not trivial because week 1 can start in previous year and first days in January may be counted as week 52 or 53.
So, providing just week number without a year can be ambiguous (for week number 52, 53, 1).
The best function I found in order to get the first day of an ISO-Week is
NEXT_DAY(TO_DATE( yearNo || '0104', 'YYYYMMDD' ) - INTERVAL '7' DAY, 'MONDAY') + ( weekNo - 1 ) * 7
So, for your need it would be
SELECT item
FROM table
WHERE my_date
between NEXT_DAY(TO_DATE( yearNo || '0104', 'YYYYMMDD' ) - INTERVAL '7' DAY, 'MONDAY') + ( weekNo - 1 ) * 7
AND 6 + (NEXT_DAY(TO_DATE( yearNo || '0104', 'YYYYMMDD' ) - INTERVAL '7' DAY, 'MONDAY') + ( weekNo - 1 ) * 7)
Actually the "full error proven" way would be this one:
FUNCTION ISOWeekDate(weekNo INTEGER, yearNo INTEGER) RETURN DATE DETERMINISTIC IS
res DATE;
BEGIN
IF weekNo > 53 OR weekNo < 1 THEN
RAISE VALUE_ERROR;
END IF;
res := NEXT_DAY(TO_DATE( yearNo || '0104', 'YYYYMMDD' ) - INTERVAL '7' DAY, 'MONDAY') + ( weekNo - 1 ) * 7;
IF TO_CHAR(res, 'fmIYYY') = yearNo THEN
RETURN res;
ELSE
RAISE VALUE_ERROR;
END IF;
END ISOWeekDate;
If the data type of the my_date column is, in fact, VARCHAR2 (which it shouldn't be, but it often is), then you do need to convert it to date first, and then back to char (string) in a different format. Then, if 44 is not necessarily a number but it may be the result of a calculation, or a subquery, or a value in another table (and if it is of NUMBER data type), then you need to do exactly what you were doing. What was the problem with it?
select item
from your_table
where to_number(to_char(to_date(my_date,'MM/DD/YYYY'),'IW')) = 44;
Perhaps you want to know if this can be done more efficiently? So, for example, can you instead write the where condition as
my_date between (something) and (something else)
which would allow the use of an index on my_date? The answer is NO, if my_date is in VARCHAR2 format - you would still have to wrap it within to_date() so you would lose the index.
But, you may build an index function on to_date(my_date) which perhaps would help you in other queries as well. So then the question of making your present query more efficient is meaningful. Now you run into the complication of ISO week not being well defined (you are not also saying which YEAR), so the assumption is that you want the date to be in ISO week 44 of whatever year it is in. If the date is a pure date (time component equal to 00:00:00), you could do this:
where to_date(my_date, 'MM/DD/YYYY') between
trunc(sysdate, 'iw') + 7 * (44 - to_number(to_char(sysdate, 'iw'))) and
trunc(sysdate, 'iw') + 7 * (44 - to_number(to_char(sysdate, 'iw'))) + 6
There are a lot of computation on the right-hand side, but they are done just once for all the rows in your table; they will take essentially no time at all.

How to convert a sysdate month value to number in oracle?

Im trying to return the CARDS of my CARD table that will expire in the next month. But the problem is that the table has two columns to represent the card date. The columns are EXPIREDAY and EXPIREMONTH ,both are numbers. So when i do that query i get an error:
select * from CARD WHERE EXPIREDAY <= sysdate - interval '2' DAY;
//Oracle error: ORA-00932: inconsistent datatypes: expected NUMBER got DATE
Is there a way to convert the sysdate - interval '2' DAY as Number data type?
Thanks!
If you want to compare the values as strings you can use this to convert the SYSDATE
SELECT TO_CHAR(sysdate, 'MM') || TO_CHAR(sysdate, 'DD') MONTH_NUM FROM DUAL
-- gives you "0922"
and this for your numeric columns which will pad with leading zeros if you only have a single digit
SELECT TO_CHAR(9, 'FM00') || TO_CHAR(22, 'FM00') MONTH_NUM FROM DUAL
-- also gives you "0922"
If you have control over the table schema it would be best practise to store both the DAY and MONTH values in a single numeric field, so that 9-SEP would be stored in this column as the numeric value 0922 where the month is first so that the natural ordering is used.
A simple and not necessarily very efficient approach is to convert the day and month values into an actual date, using to_date(), and then compare that with your target date range:
select * from card
where to_date(lpad(expireday, 2, '0')
||'/'|| lpad(expiremonth, 2, '0'), 'DD/MM')
between sysdate and add_months(sysdate, 1);
Which appears to work. But this will have problems if the dates span the end of the year. Because your table doesn't specify the year, you either have to work one out, or allow to_date to default it to the current year. And if you let it default then it won't work. For example, if you have values for December and January in your table, and run this query in December, then the January dates will be seen as January 2014, and won't be counted as being in the next month. So you'll need to do more to pick the right year.
This treats any month numbers before the current one as being next year, which may be good enough for you as you only have a one-month window:
select * from card
where to_date(lpad(expireday, 2, '0')
||'/'|| lpad(expiremonth, 2, '0')
||'/'|| (extract(year from sysdate) +
case when expiremonth < extract(month from sysdate) then 1 else 0 end),
'DD/MM/YYYY')
between sysdate and add_months(sysdate, 1);
SQL Fiddle using a date range from December to January.
And you can see the ways the two columns are being combined to form a date in this Fiddle.
As so often, the moral is... store things as the right data type. Store dates as dates, not as string or numbers.
Im trying to return the CARDS of my CARD table that will expire in the next month. But the problem is that the table has two columns to represent the card date.
Assuming:
you are using floating months (say: from 23 dec. to 23 jan.) and
your table somehow only contains one (floating ?) year of data
Why can't you use simple arithmetics? Like that:
-- some constant definitions for testing purpose
with cst as (
select EXTRACT(DAY from TO_DATE('23/12','DD/MM')) as theDay,
EXTRACT(MONTH from TO_DATE('23/12','DD/MM')) as theMonth
from dual)
-- the actual query
select card.* from card,cst
where (expiremonth = theMonth AND expireday > theDay)
or (expiremonth = 1+MOD(theMonth,12) AND expireday <= theDay);
-- ^^^^^^^^^^^^^^^^^^
-- map [01 .. 12] to [02 .. 12, 01] (i.e.: next month)
This will simply select all "pseudo-dates" from tomorrow to the end of the month, as well as any one before (and including) the current day# next month.
See this example.
For something a little bit more generic, but probably more efficient than converting all your values TO_DATE, you might want to try that:
-- the calendar is the key part of the query (see below)
with calendar as (
select extract(month from sysdate + level) as theMonth,
extract(day from sysdate + level) as theDay
from DUAL connect by ROWNUM <= 8)
-- ^
-- adjust to the right number of days you are looking for
select card.* from card join calendar
on expiremonth = theMonth and expireDay = theDay
The idea here is to simply build a calendar with all the upcoming days and then join your data table on that calendar. See an example here.
Try using to_char(sysdate - interval '2' DAY,'ddmmyyyy') to convert to character type. The date format('ddmmyyyy') will depend of the value of expiredate

Find between with separated date fields (year,month,day)

I have the following dates in my table in separated fields. How can I write a query to show the values between two dates.
For example: values between 2/1/2011 and 2/6/2011:
day month year value
--------------------------------------------------
2 6 2011 120
3 7 2011 130
5 5 2011 100
6 1 2011 50
As others have said, my first suggestion would be to use Date. Or if you need more detailed information than your example, Datetime or Timestamp with Time Zone.
But in case you actually have to work with this data, I think something like this should work, depending on your flavor of SQL:
SELECT value, CONVERT(DATE,CONCAT(CONCAT(CONCAT(CONCAT(day, "-"), month), "-"), year), 105) date FROM table_name where (2/1/2011) <= date and date <= (2/6/2011);
(with Oracle SQL, you can use to_date instead of convert and optionally use the || concatenation operator; with SQL server you have to use the + concatenation operator; with MySQL this should be right)
(2/1/2011) and (2/6/2011) could either be strings that you convert similar to the other convert, or inputted using a PreparedStatement or something like it as dates directly (this would be preferable).
I had the same scenario but with Month column displaying Month name . With slight modification on the given query i was able to fetch the data.
SELECT *
FROM Table_Name AS Tbl_Date
WHERE (Year * 10000 + DATEPART(mm, CAST(Month + Year AS DATETIME)) * 100 + 1
BETWEEN 20111101 AND 20121201)
Hope this will help
To convert to Date for easier comparisons without worrying about dmy or mdy, in a standard fashion:
DATEADD(year, year-1900, DATEADD(month, month-1, DATEADD(day, day-1, 0)))
So, something like this. The safest date format to use is yyyymmdd (especially with SQL Server)
SELECT
value,
DATEADD(year, year-1900, DATEADD(month, month-1, DATEADD(day, day-1, 0))) AS realdate
FROM Mytable_name
WHERE
'20110201' <= DATEADD(year, year-1900, DATEADD(month, month-1, DATEADD(day, day-1, 0)))
and
DATEADD(year, year-1900, DATEADD(month, month-1, DATEADD(day, day-1, 0))) <= '20110206'
if you are using oracle database then you can use TO_DATE and TO_CHAR functions to achive this target...
as follow-
select * from table_name
where to_date(day||month||year,'DDMMYYYY')
between &mindate and &maxdate
min date you can put 2-jan-2011 and max date as 2-jun-2011
I hope it should work for you :)
well i found the answer that i wanted thanks guys
SELECT Tbl_Date.Value,Tbl_Date.Year,Tbl_Date.Month,Tbl_Date.Day from Tbl_Date
where ((Tbl_Date.Year*10000)+(Tbl_Date.Month*100)+Tbl_Date.Day) between 110102 and 110602