I have the below Table. Is it possible to turn the Month column into a date?
ID Month
1 201805
Expected Results:
Id Month Date
1 201805 5/1/2018
You can just use to_date() with a suitable format mask, e.g. with a fixed value as a demo:
select to_date(to_char(201805), 'YYYYMM') from dual;
TO_DATE(TO_CHAR(201
-------------------
2018-05-01 00:00:00
You don't need to explicitly add a day number to the value (as #HoneyBadger showed, or by concatenating after converting to a string), because it will default to the first of the month anyway.
So you would need to do something like
to_date(to_char(month), 'YYYYMM')
The to_char() part could be skipped as the number would be implicitly converted to a string anyway, but it's more complete to include it.
It isn't clear if you intend to do this as part of a query (maybe in a view) or want to modify the table; if the latter then you could use a virtual column to avoid duplicating data and having to maintain the converted value if the month changes.
Related
I am trying to get only the month of August in my database and then count how many times there has been a performance during August however I can't figure out how to do it.
I have given the code which I have created so far.
SELECT f.FILM_NAME, COUNT(p.PERFORMANCE_DATE), SUM(p.TAKINGS), p.PERFORMANCE_DATE
FROM A2_PERFORMANCE p, A2_FILM f
WHERE p.PERFORMANCE_DATE LIKE TO_DATE('08-2021', 'MM-YY')
GROUP BY f.FILM_NAME, p.PERFORMANCE_DATE
ORDER BY f.FILM_NAME
I am currently trying to achieve this:
-- FILM_NAME Performances Total Takings
-- --------------------------- ------------ ----------------------
-- It Happened One Night 39 £63,571
-- Modern Times 38 £58,332
-- Parasite 23 £37,195
-- Knives Out 22 £34,362
-- Citizen Kane 25 £32,711
-- The Wizard of Oz 18 £21,716
-- Avengers: Endgame 18 £17,081
You can convert your dates to string and compare them to '08-2021' (not in the way you did - you must apply formatting to the dates themselves first), but that is inefficient.
You can also truncate the dates to the beginning of the month and compare to date '2021-08-01', but that is also inefficient.
"Inefficiency" comes from two sources, one smaller and one really big. The smaller one is having to apply functions to data from your table. The really big one has to do with indexing: if your queries often filter based on dates, then you would benefit from indexing the date column - but the index can't be used if your filters apply a function to the date column first.
So, how to do it? Best (especially if your "dates" may have time-of-day component other than midnight) is something like this:
...
where p.performance_date >= date '2021-08-01'
and p.performance_date < date '2021-09-01'
...
Note that it's always two inequalities, the first one is non-strict and the second is strict.
If instead you use performance_date between [Aug 1] and [Sep 1] (in pseudo-code), this will give the wrong answer - this makes the second inequality also non-strict, so you will include performances with a date of Sept. 1, if they are saved in the db with a time-of-day of midnight. And between [Aug 1] and [Aug 31] will miss everything on August 31 with a time-of-day OTHER THAN midnight.
Best not to use BETWEEN and instead to use two explicit inequalities, as I have shown.
You are currently doing:
WHERE p.PERFORMANCE_DATE LIKE TO_DATE('08-2021', 'MM-YY')
You are providing a 4-digit year value (2021) but have only given YY in the format model, not YYYY. Now, Oracle by default is lenient about this sort of thing - unhelpfully so, some might say - so this will work in this case. But it's not good practice, and TO_DATE('08-2021', 'MM-YYYY') would be better. Either way, that will give you midnight on the the first day of that month. You can provide that a bit more simply with a date literal: DATE '2021-08-01'.
LIKE is a pattern-matching condition, and compares strings, not dates. You are forcing an implicit conversion of both your column value and the fixed date you provided to strings, using your session's NLS_DATE_FORMAT setting. You also aren't including any wildcard characters, making it equivalent to =. So with a common NLS setting, you are really doing:
WHERE TO_CHAR(p.PERFORMANCE_DATE, 'DD-MON-RR') = TO_CHAR(DATE '2021-08-01', 'DD-MON-RR')
With that NLS setting you would match any values on August 1st - though it would match data from 1921 etc. as well as 2021, if you had any. With a more precise NLS setting you might be doing:
WHERE TO_CHAR(p.PERFORMANCE_DATE, 'YYYY-MM-DD HH24:MM:SS') = TO_CHAR(DATE '2021-08-01', 'YYYY-MM-DD HH24:MI:SS')
which would only match values at exactly midnight on August 1st.
To match the whole month you could use an explicit mask, instead of the implicit conversion; and you only need to convert the column value, as you can supply the fixed value in the format you want anyway:
WHERE TO_CHAR(p.PERFORMANCE_DATE, 'YYYY-MM') = '2021-08'
or, if you prefer (I don't, but it's not my code):
WHERE TO_CHAR(p.PERFORMANCE_DATE, 'MM-YYYY') = '08-2021'
But that conversion will prevent a normal index on that column being used. I would suggest providing a date range to avoid any conversion:
WHERE p.PERFORMANCE_DATE >= DATE '2021-08-01'
AND p.PERFORMANCE_DATE < DATE '2021-09-01'
which will find all rows where that column is on or after midnight on August 1st, and before midnight on September 1st.
If you are looking for all activities in august regardless of the year, you can convert your column to char of 'MON' and match it with 'AUG' :
SELECT f.FILM_NAME, COUNT(p.PERFORMANCE_DATE), SUM(p.TAKINGS),p.PERFORMANCE_DATE
FROM A2_PERFORMANCE p, A2_FILM f
WHERE TO_CHAR(p.PERFORMANCE_DATE,'MON') ='AUG'
GROUP BY f.FILM_NAME, p.PERFORMANCE_DATE
ORDER BY f.FILM_NAME
In my Oracle DB, I have a date field called HIGH_DATE. The format for some entries is "27-SEP-12" (DD-MON-YY) and for some entries it is "27-09-12" (DD-MM-YY).
Can someone help me in framing a select query through which I can get dates in either formats??
If you have a DATE column then it does not have any format; it is stored internally as 7-bytes (century, year-of-century, month, day, hour, minute, second) and it is only when the user interface being used to access the database returns data to the user that it then gets formatted (and all the dates will be implicitly converted to strings with a consistent format).
I'm going to assume that when you say:
I have a date field called "HIGH_DATE"
What you actually mean is: "I have a column with a VARCHAR2 data-type where I store date values".
If that is the case then all you need to do is:
SELECT TO_DATE( high_date, 'DD-MM-RR' ) AS high_date
FROM table_name;
Oracle's string-to-date conversion rules will match additionally the MON format model if you use the MM format model and don't specify an exact match using the FX format model.
If you have the test data:
CREATE TABLE table_name ( high_date ) AS
SELECT '23-09-20' FROM DUAL UNION ALL
SELECT '15-AUG-99' FROM DUAL;
Then the above query will output (depending on your NLS_DATE_FORMAT):
| HIGH_DATE |
| :------------------ |
| 2020-09-23T00:00:00 |
| 1999-08-15T00:00:00 |
db<>fiddle here
However, the best solution is going to be to stop storing the values as strings and to store them (without a format) as a date.
I need to make the date in the below particular format in big query, but somehow it is truncating leading zero's automatically. How to get the output as is.
Query used:
select FORMAT_DATETIME('%Y-%m-%d %H:%M:%E*S','0001-01-01')
output: 1-01-01 00:00:00
Desired output: 0001-01-01 00:00:00
Please help.
Use $E4Y for the year:
select FORMAT_DATETIME('%E4Y-%m-%d %H:%M:%E*S', '0001-01-01')
'%Y' uses only as many characters as needed for the year. '%E4Y' always uses 4 characters.
I have a problem with converting a varchar2 fields into a date format.
I got 2 columns with the datatyp varchar2, one is called qtime the other is called ztime. Both fields contain strings in this format (f.e. 152015 -> would be a timestamp 15:20:15).
For reporting reasons I need to convert this fields into a date format, afterwards I want to substract (qtime-ztime) the fields an convert them into the format [hh] (f.e. after the operation 01:20:00 would be -> 01). Is it possible to to this within Oracle SQL 12c? The biggest problem for me right now is that I don't get those Strings converted into a date format.
select TO_DATE(qtime,'MM/DD/YYYY hh24:mi:ss') just gives me
ORA-01861:"literal does not match format string"
select TO_DATE(qtime,'hh24mmss') gives me a wrong Date
01.03.2018
select TO_TIMESTAMP(qtime,'hh24mmss') gives me a wrong Date
01.03.2018 BUT the correct time with f.e. 15:20:15,0000000
Thank you in advance, any help is appreciated
Note: I only have reading rights on the database Oracle 12c, so I need to to this within Statements
"The Database contains another column with the correct date for each time"
The missing piece of the puzzle! Concatenate the two columns to get something which can be converted to an Oracle DATE:
select to_date(qdate||qtime, 'yyyymmddhh24miss') as qdatetime
, to_date(zdate||ztime, 'yyyymmddhh24miss') as zdatetime
from your_table
Once you have done that you can perform arithmetic of the dates e.g.
select id
, zdatetime - qdatetime as time_diff
from ( select id
, to_date(qdate||qtime, 'yyyymmddhh24miss') as qdatetime
, to_date(zdate||ztime, 'yyyymmddhh24miss') as zdatetime
from your_table
)
If you want the number of hours in the difference you can include this expression in the projection of the outer query:
, extract( hour from (zdatetime - qdatetime) day to second) as hrs_ela
First off, if you are trying to convert a varchar2 into a date without specifying neither day nor month, it will default to the first day of the current month:
If you specify a date value without a date, then the default date is the first day of the current month.
You can read up more here
Also in 2nd and 3rd your example, you are using 'hh24mmss' for specifying hour, minute and second components, but do note that correct format for minutes is 'mi' and not 'mm', which is used for months.
So the solution is to concatenate both date and time component when creating the date as the other answer suggested, tho I would recommend using a single date field as it can store the information you need.
I have a table named book_data with batch_dt as column name of type varchar in sql server.
when I pass the query
SELECT DISTINCT batch FROM book_data
it gives me the following results
batch_dt
-------------
2012-10-31
-------------
2012-11-01
-------------
2012-11-02
-------------
2012-11-03
-------------
.
.
.
Now what I am doing is getting the total count of records between two dates. Fairly a simple query.
SELECT COUNT(*) FROM book_data WHERE CONVERT(varchar(12),CONVERT(datetime,batch_dt),101) BETWEEN '11/02/2012' and '10/31/2012'
the result is
112
and just by changing the month from 02 to 2 the query gives me 218 results
SELECT COUNT(*) FROM book_data WHERE CONVERT(varchar(12),CONVERT(datetime,batch_dt),101) BETWEEN '11/2/2012' and '10/31/2012'
why this different behaviour?
Use CAST(batch_dt AS DATE) instead, and pass the date in a language neutral manner, in the format YYYYMMDD. This way it will be comared as a date not as a varchar:
SELECT COUNT(*)
FROM book_data
WHERE CAST(batch_dt AS DATE)
BETWEEN '20121102' and '20121130'
But, this is not safe, if there was any value in barch_dt in a wrong format, you will get a casting error. In this case you can add ISDATE(batch_dt) = 1 to ensure that it is a valid data time. But it is better to make this column of datatype DateTime.
Another thing to note: is that BETWEEN is asymmetric in SQL Server, meaning that BETWEEN '11/02/2012' and '10/31/2012' is evaluated as:
DATE >= '11/02/2012'
AND
DATE <= '10/31/2012'
which will never be true, the reason it works for you is that the dates were be compared as strings not as a dates. But you have to keep it like BETWEEN the small value and the biggest value.
You compare string with BETWEEN. If you do so you need to make sure that you compare in the correct order => YYYYMMDD MM:SS would be a correct order.
If you can, add columns with type datetime and store real date time values in your database. If you can not do that you can split up the values and build a date value yourself. This is much slower then just use a CONVERT() or CAST() but you can make sure that it works even with wrong date-strings.
You can use PATINDEX(),LEFT(),RIGHT() keywords to get the values you need or you use a split() function (you can find many version google it, e.g. https://codereview.stackexchange.com/questions/15125/sql-server-split-function-optimized). If you use the split function, then split by / and then get year, month, day from the positions.
But best would be still to have correct datetime values stored in your db.
You get this different behavior because you don't compare the dates but the strings/varchars.
For a Date (or DateTime), 10/2/2012 is the same as 10/02/2012.
But for string, these values are (of course) different. It's just as if you'd compare 'abcd' with 'ab0cd'
SELECT COUNT(*)
FROM book_data
WHERE CONVERT(DATETIME,batch_dt,101) BETWEEN '11/2/2012' and '10/31/2012'
This would be more appropriate