Comparison between day of week in oracle - oracle

I have problems with comparing the day of week
Here is my query in oracle database.
SELECT DAY, WEEKDAY
FROM (SELECT v.TNI, v.FRMP, v.LR, v.DAY, v.HH, v.VOLUME,
CASE WHEN hd.HOLIDAY_DATE is not null then 'HOLIDAY'
ELSE to_char(v.DAY, 'Day') END AS WEEKDAY
FROM v_nem_rm16 v
LEFT JOIN DBP_ADMIN.DBP_HOLIDAY hd
ON v.DAY = hd.HOLIDAY_DATE
WHERE v.STATEMENT_TYPE != 'FORECAST')
WHERE WEEKDAY = 'Monday';
Basically, the "WEEKDAY" column has the day of week based on the "DAY". Day is just date like 13/Mar/17
If you look at the "CASE" statement, you will notice that the "WEEKDAY" column is filled by "to_char(v.DAY, 'Day')".
So the column has values like "Sunday, Saturday and so on, the day of week".
The problem is the outer query's where clause, "WHERE WEEKDAY = 'Monday'"
When I execute this query, It does not give me any rows even if I have rows having Monday as the value in "WEEKDAY" column
But when I change the WHERE clause to "WHERE WEEKDAY = to_char(sysdate, 'Day')", It works fine. The sql statement gives me the rows having "Saturday" in "WEEKDAY" column since the "to_char(sysdate, 'Day')" gives me "Saturday".
So what is problems with my first query??
I just want to filter rows by the name of the day of week like if i pass "Monday", i want to have all the rows having " Monday" in "WEEKDAY" column.
How can I do??
THANKS GUYS

It appears TO_CHAR(, 'Day') returns a fixed length string with right side padding of blanks. So, you are not getting 'Monday', you are really getting 'Monday '. The longest day is 'Wednesday', so it is a 9 character string. So, either trim the value or compare against 'Monday ' exactly.
select '|'||to_char(sysdate+level,'Day') ||'|' days_of_the_week
from dual
connect by level <= 7;
Results...
|Saturday |
|Sunday |
|Monday |
|Tuesday |
|Wednesday|
|Thursday |
|Friday |

if you would like to use the day names (either full or abbreviated), I suggest to specify the language in the to_char function as follows:
select '|'||to_char(sysdate+level, 'fmDay', 'NLS_DATE_LANGUAGE = American') ||'|' days_of_the_week_enu,
'|'||to_char(sysdate+level, 'fmDay', 'NLS_DATE_LANGUAGE = German') ||'|' days_of_the_week_ger,
'|'||to_char(sysdate+level, 'DY', 'NLS_DATE_LANGUAGE = American') ||'|' days_of_the_week_abbr_enu,
'|'||to_char(sysdate+level, 'DY', 'NLS_DATE_LANGUAGE = German') ||'|' days_of_the_week_abbr_ger
from dual
connect by level <= 7;
This way the returned day names will be independent of session locale settings.
Result:

Related

oracle - how to order by with date in the format dd-MMM-yy

I am able to select records and all like in the query below but select statement is picking records randomly that were create after the specified date. I am curious if there is a way to order by this date in the format dd-MMM-yy like 05-MAY-21..
select * from Employees
where createDt >= '05-MAY-21'
order by CreateDt asc
This statement is not giving what I am hoping for, which is start with records from date 05, 06, etc..
thank you in advance.
date in the format dd-MMM-yy like 05-MAY-21
A DATE is a binary data-type and it does NOT have any format.
If you do:
select *
from Employees
where createDt >= DATE '2021-05-05'
order by CreateDt asc
Then it will sort the dates in ascending order by year then by month and then by day and then by hour and then by minute and then by second (yes, a DATE always has a time component).
If you do:
select *
from Employees
where createDt >= '05-MAY-21'
order by CreateDt asc
Then you are implicitly asking Oracle to convert the string to a DATE and your query is actually doing:
select *
from Employees
where createDt >= TO_DATE(
'05-MAY-21',
( SELECT value
FROM NLS_SESSION_PARAMETERS
WHERE parameter = 'NLS_DATE_FORMAT' )
)
order by CreateDt asc
If the NLS_DATE_FORMAT session parameter does not match the format of your string then you will get unexpected results.
If you want to order the dates alphabetically, rather than chronologically then convert them to a string in the ORDER BY clause:
select *
from Employees
where createDt >= DATE '2021-05-05'
order by TO_CHAR(CreateDt, 'DD-MON-YY') asc
However, I'm not sure hy you would want to do this.
WHERE clause seems to be wrong.
You said that createDt column's datatype is DATE. So, why are you comparing it to a string? Use date literal or TO_DATE function, don't rely on implicit datatype conversion.
select * from Employees
where createDt >= date '2021-05-05' -- No! '05-MAY-21'
order by CreateDt asc
If, on the other hand, you only think that column's datatype is DATE but is - actually - VARCHAR2, then you'll have to "convert" it to a valid date value using TO_DATE function with appropriate date format mask, hoping that all values in that column have the same, valid format:
select * from Employees
where to_date(createDt, 'dd-mmm-yy', 'nls_date_language = english') >= date '2021-05-05'
order by to_date(createDt, 'dd-mmm-yy', 'nls_date_language = english') asc
If none of above helps, please, post some sample data. That includes CREATE TABLE and INSERT INTO several sample rows.
It would really help if you showed what results you're getting and what are your desired results...
Anyway, I'm trying to guess basing on the question and your comments
Let's start with "... statement is picking records randomly ..."
and "This statement is not giving what I am hoping for, which is start with records from date 05, 06, etc.."
and later: "and the order in which the records are weird.. like dates in the order of 08, 11, 12, 24, 24, 27, 27, 31, etc "
OK, the order "08, 11, 12, 24, 24, 27, 27, 31, etc." does not seem random, does it? It is pretty much the order you requested - ascending.
I'm guessing that all those dates are in may, so you really are getting what you requested for: records from Employees table with createDt greater or equal to 5th of may and sorted in ascending order.
But you are saying that the dates order seems weird to you, you were expecting 05, 06, ..., and you got 08, 11, 12, 24
Oh! What seems to be bothering you is the fact that you have some gaps between the dates and that the dates do not start with May 5th. Is that it? Well, then, simple answer is: those dates are simply not present in the Employees table.
You ask elsewhere ".. how do I limit that to send only 10 records?"
Ok, so let me guess what you would like to get.
You would like to see 10 consecutive dates starting with may 5th and the records which were created for each date.
In that case you have to "generate" those dates and then join them with your Employees table taking into account that for some of the dates you will have no row - hence LEFT JOIN.
with dates as
(select date '2021-05-05' + (level - 1) d from dual connect by level <= 10)
select e.*
from dates d
left join employees e
on e.createDt = d.d
order by d.d
or, if the createDt contains time component
with dates as
(select date '2021-05-05' + (level - 1) d from dual connect by level <= 10)
select e.*
from dates d
left join employees e
on e.createDt >= d.d and e.createDt < d.d + 1
order by d.d, e.createDt
I'm not sure if this is it, though
Also: you may use the literal '05-MAY-21' but only if you are absolutely sure that your session uses this exact date format. It's safer to use the datetime literal or to_date function ( to_date('05-MAY-21', 'DD-MON-YY') )

Oracle get all records between current day inserted with timestamp

Unable to retrieve records properly with the formatted date values, need select query with right date format to get all records inserted per day
I have a date string like this in my script -  
dateString :='26-MAR-20 05.00.00.00000000 AM';
I want to add 0.313 minutes to this date value. And also I want to
increment it to the next day something like this- '27-MAR-20
05.00.00.00000000 AM'; ​
I tried this 
dateString :='26-MAR-20 05.00.00.00000000 AM';
dateString :=to_char(dateField,'DD-MON-YYYY HH24:MI:SS.FF');
dateField := to_timestamp(dateString, 'DD/MON/YYYY HH24:MI:SS.FF') + 0.313 * INTERVAL '1' MINUTE;
​
I can see the output and inserted these values into the DB but
unable to retrieve records properly with these formatted date values..
I suspect it might be to do with the timestamp fields
When I run this query -
select *from rptallexceptions where exceptiontime between '27-MAR-2020 04.00.00.000000000 AM' and '28-MAR-2020 03.59.00.000000000 AM' order by exceptiontime desc;
​
--- this one gives 3833 records but expected is 4600
it shows only the records on 27th march., records inserted with timestamp containing 28th march are not retrieved..
select *from rptallexceptions where exceptiontime between '28-MAR-2020 04.00.00.000000000 AM' and '29-MAR-2020 03.59.00.000000000 AM' order by exceptiontime desc;
--- this one returns '0' rows
Excerpt from the script:
cnt :=cnt +1;
dateString :=to_char(dateField,'DD-MON-YYYY HH24:MI:SS.FF');
-- add time difference for each exception.
dateField := to_timestamp(dateString, 'DD/MON/YYYY HH24:MI:SS.FF') + 0.313 * INTERVAL '1' MINUTE;
-- after n*4600 exceptions,update date to next date.
IF REMAINDER(cnt,exceptionsPerDay) = 0 THEN
dateField := to_timestamp(dateField + 1,'DD/MON/YYYY HH24:MI:SS.FF');
END IF;
The way I see it, your problem is that you're comparing timestamps to strings.
Sample table:
SQL> create table test as
2 select to_timestamp('27.03.2020 04:00:00:000000', 'dd.mm.yyyy hh24:mi:ss:ff6') datum, 'A' name from dual
3 union all
4 select to_timestamp('28.03.2020 15:23:00:000000', 'dd.mm.yyyy hh24:mi:ss:ff6') datum, 'A' name from dual;
Table created.
Query should use timestamp, not string:
SQL> select *
2 from test
3 where datum between to_timestamp('25.03.2020 12:00:00:000000am', 'dd.mm.yyyy hh:mi:ss:ff6am') --> timestamp
4 and to_timestamp('28.03.2020 12:00:00:000000am', 'dd.mm.yyyy hh:mi:ss:ff6am'); --> timestamp
DATUM N
--------------------------------------------------------------------------- -
27.03.20 04:00:00,000000000 A
SQL>
You were kind of "lucky" (though, one might call it bad luck as your query didn't fail, but it produced wrong results, according to number of rows being returned) for not getting an error as my database raises (due to different NLS settings):
SQL> select *
2 from test
3 where datum between '25.03.2020 12:00:00:000000am' --> string
4 and '28.03.2020 12:00:00:000000am'; --> string
where datum between '25.03.2020 12:00:00:000000am'
*
ERROR at line 3:
ORA-01830: date format picture ends before converting entire input string
SQL>
Strings are handled differently than dates (or timestamps), or numbers. Maybe you'll see the difference clearly in this example:
SQL> with test (col) as
2 (select '1' from dual union all
3 select '9' from dual union all
4 select '20' from dual
5 )
6 select *
7 from test
8 where col < '9';
CO
--
1
20
SQL>
20 < 9? Yes, if those are strings. The same might have happened to you. Try with proper datatype.
When you do:
dateString :=to_char(dateField,'DD-MON-YYYY HH24:MI:SS.FF');
you are converting whatever is the initial value of dateField to a string; but it looks like that has has not been set yet, so you end up with an empty string (which is the same as null). Your original value of dateString is never being used.
If you skip that and just do:
dateString :='26-MAR-20 05.00.00.00000000 AM';
dateField := to_timestamp(dateString, 'DD/MON/YYYY HH24:MI:SS.FF') + 0.313 * INTERVAL '1' MINUTE;
​then you are converting the 2-digit year 20 with a YYYY format element, which will turn it into year 0020, not 2020; but you also don't have AM in the format model, so it will get ORA-01830 anyway, and have HH24 instead of HH. I think your to_char() is attempting to 'correct' the string format, but that's not a great approach.
If you can't control the starting string format then the model has to match that:
dateString :='26-MAR-20 05.00.00.00000000 AM';
dateField := to_timestamp(dateString, 'DD-MON-RR HH:MI:SS.FF AM') + 0.313 * INTERVAL '1' MINUTE;
which gives dateField a value of 2020-03-26 05:00:18.780000.
Later on you do:
dateField := to_timestamp(dateField + 1,'DD/MON/YYYY HH24:MI:SS.FF');
which is also doing an implicit conversion of dateField + 1 - which is converted to a date, incidentally as timestamp + number is a date, not a timestamp - to a string; which will use your NLS_DATE_FORMAT. Presumably that is something like 'DD-MON-YYYY'.
So stepping through that:
dateField + 1 => date '2020-03-27 05:00:18' (losing fractional seconds)
implicit to_char(dateField + 1) => string '27-Mar-2020' (losing time)
to_timestamp(to_char(dateField + 1), '...') => timestamp '2020-03-27 00:00:00.000000' (with time at midnight).
So your between is, at best going to find records between 2020-03-26 05:00:18.780000 and 2020-03-27 00:00:00.0000000 - so it won't pick up any records later than midnight on the 27th.
Don't convert to and from strings when you don't need to; leave data in its native data type (timestamp in this case) and use direct manipulation with intervals.
Using between isn't ideal anyway, because it is inclusive; it would be better to end up with:
exceptiontime >= timestamp '2020-03-26 05:00:18.780000'
and exceptionTime < timestamp '2020-03-27 05:00:18.780000'

Oracle sql how to get the date of a week

I have the following query that gets the week of a date:
SELECT pdm.serie, rta.matricula_ant, TO_CHAR (fecha, 'ww') semana,
SUM (rta.kms_acumulados) kms,
COUNT
(DISTINCT (CASE
WHEN v.secuencia BETWEEN rta.sec_origen AND rta.sec_destino
THEN v.cod_inc
ELSE '0'
END
)
)
- 1 numincidencias
FROM (SELECT ms.tren, ms.fecha_origen_tren, ms.secuencia, ri.cod_inc
FROM r_incidencias ri, mer_sitra ms
WHERE ri.cod_serv = ms.tren
AND ri.fecha_origen_tren = ms.fecha_origen_tren
AND ri.cod_tipoin IN (SELECT cod_tipo_iincidencia
FROM v_tipos_incidencias
WHERE grupo = '45')
AND ri.punto_desde = ms.cod_estacion) v,
r_trenes_asignar rta,
r_maquinas rm,
planificador.pl_dh_material pdm
WHERE rta.fecha BETWEEN TO_DATE ('21/09/2018', 'dd/mm/yyyy') AND TO_DATE ('21/09/2018',
'dd/mm/yyyy'
)
AND rta.serie >= 4000
AND rta.matricula_ant IS NOT NULL
AND rm.matricula_maq = rta.matricula_ant
AND rm.cod_serie = pdm.id_material
AND rta.grafico BETWEEN pdm.desde AND pdm.hasta
AND v.tren(+) = rta.tren
AND v.fecha_origen_tren(+) = rta.fecha
GROUP BY pdm.serie, rta.matricula_ant, TO_CHAR (fecha, 'ww')
ORDER BY pdm.serie, rta.matricula_ant, TO_CHAR (fecha, 'ww')
For example week 1
I want to display
week 1 : 1 january - 7 january
How can I get this?
Oracle offers the TRUNC(datestamp, format) function to manipulate dates this way. You may use a variety of format strings to get the first day of a quarter, year, or even the top of the hour.
Given a particular datestamp value, Oracle returns midnight on the first day of the present week with this expression:
TRUNC(datestamp,'DY')
You can add days to a datestamp. Therefore this expression gives you midnight on the last day of the week
TRUNC(datestamp,'DY') + 6
A WHERE-clause selector for all rows in the present week might be this.
WHERE datestamp >= TRUNC(SYSDATE,'DY')
AND datestamp < TRUNC(SYSDATE,'DY') + 7
Notice that the end of the range is just before (<) midnight on the first day of the next week. You need that because you may have datestamps after midnight on the last day of the week. (Beware using BETWEEN for datestamp ranges.)
And,
SELECT TO_CHAR(TRUNC(SYSDATE,'DY'),'YYYY-MM-DD'),
TO_CHAR(TRUNC(SYSDATE,'DY')+6,'YYYY-MM-DD')
FROM DUAL;
displays the first and last dates of the present week in ISO-like format.
Date arithmetic is cool. It's worth your trouble to study the date-arithmetic functions in your DBMS at least once a year.

Same Function returning different results in Oracle

I run two queries and they are returning different results. Please help me find what am I missing.
select UPPER(TO_CHAR(Hire_date,'MONTH'))
from employees
returns a list of months(containing "2" entries for 'MARCH')
SELECT COUNT(EMPLOYEEID) "March Joinees"
FROM Employees
WHERE UPPER(TO_CHAR(Hire_date,'MONTH')) = 'MARCH';
returns 0 as count
to_char(<date>, 'Month') produces a string of length equal to the greatest length of any month name (in the current session's language) - it does so by padding with spaces. This is why the second query produces no results; if you wrap the upper.... within trim(...) it will work, but it would be better not to use to_char(..., 'Month') for this kind of query to begin with. I am sure you came to the same conclusion but you just want to know what is going on...
Here's an illustration. It may seem like the first column in the result is the string 'March'; however, the second column doesn't lie: the result in the first column is actually 'March ' (with four spaces at the end).
select to_char(date '2016-03-01', 'Month') as month_as_string,
length( to_char(date '2016-03-01', 'Month') ) as len
from dual
;
MONTH_AS_STRING LEN
--------------- ---
March 9
Then you may ask why Oracle made such a weird choice. That's a much tougher question. Anyway, this behavior is documented. http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34924 and scroll down to MONTH in table 2.15.
As stated by mathguy already, 'MONTH' makes padding with spaces. Try
SELECT '"'||TO_CHAR(Hire_date, 'Month')||'"' FROM employees
to see the effect. Use either function TRIM or Format Model Modifiers FM
Then result of TO_CHAR(Hire_date, 'Month') depends your current session NLS_DATE_LANGUAGE value. Other session may get "März" or "Μάρτιος". Specify either date language or use Month numbers.
Actually UPPER(TO_CHAR(Hire_date,'MONTH')) is redundant. Format MONTH returns month name in upper case, you don't have to make UPPER() again.
Taking all this into account you should use one of the expressions below
WHERE TO_CHAR(Hire_date, 'fmMONTH', 'NLS_DATE_LANGUAGE = english') = 'MARCH'
WHERE UPPER(TO_CHAR(Hire_date, 'fmMonth', 'NLS_DATE_LANGUAGE = english')) = 'MARCH'
WHERE TRIM(TO_CHAR(Hire_date, 'MONTH', 'NLS_DATE_LANGUAGE = english')) = 'MARCH'
WHERE TO_CHAR(Hire_date, 'MM') = '03'
WHERE EXTRACT(MONTH FROM Hire_date) = 3

"BETWEEN" SQL Keyword for Oracle Dates -- Getting an error in Oracle

I have dates in this format in my database "01-APR-12" and the column is a DATE type.
My SQL statement looks like this:
SELECT DISTINCT c.customerno, c.lname, c.fname
FROM customer c, sales s
WHERE c.customerno = s.customerno AND s.salestype = 1
AND (s.salesdate BETWEEN '01-APR-12' AND '31-APR-12');
When I try to do it that way, I get this error -- ORA-01839: date not valid for month specified.
Can I even use the BETWEEN keyword with how the date is setup in the database?
If not, is there another way I can get the output of data that is in that date range without having to fix the data in the database?
Thanks!
April has 30 days not 31.
Change
SELECT DISTINCT c.customerno, c.lname, c.fname
FROM customer c, sales s
WHERE c.customerno = s.customerno AND s.salestype = 1
AND (s.salesdate BETWEEN '01-APR-12' AND '31-APR-12');
to
SELECT DISTINCT c.customerno, c.lname, c.fname
FROM customer c, sales s
WHERE c.customerno = s.customerno AND s.salestype = 1
AND (s.salesdate BETWEEN '01-APR-12' AND '30-APR-12');
and you should be good to go.
In case the dates you are checking for range from 1st day of a month to the last day of a month then you may modify the query to avoid the case where you have to explicitly check the LAST day of the month
SELECT DISTINCT c.customerno, c.lname, c.fname
FROM customer c, sales s
WHERE c.customerno = s.customerno
AND s.salestype = 1 AND (s.salesdate BETWEEN '01-APR-12' AND LAST_DAY(TO_DATE('APR-12', 'MON-YY'));
The LAST_DAY function will provide the last day of the month.
The other answers are missing out on something important and will not return the correct results. Dates have date and time components. If your salesdate column is in fact a date that includes time, you will miss out on any sales that happened on April 30 unless they occurred exactly at midnight.
Here's an example:
create table date_temp (temp date);
insert into date_temp values(to_date('01-APR-2014 15:12:00', 'DD-MON-YYYY HH24:MI:SS'));
insert into date_temp values(to_date('30-APR-2014 15:12:00', 'DD-MON-YYYY HH24:MI:SS'));
table DATE_TEMP created.
1 rows inserted.
1 rows inserted.
select * from date_temp where temp between '01-APR-2014' and '30-APR-2014';
Query Result: 01-APR-14
If you want to get all records from April that includes those with time-components in the date fields, you should use the first day of the next month as the second side of the between clause:
select * from date_temp where temp between '01-APR-2014' and '01-MAY-2014';
01-APR-14
30-APR-14

Resources