I have the following end of code on SQL (Oracle 19c)
WHERE DATE >= '20220101' AND DATE BETWEEN add_months(trunc(sysdate,'mm'),-1) AND last_day (add_months(trunc(sysdate,'mm'),-1)) AND DATE != 'None' AND QT_MOCK_DATA IS NOT NULL ORDER BY DATE DESC'
ERROR:
ORA-01861: literal does not match format string
Can someone help?
Thanks!
Column name certainly isn't date; it is reserved word, reserved for the datatype name. So, no - you don't have that code.
Next: it seems you are storing strings into that column (as you're comparing it to 'None', which is a string). That's a bad idea - dates should be stored as dates. Anything else brings problems (you hit one).
Then, you're comparing that string to date values. How come? The between part of the code returns date datatype values:
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> SELECT ADD_MONTHS (TRUNC (SYSDATE, 'mm'), -1) val1,
2 LAST_DAY (ADD_MONTHS (TRUNC (SYSDATE, 'mm'), -1)) val2
3 FROM DUAL;
VAL1 VAL2
------------------- -------------------
01.06.2022 00:00:00 30.06.2022 00:00:00
SQL>
Maybe you wanted to apply the TO_CHAR function with appropriate format model?
SQL> SELECT TO_CHAR (ADD_MONTHS (TRUNC (SYSDATE, 'mm'), -1), 'yyyymmdd') val1,
2 TO_CHAR (LAST_DAY (ADD_MONTHS (TRUNC (SYSDATE, 'mm'), -1)),
3 'yyyymmdd') val2
4 FROM DUAL;
VAL1 VAL2
-------- --------
20220601 20220630
SQL>
Because, with it, query returns something.
Compare
your code
SQL> WITH test (date1, qt_mock_data) AS (SELECT '20220615', 'A' FROM DUAL)
2 SELECT *
3 FROM test
4 WHERE date1 >= '20220101'
5 AND date1 BETWEEN ADD_MONTHS (TRUNC (SYSDATE, 'mm'), -1)
6 AND LAST_DAY (ADD_MONTHS (TRUNC (SYSDATE, 'mm'), -1))
7 AND date1 != 'None'
8 AND qt_mock_data IS NOT NULL
9 ORDER BY date1 DESC;
WHERE date1 >= '20220101'
*
ERROR at line 4:
ORA-01861: literal does not match format string
to my code
SQL> WITH test (date1, qt_mock_data) AS (SELECT '20220615', 'A' FROM DUAL)
2 SELECT *
3 FROM test
4 WHERE date1 >= '20220101'
5 AND date1 BETWEEN TO_CHAR (ADD_MONTHS (TRUNC (SYSDATE, 'mm'), -1),
6 'yyyymmdd')
7 AND TO_CHAR (
8 LAST_DAY (ADD_MONTHS (TRUNC (SYSDATE, 'mm'), -1)),
9 'yyyymmdd')
10 AND date1 != 'None'
11 AND qt_mock_data IS NOT NULL
12 ORDER BY date1 DESC;
DATE1 Q
-------- -
20220615 A
SQL>
I don't know your DB's schema. Maybe try using date literal?
WHERE
DATE >= date '20220101'
AND DATE BETWEEN add_months(
trunc(sysdate, 'mm'),
-1
)
AND last_day (
add_months(
trunc(sysdate, 'mm'),
-1
)
)
AND DATE != 'None'
AND QT_MOCK_DATA IS NOT NULL
ORDER BY
DATE DESC
Do not store dates as string, use a DATE data type (and do not use reserved words such as DATE as an identifier).
However, since you are storing the values as dates then convert the value you are comparing to to a string using TO_CHAR:
WHERE "DATE" >= '20220101'
AND "DATE" >= TO_CHAR(add_months(trunc(sysdate,'mm'),-1), 'YYYYMMDD')
AND "DATE" < TO_CHAR(trunc(sysdate,'mm'), 'YYYYMMDD')
AND "DATE" != 'None'
AND QT_MOCK_DATA IS NOT NULL
ORDER BY "DATE" DESC
which can be simplified to:
WHERE "DATE" >= TO_CHAR(add_months(trunc(sysdate,'mm'),-1), 'YYYYMMDD')
AND "DATE" < TO_CHAR(trunc(sysdate,'mm'), 'YYYYMMDD')
AND QT_MOCK_DATA IS NOT NULL
ORDER BY "DATE" DESC
(Since the start of the range will, assuming SYSDATE is not set to a past dates, always be greater than 20220101 and if the date range is valid then != 'None' will always also be true.)
Related
I'm trying to get it to display but it is fetching me null when the output should be 'YES'
SELECT STRT_DT,END_DT,SYSDATE AS DT,
CASE
WHEN TO_CHAR(STRT_DT,'MON-YYYY') <= TO_CHAR(SYSDATE,'MON-YYYY')
AND TO_CHAR(END_DT,'MON-YYYY') = TO_CHAR(SYSDATE,'MON-YYYY') THEN 'Yes'
END AS D
FROM
(
SELECT TO_DATE('14-JAN-2022','DD-MON-YYYY') AS STRT_DT
, TO_DATE('05-APR-2022','DD-MON-YYYY') AS END_DT
FROM DUAL);
It is not working because of this line:
TO_CHAR(STRT_DT,'MON-YYYY') <= TO_CHAR(SYSDATE,'MON-YYYY')
you are using 'Comparison Operator' on a string, if it is a date datatype the engine would know what is a 'higher' date between these two but because it is a string this condition won't work the way you intended (it is comparing those strings on alphabetical basis), simple solution would be to replace it just with:
STRT_DT <= SYSDATE
or if u want to compare only month and year:
TRUNC(STRT_DT,'MM') <= TRUNC(sysdate,'MM')
here we are truncating month so only month and year are relevant for comparison in other words if STRT_DT and SYSDATE are in the same year and month the condition will be satisfied even if the STRT_DT is 'higher' (day from the future of that month) then SYSDATE.
It can't work; you're comparing month names. Switch to a different format model which allows meaningful comparing, e.g. YYYYMM instead of MON-YYYY:
SQL> SELECT STRT_DT,
2 END_DT,
3 SYSDATE AS DT,
4 CASE
5 WHEN TO_CHAR (STRT_DT, 'yyyymm') <= TO_CHAR (SYSDATE, 'yyyymm')
6 AND TO_CHAR (END_DT, 'yyyymm') = TO_CHAR (SYSDATE, 'yyyymm')
7 THEN
8 'Yes'
9 ELSE
10 'No'
11 END AS D
12 FROM (SELECT TO_DATE ('14-JAN-2022', 'DD-MON-YYYY') AS STRT_DT,
13 TO_DATE ('05-APR-2022', 'DD-MON-YYYY') AS END_DT
14 FROM DUAL);
STRT_DT END_DT DT D
---------- ---------- ---------- ---
14.01.2022 05.04.2022 22.04.2022 Yes
SQL>
While i execute below query
SELECT (TO_NUMBER (TO_CHAR ('25-07-19', 'YYYY')) - 1) + CEIL
(
CASE
WHEN (MONTHS_BETWEEN ( LAST_DAY ( TO_DATE ( TO_CHAR ('18-05-2020', 'DD/MM/YYYY'), 'DD-MM-YYYY')), LAST_DAY ('25-07-19')))>0 THEN
MONTHS_BETWEEN ( LAST_DAY ( TO_DATE ( TO_CHAR ('18-05-2020', 'DD/MM/YYYY'), 'DD-MM-YYYY')), LAST_DAY ('25-07-19'))
ELSE 1 END / 12) Year
FROM dual;
Am getting returns error as below
ORA-01722: invalid number
01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
Your error comes from calling TO_CHAR, such as:
TO_CHAR ('25-07-19', 'YYYY')
or
TO_CHAR ('18-05-2020', 'DD/MM/YYYY')
The TO_CHAR function is overloaded and the first argument can either be a number or a date; you have supplied neither and passed in a string so Oracle is attempting to implicitly convert '25-07-19' and '18-05-2020' to numbers, since it does not know that you want it to be a date. You should not rely on implicit conversions.
Instead you could explicitly convert your string to a date:
TO_CHAR ( TO_DATE( '18-05-2020', 'DD-MM-YYYY' ), 'DD/MM/YYYY')
But, better would be to use a date literal DATE '2020-05-18' or, if you are immediately going to convert it to a string again then just supply the string in the correct format.
So, you can simplify your function a lot by eliminating the TO_CHAR calls and replacing most of TO_DATE calls with date literals. Similarly, you don't need to use TO_NUMBER( TO_CHAR( date_value, 'YYYY' ) ) and can instead use EXTRACT. You can also replace your CASE statement with GREATEST.
Which gets you to a much simpler query:
SELECT EXTRACT( YEAR FROM DATE '2019-07-25' )
- 1
+ CEIL(
GREATEST(
MONTHS_BETWEEN(
LAST_DAY( DATE '2020-05-18' ),
LAST_DAY( DATE '2019-07-25' )
),
1
) / 12
) AS Year
FROM DUAL;
Which outputs:
| YEAR |
| ---: |
| 2019 |
db<>fiddle here
You can try this:
SELECT (TO_NUMBER(TO_CHAR(TO_DATE('25-07-19', 'DD-MM-YY'), 'YYYY')) - 1) +
CEIL (
CASE
WHEN (MONTHS_BETWEEN(LAST_DAY(TO_DATE('18-05-2020', 'DD-MM-YYYY')), LAST_DAY(TO_DATE('25-07-19','DD-MM-YY')))) > 0 THEN
MONTHS_BETWEEN(LAST_DAY(TO_DATE('18-05-2020', 'DD-MM-YYYY')), LAST_DAY(TO_DATE('25-07-19','DD-MM-YY')))
ELSE 1
END / 12) YEAR
FROM dual;
You have to convert the string values with date to a date datatype first, using TO_DATE:
SELECT (TO_NUMBER (TO_CHAR (TO_DATE('25-07-19', 'DD-MM-YY'), 'YYYY')) - 1) + CEIL
(
CASE WHEN (
MONTHS_BETWEEN(LAST_DAY(TO_DATE('18-05-2020', 'DD-MM-YYYY')), LAST_DAY(TO_DATE('25-07-19', 'DD-MM-YY')))) > 0 THEN
MONTHS_BETWEEN(LAST_DAY(TO_DATE('18-05-2020', 'DD-MM-YYYY')), LAST_DAY (TO_DATE('25-07-19', 'DD-MM-YY')))
ELSE 1 END / 12) Year
FROM dual;
demo on dbfiddle.uk
Oracle (SQL) - I have 3 available dates in a month (1st, 10th and 25th). I need a query to find out the closest among the 3 dates based on the date of executing my query. For e.g, when i run the query on 4th, i should get 10th as my result, when i run on 12th, the result should be 25th and when i run on 27th, the result should be the 01st of next month.
I am struggling with the logic. Please help..
with
inputs ( dt ) as (
select to_date( '03/24/2015 11:30:00', 'mm/dd/yyyy hh24:mi:ss') from dual union all
select to_date( '08/03/2016 07:15:00', 'mm/dd/yyyy hh24:mi:ss') from dual union all
select to_date( '02/29/2016 22:30:00', 'mm/dd/yyyy hh24:mi:ss') from dual
)
-- End of simulated inputs (for testing only, not part of the solution).
-- SQL query begins BELOW THIS LINE. Use your actual table and column names.
select dt,
case when extract(day from dt) < 10 then trunc(dt, 'mm') + interval '9' day
when extract(day from dt) < 25 then trunc(dt, 'mm') + interval '24' day
else add_months(trunc(dt, 'mm'), 1)
end as next_std_dt
from inputs;
DT NEXT_STD_DT
------------------- -------------------
03/24/2015 11:30:00 03/25/2015 00:00:00
08/03/2016 07:15:00 08/10/2016 00:00:00
02/29/2016 22:30:00 03/01/2016 00:00:00
I believe this is much more efficient and simpler than the other solutions.
WITH
possible_dates
AS
-- generate the three available dates for the current month
(SELECT TRUNC (SYSDATE, 'MM') available_date
FROM DUAL
UNION ALL
SELECT TRUNC (SYSDATE, 'MM') + 9
FROM DUAL
UNION ALL
SELECT TRUNC (SYSDATE, 'MM') + 24
FROM DUAL
UNION ALL
SELECT ADD_MONTHS (TRUNC (SYSDATE, 'MM'), 1)
FROM DUAL),
delta
AS
-- calculate the distance of those available dates
(SELECT (available_date - SYSDATE) diff, available_date
FROM possible_dates)
SELECT *
FROM delta
WHERE diff = (SELECT MIN (diff)
FROM delta
WHERE diff >= 0);
If using PL SQL is an option, then use the query as following:
`DECLARE
curr_month CHAR(2);
curr_year CHAR(4);
future_date DATE;
BEGIN
select to_char(sysdate, 'MM') INTO curr_month from dual;
select to_char(sysdate, 'YYYY') INTO curr_year from dual;
future_date := TO_DATE('12' || curr_month || curr_year, 'DD/MM/YYYY');
IF (SYSDATE > future_date) THEN
{..whatever you want to do...}
ELSIF (SYSDATE > future_date2) THEN
{..whatever you want to do...}
END IF;
END;`
WITH mytable(dt) AS
(SELECT '01'
FROM dual
UNION ALL SELECT '10'
FROM dual
UNION ALL SELECT '25'
FROM dual),
given_dates AS
(SELECT Trunc (To_date (dt || To_char(sysdate, 'MMYYYY'), 'DDMMYYYY')) dt,
Trunc(sysdate) cdate
FROM mytable),
comp AS
(SELECT cdate,
CASE
WHEN ABS (cdate - dt) < ABS (cdate - Add_months (dt, 1)) THEN dt
ELSE Add_months (dt, 1)
END dt_comp
FROM given_dates)
SELECT dt_comp closest_date
FROM
(SELECT dt_comp,
rank() OVER (
ORDER BY ABS (cdate - dt_comp)) rn
FROM comp)
WHERE rn = 1;
I can't seem to find a straightforward sql without delving into PL SQL for always bringing current financial year in which case 01-04-2015 to sysdate. I want this to always update automatically so when it comes next financial year in 01/04/2016 it will bring whatever is held from that date to whenever the report is being run.
If anyone can please shed some light for me. thanks
sql is:
SELECT
PROPERTY.PRO_MANAGINGCOMPANY_DESCR,
PROPERTY.PRO_SCHEME_DESCR,
PROPERTY.PRO_SCHEME,
SUM(REPAIR_CURRENT.REP_ESTIMATED_COST) as "Estimated Cost",
nvl(SUM(REPAIR_CURRENT.REP_INVOICED_COST),SUM(REPAIR_CURRENT.REP_ESTIMATED_COST)) as "Estimated Cost Invoiced",
SUM(REPAIR_CURRENT.REP_INVOICED_COST) as "Invoice Cost",
to_char(REPAIR_CURRENT.REP_RAISED_DATE,'Mon') as "Month",
to_number(to_char(to_date(REPAIR_CURRENT.REP_RAISED_DATE,'dd-mon-yy'),'mm')) as "Month No."
FROM
PROPERTY,
REPAIR_CURRENT,
SERVICE_REQUEST
WHERE
( SERVICE_REQUEST.SRQ_PRO_REFNO=PROPERTY.PRO_REFNO )
AND ( REPAIR_CURRENT.REP_SRQ_NO=SERVICE_REQUEST.SRQ_NO )
AND
(
--PROPERTY.PRO_SCHEME = ( '00054' )
--AND
REPAIR_CURRENT.REP_RAISED_DATE BETWEEN '01-APR-2015' AND sysdate
AND
REPAIR_CURRENT.REP_STATUS <> 'CAN'
)
GROUP BY
PROPERTY.PRO_MANAGINGCOMPANY_DESCR,
PROPERTY.PRO_SCHEME_DESCR,
PROPERTY.PRO_SCHEME,
to_char(REPAIR_CURRENT.REP_RAISED_DATE,'Mon'),
to_number(to_char(to_date(REPAIR_CURRENT.REP_RAISED_DATE,'dd-mon-yy'),'mm'))
If you just want to get the beginning of the fiscal year for the current date:
SELECT TO_DATE('01-04' || CASE
WHEN EXTRACT(MONTH FROM SYSDATE) > 4 THEN
EXTRACT(YEAR FROM SYSDATE)
ELSE
EXTRACT(YEAR FROM SYSDATE)-1
END, 'DD-MM-RRRR') FISCAL_YEAR
FROM DUAL
This works for any date:
REPAIR_CURRENT.REP_RAISED_DATE
BETWEEN Add_Months(Trunc(Add_Months(sysdate,-3),'YYYY'),3)
AND Sysdate
Basically, subtract three months, truncate to the year, and add three months back on.
To just get the financial year for a date, use:
Extract(Year from Add_Months(Trunc(Add_Months(sysdate,-3),'YYYY'),3))
SELECT *
FROM your_table
WHERE datetime >= CASE
WHEN SYSDATE < TRUNC( SYSDATE, 'YEAR' ) + INTERVAL '3' MONTH
THEN TRUNC( SYSDATE, 'YEAR' ) - INTERVAL '9' MONTH
ELSE TRUNC( SYSDATE, 'YEAR' ) + INTERVAL '3' MONTH
END;
Thank you, the following worked! add_months(trunc(sysdate,'year'),3) AND sysdate
thank you all for your input :)
REPAIR_CURRENT.REP_RAISED_DATE BETWEEN '01-APR-2015' AND sysdate
Firstly, '01-APR-2015' is not a DATE it is a string. You must always use TO_DATE along with proper format model to explicitly convert the string into DATE. Or, use the ANSI Date literal as you are not concerned with the time portion. It uses a fixed format 'YYYY-MM-DD'.
Now, coming to your date arithmetic, you could use a CASE expression to evaluate the financial date depending on the year.
REP_RAISED_DATE
BETWEEN
CASE
WHEN
SYSDATE < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
THEN
ADD_MONTHS(TRUNC(SYSDATE, 'YEAR') , -9)
ELSE
ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
END
AND SYSDATE
Basically, SYSDATE >= ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3) is to check whether SYSDATE is greater than 1-APR of current year. And, SYSDATE < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 15) is to check whether it is between JAN and MARCH of next year.
For example,
SQL> SELECT
2 CASE
3 WHEN
4 SYSDATE < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
5 THEN
6 ADD_MONTHS(TRUNC(SYSDATE, 'YEAR') ,-9)
7 ELSE
8 ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
9 END FINANCIAL_YEAR
10 FROM dual;
FINANCIAL
---------
01-APR-15
For date between JAN and MAR of next year:
SQL> SELECT
2 CASE
3 WHEN
4 DATE '2016-02-01' < ADD_MONTHS(TRUNC(DATE '2016-02-01', 'YEAR'), 3)
5 THEN
6 ADD_MONTHS(TRUNC(DATE '2016-02-01', 'YEAR') ,-9)
7 ELSE
8 ADD_MONTHS(TRUNC(DATE '2016-02-01', 'YEAR'), 3)
9 END FINANCIAL_YEAR
10 FROM dual;
FINANCIAL
---------
01-APR-15
Following SQLreturns start and end date for Financial Year of current date.
SELECT
TO_DATE('01-04' || EXTRACT(YEAR FROM add_months(sysdate, -3)),'DD-MM-RRRR') from_dt ,
TO_DATE('31-03' || EXTRACT(YEAR FROM add_months(sysdate, 9)),'DD-MM-RRRR') to_dt from dual;
For any random date, you can use the following SQL: example for 01-04-2020
SELECT
TO_DATE('01-04' || EXTRACT(YEAR FROM add_months(to_date('01-04-2020','DD-MM-RRRR'), -3)),'DD-MM-RRRR') from_dt ,
TO_DATE('31-03' || EXTRACT(YEAR FROM add_months(to_date('01-04-2020','DD-MM-RRRR'), 9)),'DD-MM-RRRR') to_dt from dual;
In ORACLE I am trying to get values from PS_EMP_REVIEW_GOAL with a REVIEW_DT between 01-01-YYYY and 12-31-YYYY from last year.
I get the following error msg:
ORA-01843: not a valid month
01843. 00000 - "not a valid month"
*Cause:
*Action:
SELECT
ERG.REVIEW_DT,
ERG.CAREER_GOAL
from PS_EMP_REVIEW_GOAL ERG, PS_PERSONNEL P
where ERG.EMPLID = P.EMPLID
and ERG.REVIEW_DT = (Select max(ERG1.REVIEW_DT) from PS_EMP_REVIEW_GOAL ERG1
where ERG1.EMPLID = ERG.EMPLID
and ERG1.REVIEW_DT BETWEEN to_date('01-01-' || trunc(sysdate, 'YYYY'))-1
AND to_date('12-31-' || trunc(sysdate, 'YYYY'))-1
);
The problem is that TRUNC(SYSDATE, 'YYYY') will return whole date, which you then try to concatenate with day and month:
SELECT TRUNC(SYSDATE, 'YYYY') FROM dual;
TRUNC(SYSDATE,'YYYY')
---------------------
01-01-2013
What you should do instead is EXTRACT the year from SYSDATE and use the date format model to convert the string into a DATE:
SELECT TO_DATE('01-01-' || EXTRACT(YEAR FROM SYSDATE), 'MM-DD-YYYY') - 1 AS val
FROM dual;
VAL
----------
31-12-2012
So your code should look like this:
SELECT
ERG.REVIEW_DT,
ERG.CAREER_GOAL
from PS_EMP_REVIEW_GOAL ERG, PS_PERSONNEL P
where ERG.EMPLID = P.EMPLID
and ERG.REVIEW_DT = (Select max(ERG1.REVIEW_DT) from PS_EMP_REVIEW_GOAL ERG1
where ERG1.EMPLID = ERG.EMPLID
and ERG1.REVIEW_DT BETWEEN TO_DATE('01-01-' || EXTRACT(YEAR FROM SYSDATE), 'MM-DD-YYYY') - 1
AND TO_DATE('12-31-' || EXTRACT(YEAR FROM SYSDATE), 'MM-DD-YYYY') - 1
);
Edit If you want to compare the date with the dates from the previous year, you should move the - 1 inside the TO_DATE like this:
and ERG1.REVIEW_DT BETWEEN TO_DATE('01-01-' || (EXTRACT(YEAR FROM SYSDATE) - 1), 'MM-DD-YYYY')
AND TO_DATE('12-31-' || (EXTRACT(YEAR FROM SYSDATE) - 1), 'MM-DD-YYYY')
Another shorter option would be:
extract(year from ERG1.REVIEW_DT) = extract(year from current_date) - 1
BUT: this would not use an an index on ERG1.REVIEW_DT so it might be to slow for your purposes. IN that case Przemyslaw's answer is much better, because that expression can use an index on that column.
Something like this will implement the logic you're asking for
and ERG1.REVIEW_DT BETWEEN trunc(sysdate, 'YYYY')
AND add_months( trunc(sysdate, 'YYYY'), 12 ) - 1
Given that an Oracle DATE always has a day and a time component, though, this will exclude rows where review_dt is on 12/31 but after midnight. My guess is that you really want
and ERG1.REVIEW_DT >= trunc(sysdate, 'YYYY')
AND ERG1.REVIEW_DT < add_months( trunc(sysdate, 'YYYY'), 12 )