select A,B,C,
TO_DATE(year ||'-'|| LPAD(month,2,0) ||'-01','YYYY-MM-DD') as firstday,
LAST_DAY(TO_DATE(year ||'-'|| LPAD(month,2,0) ||'-01','YYYY-MM-DD')) as lastday
from test;
Error : "FROM keyword not found where expected"
Whole lot of things.
SQL> with test (year, month) as
2 (select '2022', '4' from dual)
3 select to_date(year ||'-'|| lpad(month, 2, '0') || '-01', 'yyyy-mm-dd') result
4 from test;
RESULT
--------------------
01-APR-22
SQL>
Though, as it seems you want the 1st of that month, you can shorten it to
SQL> with test (year, month) as
2 (select '2022', '4' from dual)
3 select to_date(year || month, 'yyyymm') result
4 from test;
RESULT
--------------------
01-APR-22
SQL>
The LAST_DAY option:
SQL> with test (year, month) as
2 (select '2022', '4' from dual)
3 select
4 to_date(year ||'-'|| lpad(month,2,0) ||'-01','YYYY-MM-DD') as firstday,
5 last_day(to_date(year ||'-'|| lpad(month,2,0) ||'-01','YYYY-MM-DD')) as lastday
6 from test;
FIRSTDAY LASTDAY
---------- ----------
01.04.2022 30.04.2022
SQL>
Related
I have a table like this.
Date
Enddate
20012022
21012022
21012022
23012022
23012022
24012022
20012022
26012022
26012022
27012022
27012022
27012022
The next date entry is equal to the last one enddate. How do I find lines that don't follow this rule? In the example, line 4 (previus enddate 24012022 - next date 20012022).
I tried use
lag()
I can't understand how it works... Thanks for helping..
Here's one option.
Sample data:
SQL> with test (datum, enddatum) as
2 (select date '2022-01-20', date '2022-01-21' from dual union all
3 select date '2022-01-21', date '2022-01-23' from dual union all
4 select date '2022-01-23', date '2022-01-24' from dual union all
5 select date '2022-01-20', date '2022-12-26' from dual union all
6 select date '2022-12-26', date '2022-12-27' from dual union all
7 select date '2022-12-27', date '2022-12-27' from dual
8 ),
Query begins here: find previous enddatum so that you could compare it to datum (line #17):
9 temp as
10 (select datum,
11 enddatum,
12 lag(enddatum) over (order by enddatum) previous_enddatum
13 from test
14 )
15 select datum, enddatum
16 from temp
17 where datum <> previous_enddatum;
DATUM ENDDATUM
---------- ----------
20.01.2022 26.12.2022
SQL>
The LAG() function's result depends on query partition clause and order by clause. Here are two codes giving different results if ordered by Start or End date:
Your sample data:
WITH
tbl (START_DATE, END_DATE) as
( Select DATE '2022-01-20', DATE '2022-01-21' From dual Union All
Select DATE '2022-01-21', DATE '2022-01-23' From dual Union All
Select DATE '2022-01-23', DATE '2022-01-24' From dual Union All
Select DATE '2022-01-20', DATE '2022-12-26' From dual Union All
Select DATE '2022-12-26', DATE '2022-12-27' From dual Union All
Select DATE '2022-12-27', DATE '2022-12-27' From dual
)
Using Order By END_DATE:
Select START_DATE, END_DATE,
CASE
WHEN START_DATE != LAG(END_DATE) OVER(ORDER BY END_DATE)
THEN 'Should be ' || LAG(END_DATE) OVER(ORDER BY END_DATE)
END "END_DATE_CHECK"
From tbl
START_DATE END_DATE END_DATE_CHECK
---------- --------- -------------------
20-JAN-22 21-JAN-22
21-JAN-22 23-JAN-22
23-JAN-22 24-JAN-22
20-JAN-22 26-DEC-22 Should be 24-JAN-22
26-DEC-22 27-DEC-22
27-DEC-22 27-DEC-22
Using Order By START_DATE
Select START_DATE, END_DATE,
CASE
WHEN START_DATE != LAG(END_DATE) OVER(ORDER BY START_DATE)
THEN 'Should be ' || LAG(END_DATE) OVER(ORDER BY START_DATE)
END "END_DATE_CHECK"
From tbl
START_DATE END_DATE END_DATE_CHECK
---------- --------- -------------------
20-JAN-22 21-JAN-22
20-JAN-22 26-DEC-22 Should be 21-JAN-22
21-JAN-22 23-JAN-22 Should be 26-DEC-22
23-JAN-22 24-JAN-22
26-DEC-22 27-DEC-22 Should be 24-JAN-22
27-DEC-22 27-DEC-22
It looks like there is something missing in your sample data (some ID column maybe). Let's say that there is some column the dates belong to and that we could partition the dates by that column like below. There is no checking problems at all:
3. Using Partition By
WITH
tbl (ID, START_DATE, END_DATE) as
( Select 1, DATE '2022-01-20', DATE '2022-01-21' From dual Union All
Select 1, DATE '2022-01-21', DATE '2022-01-23' From dual Union All
Select 1, DATE '2022-01-23', DATE '2022-01-24' From dual Union All
Select 2, DATE '2022-01-20', DATE '2022-12-26' From dual Union All
Select 2, DATE '2022-12-26', DATE '2022-12-27' From dual Union All
Select 2, DATE '2022-12-27', DATE '2022-12-27' From dual
)
Select ID, START_DATE, END_DATE,
CASE
WHEN START_DATE != LAG(END_DATE) OVER(Partition By ID ORDER BY START_DATE)
THEN 'Should be ' || LAG(END_DATE) OVER(Partition By ID ORDER BY START_DATE)
END "END_DATE_CHECK"
From tbl
ID START_DATE END_DATE END_DATE_CHECK
---------- ---------- --------- -------------------
1 20-JAN-22 21-JAN-22
1 21-JAN-22 23-JAN-22
1 23-JAN-22 24-JAN-22
2 20-JAN-22 26-DEC-22
2 26-DEC-22 27-DEC-22
2 27-DEC-22 27-DEC-22
In this case there is no difference using Start or End date ordering... More about LAG() OVER() here.
Whats the best way of grouping or sorting the following query, Also any way to improve the query would be appreciated.
SELECT TO_CHAR(B.EVENTDATE,'Month YYYY') Month_Year,
LP_TOOLS.P_GETCODEDESCR_F('SomeOrgId', 'SomeOrgId','MSTS', TRIM(B.OLDSTATUS),'en') OLDSTATUS ,
LP_TOOLS.P_GETCODEDESCR_F('SomeOrgId', 'SomeOrgId','MSTS', TRIM(B.NEWSTATUS),'en') NEWSTATUS,
COUNT(*) STATUSCOUNT
FROM PTCIS_PRODUCTMEMBER A, PTLOY_STATUSCHANGE B
WHERE A.ORGID = 'SomeOrgId'
AND A.PRODUCTID ='SomeOrgId'
AND A.STATUS NOT IN (SELECT STATUS
FROM PTGEN_STATUSVAL
WHERE INCLUDEGENREPORTS != 'Y'
AND ORGID=A.ORGID AND PRODUCTID=A.PRODUCTID)
AND A.MEMBERTYPE IN ('INDV','COPM')
AND A.ORGID=B.ORGID
AND A.PRODUCTID=B.PRODUCTID
AND A.MPACC = B.MPACC
GROUP BY TO_CHAR(B.EVENTDATE,'Month YYYY'),B.OLDSTATUS,B.NEWSTATUS
ORDER BY Month_Year DESC,OLDSTATUS,NEWSTATUS
This will give me wrong values example 2014 Jan, 2014 Feb, 2015 Feb , 2014 April etc
It's simple - in your order by clause, you need to convert the month_year column back into a date. Here's something that will hopefully explain it to you:
with sample_data as (select sysdate dt, 1 val from dual union all
select sysdate - 31.53 dt, 10 val from dual union all
select sysdate + 366 dt, 100 val from dual)
select to_char(dt, 'fmMonth yyyy') my,
sum(val)
from sample_data
group by to_char(dt, 'fmMonth yyyy')
order by to_date(to_char(dt, 'fmMonth yyyy'), 'fmMonth yyyy');
MY SUM(VAL)
-------------------------------- ----------
July 2015 10
August 2015 1
August 2016 100
or, since the order by clause can accept column aliases, simply:
with sample_data as (select sysdate dt, 1 val from dual union all
select sysdate - 31.53 dt, 10 val from dual union all
select sysdate + 366 dt, 100 val from dual)
select to_char(dt, 'fmMonth yyyy') my,
sum(val)
from sample_data
group by to_char(dt, 'fmMonth yyyy')
order by to_date(my, 'fmMonth yyyy');
MY SUM(VAL)
-------------------------------- ----------
July 2015 10
August 2015 1
August 2016 100
N.B. I used fm in my format model, since without it, you end up with extra spaces between the month and the year. You may not need to use it if you want all the years to line up in the results.
This is what I got so far:
select
to_char(sysdate, 'yyyy') Time
from
dual;
Which gives me:
TIME
2015
Its working until this point.
I would like to add
if the month is >= 7 I get as output 01.07.current year
if the month is <= 7 I get as output 01.07.(current year - 1 year)
Any ideas how to handle this? I thought about CASE WHEN but I dont get know how.
Thanks!
A simple CASE expression would do the job.
For example,
SQL> SELECT
2 '01.07.' ||
3 CASE
4 WHEN TO_CHAR(SYSDATE, 'MM') < '07'
5 THEN
6 TO_CHAR(SYSDATE, 'YYYY')
7 ELSE
8 TO_CHAR(add_months(SYSDATE,-12), 'YYYY')
9 END case_date
10 FROM dual;
CASE_DATE
----------
01.07.2015
SQL>
To keep it even more precise, you could keep the common value outside the CASE expression:
SQL> SELECT '01.07.'
2 ||
3 CASE
4 WHEN TO_CHAR(SYSDATE, 'MM') < '07'
5 THEN TO_CHAR(SYSDATE, 'YYYY')
6 ELSE TO_CHAR(add_months(SYSDATE,-12), 'YYYY')
7 END case_date
8 FROM dual;
CASE_DATE
----------
01.07.2015
SQL>
Using extract more readable
SELECT
to_date((CASE
WHEN extract(MONTH FROM SYSDATE) >= 7 THEN
0
ELSE
-1
END) + extract(YEAR FROM SYSDATE) || '07-01', 'yyyy-mm-dd') END
FROM dual
I need to populate a calendar table in a Oracle 11g database with the following:
calendar_date,
week_number,
week_year,
day_since_2000,
week_since_2000,
month_since_2000,
quarter_since_2000,
week_of_month
The table is already populated with data since 2000 I now need to populate it going back to 1994 with negative numbers for the since_2000 columns. I think I have everything figured but for week since 2000 and quarter since 2000.
EDIT1: Just noticed my week since 200 and month since 2000 is still messed up. Quarter since 2000 looks good after using the answer mentioned below. I am updating the query with the latest.
EDIT2: It was not working because of the missing trunc(). Its working fine now. Latest query updated.
This is what I am using to do that:
/* Formatted on 2/1/2013 11:54:27 AM (QP5 v5.227.12220.39724) */
CREATE OR REPLACE PROCEDURE populate_d_calendar (start_date IN DATE,
end_date IN DATE)
AS
BEGIN
INSERT INTO d_calendar (calendar_date,
week_number,
week_year,
day_since_2000,
week_since_2000,
month_since_2000,
quarter_since_2000,
week_of_month)
SELECT dt, --Date
TO_CHAR (dt,
'ww'), --Week in the year
TO_CHAR (dt,
'yyyy'), --Year
TO_CHAR (dt - end_date), --Day Since 2000
TRUNC (TO_CHAR (dt - end_date) / 7), --Week Since 2000
TRUNC (MONTHS_BETWEEN (dt,
end_date)), --Month Since 2000
CASE
WHEN TO_CHAR (dt,
'MMDD') >= '0101'
AND TO_CHAR (dt,
'MMDD') < '0401'
THEN
0
+ 4
* TRUNC ( MONTHS_BETWEEN (TO_DATE ( '0101'
|| TO_CHAR (dt,
'yyyy'),
'mmddyyyy'),
TO_DATE ('01Jun2000'))
/ 12) --q1
WHEN TO_CHAR (dt,
'MMDD') >= '0401'
AND TO_CHAR (dt,
'MMDD') < '0701'
THEN
1
+ 4
* TRUNC ( MONTHS_BETWEEN (TO_DATE ( '0101'
|| TO_CHAR (dt,
'yyyy'),
'mmddyyyy'),
TO_DATE ('01Jun2000'))
/ 12) --q2
WHEN TO_CHAR (dt,
'MMDD') >= '0701'
AND TO_CHAR (dt,
'MMDD') < '1001'
THEN
2
+ 4
* TRUNC ( MONTHS_BETWEEN (TO_DATE ( '0101'
|| TO_CHAR (dt,
'yyyy'),
'mmddyyyy'),
TO_DATE ('01Jun2000'))
/ 12) --q3
WHEN TO_CHAR (dt,
'MMDD') >= '1001'
AND TO_CHAR (dt,
'MMDD') <= '1231'
THEN
3
+ 4
* TRUNC ( MONTHS_BETWEEN (TO_DATE ( '0101'
|| TO_CHAR (dt,
'yyyy'),
'mmddyyyy'),
TO_DATE ('01Jun2000'))
/ 12) --q4
END
quarters_since_2000, --Quarter Since 2000
TO_CHAR (dt,
'w') --Week of the month
FROM (SELECT start_date + LEVEL - 1 dt
FROM DUAL
CONNECT BY LEVEL <= end_date - start_date + 1);
END;
/
i think you're after a floating point like with the others so maybe:
TO_CHAR (dt - end_date)/7 week_since_2000
and
(MONTHS_BETWEEN (dt,
end_date)/3) quarter_since_2000
with weeks you can follow one of the methods here:
http://searchoracle.techtarget.com/answer/Calculating-weeks-between-two-dates
because it will depend on which rules are more useful to you
with quarters, use this:
case
WHEN TO_CHAR (dt,'MMDD') >= '0101' and TO_CHAR (dt,'MMDD') < '0401' then 0 + 4 * trunc(months_between( to_date('0101' || to_char(dt,'yyyy'),'mmddyyyy'),to_date('01Jun2000' )) /12) --q1
WHEN TO_CHAR (dt,'MMDD') >= '0401' and TO_CHAR (dt,'MMDD') < '0701' then 1 + 4 * trunc(months_between( to_date('0101' || to_char(dt,'yyyy'),'mmddyyyy'),to_date('01Jun2000' )) /12) --q2
WHEN TO_CHAR (dt,'MMDD') >= '0701' and TO_CHAR (dt,'MMDD') < '1001' then 2 + 4 * trunc(months_between( to_date('0101' || to_char(dt,'yyyy'),'mmddyyyy'),to_date('01Jun2000' )) /12) --q3
WHEN TO_CHAR (dt,'MMDD') >= '1001' and TO_CHAR (dt,'MMDD') <= '1231' then 3 + 4 * trunc(months_between( to_date('0101' || to_char(dt,'yyyy'),'mmddyyyy'),to_date('01Jun2000' )) /12) --q4
end quarters_since_2000,
This is my version of calendar table from 1/1/2013 till today. I'm not sure how do you need to insert number of months, quarters, days etc... You need to display total number of months between every year or once? You can add those calc yourself or add desired output in your post to make it clear. See addl queries/comments below:
-- Days,weeks, quarters from 1/1/2013 --
SELECT start_date -- 1/1/2013 --
, TRUNC(start_date, 'iw') wk_starts
, TRUNC(start_date, 'iw') + 7 - 1/86400 wk_ends
, TO_NUMBER (TO_CHAR (start_date, 'IW')) ISO_wk#
, TO_NUMBER (TO_CHAR (start_date, 'Q')) Quarters
FROM
(
SELECT TRUNC(SYSDATE, 'Y')-1 + LEVEL AS start_date
FROM dual
CONNECT BY LEVEL <=
( -- replace this part to go back to 1994 - see below --
SELECT TRUNC(ADD_MONTHS (SYSDATE, 12), 'Y')-TRUNC(SYSDATE, 'Y') "Num of Days in 2013"
FROM dual
)
)
/
START_DATE WK_STARTS WK_ENDS ISO_WK# QUARTERS
---------------------------------------------------------------------
1/1/2013 12/31/2012 1/6/2013 11:59:59 PM 1 1
1/2/2013 12/31/2012 1/6/2013 11:59:59 PM 1 1
......
2/19/2013 2/18/2013 2/24/2013 11:59:59 PM 8 1
2/20/2013 2/18/2013 2/24/2013 11:59:59 PM 8 1
......
3/10/2013 3/4/2013 3/10/2013 11:59:59 PM 10 1
3/11/2013 3/11/2013 3/17/2013 11:59:59 PM 11 1
.......
-- 6971 days from 1/1/1994 till today - 2/1/2013 --
SELECT TRUNC(SYSDATE) - TRUNC(ADD_MONTHS (SYSDATE, -12*19), 'Y') "Num of Days from 1994"
FROM dual
/
-- 4780 days from 1/1/2000 till today - 2/1/2013 --
SELECT TRUNC(SYSDATE) - TRUNC(ADD_MONTHS (SYSDATE, -12*13), 'Y') "Num of Days from 2000"
FROM dual
/
-- 156 Months between from 2000 toll today --
SELECT MONTHS_BETWEEN(TRUNC(SYSDATE), TRUNC(ADD_MONTHS (SYSDATE, -12*13))) mo_btw_2000_till_today
FROM dual
/
-- Number of weeks btw. 2 dates --
SELECT to_char(sysdate, 'WW') - to_char(sysdate-30, 'WW') number_of_weeks FROM dual
/
This may not be exactly what you want... To help you more I need to see the actual expected output of your query. Just trying to help...
I am trying to write a query that will give me the max date form Table_A based on the following condition.
If the given date (input parameter) falls between 01 April and 30 Sept of that year then return MAX(date) prior to April 1.
OR
If the given date (input parameter) falls between 01 Oct and 31 March of the following year then return MAX(date) prior to Oct 1.
For example :
Given date is 27th Dec, 2012 (date between 01 Oct and 31 March of the following year)
return 29th Sept, 2012
Given Date is 17th Aug, 2012 (date between 01 April and 30 Sept of the following year)
return date (MAX DATE prior to is 01 April is 29th March)
try this:
select max(case
when trunc(the_date) between to_date('01-apr-'||the_year, 'dd-mon-yyyy')
and to_date('30-sep-'||the_year, 'dd-mon-yyyy')
and dte < to_date('01-apr-'||the_year, 'dd-mon-yyyy')
then
dte
when trunc(the_date) between to_date('01-oct-'||the_year, 'dd-mon-yyyy')
and to_date('31-mar-'||(the_year+1), 'dd-mon-yyyy')
and dte < to_date('01-oct-'||the_year, 'dd-mon-yyyy')
then
dte
end) max_date
from table_a a
cross join (select v_inp_date the_date,
to_char(add_months(v_inp_date,-3), 'yyyy') the_year
from dual) dte;
so you input date is in the dte part
v_inp_date
and
to_char(add_months(v_inp_date,-3), 'yyyy') the_year
eg a small test:
SQL> create table table_a(dte date);
Table created.
SQL> insert into table_a values(to_date('28-sep-2012', 'dd-mon-yyyy'));
1 row created.
SQL> insert into table_a values(to_date('29-sep-2012', 'dd-mon-yyyy'));
1 row created.
SQL> insert into table_a values(to_date('28-mar-2012', 'dd-mon-yyyy'));
1 row created.
SQL> insert into table_a values(to_date('29-mar-2012', 'dd-mon-yyyy'));
1 row created.
SQL> insert into table_a values(to_date('28-sep-2011', 'dd-mon-yyyy'));
1 row created.
SQL> insert into table_a values(to_date('27-mar-2011', 'dd-mon-yyyy'));
1 row created.
SQL> commit;
Commit complete.
SQL> var inpdate varchar2(20);
SQL> exec :inpdate := '27-dec-2012';
PL/SQL procedure successfully completed.
SQL> select max(case
2 when trunc(the_date) between to_date('01-apr-'||the_year, 'dd-mon-yyyy')
3 and to_date('30-sep-'||the_year, 'dd-mon-yyyy')
4 and dte < to_date('01-apr-'||the_year, 'dd-mon-yyyy')
5 then
6 dte
7 when trunc(the_date) between to_date('01-oct-'||the_year, 'dd-mon-yyyy')
8 and to_date('31-mar-'||(the_year+1), 'dd-mon-yyyy')
9 and dte < to_date('01-oct-'||the_year, 'dd-mon-yyyy')
10 then
11 dte
12 end) max_date
13 from table_a a
14 cross join (select to_date(:inpdate,'dd-mon-yyyy') the_date,
15 to_char(add_months(to_date(:inpdate,'dd-mon-yyyy'), -3), 'yyyy') the_year
16 from dual) dte;
MAX_DATE
---------
29-SEP-12
SQL> exec :inpdate := '17-aug-2012';
PL/SQL procedure successfully completed.
SQL> /
MAX_DATE
---------
29-MAR-12
SQL> exec :inpdate := '01-oct-2011';
PL/SQL procedure successfully completed.
SQL> /
MAX_DATE
---------
28-SEP-11
SQL> exec :inpdate := '01-apr-2011';
PL/SQL procedure successfully completed.
SQL> /
MAX_DATE
---------
27-MAR-11
SQL>
The end date in my example is Dec-31-2012, you can extend it to 2013+. I'm building table on the fly-the max_date is printed for every row. Also, I hardcode max dates. You do not need to do any of this, this is just for example. Replace &1 and &2 with values w/out quotes...:
SELECT date_table --, start_date, end_date
, (CASE WHEN date_table BETWEEN To_Date('01-APR-2012') And To_Date('30-SEP-2012') Then max_apr_date
WHEN date_table BETWEEN To_Date('01-OCT-2012') And To_Date('31-DEC-2012') Then max_oct_date
END) max_date
FROM
(
SELECT *
FROM
(
SELECT TRUNC(SYSDATE,'Y')+LEVEL-1 date_table
, To_Date('&1') start_date
, To_Date('&2') end_date
, '29-MAR-2012' max_apr_date
, '29-Sep-2012' max_oct_date
FROM dual
CONNECT BY LEVEL <= (ADD_MONTHS(TRUNC(SYSDATE,'Y'),12)-TRUNC(SYSDATE,'Y') )
)
WHERE date_table BETWEEN start_date AND end_date
)
/