My query is
with dates_table as (
SELECT to_date(:begin_date, 'dd/mm/yyyy') + ROWNUM - 1 cal_day
FROM dual
CONNECT BY LEVEL <= to_date(:end_date, 'dd/mm/yyyy') - to_date(:begin_date, 'dd/mm/yyyy') + 1)
SELECT '1' AS ID,
'bank' AS DESC,
cal_day AS dates,
(SUM (
CASE
WHEN S_DATE BETWEEN ADD_MONTHS (cal_day, 0) - 13
AND ADD_MONTHS (cal_day, 0) - 7
THEN
VOLUME
ELSE
0
END))
LAST_14_days,
(SUM (
CASE
WHEN S_DATE BETWEEN ADD_MONTHS (cal_day, 0) - 6
AND ADD_MONTHS (cal_day, 0)
THEN
VOLUME
ELSE
0
END))
last 7day
FROM abc, day
where day.cal_day between '13-NOV-16' and '22-MAR-17'
group by cal_day
order by cal_day
you may replace you parametric sql in the upper part with the following :
SELECT to_date('&&begin_date', 'dd/mm/yyyy') + ROWNUM - 1 cal_day
FROM dual
CONNECT BY LEVEL <= to_date('&&end_date', 'dd/mm/yyyy') - to_date('&&begin_date', 'dd/mm/yyyy') + 1)
....
For numeric values you may directly define variables without single quotes'', but you should include quotes for date or string type parameters. In each case use double ampersand so that repeating parameters( you have more than one :begin_date ) don't prompt you(normally, you can use single ampersand also, but in this case you'll be prompted for each parameter even if they're the same )
Related
I have a problem again and can't ask anyone at the moment. I want to include a switch-case in a SQL SELECT which filters by date.
The goal is to display a simple grid in a window and filter my data by a date "where".
On the one hand I want to search for everything that exists in the year = e.g. '2022'.
Then I want to search only for calendar weeks = e.g. '23'.
Then show me only the month = eg. '07'
and just the complete date = e.g. '22.07.2022'.
SELECT
ID
, GEN_DATUM
FROM
TABLE_XY
WHERE
CASE
WHEN '{DATUM_MODUS}' = '#JA' THEN TO_CHAR(GEN_DATUM, 'yyyy') = TO_CHAR('{DATUM}' , 'yyyy')
WHEN '{DATUM_MODUS}' = '#KW' THEN TO_CHAR(GEN_DATUM, 'IW') = TO_CHAR('{DATUM}' , 'IW')
WHEN '{DATUM_MODUS}' = '#MO' THEN TO_CHAR(GEN_DATUM, 'MM') = TO_CHAR('{DATUM}' , 'MM')
WHEN '{DATUM_MODUS}' = '#WO' THEN TO_CHAR(GEN_DATUM, 'DD.MM.YYYY') = TO_CHAR('{DATUM}' , 'MM')
ELSE NULL
END
In the source code I pass parameters that I need for further processing. Here I want to say
if I want to search for year 'DATUM_MODUS' = #JA then show me the year
System.Collections.Hashtable htPARAM = new System.Collections.Hashtable()
{
["DATUM"] = Datum,
["DATUM_MODUS"] = Datum_MODUS,
};
Use AND and OR rather than a CASE expression.
Never build an SQL statement using string concatenation or template strings as you introduce SQL injection vulnerabilities; use bind variables instead.
If you use functions on the gen_datum column then Oracle will not be able to use an index on that column (and would require function-based indexes instead). Instead, change the datum value into a date and match the start and end of each date range and then Oracle can use an index on the gen_datum column.
Like this:
SELECT ID
, GEN_DATUM
FROM TABLE_XY
WHERE ( :datum_modus = '#JA'
-- Search by year
AND gen_datum >= TO_DATE(:datum || '-01-01', 'YYYY-MM-DD')
AND gen_datum < TO_DATE((:datum + 1) || '-01-01', 'YYYY-MM-DD')
)
OR ( :datum_modus = '#KW'
-- Search by ISO week in the current year
AND gen_datum >= TRUNC(TRUNC(SYSDATE, 'YY') + 3, 'IW') + 7 * (:datum - 1)
AND gen_datum < TRUNC(TRUNC(SYSDATE, 'YY') + 3, 'IW') + 7 * :datum
)
OR ( :datum_modus = '#MO'
-- Search by month in the current year
AND gen_datum >= ADD_MONTHS(TRUNC(SYSDATE, 'YY'), :datum - 1)
AND gen_datum < ADD_MONTHS(TRUNC(SYSDATE, 'YY'), :datum)
)
OR ( :datum_modus = '#WO'
-- Search by date
AND gen_datum >= TO_DATE(:datum, 'DD.MM.YYYY')
AND gen_datum < TO_DATE(:datum, 'DD.MM.YYYY') + 1
)
The with clause is here just to generate some sample data and, as such, it is not a part of the answer.
You missunderstood the CASE expression. CASE expression returns a value and you are trying to get another expression out of it. Also you have to keep in mind
that CASE is tested sequentially meaning that the first one with WHEN condition satisfied will return the THEN part and EXIT CASE.
Having that in mind all you have to do is compare two CASE expresions (two returned values) and try to get the result. WHEN clauses in both of them should be rearrenged too.
Try something like this:
WITH
tbl AS
(
SELECT 1 "ID", To_Date('08.01.2022', 'dd.mm.yyyy') "GEN_DATUM" FROM DUAL UNION ALL
SELECT 2 "ID", To_Date('23.04.2022', 'dd.mm.yyyy') FROM DUAL UNION ALL
SELECT 3 "ID", To_Date('17.06.2022', 'dd.mm.yyyy') FROM DUAL UNION ALL
SELECT 4 "ID", To_Date('19.06.2022', 'dd.mm.yyyy') FROM DUAL
)
SELECT
ID,
TO_CHAR(GEN_DATUM, 'dd.mm.yyyy') "GEN_DATUM",
TO_CHAR(GEN_DATUM, 'iw') "WEEK_OF_GEN_DATUM",
TO_CHAR(GEN_DATUM, 'mm') "MONTH_OF_GEN_DATUM",
TO_CHAR(GEN_DATUM, 'yyyy') "YEAR_OF_GEN_DATUM",
:DATUM_MODUS "DATUM_MODUS",
TO_CHAR(:DATUM, 'dd.mm.yyyy') "DATUM"
FROM
tbl
WHERE
CASE
WHEN :DATUM_MODUS = '#WO' THEN TO_CHAR(GEN_DATUM, 'dd.mm.yyyy') -- the most distinctive (365)
WHEN :DATUM_MODUS = '#KW' THEN TO_CHAR(GEN_DATUM, 'iw') -- second (52)
WHEN :DATUM_MODUS = '#MO' THEN TO_CHAR(GEN_DATUM, 'mm') -- next (12)
WHEN :DATUM_MODUS = '#JA' THEN TO_CHAR(GEN_DATUM, 'yyyy') -- the most undistinctive (1)
ELSE
NULL
END
=
CASE
WHEN :DATUM_MODUS = '#WO' THEN TO_CHAR(:DATUM, 'dd.mm.yyyy')
WHEN :DATUM_MODUS = '#KW' THEN TO_CHAR(:DATUM, 'iw')
WHEN :DATUM_MODUS = '#MO' THEN TO_CHAR(:DATUM, 'mm')
WHEN :DATUM_MODUS = '#JA' THEN TO_CHAR(:DATUM, 'yyyy')
ELSE
NULL
END
--
-- If you use bind variables :DATUM_MODUS = '#KW' and :DATUM = To_Date('19.06.2022', 'dd.mm.yyyy') the result is:
-- ID GEN_DATUM WEEK_OF_GEN_DATUM MONTH_OF_GEN_DATUM YEAR_OF_GEN_DATUM DATUM_MODUS DATUM
-- ---------- ---------- ----------------- ------------------ ----------------- ----------- ----------
-- 3 17.06.2022 24 06 2022 #KW 19.06.2022
-- 4 19.06.2022 24 06 2022 #KW 19.06.2022
--
-- For :DATUM_MODUS = '#MO' and :DATUM = To_Date('14.01.2022', 'dd.mm.yyyy') the result is:
-- ID GEN_DATUM WEEK_OF_GEN_DATUM MONTH_OF_GEN_DATUM YEAR_OF_GEN_DATUM DATUM_MODUS DATUM
-- ---------- ---------- ----------------- ------------------ ----------------- ----------- ----------
-- 1 08.01.2022 01 01 2022 #MO 14.01.2022
... and so on....
In the SELECT part there are all the values involved so you can check the results and/or (if needed) filter them even more if you make this a subquery of a main one. In that case you can manipulate the data further more. Regards...
SELECT * FROM dummy;
act_date
---------
27-JAN-22
SELECT * FROM dummy1;
rpt_date
---------
10-JAN-22
10-DEC-21
how to get only Saturdays between act_date of dummy and MIN(rpt_date) of dummy1 table in Oracle's SQL?
please help
Here is a simple example of how to list all saturdays between 2 dates, you should be able to convert this to your data model.
WITH dummy(start_date, end_date) AS
(
SELECT DATE'2014-01-30', DATE'2014-02-25' FROM dual
), dummy_all_dates(dt) AS
(
SELECT
e.dt
FROM
dummy d CROSS APPLY
( SELECT
d.start_date + level - 1 AS dt
FROM dual CONNECT BY level < d.end_date - d.start_date
) e
)
SELECT dt FROM dummy_all_dates
WHERE TO_CHAR(dt,'FMDAY','NLS_DATE_LANGUAGE=english') = 'SATURDAY';
01-FEB-2014
08-FEB-2014
15-FEB-2014
22-FEB-2014
Here's another method to get a years worth of Saturdays. Adjust the dates as needed.
The to_char function has various format masks you can use to extract the day-of-week from a date. This uses day to get the full day name:
with rws as (
select date'2021-12-31' + level dt
from dual
connect by level <= (
date'2022-01-01' - date'2021-01-01'
)
)
select dt
from rws
where to_char (
dt,
'fmday',
'nls_date_language = English'
) = 'saturday';
This is the simplest of all answers:
select dt,rtrim(to_char(dt, 'DAY')) from (select to_date(SELECT
min(rpt_date)
FROM dummy1, 'DD-MON-YY') + rownum -1 dt
from all_objects
where rownum <= to_date((SELECT act_date FROM dummy), 'DD-MON-YY') -
to_date(SELECT min(rpt_date) FROM dummy1, 'DD-MON-YY'));
where rtrim(to_char(dt, 'DAY')) = 'SATURDAY';
Thanks!!
For the query below, I'm trying to pull a specific date range depending on the current day of the month. If it's the 20th or less (e.g. "2/7/2020") then I want the date range for January. Otherwise, I want the date range for February. Is it possible to be done with a case statement? Or there is a better way?
SELECT
account,
start_date,
amount
FROM
table1
WHERE
CASE
WHEN (
SELECT
CAST(EXTRACT(DAY FROM sysdate) AS NUMBER)
FROM
dual
) <= 20 THEN
start_date
BETWEEN '2020-01-01' AND '2020-01-31'
ELSE start_date BETWEEN '2020-02-01' AND '2020-02-29'
END
You can do this by avoiding the case statement and using truncate the date - 20 to the month, e.g.:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= TRUNC(SYSDATE - 20, 'mm')
AND start_date < add_months(TRUNC(dt - 20, 'mm'), 1);
If you really had to use a CASE expression (you can't use a CASE statement in SQL), you would need to do something like:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN add_months(TRUNC(SYSDATE, 'mm'), -1) ELSE TRUNC(SYSDATE, 'mm') END
AND start_date < CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN TRUNC(SYSDATE, 'mm') ELSE add_months(TRUNC(SYSDATE, 'mm'), 1) END;
N.B. if you're using a function, you don't need to wrap it in a select .. from dual, you can use it directly in the SQL statement.
I've also assumed that you want a dynamic range, e.g. if the day of the month is 20 or less, the range is for the previous month, otherwise the current month.
ETA: You would use the above two queries if there is an index on the start_date column, otherwise you could simply do:
SELECT account,
start_date,
amount
FROM table1
WHERE TRUNC(start_date, 'mm') = TRUNC(SYSDATE - 20, 'mm');
Case statements return single values. As such you should pull out the start date and you'll need two case statements.
select account, start_date, amount
from table1 where
start_date between
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-01'
else '2020-02-01'
end) and
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-31'
else '2020-02-29'
end)
One method subtracts 20 days and then gets the month boundary:
where start_date >= trunc(sysdate - interval '20' day, 'MON') and
start_date < trunc(sysdate - interval '20' day, 'MON') + interval '1' month
This approach is index (and partition) friendly -- an appropriate index on start_date can be used. It is also safe if start_date has time components.
Note: You can use sysdate without having to use a subquery.
You can use or operator with last_day function as following:
Select * from your_table
Where (
start_date <= trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate,'mm') - interval '1' month and trunc(sysdate,'mm') - 1
)
Or
(
start_date > trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate, 'mm') and last_day(sysdate)
)
This approach will use index on start_date, if any.
Cheers!!
select account, amount, start_date
from table1
where ( ( (select cast (extract (day from sysdate) as number) from dual) <= 20
and start_date between date '2020-01-01' and date '2020-01-31')
or ( (select cast (extract (day from sysdate) as number) from dual) > 20
and start_date between date '2020-02-01' and date '2020-02-29')
);
Using CASE expressions as BETWEEN operands:
SELECT account
, start_date
, amount
FROM table1
WHERE start_date BETWEEN CASE
WHEN extract(day from sysdate) <= 20
THEN trunc(sysdate -interval '1' month, 'month')
ELSE trunc(sysdate, 'month')
END
AND CASE
WHEN extract(day from sysdate) <= 20
THEN last_day(sysdate -interval '1' month)
ELSE last_day(sysdate)
END
Is there any way i can calculate the first and last day of the three quarters in any year . 2012 , 2013 or 2014
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'Q'), -3) AS First,
TRUNC(SYSDATE, 'Q') - 1 AS Last
FROM DUAL
calculates the first quarter of current year. i want to calculate the first quarter of any year ?
You could do the following:
with q(qtr) as(
select add_months(
DATE '2013-01-01'
, (level-1)*3
)
from dual
connect by level <= 4
)
select qtr as first_day
, last_day(add_months(qtr, 2)) as last_day
from q
Result:
FIRST_DAY LAST_DAY
----------- -----------
01.01.2013 31.03.2013
01.04.2013 30.06.2013
01.07.2013 30.09.2013
01.10.2013 31.12.2013
SQLFIddle Demo
This is one way of doing it
select to_date('01-JAN-'||to_char(yr), 'DD-MON-YYYY') first_qtr,
to_date('01-APR-'||to_char(yr), 'DD-MON-YYYY') second_qtr,
to_date('01-JUL-'||to_char(yr), 'DD-MON-YYYY') third_qtr,
to_date('01-OCT-'||to_char(yr), 'DD-MON-YYYY') fourth_qtr
from ( select :year yr from dual )
UNION ALL
select to_date('01-APR-'||to_char(yr), 'DD-MON-YYYY')-1 first_qtr,
to_date('01-JUL-'||to_char(yr), 'DD-MON-YYYY')-1 second_qtr,
to_date('01-OCT-'||to_char(yr), 'DD-MON-YYYY')-1 third_qtr,
to_date('01-JAN-'||to_char(yr+1), 'DD-MON-YYYY')-1 fourth_qtr
from ( select :year yr from dual )
I have used bind variables so change it to your requirements accordingly.
I am fairly new to Oracle, so other's can give a simplified code.
The output when given 2009 would be as below
FIRST_QTR SECOND_QTR THIRD_QTR FOURTH_QTR
01/01/2009 04/01/2009 07/01/2009 10/01/2009
03/31/2009 06/30/2009 09/30/2009 12/31/2009
This is an old question but maybe this will be helpful:
WITH y1 AS (
SELECT LEVEL + 2000 AS the_year
FROM dual
CONNECT BY LEVEL <= 20
), q1 AS (
SELECT LEVEL AS the_quarter
FROM dual
CONNECT BY LEVEL <= 4
)
SELECT the_year, the_quarter
, TO_CHAR(first_day, 'DAY') AS first_dw, first_day
, TO_CHAR(last_day, 'DAY') AS last_dw, last_day
FROM (
SELECT the_year, the_quarter
, ADD_MONTHS(TO_DATE(the_year, 'YYYY'), 3 * (the_quarter - 1)) AS first_day
, ADD_MONTHS(TO_DATE(the_year, 'YYYY'), 3 * the_quarter) - 1 AS last_day
FROM y1, q1
)
One line per year, each line consisting of the year plus 8 (=2 dates per quarter) dates:
with params as (
select
2012 as start_year,
2014 as end_year
from
dual
)
select
start_year+ level - 1 year,
to_date((start_year+ level - 1) || '0101', 'yyyymmdd') start_q1,
to_date((start_year+ level - 1) || '0331', 'yyyymmdd') end_q1 ,
to_date((start_year+ level - 1) || '0401', 'yyyymmdd') start_q2,
to_date((start_year+ level - 1) || '0630', 'yyyymmdd') end_q2 ,
to_date((start_year+ level - 1) || '0701', 'yyyymmdd') start_q3,
to_date((start_year+ level - 1) || '0930', 'yyyymmdd') end_q3 ,
to_date((start_year+ level - 1) || '1001', 'yyyymmdd') start_q4,
to_date((start_year+ level - 1) || '1231', 'yyyymmdd') end_q4
from
dual, params
connect by
start_year + level -1 <= end_year;
How do I select dates between two given dates in an Oracle query?
SELECT TO_DATE('12/01/2003', 'MM/DD/YYYY') - 1 + rownum AS d
FROM all_objects
WHERE TO_DATE('12/01/2003', 'MM/DD/YYYY') - 1 + rownum <= TO_DATE('12/05/2003', 'MM/DD/YYYY')
from
http://forums.devshed.com/oracle-development-96/select-all-dates-between-two-dates-92997.html
SELECT * FROM your_table WHERE your_date_field BETWEEN DATE '2010-01-01' AND DATE '2011-01-01';
You can use the LEVEL pseudocolumn in a tricky way to generate a series, so, for example, to get the list of days between today and 20 days from now I can:
select trunc(sysdate+lvl) from
(select level lvl from dual connect by level < ((sysdate+20)-sysdate - 1) )
order by 1
Generically you can see how this would apply for any two given dates.
select trunc(early_date+lvl) from
(select level lvl from dual connect by level < (later_Date-early_date-1) )
order by 1
And you can adjust the clauses if you want to include the two end dates as well.
You could also use the below to get a list of calendar dates between a date range (similar to Michael Broughton's solution)
select (trunc(sysdate) - (trunc(sysdate) - (to_date('start_date')))) -1 + level from dual
connect by level <=
((select (trunc(sysdate) - (trunc(sysdate) - (to_date('end_date'))))-
(trunc(sysdate) - (trunc(sysdate) - (to_date('start_date'))))from dual)+1);
I do this so often for a scheduling app I work on that I created a pipelined table function. Sometimes I need days, hours or 15 minutes between times. This is not exactly my function, because my code is in a package. For example, here, I'm getting days between Jan 1 2020 and Jan 10 2020:
SELECT
days.date_time
FROM
table(between_times(TO_DATE('2020-01-01'),TO_DATE('2020-01-10'),(60*24), 'Y')) days
The pipelined function:
function between_times(i_start_time TIMESTAMP, i_end_time TIMESTAMP, i_interval_in_minutes NUMBER, include_end_time VARCHAR2 := 'N')
RETURN DateTableType PIPELINED
AS
time_counter TIMESTAMP := i_start_time;
BEGIN
IF i_start_time IS NULL OR i_end_time IS NULL or i_start_time > i_end_time OR i_interval_in_minutes IS NULL OR
i_interval_in_minutes <= 0 THEN
RETURN;
END IF;
LOOP
-- by default does not include end time
if (include_end_time = 'Y') THEN
exit when time_counter > i_end_time;
ELSE
exit when time_counter >= i_end_time;
END IF;
pipe row(DateType( time_counter ));
time_counter := time_counter + i_interval_in_minutes/(60*24);
END LOOP;
EXCEPTION WHEN NO_DATA_NEEDED THEN NULL;
END;
Use "between". In a general sense:
select * from someTable where dateCol between date1 and date2;
note that dateCol is defined as a date and date1 and date2 are also date values. If these aren't dates, then you'll convert them to dates using to_date function.
with all_days as (select trunc(to_date('12-03-2017','dd-mm-yyyy')+levl)-1 as all_dates from
(select level levl from dual connect by level < (sysdate-to_date('12-03-2017','DD-MM-YYYY')+1) )
order by 1)
select count(*) as no_of_days from all_days where ltrim(rtrim(to_char(all_dates,'DAY'))) not in ('SATURDAY','SUNDAY');