Oracle: how to write a query with enable / disable WHERE condition - sql

I've thougth this way to implement a parametrizable query.
Do you know any variant?
WITH temp AS (SELECT 'case1' case FROM DUAL)
SELECT 1
FROM temp
WHERE ( (1 = DECODE (case, 'case1', 1, 0))
AND SYSDATE > TO_DATE ('01/01/2013', 'DD/MM/YYYY'))
OR ( (1 = DECODE (case, 'case2', 1, 0))
AND SYSDATE < TO_DATE ('01/01/2013', 'DD/MM/YYYY'))

you can use case
WITH temp AS (SELECT 'case1' _case FROM DUAL)
SELECT 1
FROM temp
WHERE
1 = case
when _case = 'case1'
AND SYSDATE > TO_DATE ('01/01/2013', 'DD/MM/YYYY')
then 1
when _case = 'case2'
AND SYSDATE > TO_DATE ('01/01/2013', 'DD/MM/YYYY')
then 1
else 0
end
;

This seems awkward. You can do this with just basic logic:
WITH temp AS (SELECT 'case1' case FROM DUAL)
SELECT 1
FROM temp
WHERE ((case = 'case1') and SYSDATE > TO_DATE('01/01/2013', 'DD/MM/YYYY')) or
((case = 'case2') and SYSDATE < TO_DATE('01/01/2013', 'DD/MM/YYYY'))

Why not use a function:
CREATE FUNCTION x ( p_case IN VARCHAR2, p_date IN VARCHAR2 )
RETURN INTEGER
IS
BEGIN
IF p_case = 'case1' AND TO_DATE(p_date, 'DD/MM/YYYY') < SYSDATE THEN RETURN 1;
ELSE IF p_case = 'case2' AND TO_DATE(p_date, 'DD/MM/YYYY') < SYSDATE THEN RETURN 1;
ELSE RETURN 0;
END IF;
END;
SELECT 1 FROM dual WHERE x('case1', '01/01/2013') = 1;

Related

MOD command calling functions

I have two functions, which are working fine. I want to use the MOD or decode command to call the functions with different options but I can't seem to get the code below to work.
Below is my test CASE. Any help would be greatly appreciated. Thanks in advance for your time and expertise.
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'DD-MON-YYYY HH24:MI:SS.FF';
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS'
CREATE OR REPLACE FUNCTION random_timestamp(
p_from IN TIMESTAMP,
p_to IN TIMESTAMP,
p_fraction IN VARCHAR2 DEFAULT 'Y'
) RETURN TIMESTAMP
IS
return_val_y TIMESTAMP := p_from + dbms_random.value () * (p_to - p_from + INTERVAL '1' DAY);
return_val_n TIMESTAMP (0) := return_val_y;
BEGIN
RETURN CASE
WHEN UPPER (SUBSTR (p_fraction, 1, 1)) = 'Y'
THEN return_val_y
ELSE return_val_N
END;
END random_timestamp;
/
CREATE OR REPLACE FUNCTION random_interval(
p_min IN NUMBER,
p_max IN NUMBER,
p_duration IN VARCHAR2,
p_fraction IN VARCHAR2 DEFAULT 'Y'
) RETURN INTERVAL DAY TO SECOND
IS
return_val_y INTERVAL DAY TO SECOND := NUMTODSINTERVAL(DBMS_RANDOM.VALUE(p_min, p_max), p_duration);
return_val_n INTERVAL DAY TO SECOND :=
( EXTRACT(DAY FROM return_val_y) * 24 * 60 * 60
+ EXTRACT(HOUR FROM return_val_y) * 60 * 60
+ EXTRACT(MINUTE FROM return_val_y) * 60
+ FLOOR(EXTRACT(SECOND FROM return_val_y))
) * INTERVAL '1' SECOND;
BEGIN
RETURN CASE
WHEN UPPER (SUBSTR (p_fraction, 1, 1)) = 'Y'
THEN return_val_y
ELSE return_val_N
END;
END random_interval;
/
/* can't get this to work */
SELECT
CASE MOD(LEVEL, 2)
WHEN 0
THEN
random_timestamp(TIMESTAMP '2022-04-01 00:00:00', TIMESTAMP '2022-04-30 00:00:00', 'Y') as ts,
random_interval(1, 10, 'HOUR', 'Y') as invr
ELSE
random_timestamp(TIMESTAMP '2022-04-01 00:00:00', TIMESTAMP '2022-04-30 00:00:00', 'N') as ts,
random_interval(1, 10, 'HOUR', 'N') as invr
END
FROM dual
CONNECT BY level <= 10;
A CASE expression returns a single expression; not multiple expressions. If you want multiple expressions then move the CASE from wrapping both of them (which is wrong) to inside each of the function calls:
SELECT random_timestamp(
TIMESTAMP '2022-04-01 00:00:00',
TIMESTAMP '2022-04-30 00:00:00',
CASE MOD(LEVEL, 2) WHEN 0 THEN 'Y' ELSE 'N' END
) as ts,
random_interval(
1,
10,
'HOUR',
CASE MOD(LEVEL, 2) WHEN 0 THEN 'Y' ELSE 'N' END
) as invr
FROM dual
CONNECT BY level <= 10;
fiddle

I'm unable to find tabular format result in oracle sql developer

I'm unable to get output as tabular format.
DECLARE
prev VARCHAR2(10) := 'sysdate+1';
tdy VARCHAR2(10) := 'sysdate';
v_dt VARCHAR2(10);
result resultset;
BEGIN
v_dat :=
CASE
WHEN trunc(sysdate) = trunc(currnet_date) THEN
tdy
ELSE prev
END;
WITH
yesterday AS (
SELECT
src_dim_id,
rec_count
FROM
tab_total_last_updated
WHERE
to_char(dw_insert_datetime, 'mm-dd-yy') = CASE
WHEN TRIM(to_char(v_dt, 'DAY', 'NLS_DATE_LANGUAGE=ENGLISH')) =
'MONDAY' THEN
to_char(v_dt - 3, 'mm-dd-yy')
ELSE
to_char(v_dt - 1, 'mm-dd-yy')
END
AND table_type = 'EXT'
)
SELECT
*
INTO result
FROM
yesterday;
RETURN result;
END;
I'm trying to pass value sysdate into v_dt as per the condition but I'm unable to proceed with my code please help on this one.
You have many typos in your code v_dat should be v_dt, current_date is misspelt.
You cannot RETURN a result as you are using an anonymous PL/SQL block and not a function.
Also, you probably do not want to use PL/SQL and just want an SQL query (and do not need the sub-query factoring clause):
SELECT src_dim_id,
rec_count
FROM tab_total_last_updated
WHERE dw_insert_datetime >= CASE TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW')
WHEN 0 THEN TRUNC(SYSDATE) - 3 -- Monday
WHEN 6 THEN TRUNC(SYSDATE) - 2 -- Sunday
ELSE TRUNC(SYSDATE) - 1
END
AND dw_insert_datetime < CASE TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW')
WHEN 0 THEN TRUNC(SYSDATE) - 2 -- Monday
WHEN 6 THEN TRUNC(SYSDATE) - 1 -- Sunday
ELSE TRUNC(SYSDATE)
END
AND table_type = 'EXT';
It is unclear what you are trying to achieve using:
v_dt := CASE
WHEN trunc(sysdate) = trunc(current_date)
THEN SYSDATE
ELSE SYSDATE + 1
END;
But you appear to be trying to correct for differences between the dates on the server and the client. In which case either:
Use CURRENT_DATE throughout the above code instead of SYSDATE; or
Use a specific time zone and replace SYSDATE with, for example, SYSTIMESTAMP AT TIME ZONE 'UTC'.

Create calendar function in Oracle using WITH STATEMENT

I come from SQL Server and some times I'm not familiar to Oracle syntax, I want to create a function that takes a date and number of dates as a parameters and create a table function.
My original query is:
VAR TREND = 1;
VAR OBS_DATE = 20221109;
VAR N_DAYS = 21;
WITH CAL AS
(
SELECT
TO_DATE(:OBS_DATE, 'YYYYMMDD') + (LEVEL - 1 * :TREND) DT, ROW_NUMBER() OVER(ORDER BY NULL) - 1 IX
FROM
DUAL
WHERE
TO_CHAR(TO_DATE(:OBS_DATE, 'YYYYMMDD') + (LEVEL - 1 * :TREND) , 'D') NOT IN (1,7)
CONNECT BY LEVEL <= :N_DAYS + :N_DAYS/5*2+1
)
SELECT DT
FROM CAL
WHERE IX <= :N_DAYS;
But when I try to convert as a function it sends me an error and I don't know what the correct syntax is.
My attempt is:
CREATE OR REPLACE FUNCTION FUN_BUS_CALENDAR(
OBS_DATE IN DATE := SYSDATE
, NDAYS IN NUMBER
, TREND IN NUMBER
)
RETURN OBS_DATE DATE;
BEGIN
WITH CAL AS(
SELECT
TO_DATE(:OBS_DATE, 'YYYYMMDD') + (LEVEL - 1 * :TREND) OBS_DATE, ROW_NUMBER() OVER(ORDER BY NULL) - 1 IX
FROM DUAL
WHERE TO_CHAR(TO_DATE(:OBS_DATE, 'YYYYMMDD') + (LEVEL - 1 * :TREND) , 'D') NOT IN (1,7)
CONNECT BY LEVEL <= :N_DAYS + :N_DAYS/5.*2.+1.
)
SELECT OBS_DATE FROM CAL WHERE IX <= :N_DAYS
RETURN OBS_DATE
END
/
You should probably just use the initial query.
However, if you did want a function then you can use a pipelined function:
CREATE FUNCTION BARRRAF.FUN_BUS_CALENDAR(
OBS_DATE IN DATE := SYSDATE,
NDAYS IN NUMBER,
TREND IN NUMBER
) RETURN SYS.ODCIDATELIST PIPELINED
IS
BEGIN
FOR i IN 1 .. ndays LOOP
PIPE ROW( obs_date + i - trend );
END LOOP;
END;
/
Then if you want to generate a row number then just use a sub-query:
SELECT column_value AS obs_date,
ROWNUM - 1 AS rn
FROM TABLE(BARRRAF.FUN_BUS_CALENDAR(ndays => 3, trend=>1))
Which outputs:
OBS_DATE
RN
2022-11-17 23:40:22
0
2022-11-18 23:40:22
1
2022-11-19 23:40:22
2
fiddle
Here is a generic function that can be used to create a calendar for the following INTERVALs seconds, minutes, hours or days.
You can pass it any start and END date/time you like. The lower or higher date can go in any position as there is logic to figure out which is what least/greatest command
CREATE OR REPLACE FUNCTION generate_dates(i_from_dat IN TIMESTAMP, i_to_dat IN TIMESTAMP, i_interval IN NUMBER, i_interval_type IN VARCHAR2)
RETURN VARCHAR2
SQL_MACRO
IS
BEGIN
RETURN q'~SELECT LEAST(i_from_dat,i_to_dat) + NUMTODSINTERVAL( (LEVEL-1)*i_interval, i_interval_type ) AS dt
FROM DUAL
CONNECT BY LEAST(i_from_dat,i_to_dat) + NUMTODSINTERVAL( (LEVEL-1)*i_interval, i_interval_type) < GREATEST(i_from_dat, i_to_dat)~';
END ;
SELECT * FROM generate_dates(
TIMESTAMP '2022-11-03 09:47:31',
TIMESTAMP '2022-11-03 12:37:11',
30, 'MINUTE') ;
DT
03-NOV-22 09.47.31.000000 AM
03-NOV-22 10.17.31.000000 AM
03-NOV-22 10.47.31.000000 AM
03-NOV-22 11.17.31.000000 AM
03-NOV-22 11.47.31.000000 AM
03-NOV-22 12.17.31.000000 PM
SELECT * FROM generate_dates(
TIMESTAMP '2022-11-03 00:00:00',
TIMESTAMP '2022-11-08 00:00:00',
1, 'DAY') ;
DT
03-NOV-22 12.00.00.000000 AM
04-NOV-22 12.00.00.000000 AM
05-NOV-22 12.00.00.000000 AM
06-NOV-22 12.00.00.000000 AM
07-NOV-22 12.00.00.000000 AM

How can I use columns value from nested select in main select?

I'm trying to execute this SQL in Oracle:
select z.*
from (select (CASE
WHEN trunc(to_date('01.02.2015', 'DD.MM.YY'), 'MM') =
to_date('01.02.2015', 'DD.MM.YY') THEN
trunc(ADD_MONTHS(sysdate, -1), 'MM')
ELSE
trunc(sysdate - 1)
END) as sd,
trunc(sysdate) ed
from dual) t,
(SELECT *
FROM table(SOMEOWNERUSER.SOMEPACKAGE.getPipelinedTable(to_char(t.sd,
'dd.mm.yyyy'),
to_char(t.ed,
'dd.mm.yyyy')))) z
But I get error ORA-00904: "T"."SD": invalid identifier. What am I doing wrong?
You just need to directly reference the table() in the same level join as the t subquery:
create type dt_type as object (dt date);
/
create type dt_tab as table of dt_type;
/
create or replace function getpipelinedtable (p_sd date, p_ed date)
return dt_tab
as
l_tab dt_tab := dt_tab();
begin
for i in 1 .. (p_ed - p_sd) + 1
loop
l_tab.extend;
l_tab(l_tab.last) := dt_type(p_sd -1 + i);
end loop;
return l_tab;
end getpipelinedtable;
/
SELECT z.*
FROM (SELECT (CASE WHEN TRUNC (TO_DATE ('01.02.2015', 'DD.MM.YY'), 'MM') = TO_DATE ('01.02.2015', 'DD.MM.YY')
THEN TRUNC (ADD_MONTHS (SYSDATE, -1), 'MM')
ELSE TRUNC (SYSDATE - 1)
END) AS sd,
TRUNC (SYSDATE) ed
FROM DUAL) t,
TABLE ( getpipelinedtable (t.sd, t.ed) ) z;
DT
----------
01/01/2015
02/01/2015
03/01/2015
04/01/2015
05/01/2015
<snip>
06/02/2015
07/02/2015
08/02/2015
09/02/2015
drop function getpipelinedtable;
drop type dt_tab;
drop type dt_type;

Oracle case inside where clause

This is a simple question, I've read some details about using CASE in WHERE clause, but couldn't able to make a clear idea how to use it. The below is my sample query:
1 SELECT * FROM dual
2 WHERE (1 =1)
3 AND (SYSDATE+1 > SYSDATE)
4 AND (30 > 40)
5 AND (25 < 35);
I have a procedure i_value as in parameter.
I need to ignore the 4th line if i_value is 'S' and I need to ignore the 5th line if i_value is 'T'.
Thanks in advance.
I think this is the best way to solve your problem:
select *
from dual
where (1 = 1)
and (sysdate + 1 > sysdate)
and case
when i_value = 'S'
then
case
when (25 < 35)
then 1
else 0
end
when i_value = 'T'
then
case
when (30 > 40)
then 1
else 0
end
end = 1;
Of course, you could use Dynamic SQL, but it'd be more difficult and less effective.
Why so ser use case?
SELECT * FROM dual
WHERE (1 =1)
AND ( SYSDATE+1 > SYSDATE )
AND ( ((30 > 40) and i_value <> 'S') or i_value = 'S' )
AND ( ((25 < 35) and i_value <> 'T') or i_value = 'T' );
SELECT * FROM dual
WHERE (1 =1)
AND (SYSDATE+1 > SYSDATE)
AND CASE WHEN i_value = 'S' THEN 1 ELSE CASE WHEN (30 > 40) THEN 1 ELSE 0 END END = 1
AND CASE WHEN i_value = 'T' THEN 1 ELSE CASE WHEN (25 < 35) THEN 1 ELSE 0 END END = 1;
This should work as well.
case
when (i_value = 'S' and (WHERE (1=1) AND (SYSDATE+1 > SYSDATE) AND (25 < 35)) ) then 1
when (i_value = 'T' and (WHERE (1=1) AND (SYSDATE+1 > SYSDATE) AND (30 < 40)) ) then 1
else 0
end = 1