First_Day/Last_Day Query - oracle

Could you please help me to correct the script as below:
set LastDay=SELECT LAST_DAY(SYSDATE) FROM dual;
set FirstDay=Select trunc((sysdate),'month') as First_day_of_month from dual;
SELECT count(*) FROM tab1 g , h.ab1 LEFT JOIN tab2 h ON g.bba = h.bba
WHERE 1 = 1
AND g.DATE_ BETWEEN TO_DATE('FirstDay', 'YYYYMMDD') AND TO_DATE('LastDay', 'YYYYMMDD');

In Oracle, a DATE ALWAYS has year, month, day, hour, minute and second components. Using LAST_DAY(SYSDATE) only sets the year-month-day component of a date and does not modify the time component so if you filter from the start of the month to LAST_DAY(SYSDATE) then you will exclude any values from the last day of the month with a time component between the current time and 23:59:59.
What you want is to use:
SELECT count(*)
FROM tab1 g
CROSS JOIN h.ab1 -- your query is confusing around the joins
-- and may need fixing
LEFT JOIN tab2 h
ON g.bba = h.bba
WHERE g.DATE_ >= TRUNC(SYSDATE, 'MM')
AND g.DATE_ < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1);

That's just
select count(*)
from tab1 g left join tab2 h on g.bba = h.bba
and g.date_ between trunc(sysdate, 'month') and last_day(sysdate);
Isn't it?

Related

Adding hours variable to date variable in Oracle

OK all-This may be simple but I can't see to find an answer via Google.
So I have a date value ('01/01/2020') and in another column I have a variable of hours (let's say 5) that needs to be added. SO I would have 01-JAN-20 05:00:00 in the end.
Any suggestions helpful. Thanks-
with t1 as (select TO_DATE('01/01/2020','DD/MM/YYYY') as DT, '5' as HR FROM DUAL)
select t1.* , ???? from t1;
You may simply add the correct fraction of a day, given the hour value:
WITH t1 AS (
SELECT TO_DATE('01/01/2020', 'DD/MM/YYYY') AS DT, '5' AS HR
FROM DUAL
),
t2 AS (
SELECT DT, DT + TO_NUMBER(HR) / 24 AS NEW_DT
FROM t1
)
SELECT
TO_CHAR(DT, 'DD-MM-YYYY HH24:MI:SS') AS DT,
TO_CHAR(NEW_DT, 'DD-MM-YYYY HH24:MI:SS') AS NEW_DT
FROM t2;
Demo
You can also use interval clause as follows:
with t1 as (select TO_DATE('01/01/2020','DD/MM/YYYY') as DT, '5' as HR FROM DUAL)
select t1.* ,
t1.dt + hr * interval '1' hour as new_dt -- this is solution
from t1;

How to convert this code from oracle to redshift?

I am trying to implement the same in redshift and i am finding it little difficult to do that. Since redshift is in top of postgresql engine, if any one can do it in postgresql it would be really helpfull. Basically the code gets the count for previous two month at column level. If there is no count for exact previous month then it gives 0.
This is my code:
with abc(dateval,cnt) as(
select 201908, 100 from dual union
select 201907, 200 from dual union
select 201906, 300 from dual union
select 201904, 600 from dual)
select dateval, cnt,
last_value(cnt) over (order by dateval
range between interval '1' month preceding
and interval '1' month preceding ) m1,
last_value(cnt) over (order by dateval
range between interval '2' month preceding
and interval '2' month preceding ) m2
from (select to_date(dateval, 'yyyymm') dateval, cnt from abc)
I get error in over by clause. I tried to give cast('1 month' as interval) but still its failing. Can someone please help me with this windows function.
expected output:
Regards
This is how I would do it. In Redshift there's no easy way to generate sequences, do I select row_number() from an arbitrary table to create a sequence:
with abc(dateval,cnt) as(
select 201908, 100 union
select 201907, 200 union
select 201906, 300 union
select 201904, 600),
cal(date) as (
select
add_months(
'20190101'::date,
row_number() over () - 1
) as date
from <an arbitrary table to generate a sequence of rows> limit 10
),
with_lag as (
select
dateval,
cnt,
lag(cnt, 1) over (order by date) as m1,
lag(cnt, 2) over (order by date) as m2
from abc right join cal on to_date(dateval, 'YYYYMM') = date
)
select * from with_lag
where dateval is not null
order by dateval

Using Global Temporary Table in Subquery gives NULL

I am using following query.
SELECT SYSDATE,(SELECT P_PRICE_OPEN FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID='P8R3C2-R' AND P_DATE='22-OCT-18' AND P_VOLUME<>0 AND ROWNUM=1) FROM DUAL;
GTT_ADJ_PRICE_TABLE is global temporary table loaded with values when i have executed a function related to it. This GTT preserves rows after commit. This query gives me correct results.
But, If i run the query
SELECT WEEK_END, WEEK_START,
(SELECT P_PRICE_OPEN FROM GTT_ADJ_PRICE_TABLE WHERE P_DATE=WEEK_START) AS WEEKS_OPEN_PRICE,
(SELECT MAX(P_PRICE_HIGH) FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID=FID AND P_DATE<=WEEK_END
AND P_DATE>=WEEK_START AND P_VOLUME<>0) AS WEEKLY_HIGH,
(SELECT MIN(P_PRICE_LOW) FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID=FID AND P_DATE<=WEEK_END
AND P_DATE>=WEEK_START AND P_VOLUME<>0) AS WEEKLY_LOW,
(SELECT SUM(P_VOLUME) FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID=FID AND P_DATE<=WEEK_END
AND P_DATE>=WEEK_START AND P_VOLUME<>0) AS WEEKLY_VOLUME,
P_PRICE
FROM (
SELECT ROWNUM,FID,WEEK_END,P_VOLUME,P_PRICE,
(SELECT P_DATE FROM FP_V2_FP_BASIC_PRICES WHERE FSYM_ID=FID AND P_DATE>=TRUNC(WEEK_END, 'IW') AND P_VOLUME<>0 AND ROWNUM=1) AS WEEK_START
FROM (
SELECT
ROWNUM,FSYM_ID AS FID, WEEK_END,P_VOLUME, P_PRICE,P_PRICE_OPEN,P_PRICE_HIGH,P_PRICE_LOW
FROM (
SELECT ROWNUM,FSYM_ID,P_DATE AS WEEK_END, P_PRICE,P_VOLUME, P_PRICE_OPEN,P_PRICE_HIGH,P_PRICE_LOW,
CASE
WHEN (TO_CHAR(P_DATE,'D') >= AVG(TO_CHAR(P_DATE,'D')) OVER (order by P_DATE DESC rows between 1 preceding and current row) and ROWNUM>=1) or TO_CHAR(P_DATE,'D')=6
THEN 1
ELSE 0
END AS WEEKFLAG
FROM(
SELECT * FROM TABLE (ADJUSTED_PRICE('P8R3C2-R')) WHERE P_VOLUME<>0
)
)WHERE WEEKFLAG=1
)
);
It gives me NULL in WEEKS_OPEN_PRICE,WEEKLY_HIGH,WEEKLY_LOW,WEEKLY_VOLUME.
Please help me to resolve the issue. Thanks in anticipation.
Try with WITH clause. I hope a below query works. I don't have possibility to check.
WITH adjustedPrice AS (
SELECT *
FROM TABLE(adjusted_price('P8R3C2-R'))
WHERE p_volume <> 0
AND TO_CHAR(p_date, 'DY') NOT IN ('SAT', 'SUN')
)
SELECT ap.week,
ap.week_start,
ap.week_end,
ap.weeks_high_price,
ap.weeks_low_price,
ap.weekly_volume,
ws.p_price_open AS weeks_open_price,
we.p_price AS weeks_close_price
FROM (
SELECT TRUNC(p_date, 'IW') AS week,
TRUNC(MIN(p_date)) AS week_start,
TRUNC(MAX(p_date)) AS week_end,
MAX(p_price_high) AS weeks_high_price,
MIN(p_price_low) AS weeks_low_price,
SUM(p_volume) AS weekly_volume
FROM adjustedPrice
GROUP BY TRUNC(p_date, 'IW')
) ap
INNER JOIN adjustedPrice ws ON ws.p_date = ap.week_start
INNER JOIN adjustedPrice we ON we.p_date = ap.week_end
ORDER BY week DESC;
Wanted to post as a comment. But it wont fit there thats why writing it as an answer.
I tried this code to get the week start, end , weeks high price, weeks low price, weekly volume.
select
trunc("P_DATE", 'IW') as week,
min(trunc("P_DATE")) as week_start,
max(trunc("P_DATE")) as week_end,
MAX(P_PRICE_HIGH) AS WEEKS_HIGH_PRICE,
MIN(P_PRICE_LOW) AS WEEKS_LOW_PRICE,
SUM(P_VOLUME) AS WEEKLY_VOLUME
from TABLE
(ADJUSTED_PRICE('P8R3C2-R'))
WHERE
P_VOLUME<>0 AND to_char("P_DATE", 'DY') not in ('SAT','SUN')
group by
trunc("P_DATE", 'IW')
ORDER BY
trunc("P_DATE", 'IW') DESC;
That gave me results in hardly 2-4 seconds. But i want to get the weeks open price where date will be equal to WEEK_START and weeks closing price where date will be equal to WEEK_END.
I tried following approach for the same. But its taking too much time (like 300+ seconds).
SELECT
WEEK,WEEK_START, WEEK_END, WEEKS_HIGH_PRICE,WEEKS_LOW_PRICE,WEEKLY_VOLUME,
(SELECT P_PRICE_OPEN FROM TABLE (ADJUSTED_PRICE('P8R3C2-R')) WHERE P_VOLUME<>0 AND P_DATE=WEEK_START) AS WEEKS_OPEN_PRICE,
(SELECT P_PRICE FROM TABLE (ADJUSTED_PRICE('P8R3C2-R')) WHERE P_VOLUME<>0 AND P_DATE=WEEK_END) AS WEEKS_CLOSE_PRICE
FROM
(
select
trunc("P_DATE", 'IW') as week,
min(trunc("P_DATE")) as week_start,
max(trunc("P_DATE")) as week_end,
MAX(P_PRICE_HIGH) AS WEEKS_HIGH_PRICE,
MIN(P_PRICE_LOW) AS WEEKS_LOW_PRICE,
SUM(P_VOLUME) AS WEEKLY_VOLUME
from TABLE
(ADJUSTED_PRICE('P8R3C2-R'))
WHERE
P_VOLUME<>0 AND to_char("P_DATE", 'DY') not in ('SAT','SUN')
group by
trunc("P_DATE", 'IW')
ORDER BY
trunc("P_DATE", 'IW') DESC
);
If anybody can help me to improve the output time is most welcome.

Excluding rows from outer join

I am trying to extract rows from a table to gain the start and end time for events, these are held in a one to many table relationship so that for each instanceid in the master table you can have a number of entryids in the child. I have successfully written the below query that extracts the required data converting epoch timestamps, but am getting rows with NULL values. I can see that to exclude these I need to only select rows from table_a where the status value reflect 1 or 6, but am not sure of syntax for achieving this, I have tried combos of bold highlighted line below to no avail.
SELECT a.summary
to_char(date '1970-01-01' + b.create_date/86400, 'DD Mon YYYY HH24:MI:SS') as start_date,
to_char(date '1970-01-01' + c.create_date/86400, 'DD Mon YYYY HH24:MI:SS') as completed_date,
to_char (TRUNC (SYSDATE) + NUMTODSINTERVAL ((c.create_date - b.create_date), 'second'), 'hh24:mi:ss') as Elapse_Time,
a.status
FROM o2_hpov_casecreation a
LEFT OUTER JOIN o2_hpov_casecreation_audit b
ON (a.instanceid = c.entryid
AND B.action= 'Queueing Simulation phase on SYS:Action')
LEFT OUTER JOIN o2_hpov_casecreation_audit c
ON (a.instanceid = c.entryid
AND c.action = 'Notifications completed')
AND a.status IN (2,6)
You need to add your filter criteria to the where clause
below is a corrected version of your query
SELECT a.summary
to_char(date '1970-01-01' + b.create_date/86400, 'DD Mon YYYY HH24:MI:SS') as start_date,
to_char(date '1970-01-01' + c.create_date/86400, 'DD Mon YYYY HH24:MI:SS') as completed_date,
to_char (TRUNC (SYSDATE) + NUMTODSINTERVAL ((c.create_date - b.create_date), 'second'), 'hh24:mi:ss') as Elapse_Time,
a.status
FROM o2_hpov_casecreation a
LEFT OUTER JOIN o2_hpov_casecreation_audit b
ON (a.instanceid = c.entryid
AND B.action= 'Queueing Simulation phase on SYS:Action')
LEFT OUTER JOIN o2_hpov_casecreation_audit c
ON (a.instanceid = c.entryid
AND c.action = 'Notifications completed')
WHERE a.status IN (2,6)

How to exclude holidays between two dates?

I have two dates and I have to find out the number of Sundays and holidays fall between those two dates. Can I do this using BETWEEN? If so, how?
SELECT date1, date2, trunc(deposit_date - transaction_date) TOTAL
FROM Table_Name FULL OUTER JOIN Holidays ON date2 = hdate
WHERE hdate IN (date1, date2)
Using this I can definitely check whether there is a holiday on either of the two days, i.e. date1 or date2 but what I am not able to find out that whether there lies a holiday or a Sunday between these two dates. Help!
The solution you've posted is horribly inefficient; you can do all of this in a single SQL statement:
Firstly generate all possible dates between the two you have:
select trunc(:min_date) + level - 1
from dual
connect by level <= trunc(:min_date) - trunc(:max_date)
Then use your HOLIDAY table to restrict to what you want:
with all_dates as (
select trunc(:min_date) + level - 1 as the_date
from dual
connect by level <= trunc(:min_date) - trunc(:max_date)
)
select count(*)
from all_dates a
left outer join holiday b
on a.the_date = b.hdate
where b.hdate is null
and to_char(a.the_date, 'DY') <> 'SUN'
If you want to check if hdate is between the two dates you can query using
where hdate between date1 and date2
If you want to check if hdate is on the same day as date1 or date two you can query like this
where trunc(hdate) in (trunc(date1) ,trunc(date2))
The trunc function removed the time.
You should create a table with the holidays and maintain it on your own.
CREATE TABLE holidays
(
holiday VARCHAR2(100)
, d_date DATE
);
INSERT INTO holidays VALUES ('National Developer Day', DATE'2013-06-01');
SELECT *
FROM holidays;
-- National Developer Day 2013-06-01 00:00:00
The rest is just a matter of a SQL statment
Scenario 1: EXISTS
SELECT COUNT
(
CASE
WHEN TRIM(TO_CHAR(d.start_date_level, 'DAY')) = 'SUNDAY'
OR CASE
WHEN EXISTS (SELECT 1 FROM holidays h WHERE d.start_date_level = h.d_date)
THEN 1
ELSE NULL
END = 1
THEN 1
ELSE NULL
END
) AS holiday_check
FROM
(
SELECT start_date + (LEVEL - 1) AS start_date_level
FROM
(
SELECT start_date, end_date, end_date - start_date AS diff_date
FROM
(
SELECT TRUNC(ADD_MONTHS(SYSDATE, -2)) AS start_date
, TRUNC(SYSDATE) AS end_date
FROM DUAL
)
)
CONNECT BY
LEVEL <= (diff_date + 1)
) d
Scenario 2: LEFT JOIN
SELECT COUNT
(
CASE
WHEN TRIM(TO_CHAR(d.start_date_level, 'DAY')) = 'SUNDAY'
OR h.d_date IS NOT NULL
THEN 1
ELSE NULL
END
) AS holiday_check
FROM
(
SELECT start_date + (LEVEL - 1) AS start_date_level
FROM
(
SELECT start_date, end_date, end_date - start_date AS diff_date
FROM
(
SELECT TRUNC(ADD_MONTHS(SYSDATE, -2)) AS start_date
, TRUNC(SYSDATE) AS end_date
FROM DUAL
)
)
CONNECT BY
LEVEL <= (diff_date + 1)
) d
LEFT JOIN holidays h
ON d.start_date_level = h.d_date
9 Sundays + 1 "National Developer Day" = 10
CREATE OR REPLACE FUNCTION workdays (dt1 DATE, dt2 DATE) RETURN NUMBER IS
weekday_count NUMBER := 0;
date1 DATE := dt1;
date2 DATE := dt2;
cur_dt date;
holiday_count number;
begin
if date1 = date2 then
return 0;
end if;
cur_dt := transaction_date;
while cur_dt <= date2 loop
if cur_dt = date2 then
null;
else
SELECT count(*) INTO holiday_count
FROM holiday
WHERE hdate = cur_dt;
IF holiday_count = 0 THEN
IF to_char(cur_dt,'DY') NOT IN ('SUN') THEN
weekday_count := weekday_count + 1;
END IF;
END IF;
END IF;
cur_dt := cur_dt +1;
END LOOP;
RETURN weekday_count;
END;
And then I queried my database and got the right results. Do post if you have an optimal solution for this.
Here is an even better and efficient solution to the problem,
SELECT A.ID,
COUNT(A.ID) AS COUNTED
FROM tableA A
LEFT JOIN TableB B
ON A.tableB_id=B.id
LEFT JOIN holiday C
ON TRUNC(C.hdate) BETWEEN (TRUNC(a.date1) +1) AND TRUNC(B.date2)
WHERE c.hdate IS NOT NULL
GROUP BY A.ID;
where TableA contains date1 and tableB contains date2. Holiday contains the list of holidays and Sundays. And this query excludes 'date1' from the count.
RESULT LOGIC
trunc(date2) - trunc(date1) = x
x - result of the query
Make a table T$HOLIDAYS with your holidays (HDATE column). These dates will be excluded from calculation of working days within given period (sdate is start date and edate end date of period). Here is the function that calculates working days within given period excluding holidays, saturdays and sundays:
CREATE OR REPLACE FUNCTION WorkingDays(sdate IN DATE,edate IN DATE) RETURN NUMBER IS
days NUMBER;
BEGIN
WITH dates AS (SELECT sdate+LEVEL-1 AS d FROM DUAL CONNECT BY LEVEL<=edate-sdate+1)
SELECT COUNT(*) INTO days
FROM dates
WHERE d NOT IN (SELECT hdate FROM t$holidays) --exclude holidays
AND TO_CHAR(d,'D') NOT IN (6,7); --exclude saturdays + sundays
RETURN days;
END WorkingDays;
/
select sum(qq) from (
select case when to_number(to_char((trunc(sysdate-10) + level - 1),'D'))<=5 then 1 else 0 end as qq
from dual
connect by level <= trunc(sysdate) - trunc(sysdate-10))

Resources