How to make Select Statement Faster in Oracle - sql

I wrote query to find sum of money in a given period with one filial and it is working fast:
SELECT FILIAL_CODE,
sum(sum_eqv)/100 AS summa
FROM table
WHERE substr(acc,1,5) = '65434'
and cast(substr(acc,18,3) as integer) >= 600
and cast(substr(account_co,18,3) as integer)<=607
AND o_day >= to_date('01.12.2019', 'DD.MM.YYYY')
and oday < to_date('08.12.2019', 'DD.MM.YYYY')+ INTERVAL '1' DAY
AND FILIAL_CODE = '001234'
Above query is working fine. But when I want to use it multiple filials it is becoming more complex.
Below query is needs to be fixed.
SELECT FILIAL_CODE,
sum(sum_eqv)/100 AS summa
FROM table
WHERE substr(acc,1,5) = '65434'
and cast(substr(acc,18,3) as integer) >= 600
and cast(substr(account_co,18,3) as integer)<=607
AND o_day >= to_date('01.12.2019', 'DD.MM.YYYY')
and oday < to_date('08.12.2019', 'DD.MM.YYYY')+ INTERVAL '1' DAY
AND FILIAL_CODE in (select code from city where region = '26')
group by FILIAL_CODE;
This query runs long. How can I maximize this statement. Any Help is appreciated!

Try JOIN instead of IN, such as:
SELECT t.filial_code, SUM (sum_eqv) / 100 AS summa
FROM your_table t JOIN city c ON c.code = t.filial_code
WHERE SUBSTR (t.acc, 1, 5) = '65434'
AND CAST (SUBSTR (t.acc, 18, 3) AS INTEGER) >= 600
AND CAST (SUBSTR (t.account_co, 18, 3) AS INTEGER) <= 607
AND t.o_day >= TO_DATE ('01.12.2019', 'DD.MM.YYYY')
AND t.oday < TO_DATE ('08.12.2019', 'DD.MM.YYYY') + INTERVAL '1' DAY
AND c.region = '26'
GROUP BY t.filial_code;
It would probably help if city.code and table.filial_code were indexed.

Related

Hive query takes forever on Superset

I have a query that was written in Presto SQL format (100 lines of insert a query result to a table that already exists) and takes within 10 minutes to get the result.
Now I am going to use Airflow and need to change the query to Hive SQL format to append previous month's data, there is no error, but it is taking 75+ minutes now and the query is still running and not returning any result.
Shall I 'stop' it or is there anything else to consider?
SET hive.limit.query.max.table.partition = 1000000;
INSERT INTO TABLE schema.temp_tbl partition(year_month_key)
Select
distinct
tbl.account_id,
tbl.theme_status,
streaming.streaming_hours,
tbl.year_month as year_month_key
From
(
Select
tbl_0.year_month,
tbl_0.account_id,
case when max(tbl_0.theme_status) = 1 then 'With Theme' else 'No Theme' end as theme_status
From
(Select
streaming.year_month,
streaming.account_id,
case when theme_events.account_id is not null then 1 else 0 end as theme_status
from
(
Select
substring(date_key, 1, 7) as year_month,
last_day(add_months(date_key, -1)) as year_month_ed,
date_key,
upper(account_id) as account_id,
play_seconds
from agg_device_streaming_metrics_daily
Where date_key between date_add(last_day(add_months(current_date, -2)),1) and last_day(add_months(current_date, -1))
and play_seconds > 0
) streaming
left join
(
Select
upper(theme.virtualuserid) as account_id,
min(theme.createddate) as min_createddate,
min(theme.date_key) as date_key
From
(
select * from theme_activate_event_history
where date_key between '2019-01-01' and '2020-01-01'
and activate = 'true' and themetype in ('ThemeBundle','ScreenSaver','Skin','Audio')
union
select * from theme_activate_event_history
where date_key between '2020-01-01' and '2021-01-01'
and activate = 'true' and themetype in ('ThemeBundle','ScreenSaver','Skin','Audio')
union
select * from theme_activate_event_history
where date_key between '2021-01-01' and '2022-01-01'
and activate = 'true' and themetype in ('ThemeBundle','ScreenSaver','Skin','Audio')
union
select * from theme_activate_event_history
where date_key between cast('2022-01-01' as date) and last_day(add_months(current_date, -1))
and activate = 'true' and themetype in ('ThemeBundle','ScreenSaver','Skin','Audio')
) theme
group by theme.virtualuserid
) theme_events
on streaming.account_id = theme_events.account_id
and date(theme_events.date_key) <= date(streaming.year_month_ed)
) tbl_0
group by tbl_0.year_month, tbl_0.account_id
) tbl
inner join
(Select
substring(date_key, 1, 7) as year_month,
upper(account_id) as account_id,
cast(sum(play_seconds) / 3600 as double) as streaming_hours
from agg_device_streaming_metrics_daily
Where date_key between date_add(last_day(add_months(current_date, -2)),1) and last_day(add_months(current_date, -1))
and play_seconds > 0
group by substring(date_key, 1, 7), upper(account_id)
) streaming
on tbl.account_id = streaming.account_id and tbl.year_month = streaming.year_month;

Count of consecutive days ORACLE SQL

I need help with a query where a need to count de consecutive days like this
select
a.numcad, a.datapu , f.datapu , nvl(to_char(f.datapu, 'DD'),0)dia,
row_number() over (partition by a.numcad, f.datapu order by f.datapu)particao
from
ronda.r066apu a
left join (select t.numcad, t.numemp, t.datacc, t.datapu
from ronda.r070acc t
where t.datacc >= '21/01/2022'
and t.datacc <= trunc(sysdate)
group by t.numcad, t.numemp, t.datacc, t.datapu)f
on a.numemp = f.numemp
and a.numcad = f.numcad
and a.datapu = f.datapu
where a.numcad = 2675
and A.DATAPU >= '21/01/2022'
and A.DATAPU <= trunc(sysdate)
group by a.numcad, a.datapu, f.datapu, f.datacc
order by a.datapu
result is
between 24/01/2022 and 04/02/2022
is 12 days i need know this count , but i will ways get the '21/mes/year'
You can try:
SELECT TO_DATE('2022-01-24', 'YYYY-MM-DD') -
TO_DATE('2022-02-04', 'YYYY-MM-DD')
FROM dual
This returns 21, for example...

Oracle SQL variable/parameter usage in query

I have this query below and I need to change WHERE conditions depending on QUARTER.
Meaning that I have to copy that query and change the DATE conditions to 06.2020 then use UNION.
I have no idea how can I optimize this query only with SQL because I am not able to DEFINE some variables/parameters in SQL (not using PL/SQL).
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('03.2020', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('03.2020', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('03.2020', 'MM.YYYY'))
Thanks for help
If SQL*Developer, or SQLPlus, you can use the 'substitution variables'.
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&1', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&1', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&1', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&1', 'MM.YYYY'))
When you run this it will prompt you for the value.
If you used just '&1' it would prompt you for each occurence.
'&&1' reuses the value of the first occurence.
You could specify different variables using &1, &2, ...
Also you may used named variable as follows:
To prompt for the date :
ACCEPT dt CHAR PROMPT 'ENter the date (MM.YYYY): '
Or setting the value at declaration:
DEFINE dt='03.2020'
Then:
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&dt', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&dt', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&dt', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&dt', 'MM.YYYY'))
Ok first of all a disclaimer:
As I know nothing about your original query's logic - the query I propose only shows the main logic - and probably will not run as is.
Having said that - the approach would be:
Based on parameters :start_y (start year) and :end_y (end year)
generate a list with all the quarters for those years years_and_quarters.
Join the years_and_quarters with the required table based on quarter start and end dates - and then calculate the sums grouping by year and quarter.
with quarters as
(
select 'Q1' quarter, '03' q_end_date, '01' q_start_date from dual
union
select 'Q2' quarter, '06' q_end_date,'04' q_start_date from dual
union
select 'Q3' quarter, '09' q_end_date,'07' q_start_date from dual
union
select 'Q4' quarter, '12' q_end_date,'10' q_start_date from dual
),
years as
(select :start_y + (level-1) as mydate
from dual
connect by level <= (:end_y - :start_y) +1
),
years_and_quarters as (
select * from years,quarters
order by mydate
),
accounts as (
SELECT a.limit_amount ,
b.balance_amount,
'LOAN' as TYPE, b.balance_date,a.account_open_date,a.account_close_date
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
)
select sum(accounts.limit_amount) as LIMIT,
sum(accounts.balance_amount) as OUTSTANDING,
years_and_quarters.quarter,
accounts.TYPE
from
years_and_quarters,accounts
where
trunc(balance_date) = last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
and trunc(account_close_date) > last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY')) and trunc(account_open_date) <= last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
group by years_and_quarters.mydate,years_and_quarters.quarter
How about just aggregate by the quarter?
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
to_char(balance_date, '"Q"Q YYYY') as quarter
FROM accounts a LEFT JOIN
account_balances b
ON a.account_key = b.account_key AND
b.balance_type_key = 16 AND
b.balance_date = LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
WHERE a.account_close_date > LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH) AND
a.account_open_date <= LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
GROUP BY TRUNC(balance_date, 'Q') ;
Your query seems focused on the last day of the quarter. This arrives at that by adding two months to the start and using LAST_DAY().

how to use double group by and sum statements together in oracle

I am trying to use query below but it is giving an error
SELECT s.LOCAL_CODE,substr(p.ACCOUNT_CREDIT,-3),(p.SUMMA/100) as profit
FROM OPERATIONS s INNER JOIN LEADS p ON s.PAY_ID = p.PAY_ID
WHERE s.date_paid >= TO_DATE('03.12.2019', 'DD.MM.YYYY')
AND s.date_paid < TO_DATE('03.12.2019', 'DD.MM.YYYY') + INTERVAL '1' DAY
AND state = 'T'
AND s.filial_code = '006789'
AND SUBSTR(p.ACCOUNT_CREDIT, 1, 5) = '765294'
GROUP BY s.LOCAL_CODE,substr(p.ACCOUNT_CREDIT,-3);
If LEADS.SUMMA has expected value then you don't need Group By clause, else if you use Group By then all not grouped fields can be used only as arguments of aggregate functions:
SELECT s.LOCAL_CODE
, Substr(p.ACCOUNT_CREDIT, -3)
, Sum(p.SUMMA)/100 as profit
FROM OPERATIONS s
INNER JOIN LEADS p ON s.PAY_ID = p.PAY_ID
WHERE s.date_paid >= TO_DATE('03.12.2019', 'DD.MM.YYYY')
AND s.date_paid < TO_DATE('03.12.2019', 'DD.MM.YYYY') + INTERVAL '1' DAY
AND state = 'T'
AND s.filial_code = '006789'
AND SUBSTR(p.ACCOUNT_CREDIT, 1, 5) = '765294'
GROUP BY s.LOCAL_CODE
, substr(p.ACCOUNT_CREDIT, -3);

SQL: combine case and number to date conversion

I want to do a sql statement which calculates in one table the substraction of 2 Date values. If the value is negative I just want to show it as the 0 value.
The number value is the number of seconds a payment is in a current state.
I convert it with a trick to a time value(Date type).
My current code is
SELECT
max(CASE WHEN t1.time_event < SYSDATE and t2.time_event > SYSDATE THEN to_char(to_date(max(round(SYSDATE - t1.time_event) * 24 * 60 * 60)),'ssssss'),'hh24:mi:ss') else to_char(to_date(0)) END) as "current_age"
from tbl_dummyfeed t1 join tbl_dummyfeed t2 on t1.payment_Id = t2.payment_id
where t1.event = 'accepted' and t2.event = 'enriched');
You could use a similar date 'trick', which is to add the fractional-days difference to a nominal date which has the time portion as midnight - you could use a fixed date, or trunc(sysdate), as long as the time ends up as midnight - without having to multiply by 24*60*60. (Your to_date() solution does the same thing implicitly, effectively adding the number of seconds to midnight on the first day of the current month; but this might be a little clearer). But you can also move the case clauses into the where filter
select to_char(date '1970-01-01'
+ nvl(max(sysdate - t1.time_event), 0), 'HH24:MI:SS') as "current_age"
from tbl_dummyfeed t1
join tbl_dummyfeed t2
on t1.trax_id = t2.trax_id
where t1.event = 'accepted'
and t1.time_event < sysdate
and t2.event = 'enriched'
and t2.time_event > sysdate;
You could also use an analytic approach so you only have to hit the table once, with a subquery that pairs up each 'enriched' time with the previous 'accepted' time for that ID, and which you then filter against the current time:
select to_char(date '1970-01-01'
+ nvl(max(sysdate - time_accepted), 0), 'HH24:MI:SS') as "current_age"
from (
select last_value(case when event = 'accepted' then time_event end ignore nulls)
over (partition by trax_id order by time_event) as time_accepted,
case when event = 'enriched' then time_event end as time_enriched
from tbl_dummyfeed
)
where time_accepted < sysdate
and time_enriched > sysdate;
This works, only the conversion to time hh24:mi:ss still needs to happen.
SELECT max(CASE WHEN t1.time_event < SYSDATE and t2.time_event > SYSDATE THEN round((SYSDATE - t1.time_event) * 24 * 60 * 60) else 0 END) as "current_age"
from tbl_dummyfeed t1 join tbl_dummyfeed t2 on t1.payment_id= t2.payment_id
where t1.event = 'accepted' and t2.event = 'enriched';
When I add the conversion to hh24:mm:ss The solution looks like this
SELECT to_char(to_date(max(CASE WHEN t1.time_event < SYSDATE and t2.time_event > SYSDATE THEN round((SYSDATE - t1.time_event) * 24 * 60
* 60) else 0 END),'sssss'),'hh24:mi:ss') as "current_age" from tbl_dummyfeed t1 join tbl_dummyfeed t2 on t1.trax_Id = t2.trax_id where t1.event = 'accepted' and t2.event = 'enriched';
This is the only good solution for my question. Hope this helps people.