need help in re-writing this query, which uses same data set multiple times, as per explain plan - performance

We have a query run by our development team that's heavy in resources and looking at the explain plan, it looks like its uses the same data set multiple times. Is there anyway we can re-write this query.
Now, i tried to replace the co-related query with direct join but still the multiple co-related queries look the same apart from one minor difference.
select tb2.mktg_id, mktg_cd , count(distinct tb2.conf_id)
from
(select conf_id, count(distinct c.mktg_id) as num_cpg
from acc_latst c, off_latst ot
where c.mktg_id = ot.mktg_id and c.bus_eff_dt > '2019-01-01' and to_date(strt_tms) = '2019-01-10'
group by conf_id
having count(distinct c.mktg_id) >1
)tb1,
(select distinct conf_id, c.mktg_id, mktg_cd
from acc_latst c, off_latst ot
where c.mktg_id = ot.mktg_id and c.bus_eff_dt > '2019-01-01' and to_date(strt_tms) = '2019-01-10'
)tb2
where tb1.conf_id = tb2.conf_id group by tb2.mktg_id, mktg_cd

One way is using CTE -
with res1 as
(
select distinct conf_id, c.mktg_id, mktg_cd
from acc_latst c, off_latst ot
where c.mktg_id = ot.mktg_id and c.bus_eff_dt > '2019-01-01' and to_date(strt_tms) = '2019-01-10'
)
,res2 as
(
select conf_id, count(distinct c.mktg_id) as num_cpg
from res1 group by conf_id having count(distinct c.mktg_id) > 1
)
select res1.mktg_id, mktg_cd, count(distinct res1.conf_id) from res1 t1 inner join res2 t2 on t1.conf_id=t2.conf_id group by res1.mktg_id, mktg_cd;
If the query is still slow, could you provide table and partition details.

Related

Rewriting query with table join containing GROUP BY clause

Is it possible to rewrite the following query
SELECT CT.GROUP, CT.EMP_ID, HT.EFF_DT
FROM CURR_TABLE CT
JOIN (SELECT GROUP, EMP_ID, MAX(EFF_DT) AS EFF_DT
FROM HIST_TABLE
WHERE STAT = 'A'
GROUP BY GROUP, EMP_ID) HT ON CT.GROUP = HT.GROUP AND
CT.EMPID = HT.EMP_ID
WHERE CT.GROUP = :1
AND CT.EMP_ID = :2
in a way that is similar to CROSS JOIN style?
SELECT table1.column1, table2.column2...
FROM table1, table2 [, table3 ]
The reason is that I want to create such query in Peoplesoft, and the above can only be achieved by creating a separate view for the selection with the group by clause. I want to do this just in one query without creating additional views.
You may try writing your query as a single level join with an aggregation:
SELECT
CT.GROUP,
CT.EMP_ID,
MAX(HT.EFF_DT) AS EFF_DT
FROM CURR_TABLE CT
LEFT JOIN HIST_TABLE HT
ON CT.GROUP = HT.GROUP AND
CT.EMPID = HT.EMP_ID AND
HT.STAT = 'A'
WHERE
CT.GROUP = :1 AND
CT.EMP_ID = :2
GROUP BY
CT.GROUP,
CT.EMP_ID;
Note that GROUP is a reserved SQL keyword, and you might have to escape it with double quotes to make this query (or the one in your question) work on Oracle.

oracle procedure cursor query when case statement

CURSOR BULKUPDATE IS
SELECT SUM(B.ACCOUNT_BALANCE) AS ACCOUNT_BALANCE,C.CIF AS CIF_ID FROM _ACCOUNTS_STAGING2 B JOIN _RELATION_STAGING2 C
ON B.ACCOUNT_IDENTIFICATION_NUMBER = C.ACCOUNT_IDENTIFICATION_NUMBER AND B.SOURCEID=C.SOURCEID JOIN _CUSTOMER_STAGING2 A ON A.CIF=C.CIF AND A.SOURCEID=C.SOURCEID WHERE C.ROLE_ON_ACCOUNT IN
(Select Rollonaccount From _Roleaccount_Master Where Aggregatebalance='Y')
And upper(B.Scheme_Type) In (Select Scheme_Type From _Schema_Type_Master Where
Depository_Account = 'Y') Group By C.Cif;
Rec_Bulkupdate Bulkupdate%Rowtype;
I am using this query to sum account balances based on different cif and source. The question is I want to calculate four different types of sum on the basis of _Schema_Type_Master. For example I want to check now current_account='Y' instead of Depository_Account='Y'
_ACCOUNTS_STAGING2 B JOIN _RELATION_STAGING2 C
ON B.ACCOUNT_IDENTIFICATION_NUMBER = C.ACCOUNT_IDENTIFICATION_NUMBER AND B.SOURCEID=C.SOURCEID JOIN _CUSTOMER_STAGING2 A ON A.CIF=C.CIF AND A.SOURCEID=C.SOURCEID WHERE C.ROLE_ON_ACCOUNT IN
(Select Rollonaccount From _Roleaccount_Master Where Aggregatebalance='Y')
And upper(B.Scheme_Type) In (Select Scheme_Type From _Schema_Type_Master Where
current_account='Y') Group By C.Cif;
Rec_Bulkupdate Bulkupdate%Rowtype;
Is there any way or do I need to write four different cursors for that??
You can remove dipository_account='Y' and current_account='Y' and use case in select as -
SELECT SUM(CASE WHEN Depository_Account = 'Y' THEN B.ACCOUNT_BALANCE ELSE 0 END) AS DIPOSITORY_ACCOUNT_BALANCE,
SUM(CASE WHEN current_account = 'Y' THEN B.ACCOUNT_BALANCE ELSE 0 END) AS CURRENT_ACCOUNT_BALANCE
and then rest of your code. You will get two different columns for sum of Depository account and Current account.
And if filter for dipository_account='Y' and current_account='Y' is required, then use them in where condition with or operator :
AND (dipository_account='Y' or current_account='Y')

Why sub SQL in FROM area can't be null?

select col_1,
col_2
from tbl_a A,
tbl_b B,
( select col_1,col_2 from tbl_c where clo_c_1 <> '1' ) C
where A.col_1 = B.col_1
and A.col_2 = B.col_2
and (A.col_2 = B.col_2
or C.col_2 = A.col_2);
My environment is Oracle,when I run this SQL,if the sub SQL C hasn't got a result,then the entire SQL returns NULL.Whereas if C has a result(not null) which fits other condions,there could be a result.Would somebody explain why sub SQL at the from area need to be not NULL?Thanks very much.
You need to bring yourself into the 90s and start using standard joins:
select col_1,
col_2
from tbl_a A
inner join
tbl_b B
on A.col_1 = B.col_1
and A.col_2 = B.col_2
left join
( select col_1,col_2 from tbl_c where clo_c_1 <> '1' ) C
on
C.col_2 = A.col_2
As a guess. I'm not entirely sure what your join conditions should be but that's my first attempt.
This is expected behaviour. When you join two result sets, you only want to get results where the join criteria is satisfied. If the criteria are not satisfied, you should get no results.
If I run the query "get me all the employees older than 65, and get their departments", if there are no employees older than 65, you would not expect to get any results, even if there are some departments in the database.
SELECT emp.name, dept.dept_name
FROM emp
JOIN dept
ON (emp.dept_no = dept.dept_no)
WHERE emp.age > 65;
As the others said, if you actually want to get rows regardless of whether the subquery has any results, you need to use an outer join.

Using an alias in a select statement in a join query

I am very new at using Oracle (in the class now). I have a problem with the query I am trying to run. I have researched a lot of other answers on this site and none of them seem to apply directly to my problem so the solutions aren't working.
I need to find the total amount spent on lunches by each employee.
Show first_name, last_name, credit_limit, and total_price_spent in your results.
Order the answer by total_price_spent in descending order. Only show the employees who spent more than their credit limit.
I figured out how to do everything but the part about showing only the employees who spent more than their credit limit. I tried to use a select statement at the end but discovered that I can't use an alias in a select statement so I don't really know where to go from here. Any help would be appreciated. This is what I have so far.
select a.first_name, a.last_name, credit_limit, sum(c.quantity * d.price) as total_price_spent
from l_employees a join l_lunches b on a.employee_id = b.employee_id join l_lunch_items c on b.lunch_id = c.lunch_id join l_foods d on c.supplier_id = d.supplier_id and c.product_code = d.product_code
group by a.first_name, a.last_name, a.credit_limit
order by total_price_spent desc;
As Mike said : Add HAVING
select a.first_name, a.last_name, credit_limit, sum(c.quantity * d.price) as total_price_spent
from l_employees a join l_lunches b on a.employee_id = b.employee_id join l_lunch_items c on b.lunch_id = c.lunch_id join l_foods d on c.supplier_id = d.supplier_id and c.product_code = d.product_code
group by a.first_name, a.last_name, a.credit_limit
having sum(c.quantity * d.price) > credit_limit
order by total_price_spent desc;
I think what you're looking for is a HAVING clause. It's like a WHERE, but is used when you're using group by. You want to drop it in between the group by and order by. Something like 'HAVING total > a.credit_limit' should work. If using the alias 'total' doesn't work (haven't tested this), you might have to do 'sum(c.quantity * d.price)' again in the HAVING clause instead of using total, so HAVING sum(c.quantity * d.price) > a.credit_limit.

Oracle: using WHERE ROWNUM = 1

chaps and chapettes
Just a quick question. I need to return only one row from a stored proc., but no matter where I place the WHERE clause, I get errors. Can somebody take a look at the (cut-down due to sheer length) code and let me know where it should go, please?
SELECT **values**
INTO **variables**
FROM **table**
_WHERE ROWNUM = 1_
INNER JOIN **other table**
ON **join target**
ORDER BY **sort criteria**;
_WHERE ROWNUM = 1_
Thanks
I believe this is the way to structure rownum queries
SELECT * FROM
INTO **Variables * *
( SELECT * FROM X
WHERE Y
ORDER BY Z
)
WHERE ROWNUM = 1;
You were almost correct. You put the WHERE clause after the JOINs, but before the ORDER BY.
SELECT **values**
INTO **variables**
FROM **table**
INNER JOIN **other table**
ON **join target**
_WHERE ROWNUM = 1_
ORDER BY **sort criteria**;
However, this won't do what you might think - the ORDER BY is evaluated AFTER the where clause; which means this will just pick the first record it finds (that satisfies the join criteria), and will then sort that row (which obviously is a no-op).
The other answers (e.g. IvoTops') give ideas of how to get the first record according to the sort criteria.
SELECT **values**
INTO **variables**
FROM
( SELECT **values**
, ROW_MUMBER() OVER (ORDER BY **sort criteria**) AS rn
FROM **table**
INNER JOIN **other table**
ON **join target**
) tmp
WHERE rn = 1 ;
Check also this blog post: Oracle: ROW_NUMBER() vs ROWNUM
little bit late, but I got a similar problem and I solved it like this:
SELECT **values**
INTO **variables**
FROM **table**
WHERE **condition**
ORDER BY **sort criteria**
FETCH FIRST 1 ROW ONLY;
Regards

Resources