How to convert inline SQL queries to JOINS in SQL SERVER to reduce load time - sql

I need help in optimizing this SQL query.
In the main SELECT statement there are three columns which is dependent on the outer query result. This is why my query is taking a long time to return data. I have tried making left joins but this is not working properly.
Can anyone help me to resolve this issue?
SELECT
DISTINCT ou.OrganizationUserID AS StudentID,
ou.FirstName,
ou.LastName,
(
SELECT
STRING_AGG(
(ug.UG_Name),
','
)
FROM
Groups ug
INNER JOIN ApplicantUserGroup augm ON augm.AUGM_UserGroupID = ug.UG_ID
WHERE
augm.AUGM_OrganizationUserID = ou.OrganizationUserID
AND ug.UG_IsDeleted = 0
AND augm.AUGM_IsDeleted = 0
) AS UserGroups,
order1.OrderNumber AS OrderId -- UAT-2455
,
(
SELECT
STRING_AGG(
(CActe.CustomAttribute),
','
)
FROM
CustomAttributeCte CActe
WHERE
CActe.HierarchyNodeID = dpm.DPM_ID
AND CActe.OrganizationUserID = ps.OrganizationUserID
) AS CustomAttributes -- UAT-2455
,
(
SELECT
STRING_AGG(
(CActe.CustomAttributeID),
','
)
FROM
CustomAttributeCte CActe
WHERE
CActe.HierarchyNodeID = dpm.DPM_ID
AND CActe.OrganizationUserID = ps.OrganizationUserID
) AS CustomAttributeID
FROM
ApplicantData acd WITH (NOLOCK)
INNER JOIN ClientPackage ps WITH (NOLOCK) ON acd.ClientSubscriptionID = ps.ClientSubscriptionID
INNER JOIN [ClientOrder] order1 WITH (NOLOCK) ON order1.OrderID = ps.OrderID
AND order1.IsDeleted = 0
INNER JOIN OUser ou WITH (NOLOCK) ON ou.OrganizationUserID = ps.OrganizationUserID

It looks like this query can be simplified, and the dependent subqueries in your SELECT clause removed, Consider your second and third dependent subqueries. You can refactor them into one nondependent subquery with a LEFT JOIN. Using nondependent subqueries is more efficient because the query planner can run them just once, rather than once for each row.
You want two STRING_AGG() results from the same table. This subquery gives those two outputs for every possible combination of HierarchyNodeID and OrganizationUserID values. STRING_AGG() is an aggregate function like SUM() and so works nicely with GROUP BY.
SELECT HierarchyNodeID, OrganizationUserID,
STRING_AGG((CActe.CustomAttribute), ',') CustomAttributes -- UAT-2455,
STRING_AGG((CActe.CustomAttributeID), ',') CustomAttributeIDs -- UAT-2455
FROM CustomAttributeCte CActe
GROUP BY HierarchyNodeID, OrganizationUserID
You can run this subquery itself to convince yourself it works.
Now, we can LEFT JOIN that into your query. Like this. (For readability I took out the NOLOCKs and used JOIN: it means the same thing as INNER JOIN.)
SELECT DISTINCT
ou.OrganizationUserID AS StudentID,
ou.FirstName,
ou.LastName,
'tempvalue' AS UserGroups, -- shortened for testing
order1.OrderNumber AS OrderId, -- UAT-2455
uat2455.CustomAttributes, -- UAT-2455
uat2455.CustomAttributeIDs -- UAT-2455
FROM ApplicantData acd
JOIN ClientPackage ps
ON acd.ClientSubscriptionID = ps.ClientSubscriptionID
JOIN ClientOrder order1
ON order1.OrderID = ps.OrderID
AND order1.IsDeleted = 0
JOIN OUser ou
ON ou.OrganizationUserID = ps.OrganizationUserID
LEFT JOIN (
SELECT HierarchyNodeID, OrganizationUserID,
STRING_AGG((CActe.CustomAttribute), ',') CustomAttributes -- UAT-2455,
STRING_AGG((CActe.CustomAttributeID), ',') CustomAttributeIDs -- UAT-2455
FROM CustomAttributeCte CActe
GROUP BY HierarchyNodeID, OrganizationUserID
) uat2455
ON uat2455.HierarchyNodeID = dpm.DPM_ID
AND uat2455.OrganizationUserId = ps.OrganizationUserID
See how we collapsed your second and third dependent subqueries to just one, then used it as a virtual table with LEFT JOIN? We transformed the WHERE clauses from the dependent subqueries into an ON clause.
You can test this: run it with TOP(50) and eyeball the results.
When you're happy, the next step is to transform your first dependent subquery the same way.
Pro tip Don't use WITH (NOLOCK), ever, unless a database administration expert tells you to after looking at your specific query. If your query's purpose is a historical report and you don't care whether the most recent transactions in your database are represented exactly right, you can precede your query with this statement. It also allows the query to run while avoiding locks.
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Pro tip Be obsessive about formatting your queries for readability. You, your colleagues, and yourself a year from now must be able to read and reason about queries like this.

Related

How to do operations between a column and a subquery

I would like to know how I can do operations between a column and a subquery, what I want to do is add to the field Subtotal what was obtained in the subquery Impuestos, the following is the query that I am using for this case.
Select
RC.PURCHID;
LRC.VALUEMST as 'Subtotal',
isnull((
select sum((CONVERT(float, TD1.taxvalue)/100)*LRC1.VALUEMST ) as a
FROM TAXONITEM TOI1
inner join TAXDATA TD1 ON (TD1.TAXCODE = TOI1.TAXCODE and RC.DATAAREAID = TD1.DATAAREAID)
inner join TRANS LRC1 on (LRC1.VEND = RC.RECID)
WHERE TOI1.TAXITEMGROUP = PL.TAXITEMGROUP and RC.DATAAREAID = TOI1.DATAAREAID
), 0) Impuestos
from VEND RC
inner join VENDTABLE VTB on VTB.ACCOUNTNUM = RC.INVOICEACCOUNT
inner join TRANS LRC on (LRC.VEND = RC.RECID)
inner join PURCHLINE PL on (PL.LINENUMBER =LRC.LINENUM and PL.PURCHID =RC.PURCHID)
where year (RC.DELIVERYDATE) =2021 and RC.PURCHASETYPE =3 order by RC.PURCHID;
Hope someone can give me some guidance when doing operations with subqueries.
A few disjointed facts that may help:
When a SELECT statement returns only one row with one column, you can enclose that statement in parenthesis and use it as a plain value. In your case, let's say that select sum(......= TOI1.DATAAREAID returns 500. Then, your outer select's second column is equivalent to isnull(500,0)
You mention in your question "subquery Impuestos". Keep in mind that, although you indeed used a subquery as we mentioned earlier, by the time it was enclosed in parentheses it is not treated as a subquery (more accurately: derived table), but as a value. Thus, the "Impuestos" is only a column alias at this point
I dislike and avoid subqueries before the from, makes things much harder to read. Here is a solution with apply which will keep your code mostly intact:
Select
RC.PURCHID,
LRC.VALUEMST as 'Subtotal',
isnull(subquery1.a, 0) as Impuestos
from VEND RC
inner join VENDTABLE VTB on VTB.ACCOUNTNUM = RC.INVOICEACCOUNT
inner join TRANS LRC on (LRC.VEND = RC.RECID)
inner join PURCHLINE PL on (PL.LINENUMBER =LRC.LINENUM and PL.PURCHID =RC.PURCHID)
outer apply
(
select sum((CONVERT(float, TD1.taxvalue)/100)*LRC1.VALUEMST ) as a
FROM TAXONITEM TOI1
inner join TAXDATA TD1 ON (TD1.TAXCODE = TOI1.TAXCODE and RC.DATAAREAID = TD1.DATAAREAID)
inner join TRANS LRC1 on (LRC1.VEND = RC.RECID)
WHERE TOI1.TAXITEMGROUP = PL.TAXITEMGROUP and RC.DATAAREAID = TOI1.DATAAREAID
) as subquery1
where year (RC.DELIVERYDATE) =2021 and RC.PURCHASETYPE =3 order by RC.PURCHID;

Query performance: CTE using ROW_NUMBER() to select first row

We have three environments and when I run my SQL query in two of them just takes 30 or 38 seconds to run but in the other environment running is not completed and I should cancel it. Query is based on two parts, a CTE and a very simple select from a table, in both CTE and select I'm using the same table.
Could you please tell me why it takes so long time? how can I improve the query?
ALTER VIEW [fact].[vPurchase]
AS
WITH VKPL AS
(
SELECT *
FROM
(SELECT
iv.[Delivery_FK],
1 AS column2,
ROW_NUMBER() OVER(PARTITION BY [Delivery_FK] ORDER BY iv.UpdateDate) AS rk
FROM
[fact].[KRMFact] iv
LEFT JOIN
[dimension].[Product] pr ON iv.Product_FK =pr.Product_SK
LEFT JOIN
[dimension].[Delivery] le ON le.Delivery_FK = iv.Delivery_FK
WHERE
pr.Product_Key = '740') X
WHERE
rk = 1
)
SELECT
-- .... here are some columns
Delivery_FK,
Product_FK,
CAST(column2 AS VARCHAR) AS column2,
f.[UpdateDate] AS [Update date]
FROM
[fact].[KRMFact] f
LEFT JOIN
VKPL v ON f.Delivery_FK = v.Delivery_FK
This is guesswork.
I guess the environment where this query is slow is the one with lots of production data in it.
I guess some index on your KRMFact table will, maybe, help you. Here's how to figure out what you need: SQL Server Management Studio (SSMS) has a feature to show you a query's execution plan. Put your query (not simplified, please, the actual query) into SSMS, right click and choose "Include Actual Execution Plan." Then run the query. The execution plan display may recommend an index for you to create to get this query to run faster.
I guess you're trying to find rows with the earliest values of UpdateDate.
Your subquery
SELECT *
FROM
(SELECT
iv.[Delivery_FK],
1 AS column2,
ROW_NUMBER() OVER(PARTITION BY [Delivery_FK] ORDER BY iv.UpdateDate) AS rk
FROM
[fact].[KRMFact] iv
LEFT JOIN
[dimension].[Product] pr ON iv.Product_FK =pr.Product_SK
LEFT JOIN
[dimension].[Delivery] le ON le.Delivery_FK = iv.Delivery_FK
WHERE
pr.Product_Key = '740') X
WHERE
rk = 1
looks like it picks out the row with the earliest KRMFact.UpdateDate for each value of KRMFact.Delivery_FK. That's what the ROW_NUMBER() OVER... WHERE rk=1 language does.
If my guess about that is correct you can do that a different way, which may be more efficient.
SELECT *
FROM
(SELECT
iv.[Delivery_FK],
1 AS column2,
1 AS rk
FROM
[fact].[KRMFact] iv
JOIN ( SELECT Delivery_FK, MIN(UpdateDate) first_update
FROM [fact].[KRMFact]
GROUP BY Delivery_FK
) first_update ON iv.UpdateDate = first_update.first_update
LEFT JOIN
[dimension].[Product] pr ON iv.Product_FK =pr.Product_SK
LEFT JOIN
[dimension].[Delivery] le ON le.Delivery_FK = iv.Delivery_FK
WHERE
pr.Product_Key = '740') X
WHERE
rk = 1
You should probably try out the old and new versions of the subquery to determine whether they will yield the same results.
If you use this subquery query I suggest, this index will help make it run faster by optimizing the new sub-sub-query's MIN() ... GROUP BY operation.
CREATE INDEX x_KRMFact_Product_Update
ON [fact].[KRMFact]
([Product_FK],[UpdateDate])
By the way, WHERE pr.Product_Key = '740' turns your LEFT JOIN [dimension].[Product] operation into an ordinary inner JOIN.

SQL - Subtraction within a Common Table Expression,

------ SOLVED --------
Instead of trying to perform the subtraction within the scope of the CTE, I just had to place it into the sub query which was using this particular CTE, in the main select list.
The Problem was, `InnerOQLI.FreePlaceCount is not being recognized, since it hasn't been defined within the scope of the CTE and is only being used in the Exists statement.
------ PROBLEM ----------
This is the first time I've used CTE's, I've joined multiple CTE's together so that I can retrieve an overall total in a single column.
I need to perform a subtraction within one of the CTE's
I first wrote it like this
MyCount2
AS
(
SELECT DISTINCT
O.ID AS OrderID,
(
(
(SELECT SUM(InnerOC.[Count])
FROM Order InnerO
INNER JOIN SubOrder InnerSO ON InnerO.ID = InnerSO.OrderID
INNER JOIN OrderComponent InnerOC ON SO.ID = OC.SubOrderID
WHERE OC.OrderComponentTypeID IN (1,2,4,5)
AND EXISTS (SELECT * FROM OrderQuoteLineItem InnerOQLI
WHERE InnerOQLI.OrderQuoteLineItemTypeID = 9 AND Order.ID = InnerO.ID)
AND Inner0.ID = ).ID)
)
- --< Minus Here
OQLI.FreePlaceCount
) AS [SHPCommExpression2]
FROM Order O
INNER JOIN SubOrder SO ON O.ID = SO.OrderID
INNER JOIN OrderComponent OC ON SO.ID = OC.SubOrderID
INNER JOIN OrderQuoteLineItem OQLI ON SO.ID = 0QLI.SubOrderID
),
Without going into to much detail, this brings back incorrect data because of repeated rows in the main query. (I believe it cos of the same joins within the main query)
So I then wrote this
MyCount2
AS
(SELECT InnerO.ID AS OrderID
SUM(InnerOC.[Count]
- InnerOQLI.FreePlaceCount) --- Tried to place subtraction here ----
AS [SHPCommExpression12])
FROM Order InnerO
INNER JOIN SubOrder InnerSO ON InnerO.ID = InnerSO.OrderID
INNER JOIN OrderComponent InnerOC ON SO.ID = OC.SubOrderID
WHERE OC.OrderComponentTypeID IN (1,2,4,5)
AND EXISTS (SELECT * FROM OrderQuoteLineItem InnerOQLI
WHERE InnerOQLI.OrderQuoteLineItemTypeID = 9 AND Order.ID = InnerO.ID)
GROUP BY InnerO.ID)
),
You can see where I've attempted to perform the subtraction, but It doesn't recognize InnerOQLI, where I've tried to add it to perform the subtraction. I can't work out how to correct this, I realize that it cant fully recognize the InnerOQLI since it's in the Exists statement, Is there away around this? If anyone could help I'd appreciate it
Thanks
Instead of trying to perform the subtraction within the scope of the CTE, I just had to place it into the sub query which was using this particular CTE, in the main select list.
The Problem was, `InnerOQLI.FreePlaceCount is not being recognized, since it hasn't been defined within the scope of the CTE and is only being used in the Exists statement.

SQL UNIONs that use the WITH statement

I have to run this query on 20 databases to produce one unified report. I've done this before with a UNION. Is there any way to reuse the WITH statement for each sub-query? I am receiving an error saying that the previous statement must be terminated with a semicolon. Could there be a better way of doing this?
WITH
DUPS (DocNum)
AS (
SELECT DocNum
FROM Akron.dbo.PWZ3
INNER JOIN Akron.dbo.OPWZ T5 ON T5.IdNumber = PWZ3.IdEntry
WHERE T5.PmntDate = '3/10/2011'
GROUP BY DocNum
HAVING COUNT(1) > 1;
)
SELECT PWZ3.IdEntry, PWZ3.DocNum, 'ARK' + PWZ3.CardCode, QUOTENAME(CardName,'"'),
Convert(Decimal(10,2),PayAmount), Convert(Decimal(10,2),InvPayAmnt),
CONVERT(VARCHAR(10), T5.PmntDate,101), NumAtCard, PymMeth, ObjType
FROM Akron.dbo.PWZ3 PWZ3
INNER JOIN DELAWARE.dbo.OPWZ T5 ON T5.IdNumber = PWZ3.IdEntry
LEFT JOIN Dups ON DUPS.DocNum = PWZ3.DocNum
WHERE T5.PmntDate = '3/10/2011'
AND T5.Canceled = 'N'
AND Checked = 'Y'
AND Dups.DocNum is null
The WITH statement indicates that the query expressed within the AS clause is a Common Table Expression (CTE).
If you're asking if you can write more than one query that uses a CTE, then the answer is no. The closest approximation would be creating an inline table-valued function.
(The simple definition of an ordinary table-valued function versus an inline table-valued function is that the inline version is defined only as a query, much like a CTE; you can't do any procedural operations within the function, including declaring/assigning variables).
The following works for me:
WITH stuff AS (
... some select
)
SELECT some_col
FROM some_table
JOIN stuff ON ...
UNION ALL
SELECT other_col
FROM other_table
JOIN stuff ON ...
Tested on PostgreSQL and Oracle

ORA-22813 error with SQL complex query

I have a big SELECT statement which has many nested selects in it. When I run it, it gives me an ORA-22813 error:
Ora-22813:- The Collection value from one of the inner sub queries has exceeded the system limits and hence this error.
I have given below some of the nested selects which return huge data.
---The 1st select returns the most data.
Can I handle and process the huge data returned by the INNER SELECTs into the tables in any alternate way so that there is no error of memory less, sort size less.
get, any other way so that the QUERY successfully processes without error.
/*****************************************BEGIN
LEFT OUTER JOIN
( SELECT *
FROM STUDENT_COURSE stu_c
LEFT OUTER JOIN STUDENT_history ch on stu_c.course_id = ch.ch_course_id
LEFT OUTER JOIN STUDENT_master stu_mca on ch.course_history_id = stu_mca.item_id
) stu_c ON stu_c.HISTORY_ID = toa.ACTIVITY_ID ----->This table is joined earlier
LEFT OUTER JOIN
(SELECT c_e.EV_ID, c_e.EV_NAME, ma.item_id, ma.cata_id
FROM EVENTS c_e LEFT OUTER
JOIN COURSE_master ma on c_e.event_Id = ma.item_id ) c_e ON c_e.EVENT_ID = toa.ACTIVITY_ID
After these selects---we have GROUP_BYs to further sort.
---I have checked that if I put a extra limit qualification
like where rownum <30,<20 in each of these SELECTs it works fine.
Full query
SELECT * FROM (SELECT
mcat.CATALOG_ITEM_ID,
mcat.CATALOG_ITEM_NAME ,
mcat.DESCRIPTION,
mcat.CATALOG_ITEM_TYPE,
mcat.DELIVERY_METHOD,
XMLElement("TRAINING_PLAN",XMLAttributes( TP.TPLAN_ID as "id" ),
XMLELEMENT("COMPLETE_QUANTITY", TP.COMPLETE_QUANTITY),
XMLELEMENT("COMPLETE_UNIT", TP.COMPLETE_UNIT),
XMLElement("TOTAL_CREDITS", TP.numberOfCredits ),
XMLELEMENT("IS_CREDIT_BASED", TP.IS_CREDIT_BASED),
XMLELEMENT("IS_FOR_CERT", TP.IS_FOR_CERT),
XMLELEMENT("ACCREDIT_ORG_NAME", TP.ACCRED_ORG_NAME),
XMLELEMENT("ACCREDIT_ORG_ID", TP.accredit_org_id ),
XMLElement("OBJECTIVE_LIST", TP.OBJECTIVE_LIST )
).extract('/').getClobVal() AS PLAN_LIST
FROM
student_master_catalog mcat
INNER JOIN
(SELECT stu_tp.TPLAN_ID,
stu_tp.COMPLETE_QUANTITY,
stu_tp.COMPLETE_UNIT,
stu_tp.TPLAN_XML_DATA.extract('//numberOfCredits/text()').getStringVal() as numberOfCredits,
stu_tp.IS_CREDIT_BASED,
stu_tp.IS_FOR_CERT,
stu_oa.ACCRED_ORG_NAME,
stu_tp.TPLAN_XML_DATA.extract('//accreditingOrg/text()').getStringVal() as accredit_org_id,
objective_list.OBJECTIVE_LIST
FROM
student_training_catalog stu_tp
LEFT OUTER JOIN
stu_accrediting_org stu_oa on stu_tp.TPLAN_XML_DATA.extract('//accreditingOrg/text()').getStringVal() = stu_oa.ACCRED_ORG_ID
INNER JOIN
(SELECT *
FROM
(SELECT
stu_tpo.TPLAN_ID AS OBJECTIVE_TPLAN_ID,
XMLAgg(
XMLElement("OBJECTIVE",
XMLElement("OBJECTIVE_ID",stu_tpo.T_OBJECTIVE_ID ),
XMLElement("OBJECTIVE_NAME",stu_to.T_OBJECTIVE_NAME ),
XMLElement("OBJECTIVE_REQUIRED_CREDITS_OR_ACTIVITIES",stu_tpo.REQUIRED_CREDITS ),
XMLElement("ITEM_ORDER", stu_tpo.ITEM_ORDER ),
XMLElement("ACTIVITY_LIST", activity_list.ACTIVITY_LIST )
)
) as OBJECTIVE_LIST
FROM
stu_TP_OBJECTIVE stu_tpo
INNER JOIN
stu_TRAINING_OBJECTIVE stu_to ON stu_tpo.T_OBJECTIVE_ID = stu_to.T_OBJECTIVE_ID
INNER JOIN
(SELECT *
FROM
(SELECT stu_toa.T_OBJECTIVE_ID AS ACTIVITY_TOBJ_ID, XMLAgg(
XMLElement("ACTIVITY",
XMLElement("ACTIVITY_ID",stu_toa.ACTIVITY_ID ),
XMLElement("CATALOG_ID",COALESCE(stu_c.CATALOG_ID, COALESCE( stu_e.CATALOG_ID, stu_t.CATALOG_ID ) ) ),
XMLElement("CATALOG_ITEM_ID",COALESCE(stu_c.CATALOG_ITEM_ID, COALESCE( stu_e.CATALOG_ITEM_ID, stu_t.CATALOG_ITEM_ID ) ) ),
XMLElement("DELIVERY_METHOD",COALESCE(stu_c.DELIVERY_METHOD, COALESCE( stu_e.DELIVERY_METHOD, stu_t.DELIVERY_METHOD ) ) ),
XMLElement("ACTIVITY_NAME",COALESCE(stu_c.COURSE_NAME, COALESCE( stu_e.EVENT_NAME, stu_t.TEST_NAME ) ) ),
XMLElement("ACTIVITY_TYPE",initcap( stu_toa.ACTIVITY_TYPE ) ),
XMLElement("IS_REQUIRED",stu_toa.IS_REQUIRED ),
XMLElement("IS_PREFERRED",stu_toa.IS_PREFERRED ),
XMLElement("NUMBER_OF_CREDITS",stu_lac.CREDIT_HOURS),
XMLElement("ITEM_ORDER", stu_toa.ITEM_ORDER )
)) as ACTIVITY_LIST
FROM stu_TRAIN_OBJ_ACTIVITY stu_toa
LEFT OUTER JOIN
(
SELECT distinct lac.LEARNING_ACTIVITY_ID, lac.CREDIT_HOURS
FROM student_training_catalog tp
INNER JOIN stu_TP_OBJECTIVE tpo on tp.TPLAN_ID = tpo.TPLAN_ID
INNER JOIN stu_TRAIN_OBJ_ACTIVITY toa on tpo.T_OBJECTIVE_ID = toa.T_OBJECTIVE_ID
INNER JOIN stu_LEARNINGACTIVITY_CREDITS lac on lac.LEARNING_ACTIVITY_ID = toa.ACTIVITY_ID and tp.TPLAN_XML_DATA.extract ('//accreditingOrg/text()').getStringVal() = lac.ACC_ORG_ID
where tp.tplan_id ='*************'
) stu_lac ON stu_lac.LEARNING_ACTIVITY_ID = stu_toa.ACTIVITY_ID ------>This Select returns correct no. of rows
I want to join the below nested SELECTs with stu_toa.ACTIVITY_ID. This would solve my issues.
This below SELECT inside the LEFT OUTER JOIN is the Problem. it returns too much because 3 tables are joined directly without any value qualification.
LEFT OUTER JOIN
( SELECT ch.COURSE_HISTORY_ID, stu_c.COURSE_NAME, mca.catalog_item_id, mca.catalog_id, mca.delivery_method
FROM stu_COURSE stu_c
LEFT OUTER JOIN stu_course_history ch on stu_c.course_id = ch.ch_course_id -
--If I can qualify here with ch.ch_course_id = stu_toa.ACTIVITY_ID (stu_toa.ACTIVITY_ID from the above select with correct no. of rows )
--Here, I get errors because I can't access outside values inside a left outer join
LEFT OUTER JOIN student_master_catalog mca on ch.course_history_id = mca.catalog_item_id
) stu_c ON stu_c.COURSE_HISTORY_ID = stu_toa.ACTIVITY_ID
LEFT OUTER JOIN
(SELECT stu_e.EVENT_ID, stu_e.EVENT_NAME, mca.catalog_item_id, mca.catalog_id, mca.delivery_method FROM stu_EVENTS stu_e LEFT OUTER JOIN student_master_catalog mca on stu_e.event_Id = mca.catalog_item_id ) stu_e ON stu_e.EVENT_ID = stu_toa.ACTIVITY_ID
LEFT OUTER JOIN
(SELECT stu_t.TEST_HISTORY_ID, stu_t.TEST_NAME, mca.catalog_item_id, mca.catalog_id, mca.delivery_method FROM stu_TEST_HISTORY stu_t LEFT OUTER JOIN student_master_catalog mca on stu_t.test_history_id = mca.catalog_item_id) stu_t ON stu_t.test_history_id = stu_toa.ACTIVITY_ID
GROUP BY stu_toa.T_OBJECTIVE_ID) ) activity_list ON activity_list.ACTIVITY_TOBJ_ID = stu_tpo.T_OBJECTIVE_ID
GROUP BY stu_tpo.TPLAN_ID) ) objective_list ON objective_list.OBJECTIVE_TPLAN_ID = stu_tp.TPLAN_ID
)TP ON TP.TPLAN_ID = mcat.CATALOG_ITEM_ID
WHERE
mcat.CATALOG_ITEM_ID = '*****************' and mcat.CATALOG_ORG_ID = '********')
Please post the DDLs, approximate sizes (relative to each other), and the complete query, rather than just an excerpt.
Some quick hits that may or may not solve your problem (for better help, I need better information) --
Are you sure you mean OUTER join? Outer joining students to courses means students who are not taking any courses will still be around. Is that the desired behaviour?
Don't select * if you only want a limited subset of the columns. Enumerate the exact columns you need. The rest might not seem like much on a row-by-row basis, but when you multiply by the total number of rows you have, this sort of thing can mean the difference between in-memory sorts and spilling to disk.
How many rows of data are you looking at? there are times when separate queries with programmatic aggregation can work better. Someone with more knowledge of Oracle query optimization may be able to help, also, tweaking the settings could help here too...
I've had instances where a sproc was being called that aggregated data from more than one source took exponentially longer than two calls in the app, and putting it together in memory.
Post DDL of your tables and exact plan of the query.
Meanwhile, try increasing pga_aggregate_target, sort_area_size and hash_area_size