Oracle update with subquery - performanceissue - oracle

I'm having some trouble with an update statement in my oracle database.
The query takes to much time and the temp tablespace is running out of space, but it provides the correct data.
I tried to convert the subqueries to joins but i couldn't figure out how to do it correctly.
If someone knows how to improve the statement or how to convert it into a join i would be really grateful.
UPDATE table1 t1
SET t1.inxdc = (SELECT sda_x
FROM table2 t2
WHERE t1.c1 = t2.c1
AND t1.c2 = t2.c2
AND t1.c3 = t2.c3
AND t1.c4 = t2.c4
AND t1.c5 = t2.c5
AND t1.c6 = t2.c6
AND t2.ident = 'K_SDA_W'
AND rownum=1)
WHERE EXISTS
(SELECT 1
FROM table2 t2
WHERE t1.c1 = t2.c1
AND t1.c2 = t2.c2
AND t1.c3 = t2.c3
AND t1.c4 = t2.c4
AND t1.c5 = t2.c5
AND t1.c6 = t2.c6
AND t2.ident = 'K_SDA_W');
edit1:
Some information for the tables
table1 PKs = c1,c2,c3,c4,c5,c6
table2 PKs = ident,c4,c5,c6, and 3 others not mentioned in the statement (c7,c8,c9)
index: besides the PKs only on table2 c1
table1 data: 12466 rows
table2 data: 194827 rows
edit2:
Execution Plan
--------------------------------------------------------------
| Id | Operation | Name |
--------------------------------------------------------------
| 0 | UPDATE STATEMENT | |
| 1 | UPDATE | table1 |
| 2 | NESTED LOOPS SEMI | |
| 3 | TABLE ACCESS FULL | table1 |
| 4 | TABLE ACCESS BY INDEX ROWID| table2 |
| 5 | INDEX RANGE SCAN | t2.c1 |
| 6 | COUNT STOPKEY | |
| 7 | TABLE ACCESS BY INDEX ROWID| table2 |
| 8 | INDEX RANGE SCAN | t2.PK |
--------------------------------------------------------------

There are very few rows in Table1, just drop the WHERE clause in this particular situation and add NVL to the value returned from subquery:
UPDATE table1 t1
SET t1.inxdc = NVL((SELECT sda_x
FROM table2 t2
WHERE t1.c1 = t2.c1
AND t1.c2 = t2.c2
AND t1.c3 = t2.c3
AND t1.c4 = t2.c4
AND t1.c5 = t2.c5
AND t1.c6 = t2.c6
AND t2.ident = 'K_SDA_W'
AND rownum=1), t1.inxdc);
In general your update should be quick, have you checked performance of the subquery? Check if an index (and which one) is used on table2 for the subquery (best, show us the exection plan).

I think table t2 shoulkd have an index on c1,c2,c3, c4,c5,c6,ident
In this case the update of t1 should be really faster.

Related

Pull conditional statement results based on multiple table joins

I have 3 tables to join to get the output in the below format.
My table 1 is like:
--------------------------------------------------------
T1_ID1 | T1_ID2 | NAME
--------------------------------------------------------
123 | T11231 | TestName11
123 | T11232 | TestName12
234 | T1234 | TestName13
345 | T1345 | TestName14
--------------------------------------------------------
My table 2 is like:
--------------------------------------------------------
T2_ID1 | T2_ID2 | NAME
--------------------------------------------------------
T11231 | T21231 | TestName21
T11232 | T21232 | TestName21
T1234 | T2234 | TestName22
--------------------------------------------------------
My table 3 is like:
----------------------------------------------------------
T3_ID1 | TYPE | REF
----------------------------------------------------------
T21231 | 1 | 123456
T21232 | 2 | 1234#test.com
T2234 | 2 | 123#test.com
----------------------------------------------------------
My desired output is:
------------------------------------------------------
T1_ID1 | PHONE | EMAIL
------------------------------------------------------
123 | 123456 | 1234#test.com
234 | | 123#test.com
345 | |
------------------------------------------------------
Requirements:
T1_ID2 of table 1 left joins with T2_ID1 of table 2.
T2_ID2 of table 2 left joins with T3_ID1 of table 3.
TYPE of table 3 specifies 1 if the value is phone and specified 2 if value is email.
My output should contain T1_ID1 of table 1 and its corresponding value of REF in table 3, with the REF in the same row.
That is, in this case, T1_ID1 with value 123 has both phone and email. So, it is displayed in the same row in output.
If phone alone is available for corresponding value of T1_ID1, then phone should be populated in the result with email as null and vice versa.
If neither phone nor email is available, nothing should be populated.
I had tried the below SQLs but in vain. Where am I missing? Please extend your help.
Option 1:
SELECT DISTINCT
t1.t1_id1,
t3.ref
|| (
CASE
WHEN t3.type = 1 THEN
1
ELSE
0
END
) phone,
t3.ref
|| (
CASE
WHEN t3.type = 2 THEN
1
ELSE
0
END
) email
FROM
table1 t1
LEFT JOIN table2 t2 ON t1.t1_id2 = t2.t2_id1
LEFT JOIN table3 t3 ON t2.t2_id2 = t3.t3_id1;
Option 2:
SELECT DISTINCT
t1.t1_id1,
t3.ref,
(
CASE
WHEN t3.type = 1 THEN
1
ELSE
0
END
) phone,
t3.ref,
(
CASE
WHEN t3.type = 2 THEN
1
ELSE
0
END
) email
FROM
table1 t1
LEFT JOIN table2 t2 ON t1.t1_id2 = t2.t2_id1
LEFT JOIN table3 t3 ON t2.t2_id2 = t3.t3_id1;
Option 3:
SELECT DISTINCT
t1.t1_id1,
(
CASE
WHEN t3.type = 1 THEN
1
ELSE
0
END
) phone,
(
CASE
WHEN t3.type = 2 THEN
1
ELSE
0
END
) email
FROM
table1 t1
LEFT JOIN table2 t2 ON t1.t1_id2 = t2.t2_id1
LEFT JOIN table3 t3 ON t2.t2_id2 = t3.t3_id1;
select t1_id1, max(t3.ref )phone, max(t33.ref) email
from table1
left outer join
table2 on t1_id2=t2_id1
left outer join table3 t3 on t3.t3_id1=t2_id2 and t3.type=1
left outer join table3 t33 on t33.t3_id1=t2_id2 and t33.type=2
group by t1_id1
if you have maximum one phone and one email in table3 for each t2_id2 entry in table2.

Oracle SQL or PL/SQL correlated update column values Y/N

Am trying to update single column with multiple values (Y/N) by correlated update and getting error could use:
ORA-01427: single-row sub-query returns more than one row.
I have two tables.
REQUESTS_TABLE
+-------------+------+
| CUSTOMER_ID | FLAG |
+-------------+------+
| 200 | |
| 900 | |
+-------------+------+
CREDITS_TABLE
+-------------+---------------+
| CUSTOMER_ID | CUSTOMER_NAME |
+-------------+---------------+
| 100 | John |
| 200 | Smith |
| 300 | Mary |
| 400 | David |
| 500 | Jake |
+-------------+---------------+
If customer_id from requests table exists in credits table then I want to update "FLAG" column in requests table as 'Y'. If not found then 'N'. Below is the output am trying to get:
OUTPUT :
REQUESTS_TABLE
+-------------+------------+
| CUSTOMER_ID | FOUND_FLAG |
+-------------+------------+
| 200 | Y |
| 900 | N |
+-------------+------------+
Below is the query that I tried which is resulting in error:
UPDATE requests_table r
SET ( flag ) = (SELECT CASE
WHEN c.customer_id IS NOT NULL THEN 'Y'
ELSE 'N'
END
FROM credits_table c
WHERE c.customer_id = r.customer_id)
WHERE EXISTS (SELECT *
FROM credits_table c
WHERE c.customer_id = r.customer_id)
When I searched online for the help I found queries that updates multiple columns but not multiple values in single column. Hence seeking help here.
Appreciate any help.
Thanks,
Richa
You could use:
UPDATE requests_table
SET found_flag = CASE WHEN EXISTS(SELECT c.customer_id FROM credits_table c
WHERE c.customer_id = requests_table.customer_id)
THEN 'Y'
ELSE 'N'
END
You might be better off doing this as two statements (ideally in a single transaction):
UPDATE requests_table
SET found_flag = 'N';
UPDATE requests_table
SET found_flag = 'Y'
WHERE EXISTS
(SELECT *
FROM credits_table c
WHERE c.customer_id = requests_table.customer_id);
This is likely to perform better than #LukaszSzozda's answer, as it avoids the nested sub-query which would need to be run for every row in the requests_table.
Another option for doing this in a single query, which should be of a comparable speed is to use merge (I tend to use merge over update for all but the simplest updates).
MERGE INTO requests_table
USING (SELECT rt.customer_id,
CASE WHEN c.customer_id IS NULL THEN 'N' ELSE 'Y' END
AS found_flag
FROM requests_table rt
LEFT JOIN credits_table c
ON c.customer_id = rt.customer_id) src
ON (requests_table.customer_id = src.customer_id)
WHEN MATCHED THEN
UPDATE SET found_flag = src.found_flag

Frequent refresh of materialized view in Oracle

We are optimizing performance and want to create the materialized view (several millions of records) built on joins of handful of tables. This view will be used in order to show users documents in folders with delay not more than several (3-5) seconds.
I suppose it must be out-of-place MV with refresh interval several seconds.
Is it acceptable solution from database point of view?
The view will be something like this:
SELECT *
FROM documents this_
LEFT OUTER JOIN account_statements this_1_
ON this_.Id = this_1_.FK_Document
LEFT OUTER JOIN contracts this_2_ ON this_.Id = this_2_.FK_Document
LEFT OUTER JOIN pension_agreements this_3_
ON this_.Id = this_3_.FK_Contract
LEFT OUTER JOIN dead this_4_ ON this_.Id = this_4_.FK_Document
LEFT OUTER JOIN pay_orders this_5_ ON this_.Id = this_5_.FK_Document
LEFT OUTER JOIN pay_registers this_6_
ON this_.Id = this_6_.FK_Document
LEFT OUTER JOIN pocards this_7_ ON this_.Id = this_7_.FK_Document
LEFT OUTER JOIN ransom_agreements this_8_
ON this_.Id = this_8_.FK_Document
LEFT OUTER JOIN successor_statements this_9_
ON this_.Id = this_9_.FK_Document
INNER JOIN document_treenodes treenodes14_
ON this_.Id = treenodes14_.fk_document
INNER JOIN treenodes treenode2_
ON treenodes14_.fk_treenode = treenode2_.Id
LEFT OUTER JOIN registration_cards regcard1_
ON this_.fk_registration_card = regcard1_.Id
LEFT OUTER JOIN employees todirectem12_
ON regcard1_.to_direct = todirectem12_.Id
LEFT OUTER JOIN REG_CARD_STATUSES regcardsta11_
ON regcard1_.status = regcardsta11_.Id
LEFT OUTER JOIN filestorages filestorag10_
ON this_.fk_file = filestorag10_.Id
LEFT OUTER JOIN actions holdaction4_
ON this_.fk_hold = holdaction4_.Id
LEFT OUTER JOIN employees holdemploy5_
ON holdaction4_.fk_operator = holdemploy5_.Id
LEFT OUTER JOIN actions doneaction6_
ON this_.fk_done = doneaction6_.Id
LEFT OUTER JOIN employees doneemploy7_
ON doneaction6_.fk_operator = doneemploy7_.Id
LEFT OUTER JOIN actions signaction8_
ON this_.fk_signed = signaction8_.Id
LEFT OUTER JOIN employees signemploy9_
ON signaction8_.fk_operator = signemploy9_.Id
LEFT OUTER JOIN actions scanaction3_
ON this_.fk_scan = scanaction3_.Id
UPDATE
The bottleneck is in the following:
SELECT *
FROM documents this_
INNER JOIN document_treenodes treenodes14_ ON this_.Id = treenodes14_.fk_document
INNER JOIN treenodes treenode2_ ON treenodes14_.fk_treenode = treenode2_.Id
LEFT OUTER JOIN registration_cards regcard1_ ON this_.fk_registration_card = regcard1_.Id
WHERE (
regcard1_.status IS NULL OR
(
NOT (
regcard1_.status = 3 /* :p0 */)
AND
NOT (
regcard1_.status = 4 /* :p1 */)
)
)
AND
this_.fk_deleted IS NULL AND
(
this_.isdelete IS NULL OR
this_.isdelete = 0 /* :p2 */)
AND
treenode2_.Id = 1235 /* :p3 */ AND
this_.fk_done IS NULL AND
(
regcard1_.status IS NULL OR
NOT (
regcard1_.status = 1 /* :p4 */)
)
ORDER BY this_.Id DESC
OFFSET 0 ROWS
FETCH FIRST 50 /* :p5 */ ROWS ONLY
treenode2_.Id = 1235 /* :p3 */ AND
this_.fk_done IS NULL AND
(
regcard1_.status IS NULL OR
NOT (
regcard1_.status = 1 /* :p4 */)
)
ORDER BY this_.Id DESC
OFFSET 0 ROWS
FETCH FIRST 50 /* :p5 */ ROWS ONLY
And the plan is:
Plan hash value: 3579815467
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 105K| 85M| | 50518 (1)| 00:00:04 |
|* 1 | VIEW | | 105K| 85M| | 50518 (1)| 00:00:04 |
|* 2 | WINDOW SORT PUSHED RANK | | 105K| 13M| 14M| 50518 (1)| 00:00:04 |
|* 3 | HASH JOIN RIGHT OUTER | | 105K| 13M| | 48503 (1)| 00:00:04 |
| 4 | INDEX FULL SCAN | REG_CARD_STATUSES_PK | 4 | 12 | | 1 (0)| 00:00:01 |
|* 5 | FILTER | | | | | | |
|* 6 | HASH JOIN RIGHT OUTER | | 105K| 13M| 4048K| 48502 (1)| 00:00:04 |
| 7 | TABLE ACCESS FULL | REGISTRATION_CARDS | 84317 | 3046K| | 171 (2)| 00:00:01 |
|* 8 | HASH JOIN | | 183K| 17M| 3936K| 47339 (1)| 00:00:04 |
|* 9 | INDEX FAST FULL SCAN| DOCUMENT_TREENODE_PK | 183K| 1788K| | 1872 (2)| 00:00:01 |
|* 10 | TABLE ACCESS FULL | DOCUMENTS | 5064K| 425M| | 24635 (2)| 00:00:02 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("from$_subquery$_010"."rowlimit_$$_rownumber"<=0+50 AND
"from$_subquery$_010"."rowlimit_$$_rownumber">0)
2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("THIS_"."ID") DESC )<=0+50)
3 - access("REGCARD1_"."STATUS"="REGCARDSTA11_"."ID"(+))
5 - filter(("REGCARD1_"."STATUS" IS NULL OR "REGCARD1_"."STATUS"<>3 AND
"REGCARD1_"."STATUS"<>4) AND ("REGCARD1_"."STATUS" IS NULL OR "REGCARD1_"."STATUS"<>1))
6 - access("THIS_"."FK_REGISTRATION_CARD"="REGCARD1_"."ID"(+))
8 - access("THIS_"."ID"="TREENODES14_"."FK_DOCUMENT")
9 - filter("TREENODES14_"."FK_TREENODE"=1235)
10 - filter("THIS_"."FK_DONE" IS NULL AND ("THIS_"."ISDELETE"=0 OR "THIS_"."ISDELETE" IS NULL)
AND "THIS_"."FK_DELETED" IS NULL)
I took the liberty of 'reformatting' your query (BTW: I think there is a bit of a copy-paste error in there, some parts look doubled near the end)
The 'layout' will have zero effect on the actual execution times, but it makes it easier for me to understand what you're doing (simply because I'm used to my own style, I'm not claiming it's better, it's simply what I am used to)
Anyway, if I understood correctly and didn't mess up the brackets, then this should be equivalent to your query:
SELECT *
FROM documents this_
INNER JOIN document_treenodes treenodes14_
ON treenodes14_.fk_document = this_.Id
INNER JOIN treenodes treenode2_
ON treenode2_.Id = treenodes14_.fk_treenode
AND treenode2_.Id = 1235 /* :p3 */
LEFT OUTER JOIN registration_cards regcard1_
ON regcard1_.Id = this_.fk_registration_card
WHERE this_.fk_deleted IS NULL
AND this_.fk_done IS NULL
AND (
this_.isdelete IS NULL OR this_.isdelete = 0 /* :p2 */
)
AND (
regcard1_.status IS NULL OR regcard1_.status NOT IN (3 /* :p0 */, 4 /* :p1 */, 1 /* :p4 */)
)
ORDER BY this_.Id DESC
OFFSET 0 ROWS
FETCH FIRST 50 /* :p5 */ ROWS ONLY
I doubt this change will make much difference, but it might change the way the system approaches steps 8 & 9 in the current plan. Worth a try =)
Anyway, what I 'learned' from the query is that you seem to want all records that have no matching [registration_cards] record, but if there are, then they should not have status 3, 4 or 1 (:p0, :p1, :p4 respectively).
=> wouldn't this be equivalent to saying that you want all [documents] records for which there is no matching [registration_cards] record that has status 3, 4 or 1?
SELECT *
FROM documents this_
INNER JOIN document_treenodes treenodes14_
ON treenodes14_.fk_document = this_.Id
INNER JOIN treenodes treenode2_
ON treenode2_.Id = treenodes14_.fk_treenode
AND treenode2_.Id = 1235 /* :p3 */
WHERE this_.fk_deleted IS NULL
AND this_.fk_done IS NULL
AND (
this_.isdelete IS NULL OR this_.isdelete = 0 /* :p2 */
)
AND NOT EXISTS ( SELECT *
FROM registration_cards regcard1_
WHERE regcard1_.Id = this_.fk_registration_card
AND regcard1_.status IN (3 /* :p0 */, 4 /* :p1 */, 1 /* :p4 */) )
ORDER BY this_.Id DESC
OFFSET 0 ROWS
FETCH FIRST 50 /* :p5 */ ROWS ONLY
Assuming [registration_card].Id is the PK of the table, or there is a covering index on the status and Id field, this might be slightly faster. Like I said before, I'm under the impression most of the time is lost in sorting the result-set but then again I might be totally misinterpreting the explain plan. Googling around actually seems to inform me that the explain plan is but guesswork and not 'the real deal'.... sigh, at times I really pity you poor Oracle users =P

Retrieve from Oracle db key value pair

I need to retrieve 3 values with different key from a key value pair table.
My database schema as follows. I need to reach to table3 from table1 by taking the E_SUBID and then joining the table2 with E_SUBID. Once table1 and table2 are joined I need take to take E_CID from table2 to join it with table2 E_CID to get the "Attr_Value" keeping E_CID as a criteria.
Table1
------------------------
|E_SUBID| B_LocationID |
|1 100 |
|2 101 |
|3 102 |
Table2
-----------------
|E_CID | E_SUBID|
|10 1 |
|11 2 |
|12 3 |
Table3
---------------------------------
|E_CID | Attr_name | Attr_Value |
|10 Product Samsung |
|10 Model Smartphone |
|10 usage daily |
|11 Product Apple |
|11 Model Ipad |
|11 usage everyday |
|12 Model smartwatch |
I have been successful to join table1,table2 and table3 but I cannot get the required output which as follows
OUTPUT
|Product | Model | Usage |
Samsung Smartphone daily
Apple Ipad everyday
null smartwatch null
The query which joins table1, table2 and table3 as follows
select distinct t3.Attr_value as Product
from table1 t1, table2 t2, table3 t3
where t1.E_SUBID = t2.E_SUBID and
t2.E_CID = t3.E_CID and
t3.Attr_name=?????
order by Product;
Thank you for your time.
In a case like this, you can join to table3 as often as you need to for each attribute name you wish to display:
select
p.attr_value product,
m.attr_value "model", -- Quotes to escape reserved word
u.attr_value usage
from table1 t1
join table2 t2 on t1.e_subid = t2.e_subid
left outer join table3 p on t2.e_cid = p.e_cid and p.attr_name = 'Product'
left outer join table3 m on t2.e_cid = m.e_cid and m.attr_name = 'Model'
left outer join table3 u on t2.e_cid = u.e_cid and u.attr_name = 'Usage'
order by 1;
Edit
Based on the comment, by making table3 optional (outer join) the query should return all rows and whether or not a Model or Usage or Product has been defined.
Try as below ... Basically you are trying to transpose the rows to column in table3.
Select Product, "Model", Usage
From
(
Select
t1.E_SUBID,
t2.E_CID,
Max(Case when T3.Attr_name = 'Product' Then T3.Attr_Value else null end) Product,
max(Case when T3.Attr_name = 'Model' Then T3.Attr_Value else null end) Model,
max(Case when T3.Attr_name = 'Usage' Then T3.Attr_Value else null end) Usage
From Table1 t1,
Table2 t2,
Table3 t3
Where
t1.E_SUBID = t2.E_SUBID
and t2.E_CID = t3.E_CID
group by t1.t1.E_SUBID,t2.E_CID
);

Update query Oracle

I have table TB1 which has the following cols:
ID | date
---------------------
1 | 12-JUL-10
2 | 12-JUL-10
3 | 12-JUL-10
4 | 12-JUL-10
.
.
.
10000 | 12-JUL-10
table2
ID | date
---------------------
1 | 12-JAN-09
2 | 12-JUL-09
3 | 12-JUL-09
4 | 12-JUL-08
.
.
.
5800 | 12-JUL-08
How to update the table2's date which has similar ID as table1.
Thanks :)
In general
UPDATE table2 t2
SET date_col = (SELECT t1.date_col
FROM table1 t1
WHERE t1.id = t2.id)
WHERE EXISTS (
SELECT 1
FROM table1 t1
WHERE t1.id = t2.id )
If you can be guaranteed that every ID in table2 exists in table1 (or if you want the date_col set to NULL if there is no match), you can eliminate the WHERE EXISTS. But generally you only want to do an update if there is a matching record.
Then there is also using an inline view for the update. This is slightly trickier to get right because I think it requires a primary key to exist on both sides of the join otherwise it fails with an error.
update (
select
t1.id as t1_id,
t1.value as t1_date,
t2.id as t2_id,
t2.value as t2_date
from
table1 t1
join table2 t2 on (t1.id = t2.id)
)
set t2_date = t1_date

Resources