I would like to merge two same-type hash tables in Ocaml, so that the information of both of them gets stored in a single table. Imagine, something like:
type tabType = (string, variable) Hashtbl.t
let tabExample:tabType = Hashtbl.create 1000 in
let tab1 = do_stuff tabExample a true in
let tab2 = do_stuff tabExample a false in
let tabFinal = tab1#tab2
Any idea?
If tab1 can be overwritten and the entries in tab2 should replace the one in tab1,
this is just a fold:
let merge ~into:tab1 tab2 =
Hashtbl.fold (fun key elt () -> Hashtbl.replace tab1 key elt) tab2 ();
tab1
Related
I have update query which returns updated rows ID. Execution time of query is about 90 seconds. When i remove Returning clause, then execution time is 1ms.
Table update_table has 39000 rows.
Query updates 0 rows in this case. When updates 3 rows- execution time is same.
DECLARE
type intTable IS TABLE OF INTEGER;
idCol intTable;
BEGIN
UPDATE
update_table
SET
prop1 = 3, prop2 = NULL
WHERE EXISTS (
SELECT null FROM update_table f
INNER JOIN rel_table1 u ON f.ID= u.ID
INNER JOIN rel_table2 VP ON f.another_ID = VP.another_ID
WHERE (u.prop1 = 3)
AND VP.prop1 = 1
AND (u.prop2 = 75)
AND f.ID = update_table.ID
)
ReTURNING ID BULK COLLECT INTO idCol;
.
.
.
END;
Why returning clause slows down query?
A good part of using Oracle is knowing what is "supposed" to happen and what isn't.
Adding a RETURNING INTO clause is not "supposed" to make your update run more slowly. When something happens that isn't supposed to happen, check Oracle's support site to see whether it is a known bug.
In your case, it looks like you are encountering:
Bug 27131648 - SUB OPTIMAL PLAN ON UPDATE STATEMENT WITH RETURNING INTO
I am not sure if there is a patch, but there is a simple workaround: use the UNNEST hint. In your case, that would be:
UPDATE
update_table
SET
prop1 = 3, prop2 = NULL
WHERE EXISTS (
SELECT /*+ UNNEST */ null FROM update_table f
INNER JOIN rel_table1 u ON f.ID= u.ID
INNER JOIN rel_table2 VP ON f.another_ID = VP.another_ID
WHERE (u.prop1 = 3)
AND VP.prop1 = 1
AND (u.prop2 = 75)
AND f.ID = update_table.ID
)
ReTURNING ID BULK COLLECT INTO idCol;
I would recommend splitting it into two parts, first BULK COLLECT and next FORALL collected ID's, both extremely fast and you'll keep being able to further reference updated ID's from idCol.
DECLARE
type intTable IS TABLE OF INTEGER;
idCol intTable;
BEGIN
SELECT f.id
BULK COLLECT INTO idCol
FROM update_table f
INNER JOIN rel_table1 u ON f.ID= u.ID
INNER JOIN rel_table2 VP ON f.another_ID = VP.another_ID
WHERE (u.prop1 = 3)
AND VP.prop1 = 1
AND (u.prop2 = 75);
FORALL indx IN 1..idCol.COUNT
UPDATE update_table
SET prop1 = 3, prop2 = NULL
WHERE id = idCol(indx);
.
.
.
END;
I hope I helped!
I have a Oracle procedure which is updating below 10,000 records. if I run the normal SQL statement, it is returning the result immediately with in seconds(30).
same statment in procedure loop it is going to endlessly.
My loop statment below.
Note: data FIELD Data type is a clob not varchar2.
statment:
select
'LB_COPY_CHANGE-'||8 LAST_MODIFIED_BY,
rec.COR_ID_old,
rec.COR_ID_NEW,
replace(replace(replace(a.data,'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||','),''id':'||rec.COR_ID_OLD||',',''id':'||rec.COR_ID_NEW||','),'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||',') as data
from KPI_MET_FIELD_DATA a, CUSTOM_TEMP_TABLE_SESSION_1 rec
where A.cmf_fk_id in (145,146,147)
and TYPE_LB in (14,15,16)
and a.KDB_FK_ID in (
select distinct km.KDB_FK_ID
from KPI_MET_FIELD_DATA km , KPI_DET_BASE kp, KPI_REL_KPI_SCORECARD ksc, STR_DET_EMP_SCORECARD sc
where ksc.SDE_FK_ID=sc.SDE_PK_ID
and km.KDB_FK_ID = ksc.KDB_KPI_FK_ID
and km.is_deleted=0
and kp.kdb_pk_id = km.KDB_FK_ID
and kp.is_deleted=0
and km.cmf_fk_id in (145,146,147)
and sc.sdp_fk_id = 8)
and a.is_deleted=0
and (a.data like '%'||rec.COR_ID_OLD||'%');
FOR rec in (SELECT * FROM CUSTOM_TEMP_TABLE_SESSION where TYPE_LB in (14,15,16)) LOOP
update KPI_MET_FIELD_DATA
set LAST_MODIFIED_BY='LB_COPY_CHANGE-'||p2 ,
data = replace(replace(replace(data,'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||','),''id':'||rec.COR_ID_OLD||',',''id':'||rec.COR_ID_NEW||','),'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||',')
where cmf_fk_id in (145,146,147)
and KDB_FK_ID in (
select distinct km.KDB_FK_ID
from KPI_MET_FIELD_DATA km , KPI_DET_BASE kp, KPI_REL_KPI_SCORECARD ksc, STR_DET_EMP_SCORECARD sc
where ksc.SDE_FK_ID=sc.SDE_PK_ID
and km.KDB_FK_ID = ksc.KDB_KPI_FK_ID
and km.is_deleted=0
and kp.kdb_pk_id = km.KDB_FK_ID
and kp.is_deleted=0
and km.cmf_fk_id in (145,146,147)
and sc.sdp_fk_id = p2)
and is_deleted=0 ;
There are several weaknesses in your code.
WHERE KDB_FK_ID in (select distinct ... does not make any sense. There is no need to make DISTINCT for an IN () clause.
Use ANSI join syntax instead of old Oracle join syntax, it is less error-prone
But the main difference is, your loop does not contain join condition (a.data like '%'||rec.COR_ID_OLD||'%'), i.e. you update entire table KPI_MET_FIELD_DATA again and again for each row in CUSTOM_TEMP_TABLE_SESSION where TYPE_LB in (14,15,16)
Think of my two tables have the same columns. One column is the ID, and the other one is the text. Is it possible to implement the following pseudo code in PLSQL?
Compare each row (They will have the same ID)
If anything is different about them
Run a couple of queries: an Update, and an Insert
ElseIf they are the same
Do nothing
Else the row does not exist
So add the row to the table compared on
Is it easy to do this using PLSQL or should I create a standalone application to do do this logic.
As your table have the same columns, by using NATURAL JOIN you can easily check if two corresponding rows are identical -- without need to update your code if a column is added to your table.
In addition, using OUTER JOIN allow you to find the rows present in one table but not in the other.
So, you can use something like that to achieve your purpose:
for rec in (
SELECT T.ID ID1,
U.ID ID2,
V.EQ
FROM T
FULL OUTER JOIN U ON T.ID = U.ID
FULL OUTER JOIN (SELECT ID, 1 EQ FROM T NATURAL JOIN U) V ON U.ID = V.ID)
loop
if rec.id1 is null
then
-- row in U but not in T
elsif rec.id2 is null
then
-- row in T but not in U
elsif rec.eq is null
-- row present in both tables
-- but content mismatch
end if
end loop
Else the row does not exist
So add the row to the table compared on
Is this condition means that rows can be missed in both tables? If only in one, then:
insert into t1 (id, text)
select id, text
from t2
minus
select id, text
from t1;
If missed records can be in both tables, you need the same query that inserts into table t2 rows from t1.
If anything is different about them
If you need one action for any amount of different rows, then use something like this:
select count(*)
into a
from t1, t2
where t1.id = t2.id and t1.text <> t2.text;
if a > 0 then
...
otherwise:
for i in (
select *
from t1, t2
where t1.id = t2.id and t1.text <> t2.text) loop
<do something>
end loop;
A 'merge' statement is what u needed.
Here is the syntax:
MERGE INTO TARGET_TABLE
USING SOURCE_TABLE
ON (CONDITION)
WHEN MATCHED THEN
UPDATE SET (DO YOUR UPDATES)
WHEN NOT MATCHED THEN
(INSERT YOUR NEW ROWS)
Google MERGE syntax for more about the statement.
Just use MINUS.
query_1
MINUS
query_2
In your case, if you really want to use PL/SQL, then select count into a local variable. Write a logic, if count > 0 then do other stuff.
How do I merge data from multiple rows in one table to a single column in a new table?
create table new_paragraphs
(
id NUMBER
paragraph CLOB
);
create table old_paragraphs
(
id
paragraph CLOB
);
merge into new_paragraphs a
using (select * from old_paragraphs) b
on (id = id)
when matched then
update set a.paragraph = a.paragraph || b.paragraph;
-- Results in error: unable to get a stable set of rows in the source tables
The above throws an exception.
It would work if id were a primary key in at least *old_paragraphs* (or if it were unique for each id found in *new_paragraph*)
Other than that, you want to use aliases in on (id = id) so that it reads on (a.id = b.id):
merge into new_paragraphs a
using (select * from old_paragraphs) b
on (a.id = b.id)
when matched then
update set a.paragraph = a.paragraph || b.paragraph;
Why are you doing a MERGE here? Why not a simple UPDATE (assuming ID is the primary key of both tables)
UPDATE new_paragraphs a
SET paragraph = (select a.paragraph || b.paragraph
from old_paragraphs b
where a.id = b.id)
WHERE EXISTS (SELECT 1
FROM old_paragraphs b
WHERE a.id = b.id)
I need the fields in 1 table contingent on 1 property matching rows in another table.
I can write this query in SQL with a subquery as such:
SELECT *
FROM Table1
WHERE Property1 IN
(
SELECT Property1
FROM Table2
WHERE Property0 = 1
)
But I read here that it's less complicated and just as easy to write with a join, which I did. However, so far I'm unable to return just Table1 as I'd like since I'm using a join, which if I'm not mistaken, requires me to create this anonymous type, as below. What I did here works (I created another object with the same properties of Table1 I need), but I can't help thinking there's a better way to do this.
Table1.Join(Table2, t1 => t1.Property1, t2 => t2.Property1, (t1, t2) => new
{
t1.Property1,
t1.Property2,
t1.Property3
})
.Select(ob => new UnnecessaryObject
{
Property1 = ob.Property1,
Property2 = ob.Property2,
Property3 = ob.Property3
}
I also tried just creating a Table1 in the .Select part, but I got an error about explicit construction not being allowed.
Just to clarify, I'd like to be able to return the IQueryable of type Table1, which it seems like I ought to be able to do without having to create UnnecessaryObject...but I'm still pretty new to LINQ, so I'd appreciate any help you can offer. Thanks in advance.
You could just do:
from t1 in table1
join t2 in table2 on t1.property1 equals t2.property1
select t1;
That would return a collection of table1 objects. This assumes from your example table1 is a collection of table1 objects and table2 is a collection of table2 objects.
The best translation of your original query I can come up with is:
from item in context.Table1
where context.Table2
.Where(x => x.Property0 == 0)
.Any(x => x.Property1 == item.Property1)
select item
This selects all items from Table1, where there's an item with matching Property1 and Property0 == 0 from Table2
It can also be solved with a join indeed. To get an efficient join, you need to have a relation between the two tables. Then you can do something like assuming the relation is called RelatedItems:
from item in context.Table1
join relatedItem in item.RelatedItems
on item.Property1 equals relatedItem.Property
where relatedItem.Property0 == 0
select item
This is equivalent to the SQL:
SELECT *
FROM Table1
JOIN Table2 ON Table1.Property1 = Table2.Property1
WHERE Table2.Property0 = 0