Efficient way to insert values in an Oracle SQL table - sql

I have two tables like
create table nodes_tbl as (
select 'a' as nodeid, 'some string' as dummy_string, 0 as subnetid from dual union all
select 'b', 'qwe', 0 from dual union all
select 'c', 'asd', 0 from dual union all
select 'd', 'zxc', 0 from dual union all
select 'e', 'rty', 0 from dual);
And
create table subnets as (
select 'a' as nodeid, 1 as subnetid from dual union all
select 'b', 2 from dual union all
select 'c', 2 from dual union all
select 'd', 3 from dual union all
select 'e', 4 as nodeid from dual);
With several millions of records a join works fast.
select n.NODEID, n.DUMMY_STRING, s.subnetid
from nodes_tbl n, subnets s where s.nodeid=n.nodeid
Writes are fast as well
create table test_tbl as n.NODEID, s.subnetid
from nodes_tbl n, subnets s where s.nodeid=n.nodeid --10M records in 2s.
However, when I try to update table and add values to the column the query is very slow
UPDATE nodes_tbl n
SET subnetid = (SELECT subnetid
FROM subnets s
WHERE s.nodeid = n.nodeid)
WHERE EXISTS (
SELECT subnetid FROM subnets s
WHERE s.nodeid = n.nodeid) --8 minutes for 100K records
Why is insert so much slower than a create table from a select statement?
What is the most efficient way to do this insert?
I know about create view option, but want to avoid it.

Try MERGE instead:
merge into nodes_tbl n
using (select s.subnetid, s.nodeid
from subnets s
) x
on (x.nodeid = n.nodeid)
when matched then update set
n.subnetid = x.subnetid;
Any improvement?
By the way, did you create index on NODEID column in both tables?

Related

Shouldn't this statement end with an error?

Consider the SELECT statement below:
SELECT 1, 'A'
UNION ALL
SELECT 2, 'B'
UNION ALL
SELECT 3, 'C';
The result is obvious:
1 'A'
2 'B'
3 'C'
I tried to store it as a separate table:
CREATE TABLE tmp AS
SELECT 1, 'A'
UNION ALL
SELECT 2, 'B'
UNION ALL
SELECT 3, 'C';
The contents of the tmp table are surprising:
1 'A'
2 'A'
3 'A'
Ok, that can be fixed providing explicit field names:
CREATE TABLE tmp AS
SELECT 1 AS field1, 'A' AS field2
UNION ALL
SELECT 2, 'B'
UNION ALL
SELECT 3, 'C';
Now I have a question whether the observed behavior is defined and valid. I feel that the statement without explicit field names should end with an error instead of producing a surprising result.
I'm using SQLite.

How to filter data from 2 different tables in SQL but the other table has few strings to ignore?

For example:
One table has account name such as Apple, Google, Facebook.
Other table has account name as 2-apple,3-google,4-facebook.
I need to combine those 2 tables based on account name but want the query to ignore the first 2 string values from the other table = bolded values(2-apple, 3-google, 4-facebook).
In Oracle (as you tagged it), you'd use SUBSTR function:
select whatever
from t1 join t2 on t1.name = substr(t2.name, 3)
as it returns what you wanted, e.g.
SQL> select substr('2-apple', 3) result from dual;
RESUL
-----
apple
SQL>
Keeping in mind lowercase to avoid problems with the comparison
with x as (
select 'Google' as mark from dual union all
select 'Facebook' as mark from dual
),
y as (
select '2-apple' as other from dual union all
select '3-google' as other from dual union all
select '4-facebook' as other from dual
)
select x.mark, y.other
from x inner join y
on ( lower(x.mark) = lower(substr(y.other,3)) );
Result
SQL> with x as (
select 'Google' as mark from dual union all
select 'Facebook' as mark from dual
),
y as (
select '2-apple' as other from dual union all
select '3-google' as other from dual union all
select '4-facebook' as other from dual
)
select x.mark, y.other
from x inner join y
on ( lower(x.mark) = lower(substr(y.other,3)) );
MARK OTHER
-------- ----------
Google 3-google
Facebook 4-facebook

Comparing 2 lists in Oracle

I have 2 lists which I need to compare. I need to find if at least one element from List A is found in List B. I know IN doesn't work with 2 lists. What are my other options?
Basically something like this :
SELECT
CASE WHEN ('A','B','C') IN ('A','Z','H') THEN 1 ELSE 0 END "FOUND"
FROM DUAL
Would appreciate any help!
You are probably looking for something like this. The WITH clause is there just to simulate your "lists" (whatever you mean by that); they are not really part of the solution. The query you need is just the last three lines (plus the semicolon at the end).
with
first_list (str) as (
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual
),
second_list(str) as (
select 'A' from dual union all
select 'Z' from dual union all
select 'H' from dual
)
select case when exists (select * from first_list f join second_list s
on f.str = s.str) then 1 else 0 end as found
from dual
;
FOUND
----------
1
In Oracle you can do:
select
count(*) as total_matches
from table(sys.ODCIVarchar2List('A', 'B', 'C')) x,
table(sys.ODCIVarchar2List('A', 'Z', 'H')) y
where x.column_value = y.column_value;
You need to repeat the conditions:
SELECT (CASE WHEN 'A' IN ('A', 'Z', 'H') OR
'B' IN ('A', 'Z', 'H') OR
'C' IN ('A', 'Z', 'H')
THEN 1 ELSE 0
END) as "FOUND"
FROM DUAL
If you are working with collection of String you can try Multiset Operators.
create type coll_of_varchar2 is table of varchar2(4000);
and:
-- check if exits
select * from dual where cardinality (coll_of_varchar2('A','B','C') multiset intersect coll_of_varchar2('A','Z','H')) > 0;
-- list of maching elments
select * from table(coll_of_varchar2('A','B','C') multiset intersect coll_of_varchar2('A','Z','H'));
Additionally:
-- union of elemtns
select * from table(coll_of_varchar2('A','B','C') multiset union distinct coll_of_varchar2('A','Z','H'));
select * from table(coll_of_varchar2('A','B','C') multiset union all coll_of_varchar2('A','Z','H'));
-- eelemnt from col1 not in col2
select * from table(coll_of_varchar2('A','A','B','C') multiset except all coll_of_varchar2('A','Z','H'));
select * from table(coll_of_varchar2('A','A','B','C') multiset except distinct coll_of_varchar2('A','Z','H'));
-- check if col1 is subset col2
select * from dual where coll_of_varchar2('B','A') submultiset coll_of_varchar2('A','Z','H','B');
I am trying to do something very similar but the first list is another field on the same query created with listagg and containing integer numbers like:
LISTAGG(my_first_list,', ') WITHIN GROUP(
ORDER BY
my_id
) my_first_list
and return this with all the other fields that I am already returning
SELECT
CASE WHEN my_first_list IN ('1,2,3') THEN 1 ELSE 0 END "FOUND"
FROM DUAL

SELECT STATMENT IN OR LIKE OR REGEXP

I have a table new_table
ID DESCRIPTION
1 abdad jadjnd kandkadn 01/19-P-154-37
2 jscbsjc jscnscj 01/19-H-443-38 sbjcj sjcnjscn
3 scjbcs sc, scnsc 01/19-P-16-39 sjcbnjcs
4 scbcsjc 01/19-K-139-40 hcbchsb
AND LISTS
01/19-P-154-37
01/19-H-443-38
01/19-K-139-40
I want to
select * from new_table where descriptin in (
01/19-P-154-37
01/19-H-443-38
01/19-P-16-39
01/19-K-139-40
)
OR LIKE I don't now please help
I guess you want something like this (join with like operator):
with new_table (ID, DESCRIPTION) as (
select 1 ,'abdad jadjnd kandkadn 01/19-P-154-37' from dual union all
select 2 ,'jscbsjc jscnscj 01/19-H-443-38 sbjcj sjcnjscn' from dual union all
select 3 ,'scjbcs sc, scnsc 01/19-P-16-39 sjcbnjcs' from dual union all
select 4 ,'scbcsjc 01/19-K-139-40 hcbchsb' from dual
) ,
LISTS(val) as(
select '01/19-P-154-37' from dual union all
select '01/19-H-443-38' from dual union all
select '01/19-K-139-40' from dual
)
-- Below is actual query:
select new_table.* from new_table
inner join LISTS
on new_table.DESCRIPTION like '%'||LISTS.val||'%'

Find duplicate records in database against unique attributes

I want to find duplicate of IRN # entered into a table in database. Here are the unique attributes (logically unique) of the IRN.
ProjectNo, DrawingNo, DrawingRev, SpoolNo, WeldNo
An IRN can have multiple WeldNos meaning the above unique attributes may repeat for one IRN # (with of course one of the 5 attribute values must be unique).
Now I am trying to find out whether there are any duplicate IRNs entered into the system or not? How can I find that through a sql query?
P.S: Due to bad design of database, there is no primary key in the table..
Here is what I have tried so far but this does not give the correct results.
select * from WeldInfo a, WeldInfo b
where a.ProjectNo = b.ProjectNo and
a.DrawingNo = b.DrawingNo and
a.DrawingRev = b.DrawingRev and
a.SpoolNo = b.SpoolNo and
a.WeldNo = b.WeldNo and
a.IrnNo <> b.IrnNo;
But i'm not sure, have i understood your question.
select * from (
select count(*) over ( partition by ProjectNo, DrawingNo, DrawingRev, SpoolNo, WeldN) rr,t.* from WeldInfo t)
where rr > 1;
Explanation.
with tab as (
select 1 as id, 'a' as a , 'b' as b , 'c' as c from dual
union all
select 2 , 'a', 'b', 'c' from dual
union all
select 3 , 'x', 'b', 'c' from dual
union all
select 3 , 'x', 'b', 'c' from dual
union all
select 3 , 'x', 'd', 'c' from dual
)
select t.*
, count(*) over (partition by a,b,c) cnt1
, count(distinct id) over (partition by a,b,c) cnt2
from tab t;