delete rows using sql 'like' command using data from another table - sql

I am trying to delete rows from a table ("lovalarm") where a field ("pointid") is like any one of a number of strings.
Currently I am entering them all manually however I need to be able to have a list of over 100,000 options.
My thoughts are to have a table ("lovdata") containing all possible strings and running a query to delete rows where the field is 'like' any of the strings in the other table.
Can anyone point me in the right direction as to if/how I can use like in this way?
Many thanks,
Cap

sure you can join with the LIKE operator:
DELETE FROM lovalarm a
WHERE EXISTS (SELECT NULL
FROM lovdata d
WHERE a.pointid LIKE d.pointid)
The lovdata.pointid column may contain wildcards, consider:
SQL> CREATE TABLE lovalarm AS
2 SELECT 'AA' pointid FROM dual
3 UNION ALL SELECT 'AB' FROM dual
4 UNION ALL SELECT 'AC' FROM dual
5 UNION ALL SELECT 'BA' FROM dual
6 UNION ALL SELECT 'BB' FROM dual
7 UNION ALL SELECT 'BC' FROM dual;
Table created
SQL> --# suppose that you want to remove all entries that start
2 --# with A or end with C
3 CREATE TABLE lovdata AS
4 SELECT 'A%' pointid FROM dual
5 UNION ALL SELECT '%C' FROM dual;
Table created
SQL> DELETE FROM lovalarm a
2 WHERE EXISTS (SELECT NULL
3 FROM lovdata d
4 WHERE a.pointid LIKE d.pointid);
4 rows deleted
SQL> select * from lovalarm;
POINTID
-------
BA
BB

Just use a sub query, like this:
DELETE
FROM lovalarm
WHERE pointid IN (SELECT mystring FROM lovdata)
EDIT: Just noticed that LIKE is required, so IN isn't going to work as this will be exact matches. I'll leave this here for reference as Vincent has already added the correct query.

here is an example with the % concatenations
CREATE TABLE LOVALARM(POINTID VARCHAR2(50) ,TXT VARCHAR2(50));
create table deleteThese(deleteStringList varchar2(50));
/
INSERT INTO LOVALARM(POINTID, TXT) VALUES('abc def','1');
INSERT INTO LOVALARM(POINTID, TXT) VALUES('def','2');
INSERT INTO LOVALARM(POINTID, TXT) VALUES('abc','3');
INSERT INTO LOVALARM(POINTID, TXT) VALUES('efd','4');
INSERT INTO LOVALARM(POINTID, TXT) VALUES('abb','5');
INSERT INTO LOVALARM(POINTID, TXT) VALUES('efd bbb','6');
INSERT INTO LOVALARM(POINTID, TXT) VALUES('abb ccc','7');
INSERT INTO LOVALARM(POINTID, TXT) VALUES('abbbccc','8');
INSERT INTO DELETETHESE(DELETESTRINGLIST) VALUES('abc');
INSERT INTO DELETETHESE(DELETESTRINGLIST) VALUES('bbb');
COMMIT;
/
DELETE LOVALARM
where exists(
SELECT NULL
FROM DELETETHESE
where LOVALARM.pointid like '%' || dELETETHESE.DELETESTRINGLIST || '%'
);
select * from LOVALARM;
POINTID TXT
-------------------------------------------------- -----
def 2
efd 4
ABB 5
abb ccc 7

Related

How to unescape % in LIKE clause

I have my search patterns stored in database in patterns table. For example my table column name_pattern contains string 'Basic%'. I'd like to create dynamic search where search patterns will be fetched from name_pattern column.
So my SQL query should look something like:
SELECT *
FROM products
WHERE product_name LIKE name_pattern <-- somehow joined from patterns table
Seems that Oracle escapes % in my string but I want to take it unescapped in order my query to work like:
SELECT *
FROM products
WHERE product_name LIKE 'Basic%'
I found that my problem is with stable set of rows:
CREATE TABLE patterns(code CHAR(1),name_pattern VARCHAR2(20));
INSERT INTO patterns(code,name_pattern) VALUES('B','Basic%');
INSERT INTO patterns(code,name_pattern) VALUES('T','%thing');
CREATE TABLE products (id NUMBER,name VARCHAR2(20),code CHAR(1));
INSERT INTO products(id,name,found) VALUES(1,'Basic instinct',NULL);
INSERT INTO products(id,name,found) VALUES(2,'Basic thing',NULL);
INSERT INTO products(id,name,found) VALUES(3,'Super thing',NULL);
INSERT INTO products(id,name,found) VALUES(4,'Hyper instinct',NULL);
MERGE INTO products p USING
(
SELECT code,name_pattern FROM patterns
) s
ON (p.name LIKE s.name_pattern)
WHEN MATCHED THEN UPDATE SET p.code=s.code;
SELECT * FROM products;
If my search patterns were Basic% and Super% in patterns table then this MERGE will work, but if my search patterns are Basic% and %thing, the second product should be marked with both codes 'B' and 'T' and that causes the error:
ORA-30926: unable to get a stable set of rows in the source tables
So my problem is not in (un)escaping :-(, sorry
You don't have to (un)escape anything, I'd say.
SQL> with
2 patterns (name_pattern) as
3 (select 'Basic%' from dual union all
4 select '%foot%' from dual
5 ),
6 products (id, name) as
7 (select 1, 'Basic instinct' from dual union all
8 select 2, 'Visual Basic' from dual union all
9 select 3, 'Littlefoot' from dual union all
10 select 4, 'Happy feet' from dual
11 )
12 select b.id, b.name, a.name_pattern
13 from products b join patterns a on b.name like a.name_pattern;
ID NAME NAME_P
---------- -------------- ------
1 Basic instinct Basic%
3 Littlefoot %foot%
SQL>
Based on test case you provided: don't merge, update!
SQL> update products p set
2 p.found = 1
3 where exists (select null
4 from patterns o
5 where p.name like o.name_pattern
6 );
3 rows updated.
SQL> select * from products;
ID NAME FOUND
---------- -------------------- ----------
1 Basic instinct 1
2 Basic thing 1
3 Super thing 1
4 Hyper instinct 0
SQL>
After you changed your mind (again), it is still update. Though, you didn't explain which code you want to take when there's multiple match (for example, product 2 matches both "Basic%" and "%thing") so I took any of them, using the min function.
SQL> update products p set
2 p.code = (select min(o.code)
3 from patterns o
4 where p.name like o.name_pattern
5 );
4 rows updated.
SQL> select * from products;
ID NAME CODE
---------- -------------------- ----------
1 Basic instinct B
2 Basic thing B
3 Super thing T
4 Hyper instinct NULL
SQL>

Using regexp_like in Oracle to match on multiple string conditions

I have a column called Keywords in my Oracle database, Basically Keywords column contains all the data of other columns in particular row separated by _.
Example Table:
NAME PHONE_NUMBER COMPANY ADDRESS ZIPCODE KEYWORD
ABCD 9849523459 MICRO RAJAHMU 532819 ABCD_9849523459_MICRO_RAJAHMU_532819
ABCD 8628738646 INFOS KAKINAD 532775 ABCD_8628738646_INFOS_KAKINAD_532775
ABCD 8473874381 ICUBE RAVULAP 537238 ABCD_8473874381_ICUBE_RAVULAP_537238
Now, How can i get exact string match by using REGEXP_LIKE. When i'm using the below query
SELECT * FROM USER_DATA WHERE 1=1 AND REGEXP_LIKE ('KEYWORD', 'ABCD_MICRO_RAVULAP', 'i'));
It's returning 0 records
My expected output should be
NAME PHONE_NUMBER COMPANY ADDRESS ZIPCODE KEYWORD
ABCD 9849523459 MICRO RAJAHMU 532819 ABCD_9849523459_MICRO_RAJAHMU_532819
ABCD 8473874381 ICUBE RAVULAP 537238 ABCD_8473874381_ICUBE_RAVULAP_537238
I would be very grateful if anyone help me out.
Thanks Inadvance
Something like this?
SQL> with user_data (name, keyword) as
2 (select 'ABCD', 'ABCD_9849523459_MICRO_RAJAHMU_532819' from dual union all
3 select 'DEFG', 'ABCD_8628738646_INFOS_KAKINAD_532775' from dual union all
4 select 'HIJK', 'ABCD_8473874381_ICUBE_RAVULAP_537238' from dual
5 )
6 select *
7 from user_data
8 where regexp_like(keyword, 'ABCD.+MICRO', 'i');
NAME KEYWORD
---- ------------------------------------
ABCD ABCD_9849523459_MICRO_RAJAHMU_532819
SQL>
If you want to search the string for two words in any order, use | operator:
SQL> with user_data (name, keyword) as
2 (select 'ABCD', 'ABCD_9849523459_MICRO_RAJAHMU_532819' from dual union all
3 select 'DEFG', 'ABCD_8628738646_INFOS_KAKINAD_532775' from dual union all
4 select 'HIJK', 'ABCD_8473874381_ICUBE_RAVULAP_537238' from dual union all
5 select 'LMNO', 'MICRO_241241242_ABCD_WHATEVER_241424' from dual
6 )
7 select *
8 from user_data
9 where regexp_like(keyword, '(ABCD).+(MICRO)|(MICRO).+(ABCD)', 'i');
NAME KEYWORD
---- ------------------------------------
ABCD ABCD_9849523459_MICRO_RAJAHMU_532819
LMNO MICRO_241241242_ABCD_WHATEVER_241424
SQL>
However, it isn't practical. Perhaps you should consider Oracle Text feature, then.
SQL> create table
2 user_data (name, keyword) as
3 (select 'ABCD', 'ABCD_9849523459_MICRO_RAJAHMU_532819' from dual union all
4 select 'DEFG', 'ABCD_8628738646_INFOS_KAKINAD_532775' from dual union all
5 select 'HIJK', 'ABCD_8473874381_ICUBE_RAVULAP_537238' from dual union all
6 select 'LMNO', 'MICRO_241241242_ABCD_WHATEVER_241424' from dual
7 );
Table created.
SQL> create index i1_ud on user_data(keyword) indextype is ctxsys.context;
Index created.
SQL> select *
2 from user_data
3 where contains (keyword, '$micro and abcd', 1) > 0;
NAME KEYWORD
---- ------------------------------------
ABCD ABCD_9849523459_MICRO_RAJAHMU_532819
LMNO MICRO_241241242_ABCD_WHATEVER_241424
SQL>
Use IN on the underlying columns rather than trying to parse the composite column:
SELECT *
FROM USER_DATA
WHERE (name, company, address) IN (
('ABCD', 'MICRO', 'RAVULAP'),
('MICRO', 'ABCD', 'RAVULAP')
);
If you want to compare case-insensitively then use UPPER:
SELECT *
FROM USER_DATA
WHERE (UPPER(name), UPPER(company), UPPER(address)) IN (
('ABCD', 'MICRO', 'RAVULAP'),
('MICRO', 'ABCD', 'RAVULAP')
);
If you want to match a single triplet of values to the terms in any order then you can reverse the IN clause:
SELECT *
FROM USER_DATA
WHERE ('ABCD', 'MICRO', 'RAVULAP') IN (
(UPPER(name), UPPER(company), UPPER(address)),
(UPPER(name), UPPER(address), UPPER(company)),
(UPPER(company), UPPER(name), UPPER(address)),
(UPPER(company), UPPER(address), UPPER(name)),
(UPPER(address), UPPER(name), UPPER(company)),
(UPPER(address), UPPER(company), UPPER(name))
);

How to insert long-format data into two separate tables using SQL?

I have selected the following data that I want to insert into the database.
Letter
Value
A
1
A
2
B
3
B
4
Since there is a repetition of "A" and "B" in this format, I want to split data into two separate tables: table1 and table2.
table1:
ID
Letter
1
A
2
B
ID here is automatically inserted by database (using a sequence).
table2:
table1_id
Value
1
1
1
2
2
3
2
4
In this particular example, I don't gain anything on storage but it illustrates in the best way what problem I have encountered.
How can I use SQL or PL/SQL to insert data into table1 and table2?
First populate table1 from the source
insert table1(Letter)
select distinct Letter
from srcTable;
Then load data from the source decoding letter to id
insert table2(table1_id, Value)
select t1.id, src.value
from srcTable src
join table1 t1 on t1.Letter = src.Letter;
You may use multitable insert with workaround to get stable nextval on sequence. Since nextval is evaluated on each row regardless of when condition, it is not sufficient to use it inside values.
insert all
when rn = 1 then into l(id, val) values(seq, letter)
when rn > 0 then into t(l_id, val) values(seq, val)
with a(letter, val) as (
select 'A', 1 from dual union all
select 'A', 2 from dual union all
select 'B', 3 from dual union all
select 'B', 4 from dual union all
select 'C', 5 from dual
)
, b as (
select
a.*,
l.id as l_id,
row_number() over(partition by a.letter order by a.val asc) as rn
from a
left join l
on a.letter = l.val
)
select
b.*,
max(decode(rn, 1, coalesce(
l_id,
extractvalue(
/*Hide the reference to the sequence due to restrictions
of multitalbe insert*/
dbms_xmlgen.getxmltype('select l_sq.nextval as seq from dual')
, '/ROWSET/ROW/SEQ/text()'
) + 0
))
) over(partition by b.letter) as seq
from b
select *
from l
ID | VAL
-: | :--
1 | A
2 | B
3 | C
select *
from t
L_ID | VAL
---: | --:
1 | 1
1 | 2
2 | 3
2 | 4
3 | 5
db<>fiddle here
Principally you need to produce and ID value for the table1 to be inserted into table2. For this, You can use INSERT ... RETURNING ID INTO v_id statement after creating the tables containing some constraints especially unique ones such as PRIMARY KEY and UNIQUE
CREATE TABLE table1( ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, letter VARCHAR2(1) NOT NULL );
ALTER TABLE table1 ADD CONSTRAINT uk_tab1_letter UNIQUE(letter);
CREATE TABLE table2( ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, table1_id INT, value INT );
ALTER TABLE table2 ADD CONSTRAINT fk_tab2_tab1_id FOREIGN KEY(table1_id) REFERENCES table1 (ID);
and adding exception handling in order not to insert repeating letters to the first table. Then use the following code block ;
DECLARE
v_id table1.id%TYPE;
v_letter table1.letter%TYPE := 'A';
v_value table2.value%TYPE := 1;
BEGIN
BEGIN
INSERT INTO table1(letter) VALUES(v_letter) RETURNING ID INTO v_id;
EXCEPTION WHEN OTHERS THEN NULL;
END;
INSERT INTO table2(table1_id,value) SELECT id,v_value FROM table1 WHERE letter = v_letter;
COMMIT;
END;
/
and run by changing the initialized values for v_letter&v_value as 'A'&2, 'B'&1,'B'&2 ..etc respectively.
Alternatively you can convert the code block to a stored procedure or function such as
CREATE OR REPLACE PROCEDURE Pr_Ins_Tabs(
v_letter table1.letter%TYPE,
v_value table2.value%TYPE
) AS
v_id table1.id%TYPE;
BEGIN
BEGIN
INSERT INTO table1(letter) VALUES(v_letter) RETURNING ID INTO v_id;
EXCEPTION WHEN OTHERS THEN NULL;
END;
INSERT INTO table2(table1_id,value) SELECT id,v_value FROM table1 WHERE letter = v_letter;
COMMIT;
END;
/
in order to revoke resiliently such as
BEGIN
Pr_Ins_Tabs('A',2);
END;
/
Demo
PS. If your DB verion is prior to 12c, then create sequences(seq1&seq2) and use seq1.nextval&seq2.nextval within the Insert statements as not possible to use GENERATED ALWAYS AS IDENTITY clause within the table creation DDL statements.

Using Insert/Update with 2 Tables - Oracle Apex

I'm still new to SQL and am having a hard time figuring out how to update and insert from one table into another table in Oracle Apex (uses SQL).
The two tables are named Temp and Table (example) and both have the same columns. Basically they are copies of each other but with different data. I would like to compare the ID field in Temp to the ID field in Table and if there is a row that matches that ID field in Temp, then to overwrite all the data on the row in Table with the data in the corresponding row in Temp.
Note: Table has 10 million rows of data, Temp has like 500.
if Temp.ID = Table.ID then
update
set
Table.ID = Temp.ID
Table.Address = Temp.Address
else
insert
(Table.ID,
Table.Address)
values
(Temp.ID,
Temp.Address
That is basically what I want done but not sure how to write it in SQL. Have seen a lot of different answers, but none really involving 2 tables and mostly for MySQL or SQL Server specific SQL that I am not sure also works with Oracle.
MERGE into TEST1
USING TEST2 on (TEST2.ID = TEST1.ID)
WHEN matched THEN UPDATE
SET TEST1.ID = TEST2.ID, TEST1.NAME = TEST2.NAME, TEST1.ADDRESS = TEST2.ADDRESS, TEST1.EMAIL = TEST2.EMAIL
WHEN not matched THEN INSERT (ID, NAME, ADDRESS, EMAIL) values (TEST2.ID, TEST2.NAME, TEST2.ADDRESS, TEST2.EMAIL);
I tried this but it is giving me the error:
ORA-38104: Columns referenced in the ON Clause cannot be updated: "TEST1"."ID"
UPDATE: Got it to work!
http://db-oriented.com/2013/09/20/the-merge-statement/
Was helpful with the answer to the error and I did not know that you could not have the IDs in the update clause, which makes total sense now. Thank you also to the respondent below for explaining the merge code to me. :)
Looks like a good candidate for MERGE. Have a look at the example.
Sample tables & data:
SQL> create table ttable (id number, address varchar2(20));
Table created.
SQL> create table temp (id number, address varchar2(20));
Table created.
SQL> insert into ttable
2 select 1, 'Address 1' from dual union all
3 select 2, 'Address 2' from dual union all
4 select 3, 'Address 3' from dual;
3 rows created.
SQL> insert into temp
2 select 1, 'New address 1' from dual union all
3 select 2, 'New address 2' from dual union all
4 select 4, 'New address 4' from dual;
3 rows created.
Merge & the result:
SQL> merge into ttable a
2 using temp e on (e.id = a.id)
3 when matched then update set a.address = e.address
4 when not matched then insert (id, address) values (e.id, e.address);
3 rows merged.
SQL> select * from ttable;
ID ADDRESS
---------- --------------------
1 New address 1
2 New address 2
3 Address 3
4 New address 4
SQL>

How can I create my numbers matching table populate from zero instead of one?

I'm using a numbers matching table, from one upwards. However, I realize I need to start at zero instead. Can't figure it out..
CREATE TABLE IF NOT EXISTS util_nums (n integer primary key
autoincrement not null);
insert into util_nums(n) select null from (select 0 as n union select 1
union select 2 union select 3 union select 4 union select 5 union select 6
union select 7 union select 8 union select 9 union select 10) a
cross join
(select 0 as n union select 1 union select 2 union select 3 union select 4
union select 5 union select 6 union select 7 union select 8 union select 9
union select 10) b
cross join (select 0 as n union select 1 union select 2
union select 3 union select 4 union select 5 union select 6 union select 7
union select 8 union select 9 union select 10) c;
in sql server it would be easy if you create your table like this
CREATE TABLE util_nums (n as int primary key
identity(0,1) not null,anotherfieldtoholdthenulls integer);
the identity(0,1) means start from zero and increment by 1 ..
update
try using UPDATE SQLITE_SEQUENCE SET seq = -1 WHERE name = 'util_nums' before starting the insert, and see if it is allowed....
You should also be able to do INSERT INTO util_nums VALUES(0)
Sqlite allows you to insert explicit values for the primary key fields:
insert into util_nums(n) values (0);
to Get a lot more rows insert quickly try this after that..
insert into util_nums default values;
insert into util_nums(n) select null from util_nums a, util_nums b, util_nums c, util_nums d;
insert into util_nums(n) select null from util_nums a, util_nums b, util_nums c, util_nums d;
you can temporarly disable auto increment using
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
But I
suggest update value to 0 after insert
If you're useing SQLite you should probably read this http://www.sqlite.org/autoinc.html . This caught my eye:
If the table has never before
contained any data, then a ROWID of 1
is used.
It doesn't seem to be any way to force autoincrement to start from something other than 1. Also note that it may generate gaps by skipping numbers.
This may work but I have no means of testing at the moment:
Add a row with an ID of -1. Then delete it. It isn't clear from the documentation what happens when you have only negative IDs in the table.
"SQLite Autoincrement"
The important part seems to be...
If no negative ROWID values are inserted explicitly, then automatically
generated ROWID values will always be greater than zero.
So? Create the table, insert a dummy record, with a forced id of -1, and then insert your data. Deleting the dummy record afterwards as/if necessary.
(Inserting a value with -1 will force the next inserted rwo to have an id of 0, assuming the table was otherwise empty.)
SQLite allows you to specify a value here.
Just insert c.n - 1, from your cartesian product, instead of null, and call it a day.