In Clause With Subquery In Oracle - sql

I have two tables say table1 and table2 with below details
create table test1(id number, name varchar2(20));
insert into test1 values(11,'micro');
insert into test1 values(22,'soft');
create table test2(id number, name varchar2(20));
insert into test2 values(77,'micro,soft');
1) if I use below query I am getting no rows selectd
select * from test1 t1 where t1.name in ( select ''''||replace(t2.name,',',''',''')||'''' from test2;
2) if I fire subquery alone output I am getting is : 'micro','soft'
select ''''||replace(t2.name,',',''',''')||'''' from test2;
but if I fire query(1) I need the result
id name
------------
11 micro
22 soft
can some one please help me to get the same result with query (1).

You have a very strange data layout and should probably change it.
You can do what you want with a join or with a correlated subquery using like:
select *
from test1 t1
where exists (select 1
from test2 t2
where ','||t2.name||',' like '%,'||t1.name||',%'
);
You version doesn't work because the expression:
where x in ('a,b,c')
tests where x is equal to the string value 'a,b,c', not whether it is equal to one of the three values.

Use REGEXP_SUBSTR function to get this done.
SELECT *
FROM test1 t1
WHERE t1.NAME IN(SELECT regexp_substr(t2.NAME, '[^,]+', 1, ROWNUM)
FROM test2 t2
CONNECT BY LEVEL <= LENGTH (regexp_replace (t2.NAME, '[^,]+')) + 1
);

Related

Find duplicates in table 2 and return back the record based on the id

I am trying to make a query to remove the record in table 1 based on the duplicate records found in table 2. id is the common link between these two tables. The Database is oracle. I am new in writing up queries and the below is the query i came up with so far which is not working out. Can anyone please suggest?
I am actually trying to delete record based on the id in table 1 on a condition when there are duplicate records in table 2 for that id as well as one more column? Below is the error message i am getting, am really not sure if query is accurate either or need to re write the whole query itself?
"invalid sql statement" - ORA-00900
DELETE TABLE AS m WHERE m.id IN
(SELECT id from table2 t WHERE ROWID >
(SELECT MIN(ROWID) FROM table2 r WHERE t.column2 = r.column2);
You can try the following one(use only tab as the table in the statement) :
create table tab ( id int, val int );
insert into tab values(1 ,331);
insert into tab values(1 ,332);
insert into tab values(2 ,333);
insert into tab values(2 ,333);
select * from tab;
ID VAL
1 331
1 332
2 333
2 333
delete tab a
where
rowid >
(select min(rowid)
from tab b
where b.id=a.id
group by b.id);
select * from tab;
ID VAL
1 331
2 333
sqlfiddle demo
As a side note : table is a reserved keyword in Oracle and may not be a table name, "table" may be used alternatively.
You would find the duplicates somehow. This is a little unclear, but perhaps:
select t2.column2, count(*)
from table2 t2
group by t2.column2
having count(*) >= 2;
You can then put this into the delete:
delete from m
where m.id in (select t2.column2
from table2 t2
group by t2.column2
having count(*) >= 2
);

Oracle -- Update the exact column referenced in the ON clause

I think this requirement is rarely encountered so I couldn't search for similar questions.
I have a table that needs to update the ID. For example ID 123 in table1 is actually supposed to be 456. I have a separate reference table built that stores the mapping (e.g. old 123 maps to new id 456).
I used the below query but apparently it returned error 38104, columns referenced in the ON clause cannot be updated.
MERGE INTO table1
USING ref_table ON (table1.ID = ref_table.ID_Old)
WHEN MATCHED THEN UPDATE SET table.ID = ref_table.ID_New;
Is there other way to achieve my purpose?
Thanks and much appreciated for your answer!
Use the ROWID pseudocolumn:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TABLE1( ID ) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL;
CREATE TABLE REF_TABLE( ID_OLD, ID_NEW ) AS
SELECT 1, 4 FROM DUAL UNION ALL
SELECT 2, 5 FROM DUAL;
MERGE INTO TABLE1 dst
USING ( SELECT t.ROWID AS rid,
r.id_new
FROM TABLE1 t
INNER JOIN REF_TABLE r
ON ( t.id = r.id_old ) ) src
ON ( dst.ROWID = src.RID )
WHEN MATCHED THEN
UPDATE SET id = src.id_new;
Query 1:
SELECT * FROM table1
Results:
| ID |
|----|
| 4 |
| 5 |
| 3 |
You can't update a column used in the ON clause in a MERGE. But if you don't need to make other changes that MERGE allows like WHEN NOT MATCHED or deleting, etc. you can just use a UPDATE to achieve this.
You mentioned this is an ID that needs an update. Here's an example using a scalar subquery. As it is an ID, this presumes UNIQUE ID_OLD values in REF_TABLE. I wasn't sure if Every row needs an update or only a sub-set, so set the update here to only update rows that have a value in REF_TABLE.
CREATE TABLE TABLE1(
ID NUMBER
);
CREATE TABLE REF_TABLE(
ID_OLD NUMBER,
ID_NEW NUMBER
);
INSERT INTO TABLE1 VALUES (1);
INSERT INTO TABLE1 VALUES (2);
INSERT INTO TABLE1 VALUES (100);
INSERT INTO REF_TABLE VALUES (1,10);
INSERT INTO REF_TABLE VALUES (2,20);
Initial State:
SELECT * FROM TABLE1;
ID
1
2
100
Then make the UPDATE
UPDATE TABLE1
SET TABLE1.ID = (SELECT REF_TABLE.ID_NEW
FROM REF_TABLE
WHERE REF_TABLE.ID_OLD = ID)
WHERE TABLE1.ID IN (SELECT REF_TABLE.ID_OLD
FROM REF_TABLE);
2 rows updated.
And check the change:
SELECT * FROM TABLE1;
ID
10
20
100

SQL Server 2008 R2: Multiple UNION on different databases table's

I have two databases namely db1 and db2.
The database db1 contain 1 table namely test1 and database db2 contain two tables namely test2 and test3.
Here is the following table's with some demo records.
Database: db1
Table: test1
Create table test1
(
ID int,
hrs int,
dates date,
st_date date,
ed_date date
);
insert into test1 values(1,10,'2000-01-01','1900-01-01','2016-01-01');
insert into test1 values(2,20,'2000-02-01','1900-01-01','2016-01-01');
insert into test1 values(3,30,'2000-03-01','1900-01-01','2016-01-01');
insert into test1 values(4,40,'2000-04-01','1900-01-01','2016-01-01');
Database: db2
Table: test2
create table test2
(
ID int,
ID2 int
);
insert into test2 values(1,11);
insert into test2 values(2,22);
insert into test2 values(3,33);
insert into test2 values(4,44);
Database: db2
Table: test3
create table test3
(
ID int,
date date
);
insert into test3 values(1,'2000-01-01');
insert into test3 values(2,'2000-02-02');
insert into test3 values(3,'2000-03-03');
insert into test3 values(4,'2000-04-04');
Note: Now i am executing following query by union all three table's but getting performance issue. There are also test2,test3 tables are present in the db1
select nm,sum(x.avghrs)
from
(
select t1.nm, sum(hrs) as avghrs
from db2.test3 t3,
db2.test2 t2,
db1.test1 t1
where t1.id = t2.id
t3.id = t2.id
group by t1.nm
union
select t1.nm, sum(hrs) as avghrs
from db1.test3 t3,
db1.test2 t2,
db1.test1 t1
where t1.id = t2.id
t3.id = t2.id
group by t1.nm
union
select nm, 0 as avghrs
from test1
where dates between st_date and ed_date
) x
group by nm;
Please tell me if there is any need of modification?
I think the problem is related to JOINs between columns from tables residing in different databases. You can try the following:
1) mirror the tables in a single database (replicate schema and data)
2) apply the appropriate indexes (at least to contain ids used in JOINs)
3) Change your query to SELECT only from mirrored tables
Also, do you need to perform distinct for your unioned results? If not, UNION should be replaced with UNION ALL to avoid the implicit DISTINCT.
UNION ALL will perform better than UNION when you're not concerned about eliminating duplicate records because you're avoiding an expensive distinct sort operation.

Returning rows that had no matches

I've read and read and read but I haven't found a solution to my problem.
I'm doing something like:
SELECT a
FROM t1
WHERE t1.b IN (<external list of values>)
There are other conditions of course but this is the jist of it.
My question is: is there a way to show which in the manually entered list of values didn't find a match? I've looked but I can't find and I'm going in circles.
Create a temp table with the external list of values, then you can do:
select item
from tmptable t
where t.item not in ( select b from t1 )
If the list is short enough, you can do something like:
with t as (
select case when t.b1='FIRSTITEM' then 1 else 0 end firstfound
case when t.b1='2NDITEM' then 1 else 0 end secondfound
case when t.b1='3RDITEM' then 1 else 0 end thirdfound
...
from t1 wher t1.b in 'LIST...'
)
select sum(firstfound), sum(secondfound), sum(thirdfound), ...
from t
But with proper rights, I would use Nicholas' answer.
To display which values in the list of values haven't found a match, as one of the approaches, you could create a nested table SQL(schema object) data type:
-- assuming that the values in the list
-- are of number datatype
create type T_NumList as table of number;
and use it as follows:
-- sample of data. generates numbers from 1 to 11
SQL> with t1(col) as(
2 select level
3 from dual
4 connect by level <= 11
5 )
6 select s.column_value as without_match
7 from table(t_NumList(1, 2, 15, 50, 23)) s -- here goes your list of values
8 left join t1 t
9 on (s.column_value = t.col)
10 where t.col is null
11 ;
Result:
WITHOUT_MATCH
-------------
15
50
23
SQLFiddle Demo
There is no easy way to convert "a externally provided" list into a table that can be used to do the comparison. One way is to use one of the (undocumented) system types to generate a table on the fly based on the values supplied:
with value_list (id) as (
select column_value
from table(sys.odcinumberlist (1, 2, 3)) -- this is the list of values
)
select l.id as missing_id
from value_list l
left join t1 on t1.id = l.id
where t1.id is null;
There are ways to get what you have described, but they have requirements which exceed the statement of the problem. From the minimal description provided, there's no way to have the SQL return the list of the manually-entered values that did not match.
For example, if it's possible to insert the manually-entered values into a separate table - let's call it matchtbl, with the column named b - then the following should do the job:
SELECT matchtbl.b
FROM matchtbl
WHERE matchtbl.b NOT IN (SELECT distinct b
FROM t1)
Of course, if the data is being processed by a programming language, it should be relatively easy to keep track of the set of values returned by the original query, by adding the b column to the output, and then perform the set difference.
Putting the list in an in clause makes this hard. If you can put the list in a table, then the following works:
with list as (
select val1 as value from dual union all
select val2 from dual union all
. . .
select valn
)
select list.value, count(t1.b)
from list left outer join
t1
on t1.b = list.value
group by list.value;

SQLite: accumulator (sum) column in a SELECT statement

I have a table like this one:
SELECT value FROM table;
value
1
3
13
1
5
I would like to add an accumulator column, so that I have this result:
value accumulated
1 1
3 4
13 17
1 18
5 23
How can I do this? What's the real name of what I want to do? Thanks
try this way:
select value,
(select sum(t2.value) from table t2 where t2.id <= t1.id ) as accumulated
from table t1
but if it will not work on your database, just add order by something
select value,
(select sum(t2.value) from table t2 where t2.id <= t1.id order by id ) as accumulated
from table t1
order by id
this works on an oracle ;) but it should on a sqlite too
Here's a method to create a running total without the inefficiency of summing all prior rows. (I know this question is 6 years old but it's one of the first google entries for sqlite running total.)
create table t1 (value integer, accumulated integer, id integer primary key);
insert into t1 (value) values (1);
insert into t1 (value) values (3);
insert into t1 (value) values (13);
insert into t1 (value) values (1);
insert into t1 (value) values (5);
UPDATE
t1
SET
accumulated = ifnull(
(
SELECT
ifnull(accumulated,0)
FROM
t1 ROWPRIOR
WHERE
ROWPRIOR.id = (t1.id -1 )),0) + value;
.headers on
select * from t1;
value|accumulated|id
1|1|1
3|4|2
13|17|3
1|18|4
5|23|5
This should only be run once after importing all the values. Or, set the accumulated column to all nulls before running again.
The operation is called a running sum. SQLite does not support it as is, but there are ways to make it work. One is just as Sebastian Brózda posted. Another I detailed here in another question.