I have table a and b.in table a there’s a field a1 which has two formats xxxxx or xxxxx.xx. I want to query a and b based on
if a1 Contains no dot then a.a1=b.b1 else substring(a.a1,0 to dot pos) =b.b1.How to write the join condition if the second format xxxxx.xx is not fixed length? thx.
You can use:
SELECT *
FROM a
INNER JOIN b
ON (a.a1 = b.b1 OR a.a1 LIKE b.b1 || '.%')
or:
SELECT *
FROM a
INNER JOIN b
ON ((a.a1 NOT LIKE '%.%' AND a.a1 = b.b1) OR a.a1 LIKE b.b1 || '.%')
Which for the sample data:
CREATE TABLE a (a1) AS
SELECT 'abcde' FROM DUAL UNION ALL
SELECT 'abc.def' FROM DUAL UNION ALL
SELECT 'abcd.ef' FROM DUAL;
CREATE TABLE b (b1) AS
SELECT 'abc' FROM DUAL UNION ALL
SELECT 'abcd' FROM DUAL UNION ALL
SELECT 'abcde' FROM DUAL;
Outputs:
A1
B1
abcde
abcde
abc.def
abc
abcd.ef
abcd
db<>fiddle here
A join can be done on an expression. So the solution is to create an expression on the table with the format 'xxxx.xx' that extracts the value the join needs to be on.
This can be done using an expression like this:
CASE WHEN INSTR(a.ca1,'.') = 0
THEN ca1
ELSE SUBSTR(a.ca1,1,INSTR(a.ca1,'.') - 1)
END
The function will return everything up to the first "." if the sting contains a ".", else it will return the string. This can be achieved with REGEXP_REPLACE as well but that will be less performant than the good old SUBSTR.
WITH tablea (ca1) AS
(
SELECT 'ABC.XX' FROM DUAL UNION ALL
SELECT 'CDE.HHAA88' FROM DUAL UNION ALL
SELECT 'A123.XYZ' FROM DUAL UNION ALL
SELECT 'KLM' FROM DUAL
),
tableb (cb1) AS
(
SELECT 'ABC' FROM DUAL UNION ALL
SELECT 'DEF' FROM DUAL UNION ALL
SELECT 'KLM' FROM DUAL UNION ALL
SELECT 'A123' FROM DUAL
)
SELECT a.ca1
FROM tablea a
JOIN tableb b ON b.cb1 = CASE WHEN INSTR(a.ca1,'.') = 0 THEN ca1 ELSE SUBSTR(a.ca1,1,INSTR(a.ca1,'.') - 1) END ;
CA1
----------
ABC.XX
KLM
A123.XYZ
Related
I have the below table:
COL
---
XXY
YXX
XXX
NULL
I want to filter out the rows which don't consist of all 'X's.
Expected output:
COL
---
XXX
We can use REGEXP_LIKE here:
SELECT COL
FROM yourTable
WHERE REGEXP_LIKE(COL, '^X+$'); -- ^X+$ means all X from start to end
Another similar version:
SELECT COL
FROM yourTable
WHERE NOT REGEXP_LIKE(COL, '[^X]'); -- this means no non X present
Another option(without using a regular expression) might be using
WITH t(col) AS
(
SELECT 'XXY' FROM dual UNION ALL
SELECT 'YXX' FROM dual UNION ALL
SELECT 'XXX' FROM dual UNION ALL
SELECT NULL FROM dual UNION ALL
SELECT 'XX ' FROM dual
)
SELECT *
FROM t
WHERE REPLACE(NVL(col,'Y'),'X') IS NULL;
COL
----
XXX
without forgetting the case col = NULL through use of a NVL()
You can use the following syntax (assuming you are using MySQL database 5.6 or greater version):
SELECT * FROM table_name WHERE col_name REGEXP '^X+$';
If you don't want/have regexp then:
WITH
tbl AS
( Select 'XXY' "COL" From dual Union All
Select 'YXX' "COL" From dual Union All
Select 'XXX' "COL" From dual Union All
Select null "COL" From dual
)
Select COL
From tbl
Where Length(Nvl(COL, 'Z')) - Length( Replace( Upper(Nvl(COL, 'Z')), 'X', '')) Is Null
COL
---
XXX
This covers both small 'x' and capital 'X' if needed and returns original COL value
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
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
I have a table with the field RANGE that has the values in the following way:
102453
104953-256454
The values can be single 6 digit number or two 6 digit numbers separated by a dash.
User enters the a value (either a 6 digit number or two 6 digit number separated by a dash), and the value has to be compared with the existing values in the database.
if the value entered is 6 digit number, then it should be compared with all the single 6 digit values in the database, and also with the separate 6 digit values stored as 2 digit values separated by a (-)
if the value entered is two 6 digit numbers, then it should be parsed, and each value has to be compared by each value in the database.
How do I go about it ?
I assume there is some kind of ID field (I hope).
with Dumbassery as
(
select id, 'Single' as NumType, substr(MyField,1,6) as Num1, '' as Num2
from MyTable
where length(MyField) = 6
union
select id, 'Range' as NumType, substr(MyField,1,6) as Num1, substr(MyField,8,6) as Num2
from MyTable
where length(MyField) = 13
)
, Filter1 as
(
select id,
case
when exists (select 1 from Dumbassery D2 where D1.Num1 between D2.Num1 and D2.Num2 and D2.NumType = 'Range') then 'Exists'
when exists (select 1 from Dumbassery D2 where D2.NumType = 'Single' and D1.Num1 = D2.Num1) then 'Exists'
else 'New'
end as NumExist
from Dumbassery D1
where D1.NumType = 'Single'
union
select id,
case
when exists (select 1 from Dumbassery D2 where D1.Num1 between D2.Num1 and D2.Num2 and D2.NumType = 'Range') then 'Exists'
when exists (select 1 from Dumbassery D2 where D1.Num2 between D2.Num1 and D2.Num2 and D2.NumType = 'Range') then 'Exists'
when exists (select 1 from Dumbassery D2 where D2.Num2 between D1.Num1 and D1.Num2 and D2.NumType = 'Single') then 'Exists'
else 'New'
end as NumExist
from Dumbassery D1
where D1.NumType = 'Range'
)
select distinct *
from Filter1
where NumExist = 'New'
As you field named range I think you need to find intersections with new range. I add table test_val to check some variations of new_range.
WITH
table_ranges AS /*is table with ranges*/
(select '123212' as range from dual union all
select '123214-223214' as range from dual union all
select '123900-987121' as range from dual )
,test_val as /*is table with test values*/
(select '123212' as test_range from dual union all
select '123213' as test_range from dual union all
select '123215' as test_range from dual union all
select '123213-123290' as test_range from dual union all
select '124000-125000' as test_range from dual union all
select '987000-987124' as test_range from dual union all
select '987122-987124' as test_range from dual )
select CAse WHEN EXISTS (select null
from table_ranges
where substr(t.test_range, 1,6) between substr(range, 1,6) and substr(range, -6,6)
or substr(t.test_range, -6,6) between substr(range, 1,6) and substr(range, -6,6) )
THEN 'already exists'
ELSE'intersection not found'
END AS test_status
FROM test_val t
I try to select from table_1 where ITEM_FIELD_A is not in ITEM_FIELD_B. The Item_FIELD_B value are look as below. I was expecting no COVER_TAPE & SHIPPING_REELS will be selected. But unfortunately, it's not working.
The sql I used to select the table
select * from table_1 where MST.ITEM_FIELD_A not in ITEM_FIELD_B
Question:
In Oracle, is there any function to decode the string. so that the above select statement will not return COVER_TAPE and SHIPPING_REELS??
The IN operator would be used when you wish to compare (or negate) one item in a list such as
WHERE ITEM_FIELD_A NOT IN ('COVER_TAPE', 'SHIPPING_REELS', '')
What you want is the LIKE operator:
WHERE ITEM_FIELD_B NOT LIKE '%' || ITEM_FIELD_A || '%'
Apologies if I got the wildcard wrong, been a while since I last touched Oracle.
Check out below Query:
WITH TAB1 AS
( SELECT 'COVER_TAPE' ITEM_A FROM DUAL
UNION
SELECT 'CARRIER_TAPE' ITEM_A FROM DUAL
UNION
SELECT 'SHIPPING_REELS' ITEM_A FROM DUAL
),
TAB2 AS
(
SELECT 'COVER_TAPE,SHIPPING_REELS' ITEM_B FROM DUAL
)
SELECT ITEM_A, ITEM_B FROM TAB1, TAB2 WHERE INSTR(ITEM_B, ITEM_A) <=0
INSTR will return >0 if same sequence of characters is available.
SQL> with t(x , y) as
2 (
3 select 'A', q'[('A','B','C')]' from dual union all
4 select 'R', q'[('A','B','C','D')]' from dual union all
5 select 'C', q'[('A', 'C','D')]' from dual
6 )
7 select x, y
8 from t where y not like q'[%']'||x||q'['%]'
9 /
X Y
---------- --------------------------------------------------
R ('A','B','C','D')