Oracle SQL - Comparing duplicate values - sql

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

Related

How do I select rows from table that have one or more than one specific value in a column?

I have a table containing data such as:
BP_NUMBER,CONTRACT_TYPE
0000123, 1
0000123, 2
0000123, 3
0000123, 4
0000124, 4
0000124, 4
0000124, 4
0000125, 4
0000126, 1
0000126, 5
I want to select rows containing one or more occurrences of CONTRACT_TYPE = 4. In other words, I want to know who are the clients with one or more contracts of the same type and type 4.
I tried this query:
SELECT * FROM (
SELECT BP_NUMBER, CONTRACT_TYPE, COUNT(*) OVER (PARTITION BY BP_NUMBER) CT FROM CONTRACTS
WHERE (1=1)
AND DATE = '18/10/2022'
AND CONTRACT_TYPE = 4)
WHERE CT= 1;
But it returns rows with only one occurrence of CONTRACT_TYPE = 4.
Also tried something like:
SELECT BP_NUMBER FROM CONTRACTS
WHERE (1=1)
AND CONTRACT_TYPE = 4
AND CONTRACT_TYPE NOT IN (SELECT CONTRACT_TYPE FROM CONTRACTS WHERE CONTRACT_TYPE != 4 GROUP BY CONTRACT_TYPE);
Trying to avoid any other contract types than 4. I really don't understand why it doesn't work.
The expected result would be:
0000124 --(4 occurrences of type 4)
0000125 --(1 occurrence of type 4)
Any help? Thanks
You can try something like this:
SELECT
BP_NUMBER
FROM CONTRACTS c1
WHERE CONTRACT_TYPE = 4
AND NOT EXISTS
(SELECT 1 FROM CONTRACTS c2 WHERE c2.BP_NUMBER = c1.BP_NUMBER
AND c2.CONTRACT_TYPE <> c1.CONTRACT_TYPE)
Depending on how you actually want to see it (and what other values you might want to include), you could either do a DISTINCT on the BP_NUMBER, or group on that column (and potentially others)
A similar result could also be achieved using an outer join between two instances of the CONTRACTS table. Essentially, you need the second instance of the same table so that you can exclude output rows when there are records with the "unwanted" contract types
You can just do the aggregation like here:
WITH
tbl AS
(
Select '0000123' "BP_NUMBER", '1' "CONTRACT_TYPE" From Dual Union All
Select '0000123', '2' From Dual Union All
Select '0000123', '3' From Dual Union All
Select '0000123', '4' From Dual Union All
Select '0000124', '4' From Dual Union All
Select '0000124', '4' From Dual Union All
Select '0000124', '4' From Dual Union All
Select '0000125', '4' From Dual Union All
Select '0000126', '1' From Dual Union All
Select '0000126', '5' From Dual
)
Select
BP_NUMBER "BP_NUMBER",
Count(*) "OCCURENCIES"
From
tbl
WHERE CONTRACT_TYPE = '4'
GROUP BY BP_NUMBER
ORDER BY BP_NUMBER
--
-- R e s u l t :
--
-- BP_NUMBER OCCURENCIES
-- --------- -----------
-- 0000123 1
-- 0000124 3
-- 0000125 1

How to join table on field value substring without fixed length?

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

Oracle: Comparing string columns from two different table without a primary key to find match/unmatched string

I have two tables, and we want to compare the two string columns from these tables. there is no primary key in these table to match in the where clause. Tables are as below:
The compare should ignore :
IGNORE SPACES AT ANY PLACE IN THE STRING 2) IGNORE CASE SENSITIVE
OUTPUT WANT:
You can use full outer join with upper and replace function in join condition as follows:
Select t1.str, t2.str,
Case when t1.str is not null and t2.str is not null then 'exist in both table'
when t1.str is not null then 'missing in table2'
Else 'missing in table1'
End as differences
From table1 t1 full join table2 t2
On upper(replace(t1.str,' ','') = upper(replace(t2.str,' ','')
Similarly; sample data from line #1 - 16. Query you might need begins at line #17.
SQL> with
2 tab1 (col) as
3 (select 'mg/kl' from dual union all
4 select 'k/mg' from dual union all
5 select 'l/g/kg' from dual union all
6 select 'umol_Ulp' from dual union all
7 select '10*12_/KG' from dual union all
8 select 'a/L/G' from dual
9 ),
10 tab2 (col) as
11 (select '10*12_/kg' from dual union all
12 select '1/L/G' from dual union all
13 select 'PG/7#R' from dual union all
14 select 'KG/CM/L' from dual union all
15 select 'l/g/kg' from dual
16 )
17 select a.col, b.col,
18 case when lower(a.col) = lower(b.col) then
19 'exists in both tables ' ||
20 case when a.col = b.col then 'and exact match'
21 else 'but different'
22 end
23 when a.col is null then 'missing in table 1'
24 when b.col is null then 'missing in table 2'
25 end differences
26 from tab1 a full outer join tab2 b on lower(a.col) = lower(b.col);
COL COL DIFFERENCES
--------- --------- -------------------------------------
mg/kl missing in table 2
k/mg missing in table 2
l/g/kg l/g/kg exists in both tables and exact match
umol_Ulp missing in table 2
10*12_/KG 10*12_/kg exists in both tables but different
a/L/G missing in table 2
KG/CM/L missing in table 1
PG/7#R missing in table 1
1/L/G missing in table 1
9 rows selected.
SQL>
try the below SQL once and see if works. Otherwise can you please share those values which it says 'missing in table 2' even though it exists in both the tables?
Select t1.str, t2.str,
Case when nvl(t1.str,'X') != 'X' and nvl(t2.str,'X') != 'X' then 'exist in both table'
when nvl(t1.str,'X') != 'X' then 'missing in table2'
Else 'missing in table1'
End as differences
From table1 t1 full join table2 t2
On upper(trim(t1.str)) = upper(trim(t2.str))

Excluding records from table based on rules from another table

I'm using Oracle SQL and I have a product table with diffrent attributes and sales volume for each product and another table with certain exclusion rules for different level of aggregation. Let's look at the example:
Here is our main table with sales data on which we want to perform some calculations:
And the other table contains diffrent rules which are supposed to exclude certain rows from table above:
When there is an "x", this column shouldn't be considered so our rules are:
1. exclude all rows with ATTR_3 = 'no'
2. exlcude all rows with ATTR_1 = 'Europe' and ATTR_2 = 'snacks' and ATTR_3 = 'no'
3. exlcude all rows with ATTR_1 = 'Africa'
And based on that our final output should be like that:
How this could be achived in SQL? I was thinking about join but I have no idea how to handle different levels of aggregation for exclusions.
I think your expected output is wrong. None of the rules excludes the 2nd row (Europe - snacks - yes).
SQL> with
2 -- sample data
3 test (product_id, attr_1, attr_2, attr_3) as
4 (select 81928 , 'Europe', 'beverages', 'yes' from dual union all
5 select 16534 , 'Europe', 'snacks' , 'yes' from dual union all
6 select 56468 , 'USA' , 'snacks' , 'no' from dual union all
7 select 129921, 'Africa', 'drinks' , 'yes' from dual union all
8 select 123021, 'Africa', 'snacks' , 'yes' from dual union all
9 select 165132, 'USA' , 'drinks' , 'yes' from dual
10 ),
11 rules (attr_1, attr_2, attr_3) as
12 (select 'x' , 'x' , 'no' from dual union all
13 select 'Europe', 'snacks', 'no' from dual union all
14 select 'Africa', 'x' , 'x' from dual
15 )
16 -- query you need
17 select t.*
18 from test t
19 where (t.attr_1, t.attr_2, t.attr_3) not in
20 (select
21 decode(r.attr_1, 'x', t.attr_1, r.attr_1),
22 decode(r.attr_2, 'x', t.attr_2, r.attr_2),
23 decode(r.attr_3, 'x', t.attr_3, r.attr_3)
24 from rules r
25 );
PRODUCT_ID ATTR_1 ATTR_2 ATT
---------- ------ --------- ---
81928 Europe beverages yes
16534 Europe snacks yes
165132 USA drinks yes
SQL>
You can use the join using CASE .. WHEN statement as follows:
SELECT P.*
FROM PRODUCT P
JOIN RULESS R ON
(R.ATTR_1 ='X' OR P.ATTR_1 <> R.ATTR_1)
AND (R.ATTR_2 ='X' OR P.ATTR_2 <> R.ATTR_2)
AND (R.ATTR_3 ='X' OR P.ATTR_3 <> R.ATTR_3)
You can use NOT EXISTS
SELECT *
FROM sales s
WHERE NOT EXISTS (
SELECT 0
FROM attributes a
WHERE ( ( a.attr_1 = s.attr_1 AND a.attr_1 IS NOT NULL )
OR a.attr_1 IS NULL )
AND ( ( a.attr_2 = s.attr_2 AND a.attr_2 IS NOT NULL )
OR a.attr_2 IS NULL )
AND ( ( a.attr_3 = s.attr_3 AND a.attr_3 IS NOT NULL )
OR a.attr_3 IS NULL )
)
where I considered the x values within the attributes table as NULL. If you really have x characters, then you can use :
SELECT *
FROM sales s
WHERE NOT EXISTS (
SELECT 0
FROM attributes a
WHERE ( ( NVL(a.attr_1,'x') = s.attr_1 AND NVL(a.attr_1,'x')!='x' )
OR NVL(a.attr_1,'x')='x' )
AND ( ( NVL(a.attr_2,'x') = s.attr_2 AND NVL(a.attr_2,'x')!='x' )
OR NVL(a.attr_2,'x')='x' )
AND ( ( NVL(a.attr_3,'x') = s.attr_3 AND NVL(a.attr_3,'x')!='x' )
OR NVL(a.attr_3,'x')='x' )
)
instead.
Demo
I would do this with three different not exists:
select p.*
from product p
where not exists (select 1
from rules r
where r.attr_1 = p.attr_1 and r.attr_1 <> 'x'
) and
not exists (select 1
from rules r
where r.attr_2 = p.attr_2 and r.attr_2 <> 'x'
) and
not exists (select 1
from rules r
where r.attr_3 = p.attr_3 and r.attr_3 <> 'x'
) ;
In particular, this can take advantage of indexes on (attr_1), (attri_2) and (attr_3) -- something that is quite handy if you have a moderate number of rules.

SQL order by included character and string

I have a table and i want to colum joint_no column. The column's values are like these
FW-1
FW-2
.
.
.
FW-13
FW-R1
FW-1A
When i ordered them i get this results
FW-1
FW-10
FW-11
FW-12
FW-13
FW-1A
.
.
FW-R1
I want to get this result after sql query
FW-1
FW-1A
FW-2
FW-3
..
FW-13
FW-R1
can anybody help me?
If you can do it, I'd advise you to renumber the values so that the 'logical' order sticks to the alphabetical order. F-1 will then be updated to F-01, or F-001.
If you cannot do it, add a field that will be populated with the 'ordered' form of your code. You 'll then be able to order by the F-001 column and still display the F-1 value
Otherwise ordering your records will rapidly become your nightmare.
Using Patindex to find the first numeric expression as first sort field, then extracting the numeric part as integer as second sortfield and using the whole string as third sort field you might get the desired result.
Declare #a Table (c varchar(50))
Insert Into #a
Select 'FW-1'
Union Select 'FW-10'
Union Select 'FW-11'
Union Select 'FW-12'
Union Select 'FW-13'
Union Select 'FW-1A'
Union Select 'FW-2'
Union Select 'FW-3'
Union Select 'FW-R1'
Union Select 'FW-A1'
;With CTE as
(Select 1 as ID
Union All
Select ID + 1 from CTE where ID < 100
)
Select * from
(
Select c
,PATINDEX('%[0-9]%',c) as s1
,(Select Cast(
(Select Case
When SUBSTRING(c, ID, 1) LIKE '[0-9]'
Then SUBSTRING(c, ID, 1)
Else ''
End
From (Select * from CTE) AS X(ID)
Where ID <= LEN(c)
For XML PATH(''))
as int)
)
as s2
from
#a
) x
order by
s1,s2,c
With the output:
FW-1 4 1 -1
FW-1A 4 1 -1A
FW-2 4 2 -2
FW-3 4 3 -3
FW-10 4 10 -10
FW-11 4 11 -11
FW-12 4 12 -12
FW-13 4 13 -13
FW-A1 5 1 A1
FW-R1 5 1 R1
If the leading part is not fixed (FW-) you might need to add one additional sort field
Declare #a Table (c varchar(50))
Insert Into #a
Select 'FW-1'
Union Select 'FW-10'
Union Select 'FW-11'
Union Select 'FW-12'
Union Select 'FW-13'
Union Select 'FW-1A'
Union Select 'FW-2'
Union Select 'FW-3'
Union Select 'FW-R1'
Union Select 'FW-A1'
Union Select 'AB-A1'
Union Select 'AB-11'
;With CTE as
(Select 1 as ID
Union All
Select ID + 1 from CTE where ID < 100
)
Select * from
(
Select c
,SubString(c,1,PATINDEX('%[0-9]%',c)-1) as S0
,PATINDEX('%[0-9]%',c) as s1
,(Select Cast(
(Select Case
When SUBSTRING(c, ID, 1) LIKE '[0-9]'
Then SUBSTRING(c, ID, 1)
Else ''
End
From (Select * from CTE) AS X(ID)
Where ID <= LEN(c)
For XML PATH(''))
as int)
)
as s2
from
#a
) x
order by
s0,s1,s2,c