Multiple criteria on the same column - sql

How do I search with multiple criteria on the same column.
T1 has the IDs.
T2:
ID T1_ID(FK) Value
1 1 Apple
2 1 Orange
3 1 Kiwi
4 2 Orange
5 2 Kiwi
6 3 Pear
7 3 Berry
8 3 Orange
9 4 Apple
10 5 Apple
11 5 Apple
12 5 Kiwi
Output:
T2_ID(FK) Value
1 Apple
1 Orange
1 Kiwi
select t2.t1_id, t2.value
from t1, t2
where t1.id = t2.id
and t2.value in ('Apple','Orange','Kiwi')
group by t1.id having count(t2.value)=3
Is this query correct? Doesn't it also bring t2_id = 5 because #5 matches with apple and kiwi although apple is duplicate?

You don't need to join t1 and you need COUNT(DISTINCT column_name):
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE t2 (ID, T1_ID, Value ) AS
SELECT 1, 1, 'Apple' FROM DUAL UNION ALL
SELECT 2, 1, 'Orange' FROM DUAL UNION ALL
SELECT 3, 1, 'Kiwi' FROM DUAL UNION ALL
SELECT 4, 2, 'Orange' FROM DUAL UNION ALL
SELECT 5, 2, 'Kiwi' FROM DUAL UNION ALL
SELECT 6, 3, 'Pear' FROM DUAL UNION ALL
SELECT 7, 3, 'Berry' FROM DUAL UNION ALL
SELECT 8, 3, 'Orange' FROM DUAL UNION ALL
SELECT 9, 4, 'Apple' FROM DUAL UNION ALL
SELECT 10, 5, 'Apple' FROM DUAL UNION ALL
SELECT 11, 5, 'Apple' FROM DUAL UNION ALL
SELECT 12, 5, 'Kiwi' FROM DUAL;
Query 1:
select t1_id,
LISTAGG( value, ',' ) WITHIN GROUP ( ORDER BY value ) As "values"
from t2
where value in ('Apple','Orange','Kiwi')
group by t1_id
having count( DISTINCT value) = 3
Results:
| T1_ID | values |
|-------|-------------------|
| 1 | Apple,Kiwi,Orange |
Query 2:
You can also do it using collections:
CREATE TYPE STRINGLIST IS TABLE OF VARCHAR2(10);
/
SELECT *
FROM (
SELECT t1_id,
CAST( COLLECT( value ORDER BY value ) AS STRINGLIST ) AS "values"
FROM t2
GROUP BY t1_id
)
WHERE STRINGLIST( 'Apple', 'Kiwi', 'Orange' ) SUBMULTISET OF "values"
Results:
| T1_ID | values |
|-------|-------------------|
| 1 | Apple,Kiwi,Orange |

Related

spaces in the result of a listagg

hello I have 2 database;
table1:IDSPUBPIPE
ID; ID1
1;1
1;2
1;3
2;1
3;3
4;1
5;2
6;1
table2:IDSPUBCIRCUIT
ID;NOM
1; test1
2; test2
3; test3
4; test4
5; test5
6; test6
result hope
ID;ID1;nom
1;1,2,4,6;test1,test2,test4,test6
2;1,5;test1,test5
obtained result
ID;ID1;nom
1;1,2,4,6;t e s t 1 , t e s t 2 , t e s t 4 , t e s t 6
2;1,5;t e s t 1 , t e s t 5
select cast(pipeci.ID as numeric) as ID,
cast(pipeci.ID1 as numeric) as ID1,
cast(RSF_cir.ID as numeric) as ID_circuit,
rsf_cir.NOM,
LISTAGG(RSF_cir.NOM, '; ') WITHIN GROUP (ORDER BY pipeci.ID1, RSF_cir.NOM)
OVER (PARTITION BY pipeci.ID1) as Emp_list,
count(RSF_cir.ID) over(partition by pipeci.ID1) as NB_circuit
FROM IDSPUBPIPE pipeci
LEFT JOIN IDSPUBCIRCUIT RSF_cir ON pipeci.ID=RSF_cir.ID
I don't understand the cause of the spaces between each letter, and I can't seem to find a solution
thank you in advance for your leads
[copieecran][1]
[1]: https://i.stack.imgur.com/PipH1.jpg
You can use:
SELECT p.ID1,
LISTAGG( r.ID, ',' ) WITHIN GROUP ( ORDER BY r.NOM ) as ID_circuit,
LISTAGG( r.NOM, ',' ) WITHIN GROUP ( ORDER BY r.NOM ) AS Emp_list
FROM IDSPUBPIPE p
LEFT OUTER JOIN IDSPUBCIRCUIT r
ON ( p.ID = r.ID )
GROUP BY p.ID1
So, for your test data:
CREATE TABLE IDSPUBPIPE ( ID, ID1 ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 1, 3 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 3 FROM DUAL UNION ALL
SELECT 4, 1 FROM DUAL UNION ALL
SELECT 5, 2 FROM DUAL UNION ALL
SELECT 6, 1 FROM DUAL;
CREATE TABLE IDSPUBCIRCUIT ( ID, NOM ) AS
SELECT 1, 'test1' FROM DUAL UNION ALL
SELECT 2, 'test2' FROM DUAL UNION ALL
SELECT 3, 'test3' FROM DUAL UNION ALL
SELECT 4, 'test4' FROM DUAL UNION ALL
SELECT 5, 'test5' FROM DUAL UNION ALL
SELECT 6, 'test6' FROM DUAL;
This outputs:
ID1 | ID_CIRCUIT | EMP_LIST
--: | :--------- | :----------------------
1 | 1,2,4,6 | test1,test2,test4,test6
2 | 1,5 | test1,test5
3 | 1,3 | test1,test3
db<>fiddle here

recursive query to find the root parent in oracle

I am trying to figure out the root parent in a table with hierarchical data. The following example works as expected but I need to do something extra. I want to avoid the query to ignore null id1 and show the (root parent - 1) if the root parent is null.
with table_a ( id1, child_id ) as (
select null, 1 from dual union all
select 1, 2 from dual union all
select 2, 3 from dual union all
select 3, NULL from dual union all
select 4, NULL from dual union all
select 5, 6 from dual union all
select 6, 7 from dual union all
select 7, 8 from dual union all
select 8, NULL from dual
)
select connect_by_root id1 as id, id1 as root_parent_id
from table_a
where connect_by_isleaf = 1
connect by child_id = prior id1
order by id 1
This brings up the following data
4 4
6 5
7 5
8 5
5 5
3 null
null null
2 null
1 null
what I want is
3 1
1 1
2 1
4 4
7 5
8 5
5 5
6 5
is it possible?
Thanks for the help
Using a recursive CTE you can do:
with table_a ( id1, child_id ) as (
select null, 1 from dual union all
select 1, 2 from dual union all
select 2, 3 from dual union all
select 3, NULL from dual union all
select 4, NULL from dual union all
select 5, 6 from dual union all
select 6, 7 from dual union all
select 7, 8 from dual union all
select 8, NULL from dual
),
n (s, e) as (
select id1 as s, child_id as e from table_a where id1 not in
(select child_id from table_a
where id1 is not null and child_id is not null)
union all
select n.s, a.child_id
from n
join table_a a on a.id1 = n.e
)
select
coalesce(e, s) as c, s
from n
order by s
Result:
C S
- -
3 1
1 1
2 1
4 4
5 5
7 5
8 5
6 5
As a side note, "Recursive CTEs" are more flexible than the old-school CONNECT BY.
This looks like it works but it may be incorrect, as I do not quite understand the logic behind choosing 1 for this, looks arbitrary to me, not much like real data will be.
As Hogan has asked already, it would be helpful if you could perhaps provide an explanation or an expanded data set to test this hierarchy.
with table_a ( id1, child_id ) as (
select null, 1 from dual union all
select 1, 2 from dual union all
select 2, 3 from dual union all
select 3, NULL from dual union all
select 4, NULL from dual union all
select 5, 6 from dual union all
select 6, 7 from dual union all
select 7, 8 from dual union all
select 8, NULL from dual
)
select connect_by_root id1 as id, id1 as root_parent_id
from table_a
where connect_by_isleaf = 1 and connect_by_root id1 is not null
connect by nocycle child_id = prior nvl(id1, 1)
order by 2, 1;
Sample execution:
FSITJA#dbd01 2019-07-19 13:51:13> with table_a ( id1, child_id ) as (
2 select null, 1 from dual union all
3 select 1, 2 from dual union all
4 select 2, 3 from dual union all
5 select 3, NULL from dual union all
6 select 4, NULL from dual union all
7 select 5, 6 from dual union all
8 select 6, 7 from dual union all
9 select 7, 8 from dual union all
10 select 8, NULL from dual
11 )
12 select connect_by_root id1 as id, id1 as root_parent_id
13 from table_a
14 where connect_by_isleaf = 1 and connect_by_root id1 is not null
15 connect by nocycle child_id = prior nvl(id1, 1)
16 order by 2, 1;
ID ROOT_PARENT_ID
---------- --------------
1 1
2 1
3 1
4 4
5 5
6 5
7 5
8 5
8 rows selected.

Find rows with consecutive ones

I've two integer columns and need to display the rows with consecutive one's in the NUM column.
Sample data:
CREATE TABLE table_name ( ID, NUM ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 4, 2 FROM DUAL UNION ALL
SELECT 5, 1 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL UNION ALL
SELECT 7, 2 FROM DUAL;
Expected Output:
ID NUM
-- ---
1 1
2 1
3 1
I have tried using self-joins and achieved the result:
WITH TAB (ID, NUM) AS
(
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 4, 2 FROM DUAL UNION ALL
SELECT 5, 1 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL UNION ALL
SELECT 7, 2 FROM DUAL
)
SELECT DISTINCT
T.ID,
T.NUM
FROM
TAB T
JOIN (
SELECT
T1.ID ID1,
T2.ID ID2,
T1.NUM,
COUNT(1) OVER(
PARTITION BY T1.NUM
) RN
FROM
TAB T1
JOIN TAB T2 ON ( T1.NUM = T2.NUM
AND T1.ID = T2.ID + 1 )
) T_IN ON ( ( T.ID = T_IN.ID1
OR T.ID = T_IN.ID2 )
AND T.NUM = T_IN.NUM
AND RN >= 2 ) -- THIS CONDITION IS TO RESTRICT CONSECUTIVES LESS THAN 3
ORDER BY
1
output:
db<>fiddle demo
Use analytic functions LAG or LEAD:
Oracle Setup:
CREATE TABLE table_name ( ID, NUM ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 4, 2 FROM DUAL UNION ALL
SELECT 5, 1 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL UNION ALL
SELECT 7, 2 FROM DUAL;
Query:
SELECT id,num
FROM (
SELECT id,
num,
LAG( num ) OVER ( ORDER BY id ) AS prev_num,
LEAD( num ) OVER ( ORDER BY id ) AS next_num
FROM table_name
)
WHERE num = 1
AND ( num = prev_num
OR num = next_num )
Output:
ID | NUM
-: | --:
1 | 1
2 | 1
3 | 1
db<>fiddle here

If a then b check in where clause

I am performing some data quality checks to identify bad data, I am unable to figure out how I can perform a check-such that the data is accurately mapped based on Value 1 vs Value 2.
I ultimately need to identify all IDs in T1 that have incorrect mapping in T2.I have used the following code but doesn't seem to give desired result. The mapping is not in the database and is a rule based on which the data needs to be entered.
- When value in: Apples,Bananas,Cherries,Pears,Kiwis - then it should be mapped to Fruit
- when value in: Cheese - then Cheese
- when value in: Cashews,Almonds - then Nuts
- when value in: Skittles - then Candy
- when value in: Chocolate - then null
Edit: I have added the desired output.
SELECT t1.id, t2.*
FROM t1,t2,t3
WHERE
t1.id = t2.id
AND (
(t2.value1_id IN (01,04,05,08,09) AND t2.value2_id <> 2)
OR (t2.value1_id = 02 and t2.value2_id <> 3)
OR (t2.value1_id IN (03,10) and t2.value2_id <> 1)
OR (t2.value1_id = 06 AND t2.value2_id <> 4)
OR (t2.value1_id = 07 AND t2.value_id IS NOT NULL)
)
T1
ID
1
2
3
4
5
6
7
T2
T1.ID Value1_ID Value2_ID
1 01 2
1 02 3
1 03 1
2 04 2
2 05 2
2 02 3
2 06 4
2 07
3 08 2
3 02 3
4 09 2
4 10 1
5 02 2
5 10 1
6 04 3
6 10 2
7 07 2
T3
ID Value1
01 Apples
02 Cheese
03 Cashews
04 Bananas
05 Cherries
06 Skittles
07 Chocolate
08 Pears
09 Kiwis
10 Almonds
T4
ID Value2
1 Nuts
2 Fruit
3 Cheese
4 Candy
Desired Output:
T1.ID Value1_ID Value2_ID
5 02 2
6 04 3
6 10 2
7 07 2
T1.ID 5, value1_id 02 is in the desired output as Cheese is mapped to Fruit
T1.ID 6, value1_id 04 - Bananas is mapped to Cheese
T1.ID 6, value1_id 10 - Almonds is mapped to Fruit
T1.ID 7, value1_id 07 - Chocolate is mapped to Fruit when it should be null
One of the problems is that - when looking at T2 - it is not easy to tell whether a "mapping" is correct or not.
When creating the test data for T1 and T2, we have used CHARs for VALUE1_IDs, in order to make the subsequent queries a bit more "readable".
Tables
create table T1( id primary key )
as
select 1 from dual union all
select 2 from dual union all
select 3 from dual ;
create table T2 ( id, value1_id, value2_id )
as
select 1, '01', 2 from dual union all
select 1, '02', 3 from dual union all
select 1, '03', 1 from dual union all
select 2, '04', 2 from dual union all
select 2, '05', 2 from dual union all
select 2, '02', 3 from dual union all
select 2, '06', 4 from dual union all
select 2, '07', null from dual union all
select 3, '08', 2 from dual union all
select 3, '02', 3 from dual union all
select 4, '09', 2 from dual union all
select 4, '10', 1 from dual ;
Refactored query
--
-- find incorrect mappings
--
select t2.*, 'T1 id not valid' as status
from t2
where t2.id not in ( select id from T1 )
union all
select t2.*, 'value1_id <-> value2_id mapping incorrect '
from t1 join t2 on t1.id = t2.id
where
( t2.value1_id in ('01','04','05','08','09') and t2.value2_id <> 2 )
or
( t2.value1_id = '02' and t2.value2_id <> 3 )
or
( t2.value1_id in ('03','10') and t2.value2_id <> 1 )
or
( t2.value1_id = '06' and t2.value2_id <> 4 )
or
( t2.value1_id = '07' and t2.value2_id is null )
;
-- result
ID VALUE1_ID VALUE2_ID STATUS
4 10 1 T1 id not valid
4 09 2 T1 id not valid
2 07 NULL value1_id <-> value2_id mapping incorrect
DBfiddle
ALTERNATIVE
Another possibility may be: create a table, containing all valid mappings, in "human readable" form, and use it to validate the mappings stored in T2. However, use whatever approach you are more comfortable with - as long as you get the correct results. Example (tested w/ Oracle 12c, 18c)
-- in addition to tables T1, T2, T3, and T4: table with correct mappings
create table map( category, product )
as
select 'Fruit', 'Apples' from dual union all
select 'Cheese', 'Cheese' from dual union all
select 'Nuts', 'Cashews' from dual union all
select 'Fruit', 'Bananas' from dual union all
select 'Fruit', 'Cherries' from dual union all
select 'Candy', 'Skittles' from dual union all
select 'Candy', 'Chocolate' from dual union all
select 'Fruit', 'Pears' from dual union all
select 'Fruit', 'Kiwis' from dual union all
select 'Nuts', 'Almonds' from dual;
-- make sure that the entries in the MAP table tie in with T3 and T4
alter table map
add (
constraint m_pk primary key ( category, product )
, constraint m_category_fk foreign key ( category ) references T4 ( value2 )
, constraint m_product_fk foreign key ( product ) references T3 ( value1 )
) ;
Find incorrect mappings
-- T2 rows containing incorrect (invalid) mappings
-- -> all rows MINUS the correct ones
select T2.id, T2.value1_id, T2.value2_id
from T2
minus (
select T2.id, T2.value1_id, T2.value2_id
from T2
join (
--
select T4.id categoryid, T3.id productid, M.category, M.product
from T4
join map M on T4.value2 = M.category
join T3 on T3.value1 = M.product
--
) C -- correct mappings
on
C.productid = T2.value1_id
and C.categoryid = T2.value2_id
) ;
-- result
ID VALUE1_ID VALUE2_ID
2 07 NULL
DBfiddle
I would highly recommend that you create a table to represent the one-to-many relationship between T4 and T3. This would represent a first step towards fixing your design, while providing a simple way to solve your current question.
Here is a CREATE TABLE ... AS SELECT order that initializes such a table with your sample data :
create table cat AS
SELECT 1 t3_id, 2 t4_id FROM DUAL
UNION ALL SELECT 4, 2 FROM DUAL
UNION ALL SELECT 5, 2 FROM DUAL
UNION ALL SELECT 8, 2 FROM DUAL
UNION ALL SELECT 9, 2 FROM DUAL
UNION ALL SELECT 2, 3 FROM DUAL
UNION ALL SELECT 3, 1 FROM DUAL
UNION ALL SELECT 10, 1 FROM DUAL
UNION ALL SELECT 6, 4 FROM DUAL
UNION ALL SELECT 7, NULL FROM DUAL
;
With this table in place, indentifiying records incorrectly mapped is as simple as :
SELECT t2.*
FROM t2
WHERE t2.Value2_ID IS NOT NULL AND NOT EXISTS (
SELECT 1 FROM cat WHERE cat.t3_id = t2.Value1_ID AND cat.t4_id = t2.Value2_ID
)
This DB Fiddle demo with your sample data yields :
T1_ID | VALUE1_ID | VALUE2_ID
----: | --------: | --------:
5 | 2 | 2
6 | 4 | 3
6 | 10 | 2
7 | 7 | 2
Hint to further improve your design : you have a one-to-many relationship between T4 (families of aliments) and T3 (aliments). The classic way to represent this is to add a column in the child table (T3) that references the parent table.
If you can't create a table with the mappings from fruit to categories and you know that the values are static then just include the mappings into your query using a nested sub-query or a sub-query factoring clause:
Oracle Setup:
create table T2 ( id, value1_id, value2_id ) as
select 1, '01', 2 from dual union all
select 1, '02', 3 from dual union all
select 1, '03', 1 from dual union all
select 2, '04', 2 from dual union all
select 2, '05', 2 from dual union all
select 2, '02', 3 from dual union all
select 2, '06', 4 from dual union all
select 2, '07', null from dual union all
select 3, '08', 2 from dual union all
select 3, '02', 3 from dual union all
select 4, '09', 2 from dual union all
select 4, '10', 1 from dual union all
select 5, '02', 2 from dual union all
select 5, '10', 1 from dual union all
select 6, '04', 3 from dual union all
select 6, '10', 2 from dual union all
select 7, '07', 2 from dual;
Query:
WITH mappings ( name, category ) AS (
SELECT '01', 2 FROM DUAL UNION ALL
SELECT '02', 3 FROM DUAL UNION ALL
SELECT '03', 1 FROM DUAL UNION ALL
SELECT '04', 2 FROM DUAL UNION ALL
SELECT '05', 2 FROM DUAL UNION ALL
SELECT '06', 4 FROM DUAL UNION ALL
SELECT '07', NULL FROM DUAL UNION ALL
SELECT '08', 2 FROM DUAL UNION ALL
SELECT '09', 2 FROM DUAL UNION ALL
SELECT '10', 1 FROM DUAL
)
SELECT *
FROM T2 t
WHERE NOT EXISTS (
SELECT 1
FROM mappings m
WHERE t.value1_id = m.name
AND ( t.value2_id = m.category
OR ( t.value2_id IS NULL AND m.category IS NULL ) )
);
Results:
ID | VALUE1_ID | VALUE2_ID
-: | :-------- | --------:
5 | 02 | 2
6 | 04 | 3
6 | 10 | 2
7 | 07 | 2
db<>fiddle here

SQL grouping issue

Neea a help with grouping in sql.
I've table like
id1 id2 type
1 1 300
1 3 300
1 2 300
1 5 300
2 2 100
2 5 200
2 7 300
4 3 100
4 9 300
4 2 300
I need id1 that is mapped to one type only,
For eg, id1 '1' is mapped only to type 300, so it should only be retrieved If there is more than one type mapped to an id1 it shouldnt be retrieved. Please help.
Here is what I have attempted. But it will handle only for type 300.I need to retrieve all the id1's which are mapped to one particular type alone. So if id1 '2' is mapped for type '100' alone, it should also be retrieved.
SELECT distinct id1 from ID_TABLE where type = 300 and id1 not in
(SELECT id1 from type_table where type in (100, 200, 250))
and id1 in ( SELECT id1 FROM ID_TABLE type=300)
order by id1
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE tbl ( id1, id2, type ) AS
SELECT 1, 1, 300 FROM DUAL
UNION ALL SELECT 1, 3, 300 FROM DUAL
UNION ALL SELECT 1, 2, 300 FROM DUAL
UNION ALL SELECT 1, 5, 300 FROM DUAL
UNION ALL SELECT 2, 2, 100 FROM DUAL
UNION ALL SELECT 2, 5, 200 FROM DUAL
UNION ALL SELECT 2, 7, 300 FROM DUAL
UNION ALL SELECT 4, 3, 100 FROM DUAL
UNION ALL SELECT 4, 9, 300 FROM DUAL
UNION ALL SELECT 4, 2, 300 FROM DUAL
UNION ALL SELECT 4, 4, 200 FROM DUAL
UNION ALL SELECT 5, 2, 200 FROM DUAL
UNION ALL SELECT 5, 4, 200 FROM DUAL;
Query 1:
SELECT id1,
MIN( type )
FROM tbl
GROUP BY id1
HAVING COUNT( DISTINCT type ) = 1
Results:
| ID1 | MIN(TYPE) |
|-----|-----------|
| 1 | 300 |
| 5 | 200 |
After the group by below you may still have multiple id1 so having will limit to only those situations when there is a single id1 for type. Using MIN as an arbitrary aggregator here as there is only one value anyway:
SELECT MIN(id1), type
FROM type_table
GROUP BY type
HAVING COUNT(id1)=1;