sqloracle-database

Oracle SQL select ID where 3 condition share the same ID


My problem is this one I got a query that is like this:

SELECT ID FROM TABLE LEFT JOIN ANOTHERTABLE ON TABLE.ID=ANOTHERTABLE.ID WHERE TABLE.KEY='X' OR TABLE.KEY='Y' OR TABLE.KEY='Z';

When I play this query it will return more than one id... but 3 id are the same (because each condition pick it). I need to select the only id that show up multiple times but without using count... Basically I need to select the only ID that will be select by condition 1, 2 and 3. I don't really understand how to do it.

Do you know how?

EDIT. I was able to find a really strange solution that works but if you can make it easier to read please do it...

SELECT PC_ID 
FROM A a
left join B b 
on a.EDI_ID = b.ID
WHERE b.key='X' AND b.value='some'
AND a.PC_ID in (
SELECT PC_ID  
FROM A a
left join B b
on a.EDI_ID = b.ID
where b.key='Y' AND b.value='something'
)
AND a.PC_ID in (
SELECT PC_ID  
FROM A a 
left join B b 
on a.EDI_ID = b.ID
where b.key='Z' AND b.value='thing'
)

Maybe after this edit it will be easier to help me.

EDIT2.

I have two tables. My main table (B) contains three fields: ID, KEY and VALUE

My table (A) contains two fields: PC_ID (the id I want to select), EDI_ID (the id contained in the main table).

In my main table I am sure that by using key and value with 3 different values ​​(which are passed to me from the frontend) all the IDs of my main table will have a match in my table A (there will therefore be an EDI_ID for each ID). And in turn PC_ID corresponding to the EDI_ID can have many values ​​for each of the three conditions... but only one value will be the same for all three conditions.

So filtering in the main table (B) for key='X' value='condition1' I will get a series of ID.... This set of ID will always have a corresponding EDI_ID in table (A). There will then be a series of PC_ID values ​​that correspond to each EDI_ID selected. Same thing happens for the condition key='Y' value='condition2' Same thing happens for the condition key='Z' value='condition3' But all three of these selects will have only one PC_ID value in common, which is the one I want to select. I hope it's clearer this way.

EDIT3. I made a postgres example of the dataset + query https://www.db-fiddle.com/f/redSAVfixCHUE4zPu5aAkk/11

But like I said in the comment the 3rd query this only works on db fiddle (maybe a difference between oracle and postgres? I'm not sure...)

#this is working here but not working on my real dataset
SELECT A.PC_ID FROM A
INNER JOIN B AS bx ON A.EDI_ID = bx.ID AND bx.KEYV = 'X' AND bx.valuev='some'
INNER JOIN B AS byy ON A.EDI_ID = byy.ID AND byy.KEYV = 'Y' AND byy.valuev='something'
INNER JOIN B AS bz ON A.EDI_ID = bz.ID AND bz.KEYV = 'Z' AND bz.valuev='thing';

the example dataset should be right but I really cannot be 100% sure, I did not make the original dataset so this is still my own interpretation of it. I hope this is a good way for anybody with more skill than me to find out why and how.

FINAL EDIT. I changed the dataset to actually fit what's inside my db: https://www.db-fiddle.com/f/redSAVfixCHUE4zPu5aAkk/15 basically what I did wrong yesterday and the reason why the last one did not work on real data was that EDI_ID always change, while there can be more than one PC_ID that's the same (it was the opposite of what I did yesterday). So no answer that I can use actually worked (not even the last one with LISTAGG). If you can fix it with the new dataset I will accept the first with an answer that actually work and that's different than mine and that don't use COUNT.


Solution

  • If you have duplicate pc_id values then you can use:

    SELECT a.pc_id
    FROM   a
           INNER JOIN b
           ON a.edi_id = b.id
    WHERE  (b.key, b.value) IN (
             ('X', 'some'), ('Y', 'something'), ('Z', 'thing')
           )
    GROUP BY a.pc_id
    HAVING COUNT(DISTINCT b.key) = 3
    

    or, without using COUNT, you can use other aggregation functions:

    SELECT a.pc_id
    FROM   a
           INNER JOIN b
           ON a.edi_id = b.id
    WHERE  (b.key, b.value) IN (
             ('X', 'some'), ('Y', 'something'), ('Z', 'thing')
           )
    GROUP BY a.pc_id
    HAVING MAX(CASE b.key WHEN 'X' THEN 1 END) = 1
    AND    MAX(CASE b.key WHEN 'Y' THEN 1 END) = 1
    AND    MAX(CASE b.key WHEN 'Z' THEN 1 END) = 1
    

    Or, from Oracle 12, MATCH_RECOGNIZE for row-by-row pattern matching:

    SELECT pc_id
    FROM   (
      SELECT a.pc_id,
             b.key
      FROM   a
             INNER JOIN b
             ON a.edi_id = b.id
      WHERE  (b.key, b.value) IN (
               ('X', 'some'), ('Y', 'something'), ('Z', 'thing')
             )
    )
    MATCH_RECOGNIZE(
      PARTITION BY pc_id
      ORDER BY key
      PATTERN (x+ y+ z+)
      DEFINE x AS key = 'X',
             y AS key = 'Y',
             z AS key = 'Z'
    )
    

    Which, for your "final edit" sample data:

    CREATE TABLE A (
      PC_ID INT,
      EDI_ID INT
    );
    
    CREATE TABLE B (
      ID INT,
      KEY VARCHAR(255),
      VALUE VARCHAR(255)
    );
    
    INSERT INTO A (PC_ID, EDI_ID) VALUES (100,10), (100,12), (100,13), (101,20), (102, 30), (103,40), (12, 50),(324, 120);
    
    INSERT INTO B (ID, KEY, VALUE) VALUES
    (10, 'X', 'some'),
    (12, 'Y', 'something'),
    (13, 'Z', 'thing'),
    (20, 'Z', 'thing'),
    (30, 'Z', 'thing'),
    (40, 'Z', 'thing'),
    (50, 'Z', 'thing'),
    (120, 'NO', 'nothing to see');
    

    All output:

    PC_ID
    100

    fiddle