简体   繁体   中英

Oracle SQL query comparing multiple rows with same identifier

I'm honestly not sure how to title this - so apologies if it is unclear.

I have two tables I need to compare. One table contains tree names and nodes that belong to that tree. Each Tree_name/Tree_node combo will have its own line. For example:

Table: treenode
| TREE_NAME | TREE_NODE |
|-----------|-----------|
| 1         | A         |
| 1         | B         |
| 1         | C         |
| 1         | D         |
| 1         | E         |
| 2         | A         |
| 2         | B         |
| 2         | D         |
| 3         | C         |
| 3         | D         |
| 3         | E         |
| 3         | F         |

I have another table that contains names of queries and what tree_nodes they use. Example:

Table: queryrecord
| QUERY   | TREE_NODE |
|---------|-----------|
| Alpha   | A         |
| Alpha   | B         |
| Alpha   | D         |
| BRAVO   | A         |
| BRAVO   | B         |
| BRAVO   | D         |
| CHARLIE | A         |
| CHARLIE | B         |
| CHARLIE | F         |

I need to create an SQL where I input the QUERY name, and it returns any 'TREE_NAME' that includes all the nodes associated with the query. So if I input 'ALPHA', it would return TREE_NAME 1 & 2. If I ask it for CHARLIE, it would return nothing. I only have read access, and don't believe I can create temp tables, so I'm not sure if this is possible. Any advice would be amazing. Thank you!

You can use group by and having as follows:

Select t.tree_name
  From tree_node t 
  join query_record q
    on t.tree_node = q.tree_node
 WHERE q.query = 'ALPHA'
Group by t.tree_name
Having count(distinct t.tree_node)
       = (Select count(distinct q.tree_node) query_record q WHERE q.query = 'ALPHA');

You can do this with a join and aggregation. The trick is to count the number of nodes in query_record before joining:

select qr.query, t.tree_name
from (select qr.*,
             count(*) over (partition by query) as num_tree_node
      from query_record qr
     ) qr join
     tree_node t
     on t.tree_node = qr.tree_node
where qr.query = 'ALPHA'
group by qr.query, t.tree_name, qr.num_tree_node
having count(*) = qr.num_tree_node;

Here is a db<>fiddle.

Using an IN condition (a semi-join, which saves time over a join):

with   prep (tree_node) as (select tree_node from queryrecord where query = :q)
select tree_name
from   treenode
where  tree_node in (select tree_node from prep)
group  by tree_name
having count(*) = (select count(*) from prep)
;

:q in the prep subquery (in the with clause) is the bind variable to which you will assign the various QUERY values at runtime.

EDIT

I don't generally set up the test case on online engines; but in a comment below this answer, the OP said the query didn't work for him. So, I set up the example on SQLFiddle, here:

http://sqlfiddle.com/#!4/b575e/2

A couple of notes: for some reason, SQLFiddle thinks table names should be at most eight characters, so I had to change the second table name to queryrec (instead of queryrecord ). I changed the name in the query, too, of course. And, second, I don't know how I can give bind values on SQLFiddle; I hard-coded the name 'Alpha' . (Note also that in the OP's sample data, this query value is not capitalized, while the other two are; of course, text values in SQL are case sensitive, so one should pay attention when testing.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM