Вопрос

I have 2 tables with the following schemas:

t1(id, type) where id and type are the primary key

t2(type, rate) where type is the primary key

I want to find all id's which appear with all types from t2.

For example given the tables:

t1:

 id | type
 ---------
  1 | a 
  1 | b
  1 | c
  2 | a
  2 | b

and t2:

 type | rate
 -----------
    a | 100
    b | 100
    c | 200

We can see that there're 3 unique types in t2 and only id 2 from t1 appears with all of them.

I figured out a way to get the id's by counting the number of unique types and then grouping t1 by id's and selecting only those whose count equals of the total number of types from t2.

But I was wondering if there're other logical constructs like in/not in, exists or Cartesian product which can also be used here with more elegance?

Это было полезно?

Решение

I don't know postgresql but shouldn't the following work?

select id
from t1
join t2 on t1.type = t2.type
group by id
having count(1) = (select count(1) from t2)

You are selecting the ID values.

The join means the type value on t1 must exist in t2. (I note you did not explicitly state there is a foreign key relationship there - hence this join, to avoid the case where, say, you have three distinct type values in t1 for a given ID value, but they are not the same three distinct type values in t2).

We use group by for two reasons: first, it means each ID will receive only 1 row in the output, but more importantly, this allows us to use the aggregate count function.

The count function essentially counts the number of rows for the given ID value (given the group by clause) and we are saying that number of rows must match the number of rows in t2. We can leave it as simply as that because type - the column we are interested in - in t2, is a key column and so will be unique across each row.

Wondering if I've missed something, in my postgresql ignorance?

Edit: Seems to work fine to me. I've used the setup SQL from another answer here to create a SQL Fiddle showing my solution against your question data.

View the SQL Fiddle

Другие советы

I dont think there is any logical constructs like in/not in.

But you can check array equality like below,

select id from 
(select array_agg(type)as type, id from t1  group by id) table_1 
join 
(select array_agg(type)as type from t2)table_2 
on table_1.type=table_2.type;

May be this is useful to you :)

drop table if exists t1;
drop table if exists t2;
create table t1 (
    id int
    ,type varchar(1)
);

-- PKs are not necessary for the solution, but was part of OP's question,
-- so I included it anyway
alter table t1 add primary key (id, type);

create table t2 (
    type varchar(1) primary key
    ,rate int
);

insert into t1 (id, type) values
--(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'); -- test 1
(3, 'a'), (3, 'b'), (3, 'c'), (4, 'a'), (4, 'b'), (4, 'c'); -- test 2
--(5, 'a'), (5, 'b'), (5, 'c'); -- test 3

insert into t2 (type, rate) values
('a', 100), ('b', 100), ('c', 200);

with cteGroupCounts as (
    select id, count(*) as id_count from t1
    group by id
)
, cteMaxCount as (
    select max(id_count) as max_id_count
    from cteGroupCounts
)
, cteList as (
    select id from cteGroupCounts as G
    inner join cteMaxCount as M on G.id_count = M.max_id_count
)
select id from cteList
;

Somehow I think you may be able to solve this with windowing functions, but this seems to work.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top