2012-08-04 2 views
3

У меня есть две таблицы следующим образом:Как создать запрос подмножества в sql?

CREATE List (
    id INTEGER, 
    type INTEGER REFERENCES Types(id), 
    data TEXT, 
    PRIMARY_KEY(id, type) 
); 

CREATE Types (
    id INTEGER PRIMARY KEY, 
    name TEXT 
); 

Теперь я хочу, чтобы создать запрос, который определяет все идентификаторы из List, который дал строки типа.

Например,

 
List: 
1 0 "Some text" 
1 1 "Moar text" 
2 0 "Foo" 
3 1 "Bar" 
3 2 "BarBaz" 
4 0 "Baz" 
4 1 "FooBar" 
4 2 "FooBarBaz" 

Types: 
0 "Key1" 
1 "Key2" 
2 "Key3" 

Учитывая вход "Клавиша1", "Клавиша2", запрос должен возвращать 1, 4.

Учитывая вход "Клавиша2", "Key3", запрос должен вернуть 3, 4.

Учитывая вход "Key2", запрос должен возвращать 1, 3, 4.

Спасибо!

+0

вы ищете SQL, которые будут работать в (почти) все СУБД или для конкретного? –

+0

@ypercube Я искал общее решение, извините за то, что не указал – chacham15

ответ

5
select distinct l.id 
from list l 
inner join types t on t.id = l.type 
where t.name in ('key1', 'key2') 
group by l.id 
having count(distinct t.id) = 2 

Вы должны отрегулировать предложение having к числу ключей, которые вы помещаете в свое предложение where. Пример только для одного ключа:

select distinct l.id 
from list l 
inner join types t on t.id = l.type 
where t.name in ('key2') 
group by l.id 
having count(distinct t.id) = 1 

SQlFiddle example

+0

, это будет неправильно возвращено 2 – chacham15

+0

Вы правы. Починил это. –

+0

это теперь будет неправильно обрабатывать 3-й случай – chacham15

2

Вы можете использовать следующий трюк, чтобы расширить идею Юргена:

with keys as (
    select distinct t.id 
    from types t 
    where t.name in ('key1', 'key2') 
) 
select l.id 
from list l join 
    keys k 
    on l.type = keys.id cross join 
    (select count(*) as keycnt from keys) k 
group by l.id 
having count(t.id) = max(k.keycnt) 

То есть, вычислить ключи соответсвующей в подзапрос, а затем использовать это для подсчетов. Таким образом, вы должны изменить только одну строку, чтобы ввести ключевые значения, и вы можете иметь столько ключей, сколько хотите. (Так же, как примечание, я не проверял этот SQL, так что я прошу прощения за любые ошибки синтаксиса.)

+0

+1 для усилий, но я не думаю, что sqlite поддерживает этот тип синтаксиса – chacham15

+0

@ chacham15: Your вопрос не имеет упоминания о SQLite. ANd CTE (часть 'with') может быть легко перенесена в подзапрос (но вам придется писать список ключей дважды) –

+0

@ypercube, вы правы, я не упоминал об этом, потому что мне нужен кросс-SQL-совместимый решение. но я понял решение и дал ему +1 – chacham15

1

Если вы можете динамически произвести SQL, это может быть один из самых ЭФФЕКТИВНЫХ способов, во многой СУБД:

SELECT l.id 
FROM List l 
    JOIN Types t1 ON t1.id = l.type 
    JOIN Types t2 ON t2.id = l.type 
WHERE t1.name = 'Key1' 
    AND t2.name = 'Key2' ; 

Смотрите этот же вопрос, с более чем 10 способами, чтобы получить тот же результат, плюс некоторые тесты (для Postgres): How to filter SQL results in a has-many-through relation

+0

+1 Это работает, но менее эффективно и сложнее, чем другие ответы. – chacham15

+0

На самом деле, он будет, как правило, более эффективным, чем решения с 'GROUP BY'. Но да, сложнее построить. Это не общий запрос. –

+0

@ chacham15: Возможно, вы можете выполнить некоторые тесты производительности с помощью SQLite и сообщить нам результаты. –

Смежные вопросы