2012-07-04 9 views
5

I tej tabelinajbardziej wydajny SQL dla pobierania

---------------- 
| X | Y | 
---------------- 
| a | 1 | 
| c | 6 | 
| e | 3 | 
| d | 6 | 
| c | 4 | 
| b | 1 | 
| a | 5 | 
| g | 1 | 
---------------- 

Gdy ja podano szereg [c, d] Należy znaleźć „6” w tabeli powyżej. To znaczy. dla każdego zestawu elementów muszę znaleźć wartość Y, która jest wspólna dla wszystkich elementów w zestawie, ale tylko wtedy, gdy nie ma innego elementu (tj. elementu, który nie znajduje się w danej tablicy), który współużytkuje tę wartość. Liczba elementów w tablicy nie ma teoretycznych limitów.

Więcej przykładów: dla [a, b, c] Nie muszę niczego szukać. Dla [a, b] również nie znajduję niczego (ponieważ g ma również wpis dla Y = 1, więc dla [a, b, g] muszę znaleźć "1").

Mogę oczywiście iterować po tablicy, zapytać przez zapytanie, ale wydaje się to nieefektywnym sposobem zrobienia tego. Jaki jest najlepszy sposób robienia tego w SQL? Dziękuję Ci.

+1

[Co próbowałeś?] (Http://whathaveyoutried.com) Czy starasz się przekazać swoją „tablicę” do zapytania przy użyciu listy NA? –

+0

Jakiego RMDB używasz? –

+0

Najlepsze jest subiektywne. Zależy to od posiadanych danych, używanej bazy danych i wersji oraz dostępnych indeksów. –

Odpowiedz

2

Oto sposób na uzyskanie wartości "zapytania" w oddzielnej tabeli.

create table t (x varchar(1), y int); 

insert into t (x, y) values ('a', 1); 
insert into t (x, y) values ('c', 6); 
insert into t (x, y) values ('e', 3); 
insert into t (x, y) values ('d', 6); 
insert into t (x, y) values ('c', 4); 
insert into t (x, y) values ('b', 1); 
insert into t (x, y) values ('a', 5); 
insert into t (x, y) values ('g', 1); 

create table q (x varchar(1)); 

insert into q (x) values ('a'); 
insert into q (x) values ('b'); 

select a.y from 
(
    select t.y 
    from t join q on (t.x = q.x) 
    group by t.y 
    having count(*) = (select count(*) from q) 
) a 
join t on (a.y = t.y) 
group by a.y 
having count(*) = (select count(*) from q) 

Here's an example SQLFiddle.

Zakłada się, że nie można mieć duplikatów kombinacji.

Jeśli chcesz zrobić to bez drugiej tabeli, możesz zastąpić select count(*) liczbą wartości, do której pasujesz na liście IN, i zamiast wykonywać łączenie w podzapytaniu wewnętrznym, użyj klauzuli where.

select a.y from 
(
select t.y 
from t 
    where t.x in ('c', 'd') 
group by t.y 
having count(*) = 2 
) a 
join t on (a.y = t.y) 
group by a.y 
having count(*) = 2 
+0

Potrzebujesz GROUP BY w zewnętrznych zapytaniach. Ale ładne myślenie boczne, aby zrobić to za pomocą jednego stołu. Myślę, że podejście LEFT JOIN będzie mniej intensywne (jedno połączenie i jedna grupa, zamiast dwóch złączeń i dwóch grupowych klocków), ale wydajność będzie bardzo słaba, tak więc wydaje mi się to dobre! (+1) – MatBailie

+0

@Dems - Dzięki. MySQL nie potrzebuje grupy, ponieważ niejawnie grupuje, ale prawdopodobnie powinna ją uwzględnić, aby ułatwić jej czytanie. –

+0

@NWest: Bardzo podoba mi się twoje drugie rozwiązanie, to wnikliwe, wydaje się również działać. Dzięki! – Vincent

2

Te typy zapytań są nigdy szczególnie wydajnych na dużych zestawów danych i/lub gdzie wiele wartości w akcje te same wartości Y w X.

Powiedział, że jest to prosta wersja mojego normalny sposób robi to ...

CREATE TEMPORARY TABLE params (
    item VARCHAR(16) 
) 
INSERT INTO params SELECT 'a' 
INSERT INTO params SELECT 'b' 
INSERT INTO params SELECT 'g' 


SELECT 
    yourTable.Y 
FROM 
    yourTable 
LEFT JOIN 
    params 
    ON yourTable.X = params.item 
GROUP BY 
    yourTable.Y 
HAVING 
    COUNT(DISTINCT yourTable.X) = COUNT(DISTINCT params.item) 


Inną opcją, która nie potrzebuje tabeli parametrów, choć nie sądzę, że jest bardziej wydajnych ...

SELECT 
    y 
FROM 
    yourTable 
GROUP BY 
    y 
HAVING 
    COUNT(DISTINCT x) = COUNT(DISTINCT CASE WHEN x IN ('a', 'b', 'g') THEN x ELSE NULL END) 

Nie ma połączeń, ale odbywa się to kosztem pełnego skanowania tabeli.

+2

To również przyniesie prawdziwe wyniki dla {a, b} count = 2. Jego przykład z {a, b} (np. Bez g) nie powinien dać niczego. (jeśli dobrze zrozumiałem) BTW: to wygląda jak sudeku ... – wildplasser

+0

@wildplasser - Dobre miejsce, nie czytałem tego kawałka *** oops ***. Zaktualizowano LEFT JOIN na tabeli i zaktualizowałem HAVING, aby stwierdzić, że obie strony muszą mieć taką samą liczbę odrębnych elementów. – MatBailie

+0

@Dems, to rozwiązanie jest prawdopodobnie również dobre, dziękuję bardzo, ale używam Symfony i DQL, więc chciałbym omijać tworzenie tabel param. I zdecydowanie nie chcę pełnego skanowania tabeli. – Vincent

Powiązane problemy