2010-06-03 27 views
6

Mam tabelę zwaną transakcjami o relacji wielu do wielu elementów za pośrednictwem tabeli items_transactions.SQL: relacja wiele do wielu, W stanie

chcę zrobić coś takiego:

SELECT "transactions".* 
    FROM "transactions" 
INNER JOIN "items_transactions" 
     ON "items_transactions".transaction_id = "transactions".id 
INNER JOIN "items" 
     ON "items".id = "items_transactions".item_id 
WHERE (items.id IN (<list of items>)) 

Ale to daje mi wszystkie transakcje, które mają jedną lub więcej pozycji na liście z nim związanych, a ja tylko chcę, żeby dać mi transakcje są powiązane z tymi wszystkimi przedmiotami.

Każda pomoc zostanie doceniona.

+1

Twój kod SQL byłby łatwiejszy do odczytania, gdybyś użył klawisza RETURN. –

+0

Zapomniałem zapytać, o którym Sql mówimy - Serwer Sql? – amelvin

+0

Nie wiem jeszcze, jak to zrobić z SQL przez jego własne, ale można napisać dynamiczne zapytanie, w którym dodasz "i item.id = itemX", pierwsza w pętli będzie "gdzie item.id = itemY" .. – eugeneK

Odpowiedz

7

Trzeba poszerzyć swoje zapytanie dla wszystkich elementów w liście:

SELECT "transactions".* 
FROM "transactions" 
WHERE EXISTS (SELECT 1 FROM "items_transactions" 
       INNER JOIN "items" ON "items".id = "items_transactions".item_id 
       WHERE "items_transactions".transaction_id = "transactions".id 
       AND "items".id = <first item in list>) 
AND EXISTS (SELECT 1 FROM "items_transactions" 
       INNER JOIN "items" ON "items".id = "items_transactions".item_id 
       WHERE "items_transactions".transaction_id = "transactions".id 
       AND "items".id = <second item in list>) 
... 

Możesz również być w stanie go pomasować za pomocą IN i COUNT DISTINCT, nie jestem pewien, który byłby szybszy. Coś jak (całkowicie nieprzetestowana):

SELECT "transactions".* 
FROM "transactions" 
INNER JOIN (SELECT "items_transactions".transaction_id 
      FROM "items_transactions" 
      INNER JOIN "items" ON "items".id = "items_transactions".item_id 
      WHERE "items".id IN (<list of items>) 
      GROUP BY "items_transactions".transaction_id 
      HAVING COUNT(DISTINCT "items".id) = <count of items in list>) matches ON transactions.transaction_id = matches.transaction_id 
+0

Czy naprawdę nie ma lepszego sposobu na zrobienie tego? Na liście znajduje się 100 pozycji, nie sądzę, że postgres może obsługiwać długie zapytania. – Maarten

+0

Podoba mi się użycie 'count = liczba pozycji na liście' – eugeneK

+0

Pomysł na count działa doskonale, musiałem tylko zmienić transaction.transactions_id na "transaction" .id dla mojej sprawy. Wielkie dzięki! – Maarten

0

Ostateczna nieco zapytania wygląda źle:

WHERE (items.id IN (<list of items>)) 

„in” oświadczenie jest jak duże lub oświadczenia zamiast AND rachunku, więc to jest rozszerzany przez optymalizator jak:

WHERE (items.id = 123 OR items.id = 456 OR items.id = 789) 

EDIT

Sądzę trzeba wykonać correlated subquery na stole pozycji.

+1

(items.id = 123 i items.id = 456 i items.id = 789) nigdy nie będą prawdziwe. – dcp

+0

@dcp teraz o tym wspomnieć, to wygląda trochę nieprawdopodobnie. – amelvin

0

ja nie wykonaliśmy tego, ale które powinny Ci wynik chcesz:

SELECT t.* FROM items i 
    INNER JOIN items_transactions it ON i.id = it.item_id 
     INNER JOIN transactions t ON it.transaction_id = t.id 
WHERE i.id IN (1,2,3) 
+2

Chce transakcji, które są powiązane z * wszystkimi * przedmiotami, ale twoje rozwiązanie da mu transakcje powiązane z którymkolwiek z nich. – dcp

1

Myślę, że robi to, co chcesz.

Chciałbym umieścić listę potrzebnych przedmiotów w tabeli (temp pierwszy będzie w porządku) i dołączyć do tego. Następnie policz liczbę różnych elementów i dopasuj liczbę do liczby transakcji.

Podałem próbkę DDL & Dane, których użyłem.

Create table #trans 
(
transId int identity(1,1), 
trans varchar(10) 
) 

Create Table #itemTrans 
(
transId int, 
itemId int 
) 

Create table #items 
(
itemId int identity(1,1), 
item varchar(10) 
) 

Create table #itemsToSelect 
(
itemId int 
) 


Insert Into #trans 
Values ('Trans 1') 

Insert Into #trans 
Values ('Trans 2') 

Insert Into #trans 
Values ('Trans 3') 


Insert Into #Items 
Values ('Item 1') 

Insert Into #Items 
Values ('Item 2') 

Insert Into #Items 
Values ('Item 3') 

Insert Into #Items 
Values ('Item 4') 

Insert Into #itemTrans 
Values (1, 1) 

Insert Into #itemTrans 
Values (1, 2) 

Insert Into #itemTrans 
Values (1, 3) 

Insert Into #itemTrans 
Values (2, 1) 

Insert Into #itemTrans 
Values (2, 3) 

Insert Into #itemTrans 
Values (3, 4) 



Insert Into #itemsToSelect 
Values (1) 
Insert Into #itemsToSelect 
Values (2) 
Insert Into #itemsToSelect 
Values (3) 


Select t.transId 

From #items i 
Join #itemTrans it on i.itemId = it.itemId 
Join #trans t on it.transId = t.transId 

Join #itemsToSelect its on it.ItemId = its.ItemId 

Where it.TransId is not null 
Group by t.transId 
Having count(distinct(it.itemId)) = (Select count(distinct(itemId)) from #itemsToSelect) 
1
SELECT transactions.* 
WHERE (SELECT count(*) 
     FROM items_transactions 
     WHERE items_transactions.transaction_id = transactions.transaction_id 
      AND items_transactions.item_id IN (<list of items>) 
    ) = <number of items> 

Choć będzie to prawdopodobnie zrobić skan transakcji gniazdowania skorelowanej podzapytania dla każdego z nich ... nie jest szczególnie wydajny, więc może:

SELECT transactions.* 
WHERE EXISTS (SELECT 1 FROM items_transactions 
       WHERE items_transactions.transaction_id = transactions.transaction_id 
       AND items_transactions.item_id IN (<list of items>) 
    ) 
     AND 
     (SELECT count(*) 
     FROM items_transactions 
     WHERE items_transactions.transaction_id = transactions.transaction_id 
      AND items_transactions.item_id IN (<list of items>) 
    ) = <number of items> 

lub coś podobnego, aby przekonać DB, aby najpierw znaleźć transakcje związane z co najmniej jedną pozycją, a następnie sprawdzić, czy każda transakcja jest później połączona z wszystkimi przedmiotami.

Jak ktoś zauważył, można również po prostu wygenerować klauzule join dla każdego elementu, co może być lepsze, jeśli liczba elementów nie jest duża.