2010-06-03 50 views
6

Tôi có một bảng được gọi là các giao dịch có mối quan hệ nhiều-nhiều với các mục thông qua bảng items_transactions.SQL: mối quan hệ nhiều-nhiều, trong điều kiện

tôi muốn làm một cái gì đó như thế này:

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>)) 

Nhưng điều này mang lại cho tôi tất cả các giao dịch mà có một hoặc nhiều của các mục trong danh sách liên kết với nó và tôi chỉ muốn nó cho tôi giao dịch được liên kết với tất cả các mục đó.

Mọi trợ giúp sẽ được đánh giá cao.

+1

Mã SQL của bạn sẽ dễ đọc hơn nếu bạn đã sử dụng phím RETURN. –

+0

Quên hỏi, chúng ta đang nói về Sql nào - Sql Server? – amelvin

+0

Tôi chưa biết làm thế nào để làm điều này với SQL bởi nó là của riêng nhưng bạn có thể viết truy vấn động nơi bạn sẽ thêm 'và item.id = itemX', đầu tiên trong vòng lặp sẽ là 'where item.id = itemY' .. – eugeneK

Trả lời

7

Bạn cần phải mở rộng ra truy vấn của bạn cho tất cả các mục trong danh sách:

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>) 
... 

Bạn cũng có thể xoa bóp nó bằng cách sử dụng INCOUNT DISTINCT, tôi không chắc chắn sẽ nhanh hơn. Một cái gì đó như (hoàn toàn chưa được kiểm tra):

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

Có thực sự không có cách nào đẹp hơn để làm điều này? Có như 100 mặt hàng trong danh sách, tôi không nghĩ rằng postgres có thể xử lý các truy vấn dài. – Maarten

+0

Tôi thích việc sử dụng 'count = number of items trong danh sách' – eugeneK

+0

Ý tưởng đếm hoạt động hoàn hảo, tôi chỉ phải thay đổi transaction.transactions_id thành "transaction" .id cho trường hợp của tôi. Cảm ơn rất nhiều! – Maarten

0

Các bit cuối cùng của truy vấn trông sai:

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

'trong' tuyên bố là giống như một lớn OR tuyên bố chứ không phải là câu lệnh AND, vì vậy nó được mở rộng bằng cách tôi ưu hoa như:

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

EDIT

tôi nghĩ bạn cần phải thực hiện một correlated subquery trên bảng mục.

+1

(items.id = 123 và items.id = 456 và items.id = 789) sẽ không bao giờ đúng. – dcp

+0

@dcp bây giờ bạn đề cập đến nó, nó trông hơi khó. – amelvin

0

tôi đã không thực hiện điều này, nhưng điều đó sẽ giúp bạn có kết quả bạn muốn:

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

Anh ấy muốn các giao dịch được liên kết với * tất cả * của các mục đó, nhưng giải pháp của bạn sẽ cho anh ta các giao dịch được liên kết với bất kỳ mục nào trong số đó. – dcp

1

Tôi nghĩ rằng điều này làm những gì bạn muốn.

Tôi sẽ đặt danh sách các mục bạn cần vào một bảng (tạm thời một điều sẽ ổn) và tham gia vào đó. Sau đó, đếm số lượng các mục riêng biệt và so khớp số lượng với số lượng giao dịch mặt hàng.

Tôi đã cung cấp mẫu DDL & Dữ liệu mà tôi đã sử dụng.

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> 

Mặc dù điều này có lẽ sẽ làm một quét của các giao dịch, làm tổ subquery tương quan cho mỗi người ... không đặc biệt hiệu quả, vì vậy có thể:

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> 

hoặc một cái gì đó tương tự để thuyết phục DB để tìm các giao dịch liên quan đến ít nhất một trong các mục đầu tiên, và sau đó kiểm tra từng giao dịch được liên kết với tất cả các mục sau này.

Như đã lưu ý của một ai đó, bạn cũng có thể dễ dàng tạo mệnh đề tham gia cho từng mục thay thế, điều này có thể tốt hơn nếu số lượng mục không lớn.

Các vấn đề liên quan