SQL:多对多关系,IN条件

时间:2010-06-03 10:26:18

标签: sql select join many-to-many inner-join

我有一个名为Transactions的表,通过items_transactions表与项目有多对多关系。

我想做这样的事情:

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

但是这给了我所有与列表中的一个或多个项目相关联的交易,我只希望它给我与所有这些项目相关联的交易。

任何帮助都将不胜感激。

5 个答案:

答案 0 :(得分:9)

您必须展开列表中所有项目的查询:

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

您也可以使用INCOUNT DISTINCT按摩它,我不确定哪个更快。像(完全未经测试)的东西:

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

答案 1 :(得分:1)

我认为这可以满足您的需求。

我会把你需要的项目列表放到一个表格中(临时一个会好的)并加入到那个。然后计算不同项目的数量,并将计数与项目交易计数相匹配。

我提供了样本DDL&amp;我使用的数据。

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)

答案 2 :(得分: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>

虽然这可能会对事务进行扫描,但是为每个事务嵌套相关子查询...不是特别有效,所以可能:

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>

或类似的东西,说服数据库首先找到与至少一个项目相关的交易,然后检查每个交易是否与以后的所有项目相关联。

正如有人所说,你也可以简单地为每个项目生成连接子句,如果项目数量不大,这可能会更好。

答案 3 :(得分:0)

查询的最后一位看起来不对:

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

'in'语句就像一个大的OR语句而不是AND语句,因此它被优化器扩展为:

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

修改

我估计您需要在项目表上执行correlated subquery

答案 4 :(得分:0)

我没有执行此操作,但这应该可以获得您想要的结果:

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)