【问题标题】:SQL: many-to-many relationship, IN conditionSQL:多对多关系,IN条件
【发布时间】:2011-02-27 05:54:46
【问题描述】:

我有一个名为 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>))

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

任何帮助将不胜感激。

【问题讨论】:

  • 如果您使用了 RETURN 键,您的 SQL 代码会更容易阅读。
  • 忘记问了,我们说的是哪个Sql——Sql Server?
  • 我还不知道如何通过它自己的 SQL 来执行此操作,但您可以编写动态查询,在其中添加“and item.id=itemX”,循环中的第一个是“where item” .id=itemY' ...
  • OK 从你的答案评论它的 postgres。

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


【解决方案1】:

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

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

'in' 语句类似于大 OR 语句而不是 AND 语句,因此它被优化器扩展为:

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

编辑

我认为您需要在 items 表上执行 correlated subquery

【讨论】:

  • (items.id = 123 and items.id = 456 and items.id = 789) 永远不会是真的。
  • @dcp 现在你提到它,它看起来确实有点不太可能。
【解决方案2】:

我还没有执行此操作,但这应该会得到您想要的结果:

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)

【讨论】:

  • 他想要与所有项相关的交易,但您的解决方案将为他提供与其中任何一项相关的交易。
【解决方案3】:

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

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

【讨论】:

  • 真的没有更好的方法吗?列表中有大约 100 个项目,我认为 postgres 不能处理那么长的查询。
  • 我喜欢使用 'count=number of items in list'
  • 计数的想法非常有效,我只需将 transactions.transactions_id 更改为 "transactions".id 就可以了。非常感谢!
【解决方案4】:

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

我会将您需要的项目列表放入一个表中(临时表就可以了)并加入到该表中。然后计算不同项目的数量并将计数与项目交易计数相匹配。

我提供了我使用的示例 DDL 和数据。

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)

【讨论】:

    【解决方案5】:
    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>
    

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

    正如有人指出的那样,您也可以简单地为每个项目生成连接子句,如果项目数量不大,这可能会更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-16
      • 1970-01-01
      • 2015-08-05
      • 2021-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-15
      相关资源
      最近更新 更多