SQL:多对多关系,IN条件

发布于 2024-09-04 14:46:39 字数 456 浏览 9 评论 0原文

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

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

任何帮助将不胜感激。

I have a table called transactions with a many-to-many relationship to items through the items_transactions table.

I want to do something like this:

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

But this gives me all transactions that have one or more of the items in the list associated with it and I only want it to give me the transactions that are associated with all of those items.

Any help would be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

客…行舟 2024-09-11 14:46:39

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

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

You have to expand out your query for all of the items in the list:

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

You might also be able to massage it out using IN and COUNT DISTINCT, I'm not sure which would be faster. Something like (completely untested):

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
甜妞爱困 2024-09-11 14:46:39

我想这就是你想要的。

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

我已经提供了示例 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)

I think this does what you want.

I would put the list of items you need in to a table (temp one will be fine) and join on to that. Then count the number of distinct items and match the count to the item transactions count.

I've provided the sample DDL & Data that I used.

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)
清醇 2024-09-11 14:46:39
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>

或者类似的方法说服数据库首先找到与至少一个项目相关的事务,然后检查每笔交易都与以后的所有项目相关联。

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

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>

Although this will probably do a scan of transactions, nesting the correlated subquery for each one... not particularly efficient, so maybe:

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>

or something similar to persuade the DB to find transactions related to at least one of the items first, and then check each transaction is linked against all the items later.

As someone's noted, you can also simply generate join clauses for each item instead, which might well be better if the number of items isn't large.

人间☆小暴躁 2024-09-11 14:46:39

我还没有执行这个,但这应该会给你你想要的结果:

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)

I haven't executed this, but that should get you the result you want:

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)
挽梦忆笙歌 2024-09-11 14:46:39

查询的最后一点看起来是错误的:

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

“in”语句就像一个大的 OR 语句而不是 AND 语句,因此优化器将其扩展为:

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

编辑

我认为您需要执行项目表上的相关子查询

The final bit of the query looks wrong:

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

the 'in' statement is like a big OR statement rather than an AND statement, so it is expanded by the optimizer as:

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

EDIT

I reckon you need to perform a correlated subquery on the items table.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文