【问题标题】:How to join tables together based on subselects?如何基于子选择将表连接在一起?
【发布时间】:2010-11-12 10:02:48
【问题描述】:

我一直想弄清楚如何编写查询。基本上我有三个表(Orders、Products、Orders_Products),我想将它们连接在一起并应用一些过滤。

Orders table:
  ORDER_ID   CUSTOMER_ID
  1          1
  2          2

Products table:
  PRODUCT_ID   PRODUCT_NAME    PRODUCT_TITLE
  1            'P1'            'T1'
  2            'P1'            'T2'
  3            'P2'            'T3'
  4            'P2'            'T4'
  5            'P2'            'T5'
  6            'P3'            'T6'

Orders_Products table:
  ORDER_ID   PRODUCT_ID
  1          1
  1          3
  2          1
  2          3
  2          6

例如,我想获取所有包含(完全)产品 P1/T1 和 P2/T3 的订单。我尝试了类似的方法,但它不起作用:

SELECT * FROM Orders
LEFT JOIN Orders_Products ON Orders_Products.ORDER_ID = Orders.ORDER_ID
LEFT JOIN Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
WHERE EXISTS (SELECT * FROM Product WHERE PRODUCT_NAME = 'P1' AND PRODUCT_TITLE = 'T1')
AND EXISTS (SELECT * FROM Product WHERE PRODUCT_NAME = 'P2' AND PRODUCT_TITLE = 'T3');

编辑:澄清我真正必须实现的目标。用户应该能够搜索与给定产品匹配的订单。用户输入一个或多个产品名称/产品标题组合,并获得与该产品完全相关的所有订单。我(从 Web 应用程序)得到的只是名称/标题组合,我必须在查询中使用这些组合来获取 ORDER_ID。

【问题讨论】:

  • 你使用什么风格的 SQL? SQL Server(如果有,是哪个版本)?
  • 我正在使用 MySQL,但我想知道使用标准 SQL 是否无法完成某些事情?

标签: sql join subquery


【解决方案1】:
SELECT OrderID, COUNT(*) AS ProductsCount
FROM Orders_Products
WHERE (PRODUCT_ID = 1 OR PRODUCT_ID = 3)
GROUP BY OrderID
HAVING COUNT(*) = 2

编辑:请忽略上述声明。看看以下是否有效。

SELECT OrderID, 
SUM(CASE PRODUCT_ID WHEN 1 THEN 1 WHEN 3 THEN 1 ELSE 3 END)
AS ProductsCount
FROM Orders_Products
GROUP BY OrderID
HAVING SUM(CASE PRODUCT_ID WHEN 1 THEN 1 WHEN 3 THEN 1 ELSE 3 END) = 2

我想这应该会让您获得只有这两种产品的订单。

【讨论】:

  • 它是否提供只有这两种产品的订单? OP 正在尝试匹配 "consists (exactly) of" 这两种产品的订单,所以我读到他们不希望订单碰巧包含任何其他商品。
  • @DMA57361:如果我理解正确,OP 需要 ORDER_ID = 1 的记录。请参阅上面更正后的语句。
  • 请记住我所说的是我对 OP 需求的解释 :) - 但我认为您的修改版本可以做到这一点。
  • 我已经阐明了我想要实现的目标。这可以通过您的两种解决方案之一实现吗?
【解决方案2】:

您可能无法在 MySQL 中编写简单的查询来实现这一点。但是 ANSI SQL 支持table value constructor,这简化了这种类型的查询。

此基本查询返回完整的订单列表(5 行):

SELECT * FROM Products 
JOIN Orders_Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
JOIN Orders ON Orders_Products.ORDER_ID = Orders.ORDER_ID

这个带有table value constructor 的查询返回您需要的订单:

SELECT * FROM Products 
JOIN Orders_Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
JOIN Orders ON Orders_Products.ORDER_ID = Orders.ORDER_ID 
LEFT JOIN (VALUES('P1', 'T1'), ('P2', 'T3')) V(P_NAME, P_TITLE) ON PRODUCT_NAME = P_NAME AND PRODUCT_TITLE=P_TITLE

此查询对上述内容进行分组以返回 ORDER_ID,其中在所需列表之外没有订单(排除行包含 null 的订单):

SELECT ORDER_ID FROM Products 
JOIN Orders_Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
JOIN Orders ON Orders_Products.ORDER_ID = Orders.ORDER_ID 
LEFT JOIN (VALUES('P1', 'T1'), ('P2', 'T3')) V(P_NAME, P_TITLE) ON PRODUCT_NAME = P_NAME AND PRODUCT_TITLE=P_TITLE
GROUP BY ORDER_ID HAVING COUNT(*) = 2

在开源数据库中,HSQLDB 是支持表值构造器和其他 ANSI SQL:2008 的用户友好特性的数据库

【讨论】:

  • 非常感谢,这正是我要找的。但不幸的是 MySQL 不支持“表值构造函数”。但我可以使用LEFT JOIN (SELECT v.PRODUCT_NAME, v.PRODUCT_TITLE FROM Products v WHERE (v.PRODUCT_NAME = 'P1' AND PRODUCT_TITLE = 'T1') OR (v.PRODUCT_NAME = 'P2' AND PRODUCT_TITLE = 'T3')) tmp ON PRODUCT_NAME=tmp.PRODUCT_NAME AND PRODUCT_TITLE=tmp.PRODUCT_TITLE 模拟它
猜你喜欢
  • 1970-01-01
  • 2021-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-09
  • 1970-01-01
  • 2021-12-06
  • 2021-08-16
相关资源
最近更新 更多