MySQL查询:一个表,两个级别的SELECT。有没有更好的办法?

时间:2011-06-26 09:55:47

标签: mysql select

我有一个名为“double_select”的表,如下所示:

itemID | orderID | status
------   -------   ------
   1        1       ready
   2        1       ready
   3        1      waiting
   4        2      complete
   5        3       ready
  ...      ...       ...

我想要检索一个SQL命令:

  • 状态为“就绪”的所有项目
  • 上面返回的任何orderID的所有其他项目

所以在上面的表格片段中,我想返回itemID的1,2,3和& 5。

我可以用这句话来做到这一点:

SELECT `a`.* FROM `double_select` AS `a`, `double_select` AS `b` WHERE `a`.`status` = "ready" OR (`a`.`status` != "ready" AND `b`.`orderID` = `a`.`orderID` AND `b`.`status` = "ready") GROUP BY `a`.`itemID`;

但它似乎不太干净。有没有更好的方法呢?

表格详情如下,非常感谢,

詹姆斯

CREATE TABLE `double_select` (`itemID` int(11) NOT NULL auto_increment,`orderID` int(11) default NULL,  `status` varchar(128) default NULL,  PRIMARY KEY  (`itemID`),  KEY `Status` (`status`)) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;
insert  into `double_select`(`itemID`,`orderID`,`status`) values (1,1,'ready'),(2,1,'waiting'),(3,1,'waiting'),(4,2,'complete'),(5,2,'ready'),(6,3,'ready'),(7,3,'ready'),(8,4,'complete'),(9,5,'failed'),(10,6,'complete');

4 个答案:

答案 0 :(得分:2)

子查询示例:

select * 
from double_select
where orderID in 
    (select orderID 
     from double_select 
     where `status` = 'ready')

答案 1 :(得分:0)

你的解决方案很好。您也可以使用子查询来执行此操作,但您仍然会查询同一个表两次。

编辑:也许您可以将其更改为:

SELECT a.* FROM double_select AS a LEFT JOIN double_select AS b ON a.OrderId = b.OrderId
WHERE a.status = ready or b.status = ready
GROUP BY a.ItemId;

答案 2 :(得分:0)

编辑 我最初错过了明显要求我们应该允许每个订单处于就绪状态的多个项目。此编辑中添加了LEFT OUTER联接以及检查以排除与任何内容匹配的行(因此只考虑具有最低itemID的ready项)处理此问题 - 但此时子查询方法为< / em>远更清楚,如果您的查询计划器足够明亮(这些日子应该是最多的)也可能更高效。我已将此(更正的)答案留在原地以供参考。

or子句中的where两个条件可能会导致查询计划程序不使用您在status列上可能包含的任何索引。我会使用较新的(SQL92)JOIN语法来明确表示您希望“已准备就绪的A”作为主要限制过滤器,如下所示:

SELECT assoc_with_ready.field1, assoc_with_ready.field2
FROM   double_select AS ready_items
LEFT OUTER JOIN
       double_select AS duplicate_ready_itmes
       ON     duplicate_ready_itmes.orderID = ready_items.orderID 
       AND    duplicate_ready_itmes.status = 'ready'
       AND    duplicate_ready_itmes.itemID < ready_items.itemID
INNER JOIN
       double_select AS assoc_with_ready
       ON     assoc_with_ready.orderID = ready_items.orderID 
WHERE  ready_items.status = 'ready'
AND    duplicate_ready_itmes.itemID IS NULL

注意:不需要检查status<>'ready',因为查询不会在这样的查询中返回重复行(一些来自A,一些来自B)(尽管没有UNION或{ {1}}),除非您希望所有行都与就绪项相关联,而不是自己标记为就绪的行。如果我错误地阅读了您的问题并且希望标记为已准备好的行,请将CROSS JOIN添加到AND assoc_with_ready <> 'ready'的{​​{1}}子句中。< / p>

编码风格的两点往往会使事情以后更容易调试(或者更容易避免首先出现错误):对表别名使用描述性名称(尽管为简洁起见,您可能只使用A和B作为示例) ,并尽量避免选择ON(尽可能提供明确的字段名称)。

我不确定您的JOIN子句是否正在尝试执行,因为您根本没有应用任何聚合。使用您的示例语句,我希望有关a.orderid和a.status(由*选择)的错误不在*子句或聚合函数中。即使聚合函数中的所有选定项目,分组也不起作用,因为ItemID是唯一列,因此每个itemID最终会有一行,包含或不包含分组。如果您只想要一个已准备好或与已准备就绪相关联的所有行的列表,那么上面的查询将执行该操作而不进行任何分组。

答案 3 :(得分:0)

有多种方法可以做到这一点。只需添加更多选项:

SELECT * 
FROM double_select AS d
WHERE EXISTS  
    ( SELECT * 
      FROM double_select AS dd
      WHERE dd.status = 'ready' 
        AND dd.orderID = d.orderID 
    )

SELECT d.* 
FROM double_select AS d
  JOIN
    ( SELECT DISTINCT orderID 
      FROM double_select 
      WHERE status = 'ready'
    ) AS ds
    ON ds.orderID = d.orderID  

SELECT a.*
FROM double_select AS a 
  JOIN double_select AS b
    ON a.orderId = b.orderId
WHERE b.status = 'ready'
GROUP BY a.itemId