MySQL:在HAVING子句之前限制结果集的问题

时间:2010-11-11 02:37:08

标签: mysql

我坚持以有效的方式从MySQL数据库中检索数据。

我有一张包含很多物品的桌子。每个项目的状态可以是1,2或3.我想选择具有特定状态的项目,具体取决于其他表格中这些记录的链接。

示例:

  • items,其中包含字段:id,name,status,...
  • 包含字段的表bought:itemId,userId
  • 包含字段的表rented:itemId,userId

说我想为userId 123

  1. 购买的所有记录,状态为1
  2. 租借的所有记录,状态为1或2
  3. 所有未购买或租借的状态为3的记录
  4. 我的选择查询现在看起来像这样:

    SELECT
      i.id,
      i.name,
      i.status,
      // bought by this user?
      SUM(IF(b.userId = 123, 1, 0)) AS bought,
      // rented by this user?
      SUM(IF(r.userId = 123, 1, 0)) AS rented,
    
    FROM items i
    
    LEFT JOIN bought b
      ON b.itemId = i.id
    
    LEFT JOIN rented r
      ON r.itemId = r.id
    
    GROUP BY i.id
    
    HAVING
      // bought and status 1
      (bought > 0 AND status = 1)
      // rented and status 1 or 2
      OR (rented > 0 AND status IN (1, 2)
      // not bought or rented and status 3
      OR (bougth = 0 AND rented = 0 AND status = 3)
    
    ORDER BY i.name ASC
    

    问题1
    SELECT子句中的SUM部分是否是确定链接到项目的另一个表中是否存在条目的好方法?假设每个用户只有一个条目,总和将为1或0,为我提供所需的信息。但它似乎......某种程度上很奇怪。

    问题2
    即使这样可行,也存在一个大问题:它基本上检索所有项,然后使用HAVING子句过滤它们。由于存在相当多的条目,因此查询速度太慢。我正在试图弄清楚如何解决这个问题。

    我首先尝试了一个WHERE子句..但是如何?

    ...
    WHERE
      // only items with status 1 if bought or rented
      (t.status = 1 AND (bought > 0 OR rented > 0))
      // only items with status 2 if rented
      OR (t.status = 2 AND rented > 0)
      // only items with status 3 if not bought or rented
      OR (t.status = 3 AND bought = 0 AND rented = 0)
    ...

    但是你不能使用SELECT子句中的变量。由于项目表中没有列rentedbought,因此无效。

    我也尝试使用用户可定义的变量,但这也不起作用:

    SELECT
      ...
      @bought := SUM(IF(b.userId = 123, 1, 0)) AS bought,
    ...
    WHERE @bought = ... // does not work

    然后我尝试了一个子查询,但我无法使用主查询项id:

    ...
    WHERE
      ...
      // only items with status 2 if rented
      OR (
        t.status = 2
        AND (
          SELECT COUNT(r2.userId)
          FROM rented r2
          WHERE r2.userId = 123
            AND r2.itemId = i.itemId // it doesn't recognize i.itemId
        ) > 0
      )
      ...

    有什么想法吗?我还想将所有内容保存在一个查询中。这只是一个简单的例子,但实际的例子相当大。我确信我可以拆分所有内容并使用各种查询来单独收集所有内容,但这只会添加很多更多代码,并且不会使维护更容易。

1 个答案:

答案 0 :(得分:1)

使用两个子查询(一个用于买方,一个用于租用),然后将它们连接到主查询中的用户表。

编辑:原谅我的MySQL,已经有一段时间了,但我想到的是:

select i.id, i.name, i.status, ifnull(b.TotalBought,0) AS ItemsBought, ifnull(r.TotalRented,0) AS ItemsRented
FROM items i 
LEFT JOIN (select itemid, COUNT(*) AS TotalBought FROM bought WHERE userid=123 GROUP BY itemid) AS b ON b.itemid=i.itemid
LEFT JOIN (select itemid, COUNT(*) AS TotalRented FROM rented WHERE userid=123  GROUP BY itemid) AS r ON r.itemid=i.itemid
WHERE (i.status=1 AND ifnull(b.TotalBought,0)>0)
OR (ifnull(r.TotalRented,0) >0 AND i.status in(1,2)
OR (ifnull(b.TotalBought,0)=0 AND ifnull(r.TotalRented,0) =0 AND i.status=3)
ORDER BY i.name ASC