使用where子句

时间:2018-01-15 13:56:53

标签: mysql sql greatest-n-per-group correlated-subquery

说我有一张价格表;

+------------+----------+---+-----+----+-------+-----+
| product_id | price_id | a |  b  | c  | price | fee |
+------------+----------+---+-----+----+-------+-----+
|          1 |        1 | 1 | 100 | 10 |   500 |  60 |
|          1 |        2 | 1 | 100 | 20 |   505 |  50 |
|          1 |        3 | 1 | 200 | 10 |   510 |  30 |
|          1 |        4 | 1 | 200 | 20 |   515 |  25 |
|          1 |        5 | 1 | 300 | 10 |   520 |  15 |
|          1 |        6 | 1 | 300 | 20 |   525 |  50 |
|          1 |        7 | 2 | 100 | 10 |   530 |  40 |
|          1 |        8 | 2 | 100 | 20 |   535 |  35 |
|          1 |        9 | 2 | 200 | 10 |   540 |  60 |
+------------+----------+---+-----+----+-------+-----+

实际上,这个表有数百种产品,a,b和c列中的每一列可能占用大约10个值,每个产品的每个组合都会有价格。

我只想为每件商品显示1个价格,因此我在product_id上有一个GROUP BY

假设我最初想要显示每种产品的最低价格,我可以通过SELECT分钟(价格)实现这一点,没问题。现在,当我想显示与最低价格相关的费用时,我不能只显示最低价(费用),因为价格和费用不相关,最低价格不一定是最低费用。所以我加入一个子查询,就像这样;

SELECT
    t.product_id,
    t.price_id,
    t.a,
    t.b, 
    t.c, 
    min(t.price) as `min_price`,
    t.fee,
    t2.fee AS `min_price_fee`
FROM
    prices as t
JOIN so_q as t2 on t.product_id = t2.product_id
    AND t.a = t2.a
    AND t.b = t2.b
    AND t.c = t2.c
    AND t2.price = (
        SELECT min(price)
        FROM so_q as t3 
        WHERE t3.product_id = t.product_id
--          AND t3.b = 300
    )
-- WHERE
--  t.b = 300
GROUP BY
    t.product_id;

但是你可能已经从我已经注释掉的行中猜到了,当用户添加了过滤器并且现在有一个where子句时,问题出现了。如果没有将where子句放入子查询中,我就无法完成这项工作(如果我没有得到没有返回的行,我认为我明白了)我的问题是,有没有办法我可以这样做我只需要一次where子句吗?

感谢您的建议 - 如果我应该包含任何其他信息,请告诉我。试图从我正在使用的实际代码中提取MCVE是很复杂的,所以我可能已经忘记了一些明显的东西。

编辑,如MySQL版本5.5.56

编辑2

使用@Gordon Linoff的建议;

SELECT
    p.* 
FROM
    prices p 
WHERE
    p.price = ( 
        SELECT min( p2.price )
        FROM prices p2
        WHERE p2.product_id = p.product_id
    )
AND b = 300;

当我将b = 300条件添加到最后一行的where子句时,我仍然返回0行。

编辑3

尝试澄清我要做的事情:在添加任何过滤器之前,对于产品1,我想显示该记录的最低价格(500)和费用(60)(price_id = 1)。如果用户添加了规定c = 20的过滤器,那么我想显示c值为20(505)的最低价格和该记录中的费用(50)(price_id = 2)。我不认为我可以使用min(price) min(fee),因为我最终会得到不同记录的价格和费用,而且必须来自同一记录。因此,我需要找到满足所有用户输入标准的最低价格(最终作为主要where子句的一部分),然后找到与该价格相关的费用。

3 个答案:

答案 0 :(得分:1)

不要使用group by。您想要过滤掉所有其他行,因此请使用where

select p.*
from prices p
where p.price = (select min(p2.price)
                 from prices p2
                 where p2.product_id = p.product_id
                );

如果您需要对b进行过滤,则需要在子查询和外部查询中考虑这一点:

select p.*
from prices p
where p.b = 300 and
      p.price = (select min(p2.price)
                 from prices p2
                 where p2.product_id = p.product_id and p2.b = p.b
                );

答案 1 :(得分:1)

正如我在评论中提到的,有使用窗口函数和CTE的解决方案,但是,在较低版本的MySQL中没有这些解决方案。我可以提出的常量不重复的唯一解决方案如下:

SELECT
    t.product_id,
    t.price_id,
    t.a,
    t.b, 
    t.c, 
    min(t.price) as `min_price`,
    t.fee,
    t2.fee AS `min_price_fee`
FROM
    prices as t
JOIN so_q as t2 on t.product_id = t2.product_id
    AND t.a = t2.a
    AND t.b = t2.b
    AND t.c = t2.c
    AND t.b = 300
    AND t2.price = (
        SELECT min(price)
        FROM so_q as t3 
        WHERE t3.product_id = t.product_id  AND 
              t3.b = t.b
    )

答案 2 :(得分:1)

考虑@ GordonLinoff的答案并扩展要求,尽量减少代码重复的数量,使SQL的动态生成更简单......

更改相关子查询以返回行标识符而不是最低价格会产生两种后果

  1. 您只需将过滤器放在子查询中
  2. 如果出现平局,它将永远不会返回多行

  3. p.id = (SELECT id
              FROM prices p2
             WHERE p2.product_id = p.product_id
               AND <filters>
          ORDER BY price DESC,
                   some_tie_break_field(s)
             LIMIT 1
           )
    

    使用这样的结构,您可能会从使用product表开始,以最大限度地减少相关子查询所做的工作。

    SELECT
      prices.*
    FROM
      product
    INNER JOIN
      prices
          ON prices.id = (SELECT id
                            FROM prices p
                           WHERE p.product_id = product.id
                             AND <filters>
                        ORDER BY price DESC,
                                 some_tie_break_field(s)
                           LIMIT 1
                          )