使用 where 子句查找与另一个 col 的最小值关联的值
Find value associated with min value of another col with a where clause
假设我有 table 个价格;
+------------+----------+---+-----+----+-------+-----+
| 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 |
+------------+----------+---+-----+----+-------+-----+
实际上,这个 table 将有数百种产品,a、b 和 c 列中的每一列都可能占用大约 10 个值,并且每种产品的这些列的每种组合都会有一个价格。
我只想显示每个产品 1 个价格,所以我在 product_id 上有一个 GROUP BY
。
假设我最初想显示每个产品的最低价格,我可以通过 SELECT
min(price) 实现,没问题。现在,当我想显示与最低价格相关的费用时,我不能只显示 min(fee),因为价格和费用不相关,最低价格不一定具有最低费用。所以我加入了一个子查询,就像这样;
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 子句的一部分),然后找到与该价格相关的费用。
不要使用 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
);
正如我在评论中提到的,有使用 window 函数和 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
)
采用@GordonLinoff 的回答并扩展要求以包括最小化代码重复量,使 SQL 的动态生成更简单...
将相关子查询更改为 return 行标识符而不是最低价格有两个后果
- 你只需要将过滤器放在子查询中
- 它永远不会 return 出现平局
多行
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
table 开始,以最大限度地减少相关子查询所做的工作。
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
)
假设我有 table 个价格;
+------------+----------+---+-----+----+-------+-----+
| 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 |
+------------+----------+---+-----+----+-------+-----+
实际上,这个 table 将有数百种产品,a、b 和 c 列中的每一列都可能占用大约 10 个值,并且每种产品的这些列的每种组合都会有一个价格。
我只想显示每个产品 1 个价格,所以我在 product_id 上有一个 GROUP BY
。
假设我最初想显示每个产品的最低价格,我可以通过 SELECT
min(price) 实现,没问题。现在,当我想显示与最低价格相关的费用时,我不能只显示 min(fee),因为价格和费用不相关,最低价格不一定具有最低费用。所以我加入了一个子查询,就像这样;
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 子句的一部分),然后找到与该价格相关的费用。
不要使用 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
);
正如我在评论中提到的,有使用 window 函数和 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
)
采用@GordonLinoff 的回答并扩展要求以包括最小化代码重复量,使 SQL 的动态生成更简单...
将相关子查询更改为 return 行标识符而不是最低价格有两个后果
- 你只需要将过滤器放在子查询中
- 它永远不会 return 出现平局 多行
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
table 开始,以最大限度地减少相关子查询所做的工作。
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
)