获取给定组的每种产品的最低价格

Posted

技术标签:

【中文标题】获取给定组的每种产品的最低价格【英文标题】:Get minimum price of each product for given group(s) 【发布时间】:2020-03-05 08:15:56 【问题描述】:

我有这些简单的表格:

products

+----+------+---------+
| id | code | details |
+----+------+---------+
|  1 | P01  | ...     |
|  2 | P02  | ...     |
|  3 | P03  | ...     |
+----+------+---------+

prices

+----+---------------+-------------+--------+------------+------------+
| id | customerGroup | productCode | price  | date       | endDate    |
+----+---------------+-------------+--------+------------+------------+
|  1 | DEFAULT       | P01         | 1.2500 | 2018-01-01 | NULL       |
|  2 | DEFAULT       | P02         | 1.4000 | 2018-01-01 | NULL       |
|  3 | DEFAULT       | P03         | 2.0000 | 2018-01-01 | NULL       |
|  4 | DEFAULT       | P01         | 1.3000 | 2018-07-01 | NULL       |
|  5 | BLUE          | P01         | 1.3100 | 2019-01-01 | NULL       |
|  6 | BLUE          | P02         | 0.9000 | 2019-01-01 | 2019-05-01 |
|  7 | BLUE          | P03         | 2.0000 | 2019-09-01 | NULL       |
|  8 | DEFAULT       | P01         | 1.3200 | 2019-10-01 | NULL       |
|  9 | GREEN         | P01         | 0.5000 | 2019-10-01 | NULL       |
| 10 | GREEN         | P02         | 0.6000 | 2019-10-01 | NULL       |
| 11 | GREEN         | P03         | 0.7000 | 2019-10-01 | NULL       |
+----+---------------+-------------+--------+------------+------------+

逻辑

prices 是一个历史表。 每一行代表一个产品和一个客户组的price 记录。 endDate = NULL 表示price 条目在date 和永恒之间有效(除非出现以下情况)。 如果同一 customerGroupproductCodeprice 记录具有较新的匹配 date,它会覆盖较旧的记录 - 即使 price 更高。 相同productCode 和匹配日期的最小price 值将获胜;只要customerGroup 与给定的组匹配。

目标 1

要获得可能的最低价格记录:

对于BLUEDEFAULT 组的成员 产品P01 日期2019-10-01
SELECT
    pp.*
FROM prices AS pp
JOIN (
    SELECT customerGroup, MAX(date) AS maxDate
    FROM prices AS pp
    WHERE productCode = 'P01'
    GROUP BY customerGroup
) AS eachRow ON (pp.customerGroup = eachRow.customerGroup AND pp.date = eachRow.maxDate)
WHERE 
    pp.productCode = 'P01'
    AND FIND_IN_SET(pp.customerGroup, 'DEFAULT,BLUE') > 0
    AND ((pp.endDate IS NULL AND '2019-10-01' >= pp.date) OR (pp.endDate IS NOT NULL AND ('2019-10-01' BETWEEN pp.date AND pp.endDate)))
GROUP BY pp.customerGroup
ORDER BY pp.price ASC
LIMIT 1;

这会返回正确/预期的单一结果:

+----+---------------+-------------+--------+------------+------------+
| id | customerGroup | productCode | price  | date       | endDate    |
+----+---------------+-------------+--------+------------+------------+
|  5 | BLUE          | P01         | 1.3100 | 2019-01-01 | NULL       |
+----+---------------+-------------+--------+------------+------------+

目标 2(问题)

如何一次性获取products中每种产品的最低价格记录

结果应该是:

+----+---------------+-------------+--------+------------+------------+
| id | customerGroup | productCode | price  | date       | endDate    |
+----+---------------+-------------+--------+------------+------------+
|  2 | DEFAULT       | P02         | 1.4000 | 2018-01-01 | NULL       |
|  5 | BLUE          | P01         | 1.3100 | 2019-01-01 | NULL       |
|  7 | BLUE          | P03         | 2.0000 | 2019-09-01 | NULL       |
+----+---------------+-------------+--------+------------+------------+

注意事项:

我正在寻找一种在外部查询中不使用 GROUP_CONCAT 的方法。 MIN + GROUP BY 使用不是解决方案,因为它不会正确返回所有必要的字段,例如 customerGroupdate...

这是SQL Fiddle。

【问题讨论】:

您可以将FIND_IN_SET(pp.customerGroup, 'DEFAULT,BLUE') > 0 替换为pp.customerGroup IN ('DEFAULT', 'BLUE')。它更简单、更标准,并且性能可能更好。 您使用的是什么版本的 mysql?在 MySQL 8 中添加的Window functions 将使这更容易。 不幸的是 MySQL 5.7。 【参考方案1】:

目标 2(问题) 如何一次获取 products 中每个产品的最低价格记录?

答案是:

Select p.productcode, min(p.price)
from prices p
left join products prd on p.productcode = prd.code
Group by p.productcode

输出: P01 0.5 P02 0.6 P03 0.7

但是你的请求结果是按id分组的,所以不能按产品显示最低价格。您显示的结果中的实际数据实际上不是最低价格吗? (这令人困惑)。

【讨论】:

谢谢。但min + group by 不会返回所有必填字段。结果中的最低价格是错误的。这些价格适用于GREEN 组。请阅读 logic 部分中的问题详情。【参考方案2】:

查询第二个问题

SELECT MAX(pp.customerGroup) as CustomerGroup, MAX(pp.ProductCode) as ProductCode , MIN(pp.price) as Price FROM Prices as pp
INNER JOIN Products as pro
on pp.PRoductCode = pro.Code

GROUP BY pp.customerGroup
ORDER BY pp.customerGroup;

如果我错了,请纠正我,我会相应地更新答案..

【讨论】:

如果这是正确的,那么将回答你的第一个问题。

以上是关于获取给定组的每种产品的最低价格的主要内容,如果未能解决你的问题,请参考以下文章

php 获取产品的过滤最低价格

从产品和供应商那里获得最低价格

mysql - 如何检索组的所有属性并获取与组关联的给定产品的属性值

Laravel 从一张表中选择价格最低的独特产品

PHP - 在两个静态数字之间创建一组数字范围

SQL查询从数据中获取最新价格?