我需要在 mysql 中实现价格的时间旅行 View 。 基本价格表是这样的:
CREATE TABLE product_price (
product_id INT(11) NOT NULL,
date_valid TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
price DECIMAL(15,4) NOT NULL,
PRIMARY KEY(product_id,date_valid)
);
我的想法是,随着时间的推移,我会选择我提前输入的正确有效价格。 也许这个概念以后会更清楚。 我需要创建一个 View ,以便为每个 product_id 获得最新价格。 一段时间后,我找到了满足我需要的 SELECT:
SELECT * FROM (
SELECT product_id,price FROM product_pric
WHERE date_valid <= CURRENT_TIMESTAMP
ORDER BY date_valid DESC
) xx GROUP BY product_id;
为了创建所需的 View ,我知道我不能使用子选择并且需要创建一个或多个中间 View 。像这样:
CREATE VIEW v_product_price_time AS
SELECT product_id,price FROM product_pric
WHERE date_valid <= CURRENT_TIMESTAMP
ORDER BY date_valid DESC
;
CREATE VIEW v_product_price AS
SELECT * FROM v_product_price_time GROUP BY product_id;
然后我得到的结果与我编写的原始查询不同。 例如,我只用两行填充表:
INSERT INTO product_price (product_id,date_valid,price ) VALUES ( 1,'2013-01-01',41.40 );
INSERT INTO product_price (product_id,date_valid,price ) VALUES ( 1,'2013-01-03',42.0 );
原始查询返回正确的数据 (1,42.0),但查询 View 却没有。我总是得到 (1,41.40)。
当然我遗漏了一些东西,因为我不太了解 MySQL。 使用另一个开源 RDBMS 我已经做了类似的事情,但现在我需要应对 MySQL v5.5 并且无法更改它。 但是开发人员论坛中的文档和一些搜索并没有让我找到解决方案。 关于如何解决这个问题的任何想法? TIA。
最佳答案
使用这个查询,不管它是否在 View 中。
SELECT p1.* FROM (
SELECT * FROM product_price
WHERE date_valid <= CURRENT_TIMESTAMP
) p1
LEFT JOIN (
SELECT product_id, date_valid FROM product_price
WHERE date_valid <= CURRENT_TIMESTAMP
) p2
ON p1.product_id = p2.product_id AND p1.date_valid < p2.date_valid
WHERE p2.date_valid IS NULL
查询创建了 2 个派生表,效率不高,也有点难读。您可以尝试为此创建另一个 View :
CREATE VIEW product_price_past_dates AS (
SELECT * FROM product_price
WHERE date_valid <= CURRENT_TIMESTAMP
);
然后将原始查询重写为:
SELECT p1.* FROM product_price_past_dates p1
LEFT JOIN product_price_past_dates p2
ON p1.product_id = p2.product_id AND p1.date_valid < p2.date_valid
WHERE p2.date_valid IS NULL
然后您可以在使用先前 View 的查询上创建 View :
CREATE VIEW v_product_price_time AS (
SELECT p1.* FROM product_price_past_dates p1
LEFT JOIN product_price_past_dates p2
ON p1.product_id = p2.product_id AND p1.date_valid < p2.date_valid
WHERE p2.date_valid IS NULL
);
并以最简单的查询结束:
SELECT * FROM v_product_price_time;
fiddle here .
为什么 GROUP BY 不起作用: 错误主要在于 GROUP BY 子句的不当使用。经验法则(虽然不是 100% 正确)是始终在选择中使用与 GROUP BY 中相同的字段。否则,MySQL 将从 select 而不是 GROUP BY 中的字段中选择任何值。
有关更多信息,您应该查看 MySQL documentation .我认为这很清楚。
非常详细的解释:
SELECT * FROM (
SELECT product_id,price FROM product_pric
WHERE date_valid <= CURRENT_TIMESTAMP
ORDER BY date_valid DESC
) xx GROUP BY product_id;
0 syntactical errors
1 semantical error
1 warning
- 语义错误:选择 product_id 和 price 并仅按 product_id 分组将导致为每个价格返回不可预测的价格。您不希望结果集中有不可预测的值,否则您不会选择它。因此,您 100% 相信一个您无法预测的值。这确实是一个错误
- 警告:您正在对结果集进行排序,然后通过将其包装在 GROUP BY 中来摆脱该顺序。订购某物然后为其生成不同的订单是没有意义的。这会降低性能。
应该使用 2 个基本的每组最大 n 解决方案之一来修复以前的查询。我提供了最短的一个,即左连接。
CREATE VIEW v_product_price_time AS
SELECT product_id,price FROM product_pric
WHERE date_valid <= CURRENT_TIMESTAMP
ORDER BY date_valid DESC
;
0 syntactical errors
0 semantical errors
0 warnings
完全有效的查询。完全没有评论。
CREATE VIEW v_product_price AS
SELECT * FROM v_product_price_time GROUP BY product_id;
0 syntactical errors
1 semantical errors
0 warnings
- 语义错误:再次选择 product_id 和 price 并且仅按 product_id 分组。与上述相同,这将导致不可预测的结果。
所以,您基本上是在比较 2 个不可预测的结果,并期望结果相同。有趣的是,比较 2 个不可预测的结果比只比较 1 个不可预测的结果更容易出错。因此,认为自己很幸运能够增加在代码中发现此错误的机会。问这个问题的人:How can I update the value of one field to the most often used value of another field?
当他发现查询没有按他预期的那样工作时,他会发现一个有趣的惊喜。此外,其中 4 个答案中有 3 个没有正确分组并返回不可预测的结果。更不用说 Bohemian 的评论,他在评论中声明他的代码将始终有效
。所以,恭喜 Enzo,你刚刚除以 0 :)
希望这对您有所帮助。
关于mysql - MySQL 中的时间旅行 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18628501/