mysql - MySQL 中的时间旅行 View

标签 mysql view greatest-n-per-group

我需要在 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
  1. 语义错误:选择 product_id 和 price 并仅按 product_id 分组将导致为每个价格返回不可预测的价格。您不希望结果集中有不可预测的值,否则您不会选择它。因此,您 100% 相信一个您无法预测的值。这确实是一个错误
  2. 警告:您正在对结果集进行排序,然后通过将其包装在 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
  1. 语义错误:再次选择 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/

相关文章:

Mac OSX 上的 Mysql 5.6 令人头疼

php - 代码点火器。如何返回变量值大于数据库中字段的记录?

PostgreSQL:在where子句中使用函数参数

mysql - MySQL View 是在文件系统上创建和存储的,还是总是在运行时创建的

php - 希望 COUNT 在 PHP MYSQL 中返回一个 0 或 1

Java模型更新多个 View

postgresql - 为每个 ID 选择最大值(包括一个 ID 有多个最大值的可能性!)

sql - 选择具有最大列值的每个索引值一行

sql - 获取列表中每个 ID 的前 3 行

mysql - 在 MySQL 中获得最高分