【问题标题】:MySQL - Joining two tables with datetime columns on Date and three most recent entries before that dateMySQL - 将两个表与日期上的日期时间列和该日期之前的三个最近条目连接起来
【发布时间】:2023-03-04 21:31:02
【问题描述】:

我的 SQL 中有两个表

例如Table1 - ItemPrice

DATETIME   |  ITEM | PRICE
2011-08-28 |   ABC   123 
2011-09-01 |   ABC   125
2011-09-02 |   ABC   124 
2011-09-03 |   ABC   127 
2011-09-04 |   ABC   126

Table2 - DayScore

DATETIME   |  ITEM | SCORE
2011-08-28 |   ABC    1
2011-08-29 |   ABC    8
2011-09-01 |   ABC    4
2011-09-02 |   ABC    2
2011-09-03 |   ABC    7  
2011-09-04 |   ABC    3

我想写一个查询,给定一个项目 ID(例如 ABC),将返回该日期从 ItemPrice 的价格(如果该日期没有价格,那么查询不应该返回任何东西)。如果找到查询日期的有效价格,则查询应返回(9 列)

  • 过去三天内ItemPrice的商品价格(即查询日期前的最近3个价格)。
  • 在接下来的三列中,它应该从 DayScore 返回从 ItemPrice 中选择的这 3 个日期的匹配分数。
  • 最后选择日期(t-1 到 t-3)

换句话说,仅查看 date='2011-09-03' 作为 item='abc' 示例的此查询的结果将返回:

DATE      |  ITEM  |  PRICE  |  SCR  | PRC_t-1 | PRC_t-2 | PRC_t-3 | SCR_t-1 | SCR_t-2 | SCR_t-3 | DATE_t-1  | DATE_t-2  | DATE_t-3 
2011-09-03|  ABC   |  127    |  7    | 124     | 125     | 123     | 2       | 4       | 1       | 2011-09-02| 2011-09-01| 2011-08-28
....

ItemPrice 表中出现的每个日期等。

运行此查询的最简洁和最有效的方法是什么(因为它会在数百万行中运行)?

干杯!

【问题讨论】:

  • 我会的,但到目前为止,我很尴尬,并决定不发布,所以从头开始几乎就是我所处的位置......但简而言之,多选......
  • 虽然这是一个有趣的挑战,取决于你如何定义“整洁”,它可能真的很整洁,但我怀疑它是否能有效地完成。我希望最快的方法是简单地将 ABC 的所有行作为直线行(项目、价格、分数)并使用您在那里拥有的任何编程语言在客户端上构建您的结构。
  • 您似乎没有每个日历日的价格。换句话说,您的价格表看起来只包含商品价格更改日期的行。那是对的吗?这会让你陷入一个复杂的自我加入的境地。为了效率,你能定义一个时间范围吗?你能说不需要查看,比如说,超过 7 天的时间就可以获取最新的价格变化吗?
  • 感谢 cmets。在这个阶段,我们确实将整洁定义为可理解和高效的工作过程。 @Eddy - 是的,这是一个非常好的主意,并且已经构建了包装视图,基本上给出了我在上面的示例中所拥有的内容。使用日历日,我们可以限制它(不理想),价格基本上是在交易日的任何价格上列出的(即公共假期或周末没有价格)。但可能还有其他情况下该项目没有交易,10 天的期限会更好,但如果您有 7 天限制的想法,我们会感兴趣!
  • 有了客户端解决方案的想法,这是我们考虑过的。作为 Java,我们不应该对集合有任何问题,但我们真的希望每周将其作为批处理运行,考虑到我们正在查看的记录量,客户端解决方案变得更加令人沮丧,管理数据量,调整缓存,并冒着对数据库、网络 OH 等进行数千次调用的风险,这是我们希望避免的事情,并将此逻辑保留在以后的数据库中的数据中。多个选择、临时表等会比必须将数据移植到 Java 更好 (IMO)。

标签: mysql sql date join group-by


【解决方案1】:

非常不,但它确实产生了结果。您可能可以摆脱一些子选择并使其少一些 sql,但我尝试逐步构建它,以便您可以推断出它在做什么。

核心部分是这个选择:

SELECT 
  Sub2.*
, (Select MAX(IP3.DateTime) FROM ItemPrice IP3 where IP3.DateTime < T_2) AS T_3
FROM
   (SELECT 
        Sub1.*
      , (Select MAX(IP2.DateTime) FROM ItemPrice IP2 where IP2.DateTime < T_1) AS T_2
    FROM
       (SELECT 
            ItemPrice.DateTime
          , (Select MAX(IP.DateTime) FROM ItemPrice IP where IP.DateTime < ItemPrice.DateTime) AS T_1 
        From ItemPrice) Sub1
   ) Sub2

这将返回一个包含日期的表格(现在、t-1、t-2、t-3)。从那里可以简单地加入每个日期的价格和分数。包括 testdata 在内的所有东西都变成了大部分 sql

/*
CREATE TABLE ItemPrice (datetime Date, item varchar(3), price int);
CREATE TABLE DayScore ( datetime Date, item varchar(3), score int);

INSERT INTO ItemPrice VALUES ('20110828', 'ABC', 123);
INSERT INTO ItemPrice VALUES ('20110901', 'ABC', 125);
INSERT INTO ItemPrice VALUES ('20110902', 'ABC', 124);
INSERT INTO ItemPrice VALUES ('20110903', 'ABC', 127);
INSERT INTO ItemPrice VALUES ('20110904', 'ABC', 126);

INSERT INTO DayScore VALUES ('20110828', 'ABC', 1);
INSERT INTO DayScore VALUES ('20110829', 'ABC', 8);
INSERT INTO DayScore VALUES ('20110901', 'ABC', 4);
INSERT INTO DayScore VALUES ('20110902', 'ABC', 2);
INSERT INTO DayScore VALUES ('20110903', 'ABC', 7);
INSERT INTO DayScore VALUES ('20110904', 'ABC', 3);
*/

SELECT Hist.*, Current.Item, Current.Price, Current.Score
, Minus1.Price as PRC_1, Minus1.Score SCR_1
, Minus2.Price as PRC_2, Minus2.Score SCR_2
, Minus3.Price as PRC_3, Minus3.Score SCR_3
FROM 
    (SELECT Sub2.*, (Select MAX(IP3.DateTime) FROM ItemPrice IP3 where IP3.DateTime < T_2) AS T_3
    FROM
        (SELECT Sub1.*, (Select MAX(IP2.DateTime) FROM ItemPrice IP2 where IP2.DateTime < T_1) AS T_2
        FROM
            (SELECT ItemPrice.DateTime, (Select MAX(IP.DateTime) FROM ItemPrice IP where IP.DateTime < ItemPrice.DateTime) AS T_1 From ItemPrice) Sub1) Sub2) Hist 
INNER JOIN
    (SELECT ItemPrice.DateTime, ItemPrice.Item, ItemPrice.Price, DayScore.Score FROM ItemPrice INNER JOIN DayScore ON (ItemPrice.Item = DayScore.Item AND ItemPrice.Datetime = DayScore.DateTime)) CURRENT
ON (Current.DateTime = Hist.DateTime)        
LEFT JOIN 
    (SELECT ItemPrice.DateTime, ItemPrice.Price, DayScore.Score FROM ItemPrice INNER JOIN DayScore ON (ItemPrice.Item = DayScore.Item AND ItemPrice.Datetime = DayScore.DateTime)) MINUS1
ON (Minus1.DateTime = Hist.T_1)        
LEFT JOIN 
    (SELECT ItemPrice.DateTime, ItemPrice.Price, DayScore.Score FROM ItemPrice INNER JOIN DayScore ON (ItemPrice.Item = DayScore.Item AND ItemPrice.Datetime = DayScore.DateTime)) MINUS2
ON (Minus2.DateTime = Hist.T_2)        
LEFT JOIN 
    (SELECT ItemPrice.DateTime, ItemPrice.Price, DayScore.Score FROM ItemPrice INNER JOIN DayScore ON (ItemPrice.Item = DayScore.Item AND ItemPrice.Datetime = DayScore.DateTime)) MINUS3
ON (Minus3.DateTime = Hist.T_3)        
WHERE Current.Item = 'ABC'

;

/*
DROP TABLE ItemPrice;
DROP TABLE DayScore;
*/

当您在 1M 行上执行此操作时,我对您的解释计划感到好奇 :) 如果您有正确的索引,它甚至可能不会那么可怕。

【讨论】:

  • 艾迪,你先生是个天才!非常感谢您的回答!只需针对 prod 数据集(260 万个商品价格和 60 万个分数)运行这个,运行大约需要 2.5 分钟。返回所有预期的结果!传奇!
猜你喜欢
  • 1970-01-01
  • 2014-11-22
  • 2020-06-30
  • 2020-11-17
  • 2019-08-05
  • 1970-01-01
  • 1970-01-01
  • 2017-02-28
  • 2014-02-24
相关资源
最近更新 更多