【问题标题】:MySQL query puzzle - finding what WOULD have been the most recent dateMySQL 查询谜题 - 找出最近的日期
【发布时间】:2011-02-19 17:09:17
【问题描述】:

我已经看遍了,还没有找到一种智能的方法来处理这个问题,尽管我确信有一个可能的方法:

一个历史数据表有季度信息:

CREATE TABLE Quarterly (
unique_ID INT UNSIGNED NOT NULL,
date_posted DATE NOT NULL,
datasource TINYINT UNSIGNED NOT NULL,
data FLOAT NOT NULL,
PRIMARY KEY (unique_ID));

另一个历史数据表(非常大)包含每日信息:

CREATE TABLE Daily (
unique_ID INT UNSIGNED NOT NULL,
date_posted DATE NOT NULL,
datasource TINYINT UNSIGNED NOT NULL,
data FLOAT NOT NULL,
qtr_ID INT UNSIGNED,
PRIMARY KEY (unique_ID));

qtr_ID 字段不是填充数据库的每日数据源的一部分 - 相反,我需要使用 Quarterly.unique_ID 行 ID 追溯填充 Daily 表中的 qtr_ID 字段,使用最新的该数据源的 Daily.date_posted 的季度数据。

例如,如果季度数据是

101 2009-03-31 1 4.5
102 2009-06-30 1 4.4
103 2009-03-31 2 7.6
104 2009-06-30 2 7.7
105 2009-09-30 1 4.7

每天的数据是

1001 2009-07-14 1 3.5 ??
1002 2009-07-15 1 3.4 &&
1003 2009-07-14 2 2.3 ^^

那么我们会想要 ?? qtr_ID 字段将被分配为“102”作为该数据源在该日期的最近一个季度,&& 也将是“102”,而 ^^ 将是“104”。

挑战包括这两个表(尤其是每日表)实际上都非常大,无法对其进行规范化以消除重复日期或进行其他优化,并且对于某些每日条目,没有之前的季度条目。

我尝试了各种连接,使用 datediff(挑战是找到大于零的 datediff 的最小值)和其他尝试,但对我没有任何效果 - 通常我的语法在某个地方出现问题。欢迎任何想法 - 我将执行任何基本想法或概念并报告。

【问题讨论】:

    标签: mysql join max datediff min


    【解决方案1】:

    只需使用类似以下内容的季度 ID 子查询:

    (
     SELECT unique_ID 
     FROM Quarterly 
     WHERE 
         datasource = ? 
         AND date_posted >= ? 
     ORDER BY
         unique_ID ASC
     LIMIT 1
    )
    

    当然,这可能不会为您提供最佳性能,并且它假定日期按顺序添加到 Quarterly(否则 order by date_posted)。但是,它应该可以解决您的问题。

    您可以在 INSERTUPDATE 语句中使用此子查询作为 Daily 表的 qtr_ID 字段的值。

    【讨论】:

    • 不幸的是,这并不能完全发挥作用——它确实提取了正确的数据源,但它根据自最早的季度数据(然后提供第一个条目)以来经过的时间进行排名,而不是最新的。因此,在上面的示例中,对于来自 datasource = '1' 的所有数据,它返回 '101',对于来自 datasource = '2' 的所有数据,它返回 '103'。这是我运行的内容: UPDATE Daily SET qtr_ID = ( SELECT unique_ID FROM Quarterly WHERE Daily.datasource = Quarterly.datasource AND Daily.date_posted >= Quarterly.date_posted ORDER BY date_posted ASC LIMIT 1 )
    • 顺便说一句,这个建议比我下面的成功查询要快得多。
    • 速度上的巨大差异让我坚持上面的建议,这导致我发现这只是一个很小的改变(按 date_posted 在另一个方向排序),所以这是有效且快速的代码: UPDATE Daily SET qtr_ID = ( SELECT unique_ID FROM Quarterly WHERE Daily.datasource = Quarterly.datasource AND Daily.date_posted >= Quarterly.date_posted ORDER BY date_posted DESC LIMIT 1 );非常感谢 Kenaniah
    • Hank,它更快的原因可能与 unique_ID 是一个索引字段这一事实有关。这是假设 date_posted 不是。
    【解决方案2】:

    以下内容似乎完全按预期工作,但它肯定很难看(对同一个 DATEDIFF 的三个调用!!),也许通过查看一个有效的查询,某人可能能够进一步减少或改进它:

    UPDATE Daily SET qtr_ID = (select unique_ID from Quarterly
    WHERE Quarterly.datasource = Daily.datasource AND
    DATEDIFF(Daily.date_posted, Quarterly.date_posted) = 
    (SELECT MIN(DATEDIFF(Daily.date_posted, Quarterly.date_posted)) from Quarterly
    WHERE Quarterly.datasource = Daily.datasource AND
    DATEDIFF(Daily.date_posted, Quarterly.date_posted) > 0));
    

    【讨论】:

    • 查看上面的回复以获得比这个更快的解决方案。
    【解决方案3】:

    在对该查询进行了更多工作后,我最终获得了比原始概念更大的性能改进。最重要的改进是在 Daily 和 Quarterly 表中创建索引 - 在 Daily 中,我在 (datasource, date_posted) 和 (date_posted, datasource) USING BTREE 和 (datasource) USING HASH 上创建了索引,在 Quarterly 中我也做了同样的事情事物。这是矫枉过正,但它确保我有一个查询引擎可以使用的选项。这将查询时间减少到不到原来的 1%。 (!!)

    然后,我了解到,鉴于我的特殊情况,我可以使用 MAX() 而不是 ORDER BY 和 LIMIT,因此我使用对 MAX() 的调用来获取适当的 unique_ID。这将查询时间减少了大约 20%。

    最后,我了解到,使用 InnoDB 存储引擎,我可以使用任何一个查询对我正在更新的 Daily 表的块进行分段,这使我能够通过一点点手肘润滑和脚本来对查询进行多线程处理。并行处理效果很好,每个线程都线性减少了查询时间。

    因此,比我自己的第一次尝试执行的基本查询实际上好 1000 倍:

    UPDATE Daily
    SET qtr_ID =
    (
      SELECT MAX(unique_ID)
      FROM Quarterly
      WHERE Daily.datasource = Quarterly.datasource AND
            Daily.date_posted > Quarterly.dateposted
    )
    WHERE unique_ID > ScriptVarLowerBound AND
          unique_ID <= ScriptVarHigherBound
    ;
    

    【讨论】:

      猜你喜欢
      • 2018-03-21
      • 2015-02-08
      • 2022-01-24
      • 2022-01-24
      • 2019-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多