【问题标题】:How to find neighboring records in the SQL table in terms of month and year?SQL表中如何查找相邻记录的月份和年份?
【发布时间】:2019-05-29 15:43:06
【问题描述】:

请帮助我优化我的 SQL 查询。

我有一个包含以下字段的表:date、commercial_id、exp_month_id、exp_year、price,其中前 4 个字段是主键。月份用按字母顺序排列的字母指定:例如F(一月)、G(二月)、H(三月)等。因此,距离一月较远的月份的字母将大于距离较近的月份的字母(F

我需要根据 exp_month_id、exp_year 计算相邻记录的价格(梯度)之间的差异。作为第一步,我想为每对夫妇 (exp_month_id, exp_year) 定义有效的夫妇 (next_month_id, next_year)。这里的主要问题是,如果当前的 exp_month_id 是一年中的最后一个,那么 next_year = exp_year + 1 和 next_month_id 应该是一年中的第一个。

我编写了以下查询来完成这项工作:

WITH trading_months AS (
    SELECT DISTINCT commodity_id,
                    exp_month_id
      FROM futures
     ORDER BY exp_month_id
)
SELECT DISTINCT f.commodity_id,
                f.exp_month_id,
                f.exp_year,
                (
                WITH [temp] AS (
                        SELECT exp_month_id
                          FROM trading_months
                         WHERE commodity_id = f.commodity_id
                    )
                    SELECT exp_month_id
                      FROM [temp]
                     WHERE exp_month_id > f.exp_month_id
                    UNION ALL
                    SELECT exp_month_id
                      FROM [temp]
                     LIMIT 1
                )
                AS next_month_id,
                (
                    SELECT CASE WHEN EXISTS (
                                   SELECT commodity_id,
                                          exp_month_id
                                     FROM trading_months
                                    WHERE commodity_id = f.commodity_id AND 
                                          exp_month_id > f.exp_month_id
                                    LIMIT 1
                               )
                           THEN f.exp_year ELSE f.exp_year + 1 END
                )
                AS next_year
  FROM futures AS f

此查询用作动态表(视图)的基础,该表随后用于计算梯度。但是,此查询的执行需要一秒钟以上,因此整个过程需要几分钟。我想知道您是否可以帮我优化查询。

【问题讨论】:

  • @Shawn 不幸的是,没有。

标签: sql sqlite select


【解决方案1】:

注意:以下需要 Sqlite 3.25 或更高版本才能支持窗口功能:

缺少样本数据(最好是 CREATE TABLEINSERT 语句以便于导入)和预期结果难以测试,但如果您的最终目标是计算到期日期之间的价格差异(提出您的问题有点像XY problem,可能是这样的:

SELECT date, commodity_id, price, exp_year, exp_month_id
     , price - lag(price, 1) OVER (PARTITION BY commodity_id ORDER BY exp_year, exp_month_id) AS "change from last price"
FROM futures;

【讨论】:

  • 非常感谢您的帮助!您编写的查询完成了这项工作,与我的相比,它更紧凑。但是,性能几乎相同:48 - 50 秒。是否可以加快执行速度?
  • 我认为为了与下个月有所不同,我需要使用lead()函数而不是lag()。此外,我还需要对日期进行分区。我用窗口函数重写了我的查询,如下所示:sql SELECT DISTINCT commodity_id, exp_month_id, exp_year, lead(exp_month_id) OVER (PARTITION BY date, commodity_id ORDER BY exp_year, exp_month_id) AS next_month_id, lead(exp_year) OVER (PARTITION BY date, commodity_id ORDER BY exp_year, exp_month_id) AS next_year FROM futures
  • 执行几乎相同的工作,但是结果包含很多 NULL: ``` C K 1961 C K 1961 N 1961 C N 1961 C N 1961 U 1961 C U 1961 ``` If有办法解决吗?
  • @SergejKosov 我发布的内容计算了上个月和当前月份之间的差异。所以,是的,用lead() 表示相反的东西。 -- 如果你有很多独特的 (date, commodity_id) 对,你会得到很多空值,是的 - 如果分区中只有一条记录,则没有什么可以领先或落后。
【解决方案2】:

感谢@Shawn 使用窗口函数的提示,我可以用更短的形式重写查询:

CREATE VIEW "futures_nextmonths_win" AS
WITH trading_months AS (
    SELECT DISTINCT commodity_id,
                    exp_month_id,
                    exp_year
    FROM futures)
SELECT commodity_id,
       exp_month_id,
       exp_year,
       lead(exp_month_id) OVER w AS next_month_id,
       lead(exp_year) OVER w AS next_year
FROM trading_months
WINDOW w AS (PARTITION BY commodity_id ORDER BY exp_year, exp_month_id);

这也比原来的快一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多