【问题标题】:Loading multiple time series simultaneously using SQL使用 SQL 同时加载多个时间序列
【发布时间】:2021-08-18 23:48:45
【问题描述】:

假设我有这个确切的数据集:

date widget ID widget price widget expiry date
2020-01-01 A 1 2020-03-01
2020-01-01 B 2 2020-04-01
2020-01-01 C 3 2020-05-01
2020-01-01 D 4 2020-06-01
2020-01-02 A 1.1 2020-03-01
2020-01-02 B 2.05 2020-04-01
2020-01-02 C 3.7 2020-05-01
2020-01-02 D 3.8 2020-06-01
2020-01-03 A 1.15 2020-03-01
2020-01-03 B 2.09 2020-04-01
2020-01-03 C 3.54 2020-05-01
2020-01-03 D 4.2 2020-06-01
2020-01-04 A 1.19 2020-03-01
2020-01-04 B 2.14 2020-04-01
2020-01-04 C 3.73 2020-05-01
2020-01-04 D 4.30 2020-06-01

假设我想使用单个 SQL 查询同时检索以下两个小部件的完整时间序列:

  • 在日期 2020-01-01 价格尽可能接近 1 且到期日期尽可能接近 2020-03-10 的小部件。
  • 在日期 2020-01-03 价格尽可能接近 3.5 且到期日期尽可能接近 2020-05-15 的小部件。

换句话说,这个确切的表:

date widget ID widget price widget expiry date
2020-01-01 A 1 2020-03-01
2020-01-01 C 3 2020-05-01
2020-01-02 A 1.1 2020-03-01
2020-01-02 C 3.7 2020-05-01
2020-01-03 A 1.15 2020-03-01
2020-01-03 C 3.54 2020-05-01
2020-01-04 A 1.19 2020-03-01
2020-01-04 C 3.73 2020-05-01

你会建议怎么做?

概括这个例子,假设您有一个如下所示的元组列表,其中 price_i 是目标价格,expiry_date_i 是目标到期日期。

(date_1, price_1, expiry_date_1), (date_2, price_2, expiry_date_2), (date_3, price_3, expiry_date_3),...

如何一次性加载所有相应小部件的时间序列?

目前我正在使用类似这样的 SQL 查询分别检索这些小部件的 ID(在此示例中 date='2020-01-01', price=1, expiry date='2020-03-10' )。然后收集所有这些检索到的 ID,我加载了完整的小部件时间序列。

WITH sample AS 
(SELECT *, ABS(DATEDIFF(day,widget_expiry_date, '2020-03-10')) AS date_diff, ABS(widget_price - 1) As price_diff 
FROM data WHERE date='2020-01-01'
ORDER BY date_diff ASC, price_diff ASC)
SELECT TOP 1 widget_ID FROM sample

您可以想象这是非常低效的。我想知道是否有更聪明的方法?

感谢您抽出宝贵时间并提前为这个愚蠢的问题道歉。

【问题讨论】:

  • 并注意 TOP 不是 MySQL 构造
  • 草莓你得忍受我。这是一个非常基本且可重复的示例,我已经将问题简化到了骨子里
  • 不清楚,例如为什么 date='2020-01-01' ,到期日期应该接近到期日期='2020-03-10' 或 2020-05-15 ,是expirydate 总是这些日期来自所有“日期”?
  • 此处(日期、价格、到期日)= (2020-01-01, 1, 2020-03-10)。鉴于此输入,我正在尝试加载小部件的时间序列,该小部件在日期 2020-01-01 的价格尽可能接近 1,到期日期尽可能接近 2020-03-10。这个元组将是一个用户输入,所以我不想搜索完全匹配(因为这些可能不存在)

标签: mysql sql database time-series


【解决方案1】:

在单个查询中检索所有系列

with params (date_, price_, expiry_date_) AS (
   select date '2020-01-01', 1,   date '2020-03-10' union all
   select date '2020-01-03', 3.5, date '2020-05-15' 
)
select data.*
from params p
join data on data.widgetID = (
   SELECT widgetID
   FROM data d
   WHERE d.date = p.date_
   ORDER BY ABS(DATEDIFF(d.widget_expiry_date, p.expiry_date_))  ASC, ABS(d.widget_price - p.price_) ASC
   LIMIT 1);

db<>fiddle

【讨论】:

    【解决方案2】:

    你也可以使用窗口函数:

    SELECT indate , widgetID ,  price , expirydate FROM (
    SELECT * 
        , ROW_NUMBER() OVER (PARTITION BY indate ORDER BY ABS(price - 1), ABS(DATEDIFF(expirydate, '2020-03-10')) ) rn1 
        , ROW_NUMBER() OVER (PARTITION BY indate ORDER BY ABS(price - 3.5), ABS(DATEDIFF(expirydate, '2020-05-15')) ) rn2
    FROM  widgets
    ) t
    WHERE rn1 =1 OR rn2 = 1
    ORDER BY indate , widgetID 
    

    db小提琴here

    【讨论】:

    • 谢谢eshirvana,抱歉这里的“indate”是什么?
    • @jraffaud 刚刚重命名“日期”列,因为日期是保留字
    • @Strawberry 我的意思是“日期”是一个保留字,因为它在 mysql 文档中已经提到,几乎每个 dbms 都是这种情况。
    • 嗯,它是一个关键字,我应该更清楚
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-08
    • 1970-01-01
    • 2012-06-12
    • 1970-01-01
    相关资源
    最近更新 更多