如何对 SQL 数据库重新采样

问题描述 投票:0回答:1

我有一个如下所示的数据集:

ITEM    CITY        START_Y   START_W   FIRST_USE_Y   FIRST_USE_W   VALUE
A       NEW YORK    2023      30             2023             32    15000
A       LONDON      2024       2             2024              2    12000
A       LONDON      2024       2             2024              5    50000
B       NEW YORK    2023      49             2024              1    19540
B       MADRID      2023      10             2023             11    15444

首先需要对ITEM和CITY的组合进行分组。然后,对于每个组,我想每周重新采样最多 5 个数据点,并用零填充“VALUE”列,其中 FIRST_USE_Y 和 FIRST_USE_W 列的组合没有值。 START_W 和 FIRST_USE_W 是一年中的第几周(值可以从 1 到 52)。

我尝试使用 pandas 和 for 循环;有效。但因为它是一个非常大的数据集,有数百万行,而且我必须使用 SQL(我是一个新手)。这是我尝试过的代码:

WITH RECURSIVE weekly_intervals AS (
    SELECT MIN(start_w) AS start_w, MAX(start_w) AS end_w
    FROM citywise_values
    UNION ALL
    SELECT start_w + INTERVAL 1 WEEK, end_w
    FROM weekly_intervals
    WHERE start_w + INTERVAL 1 WEEK <= end_w
),
filled_values AS (
    SELECT 
        w.item,
        w.city,
        w.start_y,
        w.start_w,
        COALESCE(cv.value, 0) AS value
    FROM 
        (SELECT 
            item,
            city,
            start_y,
            start_w
        FROM 
            citywise_values
        GROUP BY 
            item, city) w
    LEFT JOIN 
        citywise_values cv ON w.item = cv.item
                             AND w.city = cv.city
                             AND w.start_y = cv.start_y
                             AND w.start_w = cv.start_w
)
SELECT 
    item,
    city,
    start_y,
    start_w,
    COALESCE(value, LAG(value) OVER (PARTITION BY item, city, start_y ORDER BY start_w)) AS value
FROM 
    filled_values
RIGHT JOIN
    weekly_intervals
ON
    filled_values.start_w = weekly_intervals.start_w
ORDER BY
    item, city, start_y, start_w

然后我尝试使用交叉连接,并且只能为 ITEM 和 CITY 的一个组合生成结果。但我找不到如何处理整个数据集。

我不确定我能否解释清楚。因此,我发布了我手动创建的所需输出。

ITEM    CITY        START_Y     START_W     FIRST_USE_Y     FIRST_USE_W     VALUE
A       NEW YORK    2023        30                2023              31      0
A       NEW YORK    2023        30                2023              32      15000
A       NEW YORK    2023        30                2023              33      0
A       NEW YORK    2023        30                2023              34      0
A       NEW YORK    2023        30                2023              35      0
A       LONDON      2024        2                 2024              2       12000
A       LONDON      2024        2                 2024              3       0
A       LONDON      2024        2                 2024              4       0
A       LONDON      2024        2                 2024              5       50000
A       LONDON      2024        2                 2024              6       0
B       NEW YORK    2023        49                2023              49      0
B       NEW YORK    2023        49                2023              50      0
B       NEW YORK    2023        49                2023              51      0
B       NEW YORK    2023        49                2023              52      0
B       NEW YORK    2023        49                2024              1       19540
B       MADRID      2023        10                2023              10      0
B       MADRID      2023        10                2023              11      15444
B       MADRID      2023        10                2023              12      0
B       MADRID      2023        10                2023              13      0
B       MADRID      2023        10                2023              14      0

任何帮助将不胜感激。

sql join resampling cross-join
1个回答
0
投票

这里有三项使这个查询变得棘手:

  1. 投影到 5 行(我使用了表值构造函数,但还有其他选项,包括 SQL Server 2022 中的

    generate_series()
    或递归 CTE)

  2. 年末时的处理。这里的技巧是日期是混乱永远不要尝试自己做这种工作。 始终依靠平台内置的日期选项。这意味着将年/周值转换为实际日期......这就是我建议首先以这种方式存储内容的原因之一。您可以将这些值存储为日期,其中实际日期是该周的星期日值。

    我确实必须对手动日期数学做出一个让步:因为年份并不总是在同一天开始,并且数据中的周数似乎并不总是与 SQL Server 返回的“常规”或 iso_week 匹配,我手动将周数视为自 1 月 1 日以来的 7 天区块。

我也看到这个:

START_W 和 FIRST_USE_W 是一年中的第几周(值可以从 1 到 52)。

一年有超过52周!

每年都会有部分周数53,其中至少有一两天。您需要能够解释这一点。

我想出了这个,甚至使用了交叉连接:

WITH ItemCity As (
    SELECT Item, City, MIN(  DATEADD(day, Start_W*7, DATEFROMPARTS(Start_Y, 1, 1)) ) As StartWeek
    FROM Data
    GROUP BY Item, City
), 
ItemCityWeeks As (
   SELECT Item,City, StartWeek
       ,Year(StartWeek) As Start_Y,datepart(week, StartWeek)-1 As Start_W
       ,YEAR(DATEADD(day, Weeks.num*7, StartWeek)) As First_Use_Y
       ,DATEPART(dayofyear, DATEADD(day, Weeks.num*7, StartWeek))/7 As First_Use_W
   FROM ItemCity
   CROSS JOIN ( VALUES (0), (1), (2), (3), (4)) Weeks(num)
)
SELECT icw.Item, icw.City
      , icw.Start_Y, icw.Start_W, icw.First_Use_Y, icw.First_Use_W
      , coalesce(d.value, 0) as Value
FROM ItemCityWeeks icw
LEFT JOIN Data d ON d.Item = icw.Item AND d.City = icw.City 
      and d.First_Use_Y = icw.First_Use_Y and d.First_Use_W = icw.First_Use_W
ORDER BY Item, City DESC

在这里查看它的工作原理:

https://dbfiddle.uk/1PyTujMX

另请注意,我的首次使用周比第一个城市的周要晚。我认为这是手动创建的样本结果中的一个错误,因为其他城市都以与 Start_W 同一周开始,而这个城市晚一周开始。

© www.soinside.com 2019 - 2024. All rights reserved.