【问题标题】:How to use recursive CTE to add resolution to a data set如何使用递归 CTE 为数据集添加分辨率
【发布时间】:2019-08-15 14:25:35
【问题描述】:

我正在尝试创建一个递归 CTE 语句,该语句在数据点之间添加空白行,稍后将用于插值。我是 SQL 的初学者,这是我第一次使用 CTE,我很难找到正确的方法来做到这一点。

经过一些研究后,我尝试对下面提供的代码进行一些不同的细微修改,但还没有很好地理解我的问题。以下代码应通过每 4 小时从样本数据集中进行一次观察来模拟稀疏采样,第二部分应每 0.1 小时添加具有相应 x 值的行,稍后将填充从三次样条曲线派生的插值。

--样本数据

create table #temperatures (hour integer, temperature double precision);
insert into #temperatures (hour, temperature) values
(0,18.5),
(1,16.9),
(2,15.3),
(3,14.1),
(4,13.8),
(5,14.7),
(6,14.7),
(7,13.5),
(8,12.2),
(9,11.4),
(10,10.9),
(11,10.5),
(12,12.3),
(13,16.4),
(14,22.3),
(15,27.2),
(16,31.1),
(17,34),
(18,35.6),
(19,33.1),
(20,25.1),
(21,21.3),
(22,22.3),
(23,20.3),
(24,18.4),
(25,16.8),
(26,15.6),
(27,15.4),
(28,14.7),
(29,14.1),
(30,14.2),
(31,14),
(32,13.9),
(33,13.9),
(34,13.6),
(35,13.1),
(36,15),
(37,18.2),
(38,21.8),
(39,24.1),
(40,25.7),
(41,29.9),
(42,28.9),
(43,31.7),
(44,29.4),
(45,30.7),
(46,29.9),
(47,27);

--1

WITH xy (x,y)
AS 
    (
  SELECT  TOP 12
    CAST(hour AS double precision) AS x
    ,temperature AS y 
    FROM #temperatures 
    WHERE cast(hour as integer) % 4 = 0
   )

Select x,y
INTO #xy
FROM xy

Select [x] As [x_input]
INTO #x_series
FROM #xy

--2

    with recursive
  , x_series(input_x) as (
    select
      min(x)
    from
      #xy
    union all
    select
      input_x + 0.1
    from
      x_series
    where
      input_x + 0.1 < (select max(x) from x)
  )
  , x_coordinate as (
  select
    input_x
    , max(x) over(order by input_x) as previous_x
  from
    x_series
  left join
    #xy on abs(x_series.input_x - xy.x) < 0.001
  )

第一个 CTE 按预期工作并生成 12 个列表(两天内每 4 小时一个样本),但第二个 CTE 产生语法错误。预期的输出将类似于

(4,13.8), (4.1,null/0), (4.2,null/0),....., (8,12.2)

【问题讨论】:

  • Sorry Juan 我可以提供示例数据和预期的输出,你说的 db schema 是什么意思?
  • 表的CREATE TABLE,所以我们知道数据类型、主键、索引
  • 做了这些调整感谢您的提示。
  • 只是为了明确顺序是4.1,4.2,4.3 ... 4.9,5.0,5.1, .... 7.9,8.0 ?

标签: sql-server recursive-cte


【解决方案1】:

我认为你不需要递归。

这个呢:

SQL DEMO

SELECT DISTINCT n = number *1.0 /10 , #xy.x, #xy.y
FROM master..[spt_values] step
LEFT JOIN #xy
  ON step.number*1.0 /10  = #xy.x
WHERE number BETWEEN 40 AND 480

这个 480 是基于你提到的两天。

输出

你甚至不需要临时表

SELECT DISTINCT n = number *1.0 /10 , #temperatures.temperature
FROM master..[spt_values] step
LEFT JOIN #temperatures
  ON step.number *1.0 / 10  = #temperatures.hour
 AND  #temperatures.hour % 4 = 0
WHERE number BETWEEN 40 AND 480;

【讨论】:

    【解决方案2】:

    我认为您在这里不需要递归 CTE。我认为这样的解决方案将是一种更好的方法。相应地修改。

    DECLARE @max_value FLOAT = 
        (SELECT MAX(hour) FROM  #temperatures) * 10
    
    INSERT INTO #temperatures (hour, temperature)
    SELECT X.N / 10, NULL
    FROM (
        select CAST(ROW_NUMBER() over(order by t1.number) AS FLOAT) AS N
        from   master..spt_values t1 
               cross join master..spt_values t2
    ) X
    WHERE X.N <= @max_value
        AND X.N NOT IN (SELECT hour FROM #temperatures)
    

    【讨论】:

    • 你需要#xy,y(温度)
    【解决方案3】:

    使用你在--1中产生的临时表#xy,下面会给你一个x系列:

    ;with x_series(input_x)
    as
    (
        select min(x) AS input_x
        from #xy
        union all
        select input_x + 0.1
        from x_series
        where input_x + 0.1 < (select max(x) from #xy)
    )
    SELECT * FROM x_series;
    

    【讨论】:

    • 也可以使用x_series左连接#xy得到(x,y)结果
    • 不能在递归 cte 中使用聚合函数
    • @JuanCarlosOropeza 你是对的。虽然我可以在 cte 之前将 min(x) 和 max(x) 放入变量中,但它也可以超过 100 个递归限制。所以递归 cte 可能不是一个好的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-10
    • 1970-01-01
    相关资源
    最近更新 更多