SQL 服务器 window 功能:构建历史字符串
SQL Server window functions: building up a history string
我有一个 table 的纵向数据,如下所示:
其中id
为分区变量,period
为时间维度,val
为观测值
我想为 id
的每个面板建立 val
的历史记录,如下所示:
我正在尝试使用 SQL window 函数而不是游标来执行此操作,但我保留 运行 的问题是 hist
列定义。似乎我必须每个周期创建一个 row/column。例如,我能想到的最接近的是:
IF OBJECT_ID('dbo.my_try', 'U') IS NOT NULL
DROP TABLE dbo.my_try;
GO
SELECT
id, period, val,
CASE
WHEN (
period = MIN(period)
OVER (PARTITION by id order by period ROWS
BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
) THEN CAST (val AS VARCHAR(60))
ELSE NULL
END AS hist
INTO my_try
FROM my_test
SELECT
id, period, val,
CASE
WHEN (
period = MIN(period) OVER
(PARTITION by id order by period ROWS
BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
) THEN hist
ELSE (
CONCAT(
val, ' | ', LAG(hist, 1) OVER (PARTITION by id order by period)
)
)
END AS hist2
FROM my_try
我必须假脱机进行迭代并执行 hist3
等,这样它才能最终工作。
是否可以使用 SQL window 函数来完成此操作,或者游标是否是唯一的路径?
示例数据
这里是一些生成原始代码的代码 table:
CREATE TABLE my_test (
id INT,
period INT,
val INT
)
BEGIN
DECLARE @id INT = 1;
DECLARE @period INT = 1;
WHILE @id <= 3
BEGIN
SET @period = 1
WHILE @period <= 3
BEGIN
INSERT INTO my_test VALUES (@id, @period, @period * POWER(10, @id))
SET @period = @period + 1
END
SET @id = @id + 1
END
END
其实这里不需要递归。你可以很容易地利用 STUFF。当然,如果你在 2017 年,你可以按照上面的建议使用 string_agg。但如果你像我一样,而你的公司不是最快采用最新最好的,你可以使用这个。
select t1.id
, t1.period
, t1.val
, STUFF((select ' | ' + convert(varchar(10), val)
from my_test t2
where t2.id = t1.id
and t2.period <= t1.period
order by t1.period
FOR XML PATH('')), 1, 3,'')
from my_test t1
order by t1.id
, t1.period
如评论中所述,尝试使用递归查询
with cte as(
select id, [period], val, convert(varchar(max), val) as agg from my_try where [period] = 1
union all
select t.id, t.[period], t.val, CONCAT(c.agg, ' | ', t.val) from my_try t join cte c on c.[period] +1 = t.[period] and c.id = t.id
)
select * from cte order by id, [period]
我有一个 table 的纵向数据,如下所示:
其中id
为分区变量,period
为时间维度,val
为观测值
我想为 id
的每个面板建立 val
的历史记录,如下所示:
我正在尝试使用 SQL window 函数而不是游标来执行此操作,但我保留 运行 的问题是 hist
列定义。似乎我必须每个周期创建一个 row/column。例如,我能想到的最接近的是:
IF OBJECT_ID('dbo.my_try', 'U') IS NOT NULL
DROP TABLE dbo.my_try;
GO
SELECT
id, period, val,
CASE
WHEN (
period = MIN(period)
OVER (PARTITION by id order by period ROWS
BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
) THEN CAST (val AS VARCHAR(60))
ELSE NULL
END AS hist
INTO my_try
FROM my_test
SELECT
id, period, val,
CASE
WHEN (
period = MIN(period) OVER
(PARTITION by id order by period ROWS
BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
) THEN hist
ELSE (
CONCAT(
val, ' | ', LAG(hist, 1) OVER (PARTITION by id order by period)
)
)
END AS hist2
FROM my_try
我必须假脱机进行迭代并执行 hist3
等,这样它才能最终工作。
是否可以使用 SQL window 函数来完成此操作,或者游标是否是唯一的路径?
示例数据
这里是一些生成原始代码的代码 table:
CREATE TABLE my_test (
id INT,
period INT,
val INT
)
BEGIN
DECLARE @id INT = 1;
DECLARE @period INT = 1;
WHILE @id <= 3
BEGIN
SET @period = 1
WHILE @period <= 3
BEGIN
INSERT INTO my_test VALUES (@id, @period, @period * POWER(10, @id))
SET @period = @period + 1
END
SET @id = @id + 1
END
END
其实这里不需要递归。你可以很容易地利用 STUFF。当然,如果你在 2017 年,你可以按照上面的建议使用 string_agg。但如果你像我一样,而你的公司不是最快采用最新最好的,你可以使用这个。
select t1.id
, t1.period
, t1.val
, STUFF((select ' | ' + convert(varchar(10), val)
from my_test t2
where t2.id = t1.id
and t2.period <= t1.period
order by t1.period
FOR XML PATH('')), 1, 3,'')
from my_test t1
order by t1.id
, t1.period
如评论中所述,尝试使用递归查询
with cte as(
select id, [period], val, convert(varchar(max), val) as agg from my_try where [period] = 1
union all
select t.id, t.[period], t.val, CONCAT(c.agg, ' | ', t.val) from my_try t join cte c on c.[period] +1 = t.[period] and c.id = t.id
)
select * from cte order by id, [period]