SQL 新列中的模式长度
SQL Pattern Length in New Column
我有一个(非常非常大)table,其格式类似于以下内容:
+--------+-------+
| id | value |
+--------+-------+
| 1 | 5 |
| 2 | 6 |
| 3 | 6 |
| 4 | 4 |
| 5 | 3 |
| 6 | 2 |
| 7 | 4 |
| 8 | 5 |
+--------+-------+
我想做的是 return 值列的模式长度在第三列中增加或减少(模式为负表示减少,正表示增加),同时忽略没有变化的ID。当模式被打破时,模式应该重置为 1 或 -1。
我根本没有解释那么好,所以根据上面的 table,理想情况下结果是:
+--------+-------+---------+
| id | value | pattern |
+--------+-------+---------+
| 1 | 5 | 0/NULL |
| 2 | 6 | 1 |
| 3 | 6 | 1 |
| 4 | 4 | -1 |
| 5 | 3 | -2 |
| 6 | 2 | -3 |
| 7 | 4 | 1 |
| 8 | 5 | 2 |
+--------+-------+---------+
我做了一些研究并遇到了模式匹配,但结果是我正在使用的 SQL 版本(这是使用的版本 by/on Amazon Redshift ,根据他们的说法是'based on' PostgreSQL 8.0.2 http://docs.aws.amazon.com/redshift/latest/dg/c_redshift-and-postgres-sql.html)) 不支持,或者我太傻了
那么,SQL 甚至可以做到这一点吗?如果可以,我应该怎么做?非常感谢。
在 SQL Server 2012 中,您可以使用 lead()
和 lag()
以及累计总和来执行此操作。
非常接近的是:
select t.*, sum(nextinc) over (order by id) as pattern
from (select t.*,
(case when lead(t.value) > t.value then 1
when lead(t.value) = t.value then 0
else -1 end) as nextinc,
(case when lag(t.value) > t.value then 1 else 0 end) as previnc
from table t
) t;
但是,该模式以 1 为增量上升和下降,而不是重新开始。所以,我们需要找到模式中断。以下定义了模式中的中断,然后为 increasing/decreasing 值的序列递增模式:
select t.*,
sum(nextinc) over (partition by grp order by id) as pattern
from (select t.*,
sum(case when (prev_value <= value and value <= next_value) or
(prev_value >= value and value >= next_value)
then 0 else 1
end) over (order by id) as grp
from (select t.*, lead(t.value) over (order by id) as next_value,
lag(t.value) over (order by id) as prev_value,
(case when lead(t.value) over (order by id) > t.value then 1
when lead(t.value) over (order by id) = t.value then 0
else -1 end) as nextinc
from table t
) t
) t
对于给定的示例,以下似乎可以完成工作:
SELECT
S3.id
, S3.value
, S3.pattern
, SUM(minusNullPlus) OVER (PARTITION BY sequenceID ORDER BY id) calculated
FROM
(SELECT
S2.*
, SUM(newSequence) OVER (ORDER BY id) sequenceID
FROM
(SELECT
S1.*
, CASE
WHEN minusNullPlus = LAG(minusNullPlus, 1, NULL) OVER (ORDER BY id)
OR
minusNullPlus = 0
OR
(minusNullPlus = 1
AND
value - LAG(value, 1, NULL) OVER (ORDER BY id) = 1
)
OR
(minusNullPlus = -1
AND
value - LAG(value, 1, NULL) OVER (ORDER BY id) = -1
)
THEN 0
ELSE 1
END newSequence
FROM
(SELECT
id
, value
, CASE
WHEN value > LAG(value, 1, NULL) OVER (ORDER BY id) THEN 1
WHEN value < LAG(value, 1, NULL) OVER (ORDER BY id) THEN -1
WHEN value = LAG(value, 1, NULL) OVER (ORDER BY id) THEN 0
ELSE 0
END minusNullPlus
, CASE
WHEN value - LAG(value, 1, NULL) OVER (ORDER BY id) = 0 THEN 0
ELSE 1
END change
, pattern
FROM SomeTable
) S1
) S2
) S3
ORDER BY id
;
查看实际效果:SQL Fiddle
它使用一些额外的数据来检查 - 请验证相应的 模式 是否与您的 expectations/requirements.
实际一致
注意:建议的解决方案依赖于所提供样本数据的一些特殊性(及其在上述 SQL Fiddle 中的扩展)。
如果需要调整/进一步的详细信息,请发表评论。
我有一个(非常非常大)table,其格式类似于以下内容:
+--------+-------+
| id | value |
+--------+-------+
| 1 | 5 |
| 2 | 6 |
| 3 | 6 |
| 4 | 4 |
| 5 | 3 |
| 6 | 2 |
| 7 | 4 |
| 8 | 5 |
+--------+-------+
我想做的是 return 值列的模式长度在第三列中增加或减少(模式为负表示减少,正表示增加),同时忽略没有变化的ID。当模式被打破时,模式应该重置为 1 或 -1。
我根本没有解释那么好,所以根据上面的 table,理想情况下结果是:
+--------+-------+---------+
| id | value | pattern |
+--------+-------+---------+
| 1 | 5 | 0/NULL |
| 2 | 6 | 1 |
| 3 | 6 | 1 |
| 4 | 4 | -1 |
| 5 | 3 | -2 |
| 6 | 2 | -3 |
| 7 | 4 | 1 |
| 8 | 5 | 2 |
+--------+-------+---------+
我做了一些研究并遇到了模式匹配,但结果是我正在使用的 SQL 版本(这是使用的版本 by/on Amazon Redshift ,根据他们的说法是'based on' PostgreSQL 8.0.2 http://docs.aws.amazon.com/redshift/latest/dg/c_redshift-and-postgres-sql.html)) 不支持,或者我太傻了
那么,SQL 甚至可以做到这一点吗?如果可以,我应该怎么做?非常感谢。
在 SQL Server 2012 中,您可以使用 lead()
和 lag()
以及累计总和来执行此操作。
非常接近的是:
select t.*, sum(nextinc) over (order by id) as pattern
from (select t.*,
(case when lead(t.value) > t.value then 1
when lead(t.value) = t.value then 0
else -1 end) as nextinc,
(case when lag(t.value) > t.value then 1 else 0 end) as previnc
from table t
) t;
但是,该模式以 1 为增量上升和下降,而不是重新开始。所以,我们需要找到模式中断。以下定义了模式中的中断,然后为 increasing/decreasing 值的序列递增模式:
select t.*,
sum(nextinc) over (partition by grp order by id) as pattern
from (select t.*,
sum(case when (prev_value <= value and value <= next_value) or
(prev_value >= value and value >= next_value)
then 0 else 1
end) over (order by id) as grp
from (select t.*, lead(t.value) over (order by id) as next_value,
lag(t.value) over (order by id) as prev_value,
(case when lead(t.value) over (order by id) > t.value then 1
when lead(t.value) over (order by id) = t.value then 0
else -1 end) as nextinc
from table t
) t
) t
对于给定的示例,以下似乎可以完成工作:
SELECT
S3.id
, S3.value
, S3.pattern
, SUM(minusNullPlus) OVER (PARTITION BY sequenceID ORDER BY id) calculated
FROM
(SELECT
S2.*
, SUM(newSequence) OVER (ORDER BY id) sequenceID
FROM
(SELECT
S1.*
, CASE
WHEN minusNullPlus = LAG(minusNullPlus, 1, NULL) OVER (ORDER BY id)
OR
minusNullPlus = 0
OR
(minusNullPlus = 1
AND
value - LAG(value, 1, NULL) OVER (ORDER BY id) = 1
)
OR
(minusNullPlus = -1
AND
value - LAG(value, 1, NULL) OVER (ORDER BY id) = -1
)
THEN 0
ELSE 1
END newSequence
FROM
(SELECT
id
, value
, CASE
WHEN value > LAG(value, 1, NULL) OVER (ORDER BY id) THEN 1
WHEN value < LAG(value, 1, NULL) OVER (ORDER BY id) THEN -1
WHEN value = LAG(value, 1, NULL) OVER (ORDER BY id) THEN 0
ELSE 0
END minusNullPlus
, CASE
WHEN value - LAG(value, 1, NULL) OVER (ORDER BY id) = 0 THEN 0
ELSE 1
END change
, pattern
FROM SomeTable
) S1
) S2
) S3
ORDER BY id
;
查看实际效果:SQL Fiddle
它使用一些额外的数据来检查 - 请验证相应的 模式 是否与您的 expectations/requirements.
注意:建议的解决方案依赖于所提供样本数据的一些特殊性(及其在上述 SQL Fiddle 中的扩展)。
如果需要调整/进一步的详细信息,请发表评论。