Sql 是否有任何方法可以基于月份填充实体,但不包括每个循环的第一个月?
我有一套这样的数据样本Sql 是否有任何方法可以基于月份填充实体,但不包括每个循环的第一个月?,sql,postgresql,google-bigquery,Sql,Postgresql,Google Bigquery,我有一套这样的数据样本 | id | month | x | jan | x | feb | x | mar 有没有什么方法可以让这个样本变成这样 | id | month | number | x | jan | 1 | x | feb | 2 | x | mar | 3 | x | feb | 1 | x | mar | 2 | x | mar | 1 此操作类似于union all,但不包括每个循环的第一个月如果表名为samples,则可以尝试
| id | month
| x | jan
| x | feb
| x | mar
有没有什么方法可以让这个样本变成这样
| id | month | number
| x | jan | 1
| x | feb | 2
| x | mar | 3
| x | feb | 1
| x | mar | 2
| x | mar | 1
此操作类似于union all,但不包括每个循环的第一个月如果表名为samples,则可以尝试此递归CTE:
WITH cte AS
(
SELECT id
, month
, ROW_NUMBER() OVER (ORDER BY id, month) number
FROM samples
union ALL
SELECT id
, month
, nr-1
FROM cte
where nr>1
)
SELECT
id
,month
,number
FROM cte
首先,我们使用ROW_number对行进行编号,然后使用递归CTE首先返回所有行,然后返回ROW number>1的所有行,将行数减少1,这与原始行数的迭代次数相同。下面是针对BigQuery标准SQL的
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 id, 'jan' month, 1 pos UNION ALL
SELECT 1, 'feb', 2 UNION ALL
SELECT 1, 'mar', 3
)
SELECT id, month,
pos - MIN(pos) OVER(PARTITION BY id, num) + 1 AS number
FROM `project.dataset.table`,
UNNEST(GENERATE_ARRAY(1, pos)) AS num
-- ORDER BY num
有输出
Row id month number
1 1 jan 1
2 1 feb 2
3 1 mar 3
4 1 feb 1
5 1 mar 2
6 1 mar 1
如果pos字段不显式可用,您可以按照下面的示例推导它
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 id, 'jan' month UNION ALL
SELECT 1, 'feb' UNION ALL
SELECT 1, 'mar'
), temp AS (
SELECT id, month, EXTRACT(MONTH FROM PARSE_DATE('%b', month)) pos
FROM `project.dataset.table`
)
SELECT id, month,
pos - MIN(pos) OVER(PARTITION BY id, num) + 1 AS number
FROM temp,
UNNEST(GENERATE_ARRAY(1, pos)) AS num
-- ORDER BY num
使用相同的最终输出是用于BigQuery还是PostgreSQL?cte中的from cte无法识别,它表示表名cte缺少数据集,而请求中未设置默认数据集。谢谢,这对我很有用。但是,仍然很难理解iti背后的逻辑,在时间允许的情况下,它会在一天之后回来,并给出一些解释:o但同时,试着通过简单地玩它自己得到它-开始添加num和pos来选择输出列表,这样你们就会看到这个数字是如何计算的
with data as (
select id, "month",
case "month"
when 'jan' then 1 when 'feb' then 2 when 'mar' then 3 when 'apr' then 4
when 'may' then 5 when 'jun' then 6 when 'jul' then 7 when 'aug' then 8
when 'sep' then 9 when 'oct' then 10 when 'nov' then 11 when 'dec' then 12
end as mm
from T
)
select d1.id, d1."month", row_number() over (partition by d1.id order by d1.mm) as nun
from data d1 cross apply data d2
where d1.mm <= d2.mm