在SQL中选择序列

时间:2014-05-27 00:28:40

标签: sql teradata

这个主题似乎有一些博客文章,但解决方案确实不那么直观。肯定有" Canonical"方式是什么?

我正在使用Teradata SQL。

我如何选择

  1. 一系列数字
  2. 日期范围
  3. E.g。

    SELECT 1:10 AS Nums
    
    SELECT 1-1-2010:5-1-2014 AS Dates1
    

    第一个SELECT查询的结果是10行(1 - 10),第二个查询的结果是〜(365 * 3.5)行吗?

3 个答案:

答案 0 :(得分:3)

在Teradata中,您可以使用现有的sys_calendar来获取这些日期:

SELECT calendar_date
FROM sys_calendar.CALENDAR
WHERE calendar_date BETWEEN DATE '2010-01-01' AND DATE '2014-05-01';

注意: DATE '2010-01-01'是在Teradata中编写日期的唯一推荐方式

对于贵公司的特定业务需求,可能还有另一个自定义日历。每个人都有权访问它。

您也可以将此用于数字范围:

SELECT day_of_calendar
FROM sys_calendar.CALENDAR
WHERE day_of_calendar BETWEEN 1 AND 10;

但您应该检查Explain以查看估计的行数是否正确。 sys_calendar是一种模板,day_of_calendar是一个计算列,因此没有统计数据,而Explain将返回估计的14683个数(占该表中行数的20%)而不是10.如果你在另外使用它加入优化器可能会根据完全错误的数字做一个糟糕的计划。

注意: 如果您使用sys_calendar,则限制为最多73414行,日期介于1900-01-01和2100-12-31之间,数字介于1和73414之间,您的业务日历可能会有所不同。

Gordon Linoff的递归查询在Teradata中效率不高,因为它是并行数据库中的顺序逐行处理(每个循环都是Explain中的“全AMP步骤”),优化器不知道如何将返回许多行。

如果您经常需要这些范围,您可以考虑创建一个数字表,我通常会有一个包含一百万行的数据表,或者我使用我的日程表,其全年范围为10000年: - )

--DROP TABLE nums;
CREATE TABLE nums(n INT NOT NULL PRIMARY KEY CHECK (n BETWEEN 0 AND 999999));

INSERT INTO Nums
WITH cte(n) AS
 (
   SELECT day_of_calendar - 1
   FROM sys_calendar.CALENDAR
   WHERE day_of_calendar BETWEEN 1 AND 1000
 ) 
SELECT
   t1.n +
   t2.n * 1000 
FROM cte t1 CROSS JOIN cte t2;

COLLECT STATISTICS COLUMN(n) ON Nums;

COLLECT STATS是获得正确估算的最重要步骤。 现在它很简单

SELECT n FROM nums WHERE n BETWEEN 1 AND 10;

GitHub上还有一个很好的UDF用于创建易于使用的序列:

SELECT DATE '2010-01-01' + SEQUENCE 
FROM TABLE(gen_sequence(0,DATE '2014-05-01' - DATE '2010-01-01')) AS t;

SELECT SEQUENCE 
FROM TABLE(gen_sequence(1,10)) AS t;

但通常很难说服你的DBA安装任何C-UDF,并且返回的行数再次未知。

答案 1 :(得分:2)

在SQL中执行此操作的“规范”方法是使用递归CTE,Teradata的更新版本支持递归CTE。

第一个例子:

with recursive nums(n) as (
      select 1 as n
      union all
      select n + 1
      from nums
      where n < 10
     )
select *
from nums;

您可以为日期做类似的事情。

编辑:

您也可以使用row_number()和现有表格执行此操作:

with nums(n) as (
      select n
      from (select row_number() over (order by col) as n
            from ExstingTable t
           ) t
      where n <= 10
     )
select *
from nums;

ExistingTable就是任何有足够行的表。 col的最佳选择是主键。

with digits(n) as (
      select 1 as n union all select 2 union all select 3 union all select 4 union all select 5 union all
      select 6 union all select 7 union all select 8 union all select 9 union all select 10
     )
select *
from digits;

如果您的Teradata版本支持多个CTE,您可以在上面构建:

with digits(n) as (
      select 1 as n union all select 2 union all select 3 union all select 4 union all select 5 union all
      select 6 union all select 7 union all select 8 union all select 9 union all select 10
     ),
     nums(n) as (
      select d1.n*100 + d2.n*10 + d3.n
      from digits d1 cross join digits d2 cross join digits d3
     )
select *
from nums;

答案 2 :(得分:0)

序列1到10

sel sum (1) over (ROWS UNBOUNDED PRECEDING) as seq_val
from sys_calendar.CALENDAR
qualify  row_number () over (order by 1)<=10