如何在标准 SQL 或 T-SQL 中生成 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, ... 系列?

joa*_*olo 11 postgresql sql-server t-sql functions

给定两个数字nand m,我想生成一系列的形式

1, 2, ..., (n-1), n, n, (n-1), ... 2, 1
Run Code Online (Sandbox Code Playgroud)

并重复它m几次。

例如,对于n = 3and m = 4,我想要以下 24 个数字的序列:

1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1
----------------  ----------------  ----------------  ----------------
Run Code Online (Sandbox Code Playgroud)

我知道如何通过以下两种方法之一在 PostgreSQL 中实现此结果:

使用以下查询,该查询使用该generate_series函数,以及一些确保订单正确的技巧:

WITH parameters (n, m) AS
(
    VALUES (3, 5)
)
SELECT 
    xi
FROM
(
    SELECT
        i, i AS xi
    FROM
        parameters, generate_series(1, parameters.n) AS x(i)
    UNION ALL
    SELECT
        i + parameters.n, parameters.n + 1 - i AS xi
    FROM
        parameters, generate_series(1, parameters.n) AS x(i)
) AS s0 
CROSS JOIN 
    generate_series (1, (SELECT m FROM parameters)) AS x(j)
ORDER BY
    j, i ;
Run Code Online (Sandbox Code Playgroud)

... 或使用具有伴随和嵌套循环的相同目的的函数:

CREATE FUNCTION generate_up_down_series(
    _elements    /* n */ integer,
    _repetitions /* m */ integer)
RETURNS SETOF integer AS
$BODY$
declare
    j INTEGER ;
    i INTEGER ;
begin
    for j in 1 .. _repetitions loop
        for i in         1 .. _elements loop
              return next i ;
        end loop ;
        for i in reverse _elements .. 1 loop
              return next i ;
        end loop ;
    end loop ;
end ;
$BODY$
LANGUAGE plpgsql IMMUTABLE STRICT ;
Run Code Online (Sandbox Code Playgroud)

我怎么可能在标准 SQL 或 Transact-SQL/SQL Server 中执行等效操作?

Vér*_*ace 11

长话短说

\n

这是一个很长的方法,所以我首先将最好的(即最快的)方法放在这里。它使用了INTARRAY扩展 - 对于 340 和 570 的参数,需要 21ms *。第二好的(22 ms - 相同的参数)仅使用标准 PostgreSQL 构造。如果其他人想出更快的方法,我会将它们放在这里并“退休”我的!

\n

* 8 GB RAM、Intel i5 第 11 代 CPU、四核 - 8 线程、NVMe 256 GB 驱动器

\n

</TL;DR>

\n

介绍:

\n

这个问题引起了我的兴趣(+1)并且我思考了

\n
    \n
  • a) 如何回答以及

    \n
  • \n
  • b) 答案如何概括?

    \n
  • \n
\n

下面的所有代码都可以在每个方法/贡献者的各种小提琴中找到。

\n

有趣的是,到目前为止,没有人回答过(10 个答案 - 以及一些非常高质量的 SQL/编程启动!)使用过ARRAYs (教程),我相信这在这种情况下非常有帮助。

\n

一般来说,我的方法是将第一个系列生成为 ARRAY ( {1, 2,.. n, n,..2, 1}),然后生成m这些序列以完成完整的任务。

\n

我采取了五种方法:

\n
    \n
  • 第一个是 PostgreSQL 特定的,使用GENERATE_SERIES()教程)和ARRAYs。还有一个可以用 a 替换的函数调用RECURSIVE CTE"RCTE"- 请参阅教程)。

    \n
  • \n
  • 第二个(也是 PostgreSQL 特有的)将 an与对和s 的RCTE调用结合起来。可以替换为- 请参阅解决方案 3。GENERATE_SERIES()ARRAYGENERATE_SERIES()RCTE

    \n
  • \n
  • 第三种解决方案是"Pure SQL"并且应该适用于任何支持s的RDBMS (例如RCTESQL Server ) - 也可以使用表(即序列)根据dba聊天室的讨论,我已经删除了使用s来构造序列的要求。numbersRCTE

    \n
  • \n
  • 然后有两种“快速”解决方案,它们依赖于UNNEST()保持秩序的事实。

    \n
  • \n
\n

准备步骤:

\n

我根据问题使用了一个名为的表param来存储值(3& ) - 这些值显然可以更改。5另外,对于基准测试步骤(见下文),我将这个参数表用于所有测试的查询,以便有一个公平的竞争环境。如上所述,numbers序列表也是允许的。

\n
--\n-- Setup of parameters...\n--\n\nCREATE TABLE param(n INT, m INT);\nINSERT INTO param (VALUES(3, 5));\nSELECT * FROM param;\n\n\n--\n-- Setup of numbers\n--\n\nCREATE TABLE numbers (n INT);\nINSERT INTO numbers\nSELECT GENERATE_SERIES(1, ((SELECT m FROM param))); \nSELECT * FROM numbers LIMIT 5;\n
Run Code Online (Sandbox Code Playgroud)\n

第一种方法如下:

\n

方法 1 - GENERATE_SERIES(小提琴):

\n

步骤1:

\n
--\n-- Step 1\n--\nSELECT\n  GENERATE_SERIES(1, (SELECT n FROM param)) AS the_first_series\nUNION ALL\nSELECT \n  GENERATE_SERIES((SELECT n FROM param), 1, -1);\n
Run Code Online (Sandbox Code Playgroud)\n

结果:

\n
the_first_series\n               1\n               2\n               3\n               3\n               2\n               1\n
Run Code Online (Sandbox Code Playgroud)\n

第2步:

\n
--\n--  Two possible Step 2s using a PL/pgSQL function or an RCTE\n--\nCREATE OR REPLACE FUNCTION fill_array_with_seq(the_array anyarray, seq_num INT)\nRETURNS ANYARRAY LANGUAGE PLpgSQL AS $$\nDECLARE\nBEGIN\n  FOR i IN 1..seq_num LOOP\n    the_array[i] := i;\n    i = i + 1;\n  END LOOP;\n  RETURN the_array;\nend $$;\n\nSELECT fill_array_with_seq(ARRAY[]::INT[], (SELECT n * 2 FROM param));\n\nWITH RECURSIVE cte_fill_arr (n, f_arr) AS\n(\n  SELECT 1 AS n, ARRAY[1]::INT[] AS f_arr\n  UNION ALL\n  SELECT n + 1, array_append(f_arr, n + 1)\n  FROM cte_fill_arr\n  WHERE n < (SELECT n * 2 FROM param)\n)\nSELECT f_arr FROM cte_fill_arr\nWHERE CARDINALITY(f_arr) = (SELECT n * 2 FROM param);\n
Run Code Online (Sandbox Code Playgroud)\n

结果(相同):

\n
fill_array_with_seq\n      {1,2,3,4,5,6}\n              f_arr\n      {1,2,3,4,5,6}\n
Run Code Online (Sandbox Code Playgroud)\n

步骤3:

\n
--\n-- Step 3\n--\n\nWITH RECURSIVE cte_fill_arr (n, f_arr) AS\n(\n  SELECT 1 AS n, ARRAY[1]::INT[] AS f_arr\n  UNION ALL\n  SELECT n + 1, array_append(f_arr, n + 1)\n  FROM cte_fill_arr\n  WHERE n < (SELECT n * 2 FROM param)\n)\nSELECT \n  ROW_NUMBER() OVER () AS rn,\n  (\n    SELECT f_arr FROM cte_fill_arr\n    WHERE CARDINALITY(f_arr) = (SELECT n * 2 FROM param)\n  ),\n  \n  -- could use\n  --\n  -- fill_array_with_seq(ARRAY[]::INT[], (SELECT n * 2 FROM param))\n  --\n  \n  ARRAY\n  (\n    SELECT \n      GENERATE_SERIES(1, (SELECT n FROM param))\n    UNION ALL\n    SELECT \n      GENERATE_SERIES((SELECT n FROM param), 1, -1)\n  ) AS arr\n  FROM\n    GENERATE_SERIES(1, (SELECT m FROM param)) AS x;\n
Run Code Online (Sandbox Code Playgroud)\n

结果:

\n
rn         f_arr             arr\n 1  {1,2,3,4,5,6}   {1,2,3,3,2,1}\n 2  {1,2,3,4,5,6}   {1,2,3,3,2,1}\n 3  {1,2,3,4,5,6}   {1,2,3,3,2,1}\n 4  {1,2,3,4,5,6}   {1,2,3,3,2,1}\n 5  {1,2,3,4,5,6}   {1,2,3,3,2,1}\n
Run Code Online (Sandbox Code Playgroud)\n

最后:

\n
--\n--  Steps 4 & 5 - separate subquery not shown\n--\n\nWITH RECURSIVE cte_fill_arr (n, f_arr) AS\n(\n  SELECT 1 AS n, ARRAY[1]::INT[] AS f_arr\n  UNION ALL\n  SELECT n + 1, array_append(f_arr, n + 1)\n  FROM cte_fill_arr\n  WHERE n < (SELECT n * 2 FROM param)\n)\nSELECT the_series FROM\n(\n  SELECT \n    ROW_NUMBER() OVER () AS rn,\n    UNNEST\n    (\n      (\n        SELECT f_arr FROM cte_fill_arr\n        WHERE CARDINALITY(f_arr) = (SELECT n * 2 FROM param)\n      )\n    ) AS seq,  \n    UNNEST\n    (\n      ARRAY\n      (\n        SELECT \n          GENERATE_SERIES(1, (SELECT n FROM param))\n        UNION ALL\n        SELECT \n          GENERATE_SERIES((SELECT n FROM param), 1, -1)\n      )\n    ) AS arr\n  FROM\n    GENERATE_SERIES(1, (SELECT m FROM param)) AS x\n  ORDER BY rn, seq\n) AS fin_arr\nORDER BY rn, seq;\n
Run Code Online (Sandbox Code Playgroud)\n

结果:

\n
the_series\n         1\n         2\n         3\n         3\n         2\n         1\n         1\n         2\n...\n... snipped for brevity\n...\n
Run Code Online (Sandbox Code Playgroud)\n

方法 2 - 递归 CTE ( fiddle ):

\n

在这里,我通过在同一个 RCTE 中构建所需的序列及其编号方案,成功地“一石二鸟”,如下所示:

\n
--\n-- Step 1\n--\nWITH RECURSIVE cte_fill_array AS -- (cnt, val_arr, cnt_arr) AS\n(\n  SELECT 1 AS i, 1 AS cnt, ARRAY[1] AS val_arr, ARRAY[1] AS cnt_arr\n  UNION ALL\n  SELECT i + 1, cnt + 1, \n  ARRAY_APPEND\n  (\n    val_arr,\n    (\n      SELECT \n        CASE\n          WHEN cnt < (SELECT n FROM param) THEN (i + 1)\n          WHEN cnt = (SELECT n FROM param) THEN cnt\n          WHEN cnt > (SELECT n FROM param) THEN 6 - i\n        END\n    )\n  ),\n  ARRAY_APPEND(cnt_arr, cnt + 1)\n  FROM cte_fill_array\n  WHERE cnt < 2 * (SELECT n FROM param)\n)\nSELECT i, cnt, val_arr, cnt_arr FROM cte_fill_array;\n
Run Code Online (Sandbox Code Playgroud)\n

结果:

\n
i   cnt val_arr cnt_arr\n1   1   {1} {1}\n2   2   {1,2}   {1,2}\n3   3   {1,2,3} {1,2,3}\n4   4   {1,2,3,3}   {1,2,3,4}\n5   5   {1,2,3,3,2} {1,2,3,4,5}\n6   6   {1,2,3,3,2,1}   {1,2,3,4,5,6}\n
Run Code Online (Sandbox Code Playgroud)\n

我们只想要最后一条记录,因此我们通过使用CARDINALITY()函数来选择它 - 其中等于 ( n * 2) 的是最后一条记录(步骤未单独显示)。

\n

最后一步 -有关更多详细信息,请参阅fiddle

\n
--\n--  Steps 2 - end\n--\nWITH RECURSIVE cte_fill_arr (n, f_arr) AS\n(\n  SELECT 1 AS n, ARRAY[1]::INT[] AS f_arr\n  UNION ALL\n  SELECT n + 1, array_append(f_arr, n + 1)\n  FROM cte_fill_arr\n  WHERE n < (SELECT n * 2 FROM param)\n)\nSELECT arr AS the_series FROM\n(\n  SELECT \n    ROW_NUMBER() OVER () AS rn,\n    UNNEST\n    (\n      (\n        SELECT f_arr FROM cte_fill_arr\n        WHERE CARDINALITY(f_arr) = (SELECT n * 2 FROM param)\n      )\n    ) AS seq,  \n    UNNEST\n    (\n      ARRAY\n      (\n        SELECT \n          GENERATE_SERIES(1, (SELECT n FROM param))\n        UNION ALL\n        SELECT \n          GENERATE_SERIES((SELECT n FROM param), 1, -1)\n      )\n    ) AS arr\n  FROM\n    GENERATE_SERIES(1, (SELECT m FROM param)) AS x\n  ORDER BY rn, seq\n) AS fin_arr\nORDER BY rn, seq;\n
Run Code Online (Sandbox Code Playgroud)\n

结果(与其他相同):

\n
the_series\n         1\n         2\n         3\n         3\n...\n... snipped for brevity\n...\n
Run Code Online (Sandbox Code Playgroud)\n

存在一个更简单、(恕我直言)更优雅的解决方案 - 使用GENERATE_SUBSCRIPTS()函数(解释)如下:

\n
WITH RECURSIVE cte (i) AS\n(\n  SELECT 1 AS i, 1 AS cnt\n  UNION ALL\n  SELECT \n    CASE\n      WHEN cnt < (SELECT n FROM param) THEN (i + 1)\n      WHEN cnt = (SELECT n FROM param) THEN cnt\n      ELSE i - 1\n    END AS i,\n    cnt + 1\n  FROM cte\n  WHERE cnt < 2 * (SELECT n FROM param)\n)\nSELECT the_arr\nFROM\n(\n  SELECT \n    x, \n    UNNEST(ARRAY(SELECT i FROM cte))    AS the_arr, \n    GENERATE_SUBSCRIPTS(ARRAY(SELECT i FROM cte), 1) AS ss\n\n  FROM GENERATE_SERIES(1, (SELECT m FROM param)) AS t(x)\n) AS s\nORDER BY x, ss;\n
Run Code Online (Sandbox Code Playgroud)\n

结果(相同):

\n
the_series\n1\n2\n3\n3\n...\n... snipped for brevity\n...\n
Run Code Online (Sandbox Code Playgroud)\n

方法 3 - 纯 SQL ( fiddle ):

\n

最后一步:

\n

由于下面的所有代码都以一种或另一种形式在上面看到,因此我只包括最后一步。没有使用 PostgreSQL 特定功能,它也可以在 SQL Server 2019 (Linux fiddle ) 下运行 - 也可以追溯到 2016 年 - 所有版本。

\n
WITH RECURSIVE cte (i, cnt) AS\n(\n  SELECT 1 AS i, 1 AS cnt\n  UNION ALL\n  SELECT \n    CASE\n      WHEN cnt < (SELECT n FROM param) THEN (i + 1)\n      WHEN cnt = (SELECT n FROM param) THEN cnt\n      ELSE i - 1\n    END AS i,\n    cnt + 1\n  FROM cte\n  WHERE cnt < 2 * (SELECT n FROM param)\n)\nSELECT \n  n.n, c.i, c.cnt\nFROM \n  cte c\nCROSS JOIN numbers n\nORDER BY n.n, c.cnt;\n
Run Code Online (Sandbox Code Playgroud)\n

结果(相同):

\n
i\n1\n2\n3\n3\n
Run Code Online (Sandbox Code Playgroud)\n

第四种解决方案(和clubhouse leader!)(小提琴):

\n
SELECT UNNEST(arr)\nFROM\n(\n  SELECT arr, GENERATE_SERIES(1, (SELECT m FROM param)) AS gs\n  FROM\n  (\n    SELECT \n    ARRAY\n    (\n      SELECT x \n      FROM GENERATE_SERIES(1, (SELECT n FROM param)) x\n      UNION ALL\n      SELECT x\n      FROM GENERATE_SERIES((SELECT n FROM param), 1, -1) x\n    ) AS arr\n  ) AS t\n) AS s;\n
Run Code Online (Sandbox Code Playgroud)\n

与所有其他结果相同。

\n

第五种解决方案(荣誉奖)(小提琴):

\n
SELECT\n  UNNEST\n  (\n    ARRAY_CAT\n    (\n      ARRAY\n      (\n        SELECT GENERATE_SERIES(1, (SELECT n FROM param))::INT\n      ), \n      ARRAY\n      (\n        SELECT GENERATE_SERIES((SELECT n FROM param), 1, -1)::INT\n      )\n    )\n  ) FROM GENERATE_SERIES(1, (SELECT m FROM param));\n
Run Code Online (Sandbox Code Playgroud)\n

与所有其他结果相同。

\n

第 6 个解决方案(另一个棘手的问题! - 使用 INTARRAY 扩展fiddle):

\n
WITH cte AS\n(\n  SELECT\n    ARRAY(SELECT GENERATE_SERIES(1, (SELECT n FROM param))) AS arr\n)\nSELECT UNNEST \n(\n  (\n    SELECT ARRAY_CAT(c.arr, SORT(c.arr, \'DESC\'))\n    FROM cte c\n  )\n) FROM GENERATE_SERIES(1, (SELECT m FROM param));\n
Run Code Online (Sandbox Code Playgroud)\n

结果一样!

\n

基准测试:

\n

我对所有 PostgreSQL 解决方案进行了基准测试。

\n

我已尽最大努力在这些基准测试中做到公平 - 我在 SQL 中使用了参数表(3, 5)(also(34, 57)(340, 570)at )。对于那些需要表(即序列)的(home)查询,经过讨论,我已将其包含在需要它的查询中。number我对此并不完全确定,因为顾问经常被禁止创建单独的表,无论多么微不足道,但这似乎是共识!

\n

如果您对任何测试不满意,请告诉我,我很乐意重新运行它们!

\n

我用于db<>fiddle测试,并且适用通常的警告 - 我不知道在任何给定时刻该服务器上还运行着什么 - 我对每个解决方案平均运行了几次(大部分结果都在~ 彼此的 10% - 丢弃明显的异常值(更长,而不是更短的时间)。

\n

有人向我指出(无论如何我都知道)3 和 5 并不是很大的数字 - 我确实尝试为每个参数使用低 100,但在 db<>fiddle.uk 上运行一直失败,但我只能说所有的运行都非常一致,仅相差 ~ +- 10%。

\n

第二个读数的值为 34 和 57 - 请随意尝试。

\n

通过(home)测试,我在 8GB 机器(i5 - 第 10 代,NVMe 256GB)上使用了参数(340, 570)- 没有其他运行 - 方差低 ~ 1/2%!

\n
    \n
  • V\xc3\xa9race\'s (Another scorcher using INTARRAY!)第 6 个解决方案(小提琴)(0.110 毫秒)/ 0.630 毫秒/21.5 毫秒(主页)- new leader

    \n
  • \n
  • V\xc3\xa9race\'s (前俱乐部队长) 第四解 (小提琴) 0.120 ms/ 0.625 ms /22.5 ms (主场)

    \n
  • \n
  • V\xc3\xa9race\'s(荣誉奖)第五个解决方案(小提琴)(0.95ms/ 0.615(下降!) /26 ms(主页)

    \n
  • \n
  • V\xc3\xa9race GENERATE_SERIES SQL 方法(小提琴):0.195 ms/ 3.1 ms /140ms(主页)

    \n
  • \n
  • V\xc3\xa9race RECURSIVE CTE SQL 方法(小提琴):0.200 ms/ 2.9 ms /145m(主页)

    \n
  • \n
  • V\xc3\xa9race GENERATE_SUBSCRIPTS() SQL 方法 ( fiddle ): 0.110 ms/ 2.75 ms /130ms (home)

    \n
  • \n
  • V\xc3\xa9race“纯 SQL”方法(小提琴):0.134 ms/ 2.85ms /190ms(主页)

    \n
  • \n
  • OP的SQL方法(小提琴):12.50毫秒/ 18.5毫秒/190毫秒(主页)

    \n
  • \n
  • OP的PL/pgSQL函数方法( fiddle ): 0.60 ms/ 0.075ms /86ms (home)

    \n
  • \n
  • ypercube的SQL方法(小提琴):0.175毫秒,/ 4.3毫秒/240毫秒(主页)

    \n
  • \n
  • ypercube的替代方法(fiddle):0.090 ms/ 0.95 ms /36ms(home)

    \n
  • \n
  • Erwin Brandtstetter 的 SQL 方法(小提琴):2.15 ms / 3.65 ms /160ms(home)(没有 ORDER BY - home 时 160 下降到 ~ 100)

    \n
  • \n
  • Erwin Brandtstetter 的函数方法(小提琴):0.169 ms/ 2.3 ms /180 ms(主页)

    \n
  • \n
  • Abelito\ 的 SQL 方法(小提琴)0.145/如果参数更改会失败?

    \n
  • \n
  • Evan Carroll 的 SQL 方法 ( fiddle ) 0.125 ms/ 1.1ms /45ms (home)

    \n
  • \n
  • McNet 的 PL/pgSQL 方法 (小提琴) 0.075 ms/ 0.075 ms /125ms (home)

    \n
  • \n
\n

再次,我重申(冒着重复自己的风险(多次!:-))),如果您对您的基准不满意,请告诉我,我将在此处包含任何修改 - 我只是强调我真正感兴趣公平地说,并从这个过程中实际学习 - 独角兽点都很好,但我的首要任务是增加我(我们的)知识库!

\n

PostgreSQL 的源代码有点高于我的工资等级,但我相信 使用GENERATE_SERIESARRAYs 进行的操作会保留顺序 -WITH ORDINALITY这意味着(再次,我认为) - 即使不注意顺序也会出现正确的答案(尽管这不是保证)!@ErwinBrandstetter

\n
\n
    \n
  • 我添加了 ORDER BY 以保证所请求的订单。对于当前版本或 Postgres,它也可以在没有 ORDER BY 的情况下用于简单查询 - 但不一定在更复杂的查询中!这是一个实现细节(并且不会改变),但 SQL 标准并未强制要求。
  • \n
\n
\n

我的理解是ARRAYs 在 PostgreSQL 中速度很快,因为大部分后端代码都是通过它们实现的 - 但正如我所说,我并不是真正的C

\n

当前排名(截至 2021 年 10 月 27 日 13:50 UTC)为:

\n
    \n
  • V\xc3\xa9race 第一、第二和第三,
  • \n
  • 超立方体4号,
  • \n
  • 埃文·卡罗尔 第五名
  • \n
  • 其余领域...
  • \n
\n

我发现Erwin Brandstetter ( 1 ) 和a_horse_with_no_name (2)的 ARRAY 上的这些帖子非常有帮助!我发现有帮助的其他内容如下(1 , 2

\n


Erw*_*ter 10

Postgres

您可以使用单一的 generate_series()基本数学运算(请参阅数学函数)。

包装成一个简单的SQL函数:

CREATE OR REPLACE FUNCTION generate_up_down_series(n int, m int)
  RETURNS SETOF int AS
$func$
SELECT CASE WHEN n2 < n THEN n2 + 1 ELSE n*2 - n2 END
FROM  (
   SELECT n2m, n2m % (n*2) AS n2
   FROM   generate_series(0, n*2*m - 1) n2m
   ) sub
ORDER  BY n2m
$func$  LANGUAGE sql IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)

称呼:

SELECT * FROM generate_up_down_series(3, 4);
Run Code Online (Sandbox Code Playgroud)

生成所需的结果。nm可以是任何整数,其中n*2*m不会溢出int4

如何?

在子查询中:

  • 使用简单的升序生成所需的总行数 ( n*2*m )。我的名字n2m0N-1(不是1N)以简化以下运算。

  • 拿它% n*2%是模运算符)得到一系列n升序数,m次。我的名字n2

在外部查询中:

  • 将 1 添加到下半部分 ( n2 < n )。

  • 对于具有n*2 - n2的下半部分的上半部分 ( n2 >= n ) 镜像。

  • 我添加ORDER BY以保证请求的订单。对于当前版本或 Postgres,它也适用ORDER BY于简单查询——但不一定适用于更复杂的查询!这是一个实现细节(它不会改变)但不是 SQL 标准保证的。

不幸的generate_series()是,正如所评论的那样,是 Postgres 特定的而不是标准的 SQL。但是我们可以重用相同的逻辑:

标准 SQL

您可以使用递归 CTE 代替 生成序列号generate_series(),或者更有效地重复使用,创建一个包含序列整数的表一次。谁都可以读,谁也不能写!

CREATE TABLE int_seq (i integer);

WITH RECURSIVE cte(i) AS (
   SELECT 0
   UNION ALL
   SELECT i+1 FROM cte
   WHERE  i < 20000  -- or as many you might need!
   )
INSERT INTO int_seq
SELECT i FROM cte;
Run Code Online (Sandbox Code Playgroud)

那么,上面的SELECT就变得更简单了:

SELECT CASE WHEN n2 < n THEN n2 + 1 ELSE n*2 - n2 END AS x
FROM  (
   SELECT i, i % (n*2) AS n2
   FROM   int_seq
   WHERE  i < n*2*m  -- remember: 0 to N-1
   ) sub
ORDER  BY i;
Run Code Online (Sandbox Code Playgroud)


ype*_*eᵀᴹ 5

在 Postgres 中,使用这个generate_series()函数很容易:

WITH 
  parameters (n, m) AS
  ( VALUES (3, 5) )
SELECT 
    CASE WHEN g2.i = 1 THEN gn.i ELSE p.n + 1 - gn.i END AS xi
FROM
    parameters AS p, 
    generate_series(1, p.n) AS gn (i),
    generate_series(1, 2)   AS g2 (i),
    generate_series(1, p.m) AS gm (i)
ORDER BY
    gm.i, g2.i, gn.i ;
Run Code Online (Sandbox Code Playgroud)

在标准 SQL 中 - 并假设参数 n、m 的大小有一个合理的限制,即小于一百万 - 您可以使用一个Numbers表:

CREATE TABLE numbers 
( n int not null primary key ) ;
Run Code Online (Sandbox Code Playgroud)

用您的 DBMS 的首选方法填充它:

INSERT INTO numbers (n)
VALUES (1), (2), .., (1000000) ;  -- some mildly complex SQL here
                                  -- no need to type a million numbers
Run Code Online (Sandbox Code Playgroud)

然后使用它,而不是generate_series()

WITH 
  parameters (n, m) AS
  ( VALUES (3, 5) )
SELECT 
    CASE WHEN g2.i = 1 THEN gn.i ELSE p.n + 1 - gn.i END AS xi
FROM
    parameters AS p
  JOIN numbers AS gn (i) ON gn.i <= p.n
  JOIN numbers AS g2 (i) ON g2.i <= 2
  JOIN numbers AS gm (i) ON gm.i <= p.m 
ORDER BY
    gm.i, g2.i, gn.i ;
Run Code Online (Sandbox Code Playgroud)


Abe*_*sto 5

如果您需要纯 SQL。理论上它应该适用于大多数 DBMS(在 PostgreSQL 和 SQLite 上测试):

with recursive 
  s(i,n,z) as (
    select * from (values(1,1,1),(3*2,1,2)) as v  -- Here 3 is n
    union all
    select
      case z when 1 then i+1 when 2 then i-1 end, 
      n+1,
      z 
    from s 
    where n < 3), -- And here 3 is n
  m(m) as (select 1 union all select m+1 from m where m < 2) -- Here 2 is m

select n from s, m order by m, i;
Run Code Online (Sandbox Code Playgroud)

解释

  1. 生成系列 1..n

    假如说 n=3

    with recursive s(n) as (
      select 1
      union all
      select n+1 from s where n<3
    )
    select * from s;
    
    Run Code Online (Sandbox Code Playgroud)

    它非常简单,几乎可以在任何关于递归 CTE 的文档中找到。但是我们需要每个值的两个实例所以

  2. 生成系列 1,1,..,n,n

    with recursive s(n) as (
      select * from (values(1),(1)) as v
      union all
      select n+1 from s where n<3
    )
    select * from s;
    
    Run Code Online (Sandbox Code Playgroud)

    这里我们只是将初始值加倍,它有两行,但是我们需要以相反的顺序第二组,所以我们将稍微介绍一下顺序。

  3. 在我们介绍顺序之前,请注意这也是一回事。我们可以在起始条件中有两行,每行三列,我们n<3仍然是单列条件。而且,我们仍然只是增加 的价值n

    with recursive s(i,n,z) as (
      select * from (values(1,1,1),(1,1,1)) as v
      union all
      select
        i,
        n+1,
        z 
      from s where n<3
    )
    select * from s;
    
    Run Code Online (Sandbox Code Playgroud)
  4. 同样,我们可以将它们混合起来,在这里观察我们的起始条件变化:这里我们有一个(6,2)(1,1)

    with recursive s(i,n,z) as (
      select * from (values(1,1,1),(6,1,2)) as v
      union all
      select
        i,
        n+1,
        z 
      from s where n<3
    )
    select * from s;
    
    Run Code Online (Sandbox Code Playgroud)
  5. 生成系列 1..n,n..1

    这里的技巧是生成系列 (1..n) 两次,然后简单地更改第二组的顺序。

    with recursive s(i,n,z) as (
      select * from (values(1,1,1),(3*2,1,2)) as v
      union all
      select
        case z when 1 then i+1 when 2 then i-1 end, 
        n+1,
        z 
      from s where n<3
    )
    select * from s order by i;
    
    Run Code Online (Sandbox Code Playgroud)

    i是顺序,z是序列号(如果需要,也可以是序列的一半)。因此,对于序列 1,我们将顺序从 1 增加到 3,对于序列 2,我们将顺序从 6 减少到 4。最后

  6. 将系列相乘 m

    (请参阅答案中的第一个查询)