# 如何在标准SQL或T-SQL中生成1、2、3、3、2、1、1、2、3、3、2、1…系列？

11

``1, 2, ..., (n-1), n, n, (n-1), ... 2, 1``

``````1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1
----------------  ----------------  ----------------  ----------------``````

``````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 ;``````

...或将功能用于相同目的，以及伴随和嵌套循环：

``````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 ;``````

4

``````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 ;``````

``````CREATE TABLE numbers
( n int not null primary key ) ;``````

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

``````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 ;``````

joanolo

10

## Postgres

``````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;``````

``SELECT * FROM generate_up_down_series(3, 4);``

### 怎么样？

• 生成所需的总行数（n * 2 * m），并使用简单的升序数。我命名`n2m`0N-1（不是1N）以简化以下运算。

• 取它％n * 2`%`是模运算符）以得到mn个升序数列。我命名。`n2`

• 将1加到下半部分（n2 <n）。

• 对于上半部分（n2> = n），下半部分的镜像为n * 2-n2

• 我添加`ORDER BY`以保证所请求的订单。对于当前版本或Postgres，它也可以`ORDER BY`用于简单查询，但不一定适用于更复杂的查询！这是一个实现细节（并且不会改变），但是SQL标准并不能保证。

## 标准SQL

``````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;``````

``````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;``````

5

``````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;``````

# 说明

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;``````

它非常简单，几乎可以在有关递归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;``````

在这里，我们只是将具有两行的初始值加倍，但是第二组需要以相反的顺序进行，因此我们将稍作介绍。

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;``````
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;``````
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;``````

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

6. 将系列乘以 `m`

（请参阅答案中的第一个查询）

3

``````CREATE FUNCTION UpDownSequence
(
@n int, -- Highest number of the sequence
@x int  -- Position of the number we need
)
RETURNS int
AS
BEGIN
RETURN  @n - 0.5 * (ABS((2*((@x % (@n+@n))-@n)) +1) -1)
END
GO``````

``````DECLARE @n int=3;--change the value as needed
DECLARE @m int=4;--change the value as needed

WITH numbers(num) AS (SELECT 0
UNION ALL
SELECT num+1 FROM numbers WHERE num+1<2*@n*@m)
SELECT num AS Position,
dbo.UpDownSequence(@n,num) AS number
FROM numbers
OPTION(MAXRECURSION 0)``````

（快速说明：该函数使用MODULO（）创建一个重复数字序列，并使用ABS（）将其转换成锯齿形波形。其他操作将该波形转换为与所需结果相匹配。）

2

``````CREATE OR REPLACE FUNCTION generate_up_down_series(n int, m int)
RETURNS setof int AS \$\$
SELECT x FROM (
SELECT 1, ordinality AS o, x FROM generate_series(1,n) WITH ORDINALITY AS t(x)
UNION ALL
SELECT 2, ordinality AS o, x FROM generate_series(n,1,-1) WITH ORDINALITY AS t(x)
) AS t(o1,o2,x)
CROSS JOIN (
SELECT * FROM generate_series(1,m)
) AS g(y)
ORDER BY y,o1,o2
\$\$ LANGUAGE SQL;``````

2

``````declare @max int, @repeat int, @rid int

select @max = 3, @repeat = 4

-- create a temporary table
create table #temp (row int)

--create seed rows
while (select count(*) from #temp) < @max * @repeat * 2
begin
insert into #temp
select 0
from (values ('a'),('a'),('a'),('a'),('a')) as a(col1)
cross join (values ('a'),('a'),('a'),('a'),('a')) as b(col2)
end

-- set row number can also use identity
set @rid = -1

update #temp
set     @rid = row = @rid + 1

-- if the (row/max) is odd, reverse the order
select  case when (row/@max) % 2 = 1 then @max - (row%@max) else (row%@max) + 1 end
from    #temp
where   row < @max * @repeat * 2
order by row``````

2

1）生成序列中所需的成员数（对于n = 3和m = 4，它将是24，即2 * n * m）

2）之后，在`case`表达式中使用逻辑，您可以生成所需的序列。

`Sample Demo`

``````declare @n int=3;--change the value as needed
declare @m int=4;--change the value as needed

with numbers(num) as (select 1
union all
select num+1 from numbers where num<2*@n*@m)
select case when (num/@n)%2=0 and num%@n<>0 then num%@n
when (num/@n)%2=0 and num%@n=0 then 1
when (num/@n)%2=1 and num%@n<>0 then @n+1-(num%@n)
when (num/@n)%2=1 and num%@n=0 then @n
end as num
from numbers
OPTION(MAXRECURSION 0)``````

``````with numbers(num) as (select 0
union all
select num+1 from numbers where num<2*@n*@m-1)
select case when (num/@n)%2=0 then num%@n + 1
when (num/@n)%2=1 then @n - num%@n
end as num
from numbers
OPTION(MAXRECURSION 0)``````

`Demo`

2

``````SELECT x
, s = x % (2*@n) +
(1-2*(x % @n)) * ( ((x-1) / @n) % 2)
FROM (SELECT TOP(2*@n*@m) x FROM numbers) v(x)
ORDER BY x;``````

``````...;
WITH numbers(x) AS(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM (VALUES(0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) AS n0(x)
CROSS JOIN (VALUES(0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) AS n1(x)
CROSS JOIN (VALUES(0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) AS n2(x)
)
...``````

n = 3和m = 4的输出：

``````x   s
1   1
2   2
3   3
4   3
5   2
6   1
7   1
8   2
... ...``````

``````WITH numbers(v) AS(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM (VALUES(1), (2), (3), (4), (5), (6), ...) AS n(x)
)
SELECT ord = @n*(v+2*m) + n
, n*(1-v) + ABS(-@n-1+n)*v
FROM (SELECT TOP(@n) v FROM numbers ORDER BY v ASC) n(n)
CROSS JOIN (VALUES(0), (1)) AS s(v)
CROSS JOIN (SELECT TOP(@m) v-1 FROM numbers ORDER BY v ASC) m(m)
ORDER BY ord;``````

2

T-SQL

``````create function generate_up_down_series(@max int, @rep int)
returns @serie table
(
num int
)
as
begin

DECLARE @X INT, @Y INT;
SET @Y = 0;

WHILE @Y < @REP
BEGIN

SET @X = 1;
WHILE (@X <= @MAX)
BEGIN
INSERT @SERIE
SELECT @X;
SET @X = @X + 1;
END

SET @X = @MAX;
WHILE (@X > 0)
BEGIN
INSERT @SERIE
SELECT @X;
SET @X = @X -1;
END

SET @Y = @Y + 1;
END

RETURN;
end
GO``````

Postgres

``````create or replace function generate_up_down_series(maxNum int, rep int)
returns table (serie int) as
\$body\$
declare
x int;
y int;
z int;
BEGIN

x := 0;
while x < rep loop

y := 1;
while y <= maxNum loop
serie := y;
return next;
y := y + 1;
end loop;

z := maxNum;
while z > 0 loop
serie := z;
return next;
z := z - 1;
end loop;

x := x + 1;
end loop;

END;
\$body\$ LANGUAGE plpgsql;``````

1
``````declare @n int = 5;
declare @m int = 3;
declare @t table (i int, pk int identity);
WITH  cte1 (i)
AS ( SELECT 1
UNION ALL
SELECT i+1 FROM cte1
WHERE  i < 100  -- or as many you might need!
)
insert into @t(i) select i from cte1 where i <= @m  order by i
insert into @t(i) select i from @t order by i desc
select t.i --, t.pk, r.pk
from @t as t
cross join (select pk from @t where pk <= @n) as r
order by r.pk, t.pk``````