Dados dois números n
e m
, quero gerar uma série do formulário
1, 2, ..., (n-1), n, n, (n-1), ... 2, 1
e repita isso m
vezes.
Por exemplo, para n = 3
e m = 4
, quero uma sequência dos 24 números a seguir:
1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1
---------------- ---------------- ---------------- ----------------
Eu sei como alcançar esse resultado no PostgreSQL por um dos dois métodos:
Usando a consulta a seguir, que usa a generate_series
função, e alguns truques para garantir que o pedido seja o correto:
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 ;
... ou use uma função para a mesma finalidade, com loops adjuntos e aninhados:
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 ;
Como eu poderia fazer o equivalente no SQL padrão ou no Transact-SQL / SQL Server?
fonte
Postgres
Você pode fazê-lo funcionar com uma matemática única
generate_series()
e básica (consulte funções matemáticas ).Embrulhado em uma função SQL simples:
Ligar:
Gera o resultado desejado. n e m podem ser qualquer número inteiro em que n * 2 * m não transborda
int4
.Quão?
Na subconsulta:
Gere o número total desejado de linhas ( n * 2 * m ), com um número crescente simples. Eu cito
n2m
. 0 a N-1 (não 1 a N ) para simplificar a seguinte operação do módulo .Aceite % n * 2 (
%
é o operador do módulo) para obter uma série de n números ascendentes, m vezes. Eu citon2
.Na consulta externa:
Adicione 1 à metade inferior ( n2 <n ).
Para a metade superior ( n2> = n ), espelho da metade inferior com n * 2 - n2 .
Eu adicionei
ORDER BY
para garantir o pedido solicitado. Nas versões atuais ou no Postgres, ele também funciona semORDER BY
a consulta simples - mas não necessariamente em consultas mais complexas! Esse é um detalhe da implementação (e não vai mudar), mas não é garantido pelo padrão SQL.Infelizmente, o
generate_series()
Postgres é específico e não o SQL padrão, como foi comentado. Mas podemos reutilizar a mesma lógica:SQL padrão
Você pode gerar os números de série com um CTE recursivo em vez de
generate_series()
, ou, de forma mais eficiente para uso repetido, criar uma tabela com números inteiros de série uma vez. Qualquer um pode ler, ninguém pode escrever para ele!Então, o acima
SELECT
se torna ainda mais simples:fonte
Se você precisar de SQL simples. Teoricamente, ele deve funcionar com a maioria dos DBMSs (testados no PostgreSQL e SQLite):
Explicação
Gere a série 1..n
Assumindo que
n=3
É bastante simples e pode ser encontrado em quase todos os documentos sobre CTEs recursivas. No entanto, precisamos de duas instâncias de cada valor para que
Gerar séries 1,1, .., n, n
Aqui apenas dobramos o valor inicial, que tem duas linhas, mas o segundo agrupamento é necessário na ordem inversa, portanto, apresentaremos a ordem daqui a pouco.
Antes de introduzirmos a ordem, observe que isso também é uma coisa. Podemos ter duas linhas na condição inicial com três colunas cada, a nossa
n<3
ainda é uma única coluna condicional. E ainda estamos apenas aumentando o valor den
.Da mesma forma, podemos misturá-los um pouco, observar nossa condição inicial mudar aqui : aqui temos um
(6,2)
,(1,1)
Gere a série 1..n, n..1
O truque aqui é gerar a série (1..n) duas vezes e simplesmente alterar a ordem no segundo conjunto.
Aqui
i
está a ordem e oz
número da sequência (ou metade da sequência, se você desejar). Portanto, para a sequência 1, estamos aumentando a ordem de 1 para 3 e, para a sequência 2, estamos diminuindo a ordem de 6 para 4. E finalmenteMultiplique a série para
m
(veja a primeira consulta na resposta)
fonte
Se você deseja uma solução portátil, precisa entender que isso é basicamente um problema matemático .
Dado @n como o número mais alto da sequência e @x como a posição do número nessa sequência (começando com zero), a seguinte função funcionaria no SQL Server:
Você pode verificá-lo com este CTE:
(Explicação rápida: a função usa MODULO () para criar uma sequência de números repetidos e ABS () para transformá-lo em uma onda em zig-zag. As outras operações transformam essa onda para corresponder ao resultado desejado.)
fonte
No PostgreSQL, isso é fácil,
fonte
Isso funciona no MS-SQL e acho que pode ser modificado para qualquer sabor de SQL.
fonte
Uma maneira de fazer isso no SQL Server usando um cte recursivo.
1) Gere o número necessário de membros da série (para n = 3 em = 4, seria 24 o que é 2 * n * m)
2) Depois disso, usando a lógica em uma
case
expressão, você pode gerar as séries necessárias.Sample Demo
Conforme sugerido por @AndriyM .. a
case
expressão pode ser simplificada paraDemo
fonte
Usando apenas matemática
+ - * /
e módulo básicos :Isso não requer um SGBD específico.
Com
numbers
sendo uma tabela de números:Isso gera uma tabela numérica (1-1000) sem usar um CTE recursivo. Veja exemplo . 2 * n * m deve ser menor que o número de linhas em números.
Saída com n = 3 em = 4:
Esta versão requer uma tabela numérica menor (v> = ne v> = m):
Veja exemplo .
fonte
Uma função básica usando iteradores.
T-SQL
Postgres
fonte
fonte