Dado dos números n
y m
, quiero generar una serie de la forma
1, 2, ..., (n-1), n, n, (n-1), ... 2, 1
y repítelo m
veces.
Por ejemplo, para n = 3
y m = 4
, quiero una secuencia de los siguientes 24 números:
1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1
---------------- ---------------- ---------------- ----------------
Sé cómo lograr este resultado en PostgreSQL por cualquiera de los dos métodos:
Usando la siguiente consulta, que usa la generate_series
función, y algunos trucos para garantizar que el orden sea el correcto:
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 ;
... o use una función para el mismo propósito, con bucles adjuntos y anidados:
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 ;
¿Cómo podría hacer el equivalente en SQL estándar o en Transact-SQL / SQL Server?
fuente
Postgres
Puede hacer que funcione con una matemática única
generate_series()
y básica (ver funciones matemáticas ).Envuelto en una simple función SQL:
Llamada:
Genera el resultado deseado. n y m puede ser cualquier número entero, donde n * 2 * m no se desborde
int4
.¿Cómo?
En la subconsulta:
Genere el número total deseado de filas ( n * 2 * m ), con un número ascendente simple. Lo nombro
n2m
. 0 a N-1 (no 1 a N ) para simplificar la siguiente operación de módulo .Tómelo % n * 2 (
%
es el operador del módulo) para obtener una serie de n números ascendentes, m veces. Lo nombron2
.En la consulta externa:
Agregue 1 a la mitad inferior ( n2 <n ).
Para la mitad superior ( n2> = n ) espejo de la mitad inferior con n * 2 - n2 .
Agregué
ORDER BY
para garantizar el pedido solicitado. Con las versiones actuales o Postgres también funciona sinORDER BY
la consulta simple, ¡pero no necesariamente en consultas más complejas! Ese es un detalle de implementación (y no va a cambiar) pero no está garantizado por el estándar SQL.Desafortunadamente,
generate_series()
es Postgres específico y no SQL estándar, como se ha comentado. Pero podemos reutilizar la misma lógica:SQL estándar
Puede generar los números de serie con un CTE recursivo en lugar de
generate_series()
, o, más eficientemente para uso repetido, crear una tabla con números enteros de serie una vez. ¡Cualquiera puede leer, nadie puede escribirle!Entonces, lo anterior se
SELECT
vuelve aún más simple:fuente
Si necesita SQL simple. Teóricamente debería funcionar en la mayoría de los DBMS (probado en PostgreSQL y SQLite):
Explicación
Generar series 1..n
Asumiendo que
n=3
Es bastante simple y se puede encontrar en casi cualquier documento sobre CTE recursivos. Sin embargo, necesitamos dos instancias de cada valor.
Generar series 1,1, .., n, n
Aquí simplemente duplicamos el valor inicial, que tiene dos filas, pero el segundo grupo que necesitamos en el orden inverso, por lo que introduciremos el orden en un momento.
Antes de presentar el orden, observe que esto también es una cosa. Podemos tener dos filas en la condición inicial con tres columnas cada una, nuestra
n<3
sigue siendo una sola columna condicional. Y, todavía estamos aumentando el valor den
.Del mismo modo, podemos mezclarlos un poco, ver cómo cambia nuestra condición de inicio aquí : aquí tenemos un
(6,2)
,(1,1)
Generar series 1..n, n..1
El truco aquí es generar la serie, (1..n) dos veces, y luego simplemente cambiar el orden en el segundo conjunto.
Aquí
i
está el orden y elz
número de la secuencia (o la mitad de la secuencia si lo desea). Entonces, para la secuencia 1 estamos aumentando el orden de 1 a 3 y para la secuencia 2 estamos disminuyendo el orden de 6 a 4. Y finalmenteMultiplica la serie por
m
(ver la primera consulta en la respuesta)
fuente
Si desea una solución portátil, debe darse cuenta de que esto es básicamente un problema matemático .
Dado @n como el número más alto de la secuencia y @x como la posición del número en esa secuencia (comenzando con cero), la siguiente función funcionaría en SQL Server:
Puede verificarlo con este CTE:
(Explicación rápida: la función utiliza MODULO () para crear una secuencia de números repetidos y ABS () para convertirla en una onda en zig-zag. Las otras operaciones transforman esa onda para que coincida con el resultado deseado.)
fuente
En PostgreSQL, esto es fácil,
fuente
Esto funciona en MS-SQL y creo que se puede modificar para cualquier versión de SQL.
fuente
Una forma de hacerlo en SQL Server utilizando un cte recursivo.
1) Genere el número requerido de miembros en la serie (para n = 3 ym = 4 sería 24, que es 2 * n * m)
2) Después de usar la lógica en una
case
expresión, puede generar la serie requerida.Sample Demo
Como lo sugiere @AndriyM .. la
case
expresión se puede simplificar aDemo
fuente
Usando solo Math
+ - * /
y Modulo básicos :Esto no requiere un SGBD específico.
Con
numbers
ser una tabla de números:Esto genera una tabla de números (1-1000) sin usar un CTE recursivo. Ver muestra . 2 * n * m debe ser menor que el número de filas en números.
Salida con n = 3 ym = 4:
Esta versión requiere una tabla numérica más pequeña (v> = ny v> = m):
Ver muestra .
fuente
Una función básica usando iteradores.
T-SQL
Postgres
fuente
fuente