Étant donné deux nombres n
et m
, je veux générer une série du formulaire
1, 2, ..., (n-1), n, n, (n-1), ... 2, 1
et répétez-le m
fois.
Par exemple, pour n = 3
et m = 4
, je veux une séquence des 24 chiffres suivants:
1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1
---------------- ---------------- ---------------- ----------------
Je sais comment obtenir ce résultat dans PostgreSQL par l'une des deux méthodes suivantes:
Utilisation de la requête suivante, qui utilise la generate_series
fonction, et quelques astuces pour garantir que la commande est la bonne:
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 utilisez une fonction dans le même but, avec des boucles adjointes et imbriquées:
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 ;
Comment pourrais-je éventuellement faire l'équivalent dans SQL standard ou dans Transact-SQL / SQL Server?
la source
Postgres
Vous pouvez le faire fonctionner avec une seule
generate_series()
mathématique et de base (voir fonctions mathématiques ).Enveloppé dans une simple fonction SQL:
Appel:
Génère le résultat souhaité. n et m peuvent être n'importe quel entier où n * 2 * m ne déborde pas
int4
.Comment?
Dans la sous-requête:
Générez le nombre total de lignes souhaité ( n * 2 * m ), avec un simple nombre croissant. Je le nomme
n2m
. 0 à N-1 (pas 1 à N ) pour simplifier l' opération modulo suivante .Prenez-le % n * 2 (
%
est l'opérateur modulo) pour obtenir une série de n nombres ascendants, m fois. Je le nommen2
.Dans la requête externe:
Ajoutez 1 à la moitié inférieure ( n2 <n ).
Pour la moitié supérieure ( n2> = n ) miroir de la moitié inférieure avec n * 2 - n2 .
J'ai ajouté
ORDER BY
pour garantir la commande demandée. Avec les versions actuelles ou Postgres, il fonctionne également sansORDER BY
pour la simple requête - mais pas nécessairement dans les requêtes plus complexes! C'est un détail d'implémentation (et ça ne va pas changer) mais pas garanti par la norme SQL.Malheureusement,
generate_series()
Postgres est spécifique à SQL et non standard, comme cela a été commenté. Mais on peut réutiliser la même logique:SQL standard
Vous pouvez générer les numéros de série avec un CTE récursif au lieu de
generate_series()
, ou, plus efficacement pour une utilisation répétée, créer une table avec des nombres entiers en série une fois. Tout le monde peut lire, personne ne peut y écrire!Ensuite, ce qui précède
SELECT
devient encore plus simple:la source
Si vous avez besoin de SQL simple. Théoriquement, il devrait fonctionner sur la plupart des SGBD (testés sur PostgreSQL et SQLite):
Explication
Générer la série 1..n
En admettant que
n=3
Il est assez simple et peut être trouvé dans presque tous les documents sur les CTE récursifs. Cependant, nous avons besoin de deux instances de chaque valeur afin
Générer les séries 1,1, .., n, n
Ici, nous doublons simplement la valeur initiale, qui a deux lignes, mais le deuxième groupe dont nous avons besoin dans l'ordre inverse, nous allons donc introduire l'ordre dans un peu.
Avant d'introduire la commande, notez que c'est aussi une chose. Nous pouvons avoir deux lignes dans la condition de départ avec trois colonnes chacune, notre
n<3
est toujours une seule colonne conditionnelle. Et, nous augmentons toujours la valeur den
.De même, nous pouvons les mélanger un peu, regardez notre condition de départ changer ici : nous avons ici un
(6,2)
,(1,1)
Générer les séries 1..n, n..1
L'astuce consiste à générer deux fois la série (1..n), puis à modifier simplement l'ordre sur le deuxième ensemble.
Voici l'
i
ordre et lez
numéro de la séquence (ou la moitié de la séquence si vous le souhaitez). Donc, pour la séquence 1, nous augmentons l'ordre de 1 à 3 et pour la séquence 2, nous diminuons l'ordre de 6 à 4. Et enfinMultipliez la série pour
m
(voir la première requête dans la réponse)
la source
Si vous voulez une solution portable, vous devez vous rendre compte qu'il s'agit essentiellement d'un problème mathématique .
Étant donné que @n est le numéro le plus élevé de la séquence et @x la position du numéro dans cette séquence (en commençant par zéro), la fonction suivante fonctionnerait dans SQL Server:
Vous pouvez le vérifier avec ce CTE:
(Explication rapide: la fonction utilise MODULO () pour créer une séquence de nombres répétitifs et ABS () pour la transformer en une vague en zig-zag. Les autres opérations transforment cette onde pour correspondre au résultat souhaité.)
la source
Dans PostgreSQL, c'est facile,
la source
Cela fonctionne en MS-SQL et je pense qu'il peut être modifié pour n'importe quelle saveur SQL.
la source
Une façon de le faire dans SQL Server en utilisant un cte récursif.
1) Générer le nombre requis de membres dans la série (pour n = 3 et m = 4 ce serait 24 qui est 2 * n * m)
2) Après cela, en utilisant la logique dans une
case
expression, vous pouvez générer la série requise.Sample Demo
Comme suggéré par @AndriyM .. l'
case
expression peut être simplifiée enDemo
la source
En utilisant uniquement les mathématiques de base
+ - * /
et Modulo:Cela ne nécessite pas de SGBD spécifique.
En
numbers
étant une table numérique:Cela génère une table numérique (1-1000) sans utiliser de CTE récursif. Voir l' exemple . 2 * n * m doit être inférieur au nombre de lignes en chiffres.
Sortie avec n = 3 et m = 4:
Cette version nécessite une table numérique plus petite (v> = n et v> = m):
Voir l' exemple .
la source
Une fonction de base utilisant des itérateurs.
T-SQL
Postgres
la source
la source