J'ai environ un milliard de lignes de données dans une table avec un nom et un entier compris entre 1 et 288. Pour un nom donné , chaque int est unique, et non tout entier possible dans la plage est présente - donc il y a des lacunes.
Cette requête génère un exemple de cas:
--what I have:
SELECT *
FROM ( VALUES ('foo', 2),
('foo', 3),
('foo', 4),
('foo', 10),
('foo', 11),
('foo', 13),
('bar', 1),
('bar', 2),
('bar', 3)
) AS baz ("name", "int")
Je voudrais générer une table de recherche avec une ligne pour chaque nom et séquence d'entiers contigus. Chacune de ces lignes contiendrait:
nom - la valeur du début de la colonne de nom - le premier entier de la fin de séquence contiguë - la valeur finale dans la plage de séquence contiguë -
fin - début + 1
Cette requête génère un exemple de sortie pour l'exemple ci-dessus:
--what I need:
SELECT *
FROM ( VALUES ('foo', 2, 4, 3),
('foo', 10, 11, 2),
('foo', 13, 13, 1),
('bar', 1, 3, 3)
) AS contiguous_ranges ("name", "start", "end", span)
Parce que j'ai tellement de lignes, plus efficace c'est mieux. Cela dit, je ne dois exécuter cette requête qu'une seule fois, ce n'est donc pas une exigence absolue.
Merci d'avance!
Éditer:
Je dois ajouter que les solutions PL / pgSQL sont les bienvenues (veuillez expliquer toutes les astuces - je suis encore nouveau sur PL / pgSQL).
la source
Réponses:
Que diriez-vous d'utiliser
with recursive
vue de test:
requete:
résultat:
Je serais intéressé de savoir comment cela fonctionne sur votre table de milliards de lignes.
la source
Vous pouvez le faire avec des fonctions de fenêtrage. L'idée de base est d'utiliser
lead
et delag
fonctions de fenêtrage pour tirer des lignes avant et en arrière de la ligne actuelle. Ensuite, nous pouvons calculer si nous avons le début ou la fin de la séquence:(J'ai utilisé une vue pour que la logique soit plus facile à suivre ci-dessous.) Alors maintenant, nous savons si la ligne est un début ou une fin. Nous devons réduire cela en ligne:
Ça me semble correct :)
la source
Une autre solution de fonction de fenêtre. Aucune idée de l'efficacité, j'ai ajouté le plan d'exécution à la fin (bien qu'avec si peu de lignes, il n'a probablement pas beaucoup de valeur). Si vous voulez jouer: test SQL-Fiddle
Tableau et données:
Requete:
Plan de requête
la source
Sur SQL Server, j'ajouterais une autre colonne nommée previousInt:
J'utiliserais une contrainte CHECK pour m'assurer que previousInt <int, et une contrainte FK (nom, previousInt) font référence à (nom, int), et quelques contraintes supplémentaires pour garantir l'intégrité des données étanche. Cela fait, la sélection des écarts est triviale:
Pour l'accélérer, je pourrais créer un index filtré qui n'inclurait que les lacunes. Cela signifie que toutes vos lacunes sont précalculées, les sélections sont donc très rapides et les contraintes garantissent l'intégrité de vos données précalculées. J'utilise beaucoup de telles solutions, elles sont partout dans mon système.
la source
Vous pouvez rechercher la méthode Tabibitosan:
Fondamentalement:
Je pense que cette performance est meilleure:
la source
un plan approximatif:
Répétez de 2. jusqu'à ce qu'aucune mise à jour ne se produise. À partir de là, cela se complique, Gordien, avec un regroupement sur max de min et min de max. Je suppose que j'irais pour un langage de programmation.
PS: Un joli exemple de table avec quelques valeurs d'échantillon serait bien, ce qui pourrait être utilisé par tout le monde, donc tout le monde ne crée pas ses données de test à partir de zéro.
la source
Cette solution est inspirée de la réponse de nate c en utilisant les fonctions de fenêtrage et la clause OVER. Chose intéressante, cette réponse revient aux sous-requêtes avec des références externes. Il est possible de terminer la consolidation de ligne en utilisant un autre niveau de fonctions de fenêtrage. Il peut ne pas sembler trop joli, mais je suppose qu'il est plus efficace car il utilise la logique intégrée des puissantes fonctions de fenêtrage.
J'ai réalisé à partir de la solution de nate que l'ensemble initial de lignes avait déjà produit des drapeaux nécessaires pour 1) sélectionner les valeurs de plage de début et de fin ET 2) pour éliminer les lignes supplémentaires entre les deux. La requête a imbriqué des sous-requêtes à deux profondeurs uniquement en raison des limitations des fonctions de fenêtrage, qui limitent la façon dont les alias de colonne peuvent être utilisés. Logiquement, j'aurais pu produire les résultats avec une seule sous-requête imbriquée.
Quelques autres notes : Le code suivant est pour SQLite3. Le dialecte SQLite est dérivé de postgresql, il est donc très similaire et peut même fonctionner sans modification. J'ai ajouté une restriction de cadrage aux clauses OVER, car les fonctions
lag()
etlead()
n'ont besoin que d'une fenêtre à une seule ligne, respectivement avant et après (il n'était donc pas nécessaire de conserver l'ensemble par défaut de toutes les lignes précédentes). J'ai aussi opté pour les nomsfirst
etlast
puisque le motend
est réservé.Les résultats sont comme les autres réponses, comme on s'y attend:
la source