Je crois que vous pouvez le faire en utilisant un recursive Common Table Expression comme suit, surtout si vous n'êtes pas attendre de très longues chaînes de disques:
WITH Ancestors AS
(
SELECT
InitRow.[ID] AS [Ancestor],
InitRow.[ID],
InitRow.[first],
InitRow.[end],
0 AS [level],
'00000' + InitRow.[ID] AS [hacky_level_plus_ID]
FROM
YOUR_TABLE AS InitRow
WHERE
NOT EXISTS
(
SELECT * FROM YOUR_TABLE AS PrevRow
WHERE PrevRow.[end] = InitRow.[first]
)
UNION ALL
SELECT
ParentRow.Ancestor,
ChildRow.[ID],
ChildRow.[first],
ChildRow.[end],
ParentRow.level + 1 AS [level],
-- Avoids having to build the recursive structure more than once.
-- We know we will not be over 5 digits since CTEs have a recursion
-- limit of 32767.
RIGHT('00000' + CAST(ParentRow.level + 1 AS varchar(4)), 5)
+ ChildRow.[ID] AS [hacky_level_plus_ID]
FROM
Ancestors AS ParentRow
INNER JOIN YOUR_TABLE AS ChildRow
ON ChildRow.[first] = ParentRow.[end]
)
SELECT
Ancestors.Ancestor + '-' + SUBSTRING(MAX([hacky_level_plus_ID]),6,10) AS [IDs],
-- Without the [hacky_level_plus_ID] column, you need to do it this way:
-- Ancestors.Ancestor + '-' +
-- (SELECT TOP 1 Children.ID FROM Ancestors AS Children
-- WHERE Children.[Ancestor] = Ancestors.[Ancestor]
-- ORDER BY Children.[level] DESC) AS [IDs],
MIN(Ancestors.[first]) AS [first],
MAX(Ancestors.[end]) AS [end]
FROM
Ancestors
GROUP BY
Ancestors.Ancestor
-- If needed, add OPTION (MAXRECURSION 32767)
Une explication rapide de ce que chaque partie fait:
Les La clause WITH Ancestors AS (...)
crée une expression de table commune (essentiellement une sous-requête) portant le nom Ancestors
. Le premier SELECT
dans cette expression établit une ligne de base: toutes les lignes qui n'ont pas d'entrée correspondante avant elle.
Ensuite, le second est SELECT
où la récursion entre en jeu. Comme il fait référence Ancestors
dans le cadre de la requête, il utilise les lignes qu'il a déjà ajouté à la table et effectue ensuite une jointure avec de nouvelles de YOUR_TABLE
. Cela trouvera récursivement de plus en plus de lignes à ajouter à la fin de chaque chaîne.
La dernière clause est la SELECT
qui utilise cette table récursive que nous avons créée. Il fait un simple GROUP BY
depuis que nous avons enregistré l'ID d'origine dans la colonne Ancestor
, donc le début et la fin sont un simple MIN
et MAX
.
La partie difficile est de déterminer l'ID de la dernière rangée de la chaîne. Il y a deux façons de le faire, toutes deux illustrées dans la requête. Vous pouvez soit revenir en arrière avec la table récursive, dans ce cas, il construira à nouveau la table récursive, ou vous pouvez essayer de garder une trace du dernier élément que vous allez. (Si la construction de la liste récursive d'enregistrements chaînés est coûteuse, vous voulez certainement minimiser le nombre de fois que vous avez besoin de le faire.)
La façon dont il suit son évolution est de garder une trace de sa position dans la chaîne (la colonne level
- remarquez comment nous ajoutons 1 chaque fois que nous recurons), le zéro-pad, puis coller l'ID à la fin. Ensuite, obtenir l'élément avec le maximum level
est simplement un MAX
suivi de l'effacement des données level
.
Si le CTE doit trop se recurer, cela générera une erreur, mais je crois que vous pouvez le modifier en utilisant l'option MAXRECURSION
. La valeur par défaut est 100. Si vous devez le définir plus haut, vous pouvez envisager de ne pas utiliser un CTE récursif pour le faire.
Ceci ne gère pas non plus très bien les données malformées. Si vous avez deux enregistrements avec le même first
ou un enregistrement où first
== end
, cela ne fonctionnera pas correctement et vous devrez peut-être modifier les conditions de jointure dans le CTE ou opter pour une autre approche.
Ce n'est pas la seule façon de le faire. Je crois qu'il serait plus facile de suivre si vous avez construit une procédure personnalisée et fait toutes les étapes manuellement. Mais cela a l'avantage de fonctionner dans une seule déclaration.
Je vois. Mais il i ont un suivi de la table: ID première extrémité un 1 3 3 8 b c 8 10 d 15 19 e 10 12 f 19 23 je pense que ce faux. Parce que je veux sélectionner ID première extrémité un-e 3 12 d-f 15 23 –
@Vuong: Je pense, cependant, vous pouvez étendre cette idée pour répondre à vos besoins. Si vous ajoutez une colonne calculée à votre sélection pour générer vos en-têtes de regroupement (pensez à utiliser une instruction 'case' pour calculer la colonne), puis groupez sur le calcul, vous pouvez toujours appliquer' MIN' et 'MAX' comme suggéré ici. – kbrimington
@Voung Mao: C'est un ensemble de données différent de ce que vous avez posté dans votre question - si c'est ce que vous traitez, vous devriez poster la question avec ce détail parce que quelqu'un doit lire votre commentaire pour connaître d'autres critères. On ne sait pas non plus comment vous savez qu'il y a deux groupes dans cet ensemble de données, ou où ils commencent et se terminent. –