2

il y a quelques minutes j'ai demandé here comment obtenir des enregistrements parent avec un CTE récursif. Cela fonctionne maintenant, mais je reçois le mauvais ordre (en arrière, ordonné par le PK idData) quand je crée une fonction Table valorisée qui retourne tous les parents. Je ne peux pas commander directement parce que j'ai besoin de l'ordre logique fourni par le CTE.Mauvais ordre dans la fonction Table value (garder "l'ordre" d'un CTE récursif)

Cela donne l'ordre (de parent à côté de ce parent, etc.):

declare @fiData int; 
set @fiData=16177344; 
WITH PreviousClaims(idData,fiData) 
AS(
    SELECT parent.idData,parent.fiData 
    FROM tabData parent 
    WHERE parent.idData = @fiData 

    UNION ALL 

    SELECT child.idData,child.fiData 
    FROM tabData child 
    INNER JOIN PreviousClaims parent ON parent.fiData = child.idData 
) 
select iddata from PreviousClaims 

Mais la retourne fonction suivante tous les enregistrements en ordre inversé (ordonnée par PK):

CREATE FUNCTION [dbo].[_previousClaimsByFiData] (
    @fiData INT 
) 

RETURNS @retPreviousClaims TABLE 
(
    idData int PRIMARY KEY NOT NULL 
) 
AS 
BEGIN 
    DECLARE @idData int; 

    WITH PreviousClaims(idData,fiData) 
    AS(
     SELECT parent.idData,parent.fiData 
     FROM tabData parent 
     WHERE parent.idData = @fiData 

     UNION ALL 

     SELECT child.idData,child.fiData 
     FROM tabData child 
     INNER JOIN PreviousClaims parent ON parent.fiData = child.idData 
    ) 

    INSERT INTO @retPreviousClaims 
     SELECT idData FROM PreviousClaims; 
    RETURN; 
END; 

select * from dbo._previousClaimsByFiData(16177344); 

MISE à JOUR: Depuis les croyances de tout le monde que le CTE ne commande pas (Toute « commande "sera totalement arbitraire et coïncident), je me demande pourquoi le contraire semble être vrai. J'ai interrogé une demande d'enfant avec de nombreux parents et l'ordre dans le CTE est exactement l'ordre logique quand je vais de l'enfant à parent et ainsi de suite. Cela signifierait que le CTE itère d'un enregistrement à l'autre comme un curseur et que le select suivant le retourne exactement dans cet ordre. Mais quand j'appelle le TVF j'ai reçu l'ordre de la clé primaire idData à la place.

La solution était simple. J'ai seulement besoin d'enlever la clé parentale de la table de retour du TVF. Alors ... changer

RETURNS @retPreviousClaims TABLE 
(
    idData int PRIMARY KEY NOT NULL 
) 

à ...

RETURNS @retPreviousClaims TABLE 
(
    idData int 
) 

.. et il conserve le droit "ordre" (même ordre dans lequel ils ont été insérées dans le jeu temporaire de résultat du CTE).

MAJ2: Parce que Damien a mentionné que le « CTE ordre » pourrait changer dans certaines circonstances, je vais ajouter une nouvelle colonne relationLevel au CTE qui décrit le niveau de la relation des enregistrements parents (ce qui est par la très utile en général pour un cube ssas). Ainsi, le Inline-TVF (qui renvoie toutes les colonnes) finale est maintenant:

CREATE FUNCTION [dbo].[_previousClaimsByFiData] (
    @fiData INT 
) 

RETURNS TABLE AS 
RETURN(
    WITH PreviousClaims 
    AS(
     SELECT 1 AS relationLevel, child.* 
     FROM tabData child 
     WHERE child.idData = @fiData 

     UNION ALL 

     SELECT relationLevel+1, child.* 
     FROM tabData child 
     INNER JOIN PreviousClaims parent ON parent.fiData = child.idData 
    ) 

    SELECT TOP 100 PERCENT * FROM PreviousClaims order by relationLevel 
) 

C'est une relation exemplaire:

select idData,fiData,relationLevel from dbo._previousClaimsByFiData(46600314); 

alt text

Merci.

+0

La solution que vous avez publiée * peut fonctionner pour vous, maintenant, sur le serveur, le service pack et la base de données d'aujourd'hui. Mais ce n'est en aucun cas garanti. La seule façon de garantir l'ordre d'un ensemble de résultats consiste à spécifier une clause ORDER BY dans l'instruction SELECT la plus externe. –

+0

Oui, cela semble mieux - cette sortie finale dépend toujours d'un détail d'implémentation (que "ORDER BY" dans INSERT est en relation avec l'ordre de récupération), mais au moins vous avez la colonne de tri disponible pour faire un ORDER BY sur cette sélection externe, si vous en avez besoin. –

+0

Est-ce que ça fait une différence quand je définirais relationLevel comme clé primaire? Est-ce que l'ordre est garanti et n'a pas besoin d'être appliqué sur un select externe? –

Répondre

3

La bonne façon de le faire est de votre commande ajoutez une clause ORDER BY à votre select le plus externe. Tout le reste repose sur des détails d'implémentation qui peuvent changer à tout moment (y compris si la taille de votre base de données/tables augmente, ce qui peut permettre plus de traitement en parallèle).

Si vous avez besoin quelque chose de pratique pour permettre la commande d'avoir lieu, regardez l'exemple D dans les exemples de la MSDN page on WITH:

WITH DirectReports(ManagerID, EmployeeID, Title, EmployeeLevel) AS 
(
    SELECT ManagerID, EmployeeID, Title, 0 AS EmployeeLevel 
    FROM dbo.MyEmployees 
    WHERE ManagerID IS NULL 
    UNION ALL 
    SELECT e.ManagerID, e.EmployeeID, e.Title, EmployeeLevel + 1 
    FROM dbo.MyEmployees AS e 
     INNER JOIN DirectReports AS d 
     ON e.ManagerID = d.EmployeeID 
) 

Ajouter quelque chose similay à la colonne EmployeeLevel à votre CTE, et tout devrait fonctionner .

+0

Merci pour cet indice. Le résultat reste le même, mais quand vous dites qu'il pourrait changer plus tard dans certaines circonstances, je vais utiliser cette méthode pour forcer l'ordre correct. Je vais mettre à jour ma question en conséquence bientôt. –

2

Je pense que l'impression que le CTE crée une commande est fausse. C'est une coïncidence que les lignes sortent dans l'ordre (probablement en raison de la façon dont elles ont été insérées à l'origine dans tabData). Quoiqu'il en soit, le TVF retourne une table, donc vous devez ajouter explicitement une clause ORDER BY pour la SELECT que vous utilisez pour appeler si vous voulez garantir la commande:

select * from dbo._previousClaimsByFiData(16177344) order by idData 
+0

Le problème est que l'ordre par idData est sans valeur, car il n'est pas garanti qu'il soit égal à l'ordre logique parent-enfant. Dois-je itérer avec un curseur au lieu d'utiliser le CTE? –

+1

Voyez si vous pouvez trouver une approche sans curseur pour trouver la commande. Je ne connais pas suffisamment votre relation de données pour le dire. Peut-être que certains exemples de données parent/enfant et la bonne commande pourraient aider. – Anna

+0

Un exemple est fourni dans la question précédente mentionnée ci-dessus, voici le lien: http://stackoverflow.com/questions/3935005/recursive-cte-to-find-parent-records –

2

Il n'y a pas ORDER BY nulle part en vue - ni dans la fonction table, ni dans le SELECT de ce TVF.

Toute "commande" sera totalement arbitraire et coïncidente.

Si vous souhaitez une commande spécifique, vous devez spécifier un ORDER BY.

Alors, pourquoi ne pouvez-vous ajouter juste une ORDER BY à votre SELECT:

SELECT * FROM dbo._previousClaimsByFiData(16177344) 
ORDER BY (whatever you want to order by).... 

ou mettre votre ORDER BY dans la TVF:

INSERT INTO @retPreviousClaims 
    SELECT idData FROM PreviousClaims 
    ORDER BY idData DESC (or whatever it is you want to order by...) 
+1

Parce que l'ordre est donné par la structure interne de la relation parent-enfant. La clé étrangère fiData est définie dans une procédure stockée exécutée tous les soirs lors de l'importation de nouvelles données.La seule façon que je peux voir (sans un curseur) est d'utiliser l'ordre de cette procédure stockée, mais cela signifie beaucoup de frais généraux qui ralentit chaque requête et rend l'approche globale redondante. Jetez un oeil à cette vieille question de la mienne où vous pouvez trouver une partie de cette logique et la commande par: http://stackoverflow.com/questions/3266299/whats-wrong-with-my-sql-find-a-previous- record –

+1

Après avoir repensé je suis arrivé à la conclusion que c'est le seul moyen et n'est pas aussi mauvais que j'ai d'abord pensé, car il n'y a que quelques parents à commander. Ok, j'aurai besoin de plus de colonnes Date au lieu de seulement le PK. Merci, je vais le tester demain. –

+1

Notez cependant que l'ajout d'un ORDEr BY dans un INSERT est un no-op fondamentalement. @retPreviousClaims n'a pas d'ordre. L'ORDER BY doit être appliqué à 'select * from dbo._previousClaimsByFiData (...) ORDER BY ....'. Si le TVF est utilisé dans un contexte de requête, ORDER BY doit être appliqué à la requête. Et ainsi de suite. Seulement ** résultats ** ont ordre. –

Questions connexes