2010-06-09 3 views
3

Je souhaite mettre à jour les lignes d'une table dans un ordre spécifique, comme on pourrait s'y attendre en incluant une clause ORDER BY, mais SQL Server ne prend pas en charge la clause ORDER BY dans les requêtes UPDATE.SQL Server 2005: mettre à jour les lignes dans un ordre spécifié (comme ORDER BY)?

J'ai vérifié this question qui a fourni une bonne solution, mais ma requête est un peu plus compliquée que celle spécifiée ici.

UPDATE TableA AS Parent 
SET Parent.ColA = Parent.ColA + (SELECT TOP 1 Child.ColA 
    FROM TableA AS Child 
    WHERE Child.ParentColB = Parent.ColB 
    ORDER BY Child.Priority) 
ORDER BY Parent.Depth DESC; 

Alors, ce que j'espère que vous remarquerez est qu'une seule table (TableA) contient une hiérarchie de lignes, dans laquelle une ligne peut être le parent ou l'enfant d'une autre rangée. Les lignes doivent être mises à jour dans l'ordre de l'enfant le plus profond jusqu'au parent racine. C'est parce que TableA.ColA doit contenir une concaténation à jour de sa propre valeur actuelle avec les valeurs de ses enfants (je réalise que cette requête ne concerne qu'un seul enfant, mais c'est pour des raisons de simplicité - le but de l'exemple dans cette question ne nécessite pas plus de verbosité), la requête doit donc être mise à jour de bas en haut.

La solution proposée dans la question je l'ai mentionné ci-dessus est la suivante:

UPDATE messages 
SET status=10 
WHERE ID in (SELECT TOP (10) Id 
    FROM Table 
    WHERE status=0 
    ORDER BY priority DESC 
); 

La raison pour laquelle je ne pense pas que je peux utiliser cette solution est parce que je suis le référencement des valeurs de colonne de la table parent dans ma sous-requête (voir WHERE Child.ParentColB = Parent.ColB), et je ne pense pas que deux sous-requêtes de frères auraient accès aux données des autres. Jusqu'à présent, j'ai seulement déterminé une façon de fusionner cette solution avec mon problème actuel, et je ne pense pas que cela fonctionne.

UPDATE TableA AS Parent 
SET Parent.ColA = Parent.ColA + (SELECT TOP 1 Child.ColA 
    FROM TableA AS Child 
    WHERE Child.ParentColB = Parent.ColB 
    ORDER BY Child.Priority) 
WHERE Parent.Id IN (SELECT Id 
    FROM TableA 
    ORDER BY Parent.Depth DESC); 

La sous-requête WHERE..IN ne reviendra pas en fait un sous-ensemble des lignes, il suffit de retourner la liste complète des ID dans l'ordre que je veux. Cependant (je ne sais pas avec certitude - s'il vous plaît dites-moi si je me trompe) je pense que la clause WHERE..IN ne se souciera pas de l'ordre des ID entre parenthèses - il va juste vérifier l'ID de la ligne qu'il veut actuellement mettre à jour pour voir si c'est dans cette liste (qui, ils le sont tous) dans l'ordre qu'il essaie déjà de mettre à jour ... Ce qui serait juste un gaspillage total de cycles, car cela ne changerait rien. Donc, en conclusion, j'ai regardé autour et ne peux pas sembler trouver un moyen de mettre à jour dans un ordre spécifié (et inclus le raison je dois mettre à jour dans cet ordre, parce que je suis sûr que je le ferais sinon, obtenez les réponses «pourquoi?» si utiles) et je suis en train de cliquer sur Stack Overflow pour voir si certains de vos gourous qui en savent plus sur SQL que moi (ce qui ne dit pas grand-chose) connaissent un moyen efficace de le faire. Il est particulièrement important que je n'utilise qu'une seule requête pour terminer cette action.

Une longue question, mais je voulais couvrir mes bases et vous donner autant d'informations que possible. :)

Des pensées? Les instructions UPDATE seront exécutées comme une seule requête, pas comme un résultat étape par étape.

Répondre

2

Vous ne pouvez pas réussir cela dans une requête, parce que vos mises à jour sont en corrélation (niveau. N dépend de la mise à jour la valeur de niveau N + 1). Les moteurs relationnels froncent les sourcils de façon très explicite à cause des Halloween Problem. Le plan de requête fera tout son possible pour s'assurer que les mises à jour se déroulent comme si elles avaient deux étapes: l'une dans laquelle l'état actuel était lu, et l'autre dans lequel l'état mis à jour était appliqué. Si nécessaire, ils spouleront des tables intermédiaires juste pour préserver cet ordre apparent d'exécution (lire tout-> écrire tout). Puisque votre question, si je comprends bien, essaie de casser cette prémisse, je ne vois pas comment vous réussirez.

0

JMTyler-

1 Quel type de données est en ColA? À quoi cela ressemble-t-il?

2 Comment/est-ce que cette colonne doit être remplie à l'origine? Je pose cette question car vous ne pourrez exécuter la mise à jour qu'une seule fois car la valeur de cette colonne sera modifiée à partir d'une exécution précédente. Toute exécution supplémentaire ne ferait que concaténer plus de données. Qu'est-ce qui me fait croire qu'il y a un autre ColC avec la valeur d'origine pour ColA (le nom d'une personne?)

3 Une rangée sera-t-elle jamais supprimée, ce sont des enfants? Si oui, à quoi devrait alors pointer ParentColB? NUL? Est-ce que leur profondeur est alors mise à 0 pour qu'ils soient maintenant au sommet de la hiérarchie?

Si vous pouvez répondre à cela, je peux vous donner une solution

Merci

+0

Salut Scot, merci pour votre intérêt. Les données commencent comme un seul caractère représentant le type de l'objet, puis ColA de chaque ligne les enveloppe (ie: en supposant que obj avec ColA 'p' a trois enfants avec ColA 'c': ColA = 'p (ccc)') jusqu'à ce que l'objet racine ait un motif représentant la hiérarchie. Cela n'arrive qu'une fois pour un lot d'objets. Si un objet est supprimé, tous ses enfants sont également supprimés. – JMTyler

+0

@JMTyler Si le premier enfant a un enfant (petit-fils de p) à quoi devrait ressembler la sortie? 'p (c (g) cc)'? ou n'est-ce pas important et tous les descendants peuvent être regroupés comme 'p (cccg)' –

+0

Le premier est correct - si le premier enfant a un enfant, le résultat est p (c (g) cc). Cela nous permet de savoir d'un coup d'œil quelle est la hiérarchie sous un objet donné et la structure ordonnée de cette hiérarchie. – JMTyler

1

Voici une solution en ligne de SQL. Si jamais vous relâchez l'exigence selon laquelle il faut une instruction de mise à jour, vous pouvez éliminer une partie de la complexité

CREATE TABLE [TableA](
    [ID] [int] NOT NULL, 
    [ParentID] [int] NULL, 
    [ColA] [varchar](max) NOT NULL, 
    [Priority] [varchar](50) NOT NULL, 
    [Depth] [int] NOT NULL) 
go 

INSERT TableA 
SELECT 1, NULL, 'p', 'Favorite', 0 UNION ALL 
SELECT 2, 1, 'm', 'Favorite', 1 UNION ALL 
SELECT 3, 1, 'o', 'Likeable', 1 UNION ALL 
SELECT 4, 2, 'v', 'Favorite', 2 UNION ALL 
SELECT 5, 2, 'v', 'Likeable', 2 UNION ALL 
SELECT 6, 2, 'd', 'Likeable', 2 UNION ALL 
SELECT 7, 6, 'c', 'Red-headed Stepchild', 3 UNION ALL 
SELECT 8, 6, 's', 'Likeable', 3 UNION ALL 
SELECT 9, 8, 'n', 'Favorite', 4 UNION ALL 
SELECT 10, 6, 'c', 'Favorite', 3 UNION ALL 
SELECT 11, 5, 'c', 'Favorite', 3 UNION ALL 
SELECT 12, NULL, 'z', 'Favorite', 0 UNION ALL 
SELECT 13, 3, 'e', 'Favorite', 2 UNION ALL 
SELECT 14, 8, 'k', 'Likeable', 4 UNION ALL 
SELECT 15,4, 'd', 'Favorite', 3 

;WITH cte AS (
SELECT a.i, a.Depth, a.maxd, a.mind, a.maxc, a.di, a.ci, a.cdi, a.ID, a.y, CAST('' AS varchar(max))z 
FROM(
    SELECT DISTINCT i = 1 
    ,p.Depth 
    ,maxd = (SELECT MAX(Depth) FROM TableA) 
    ,mind = (SELECT MIN(Depth) FROM TableA) 
    ,maxc = (SELECT MAX(c) FROM (SELECT COUNT(*) OVER(PARTITION BY ParentID) FROM TableA)f(c)) 
    ,di = (SELECT MIN(Depth) FROM TableA) 
    ,ci = 1 
    ,cdi = (SELECT MIN(Depth) FROM TableA) 
    ,p.ID 
    ,CAST(p.ID AS varchar(max)) + p.ColA + SPACE(1) + CASE WHEN g IS NULL THEN '' ELSE '(' END 
            + ISNULL(g,'') + CASE WHEN g IS NULL THEN '' ELSE ')' END y 
    FROM TableA p 
    LEFT JOIN TableA c ON (c.ParentID = p.ID) 
    CROSS APPLY (SELECT SPACE(1) + CAST(c2.ID AS varchar(max)) + ColA + SPACE(1) 
       FROM TableA c2 WHERE ParentID = p.ID 
       ORDER BY Priority 
       FOR XML PATH(''))f(g) 
    )a 
UNION ALL 
SELECT r.i, r.Depth, r.maxd, r.mind, r.maxc, r.di, r.ci, r.cdi, r.ID 
,CASE WHEN di = cdi 
     THEN REPLACE(r.y,LEFT(r.z,CHARINDEX(SPACE(1),r.z,2)), r.z) 
     ELSE r.y END [y] 
,r.z 
FROM(
    SELECT i = i + 1 
    ,Depth 
    ,[maxd] 
    ,[mind] 
    ,[maxc] 
    ,CASE WHEN ci = maxc AND cdi = maxd 
      THEN di + 1 
      ELSE di 
      END [di] 
    ,CASE WHEN cdi = [maxd] 
      THEN CASE WHEN ci + 1 > maxc 
        THEN 1 
        ELSE ci + 1 
        END 
      ELSE ci 
      END [ci] 
    ,CASE WHEN cdi + 1 > maxd 
      THEN mind 
      ELSE cdi + 1 
      END [cdi] 
    ,id,y 
    ,CAST(ISNULL((SELECT y FROM(
     SELECT p.Depth,p.ID 
     ,SPACE(1) + CAST(p.ID AS varchar(max)) + p.ColA + SPACE(1) + 
     CASE WHEN g IS NULL THEN '' ELSE '(' END + ISNULL(g,'') 
     + CASE WHEN g IS NULL THEN '' ELSE ')' END y 
     ,r1 = DENSE_RANK() OVER(ORDER BY p.ID) --child number 
     ,r2 = ROW_NUMBER() OVER(PARTITION BY p.ID ORDER BY p.ID) --DISTINCT not allowed in recursive section 
     FROM TableA p 
     JOIN TableA c ON (c.ParentID = p.ID) 
     CROSS APPLY (SELECT SPACE(1)+CAST(c2.ID AS varchar(max))+ColA+SPACE(1) 
        FROM TableA c2 
        WHERE ParentID = p.ID 
        ORDER BY Priority 
        FOR XML PATH(''))f(g) 
     WHERE p.Depth = cdi AND cdi < di AND p.ID <> cte.ID 
     )v 
    WHERE r1 = ci 
    AND r2 = 1 
    AND cte.y LIKE '%' + LEFT(v.y,CHARINDEX(SPACE(1),v.y,2)) + '%'),'') AS varchar(max)) z 
FROM cte 
WHERE [di]<[maxd] or [ci]<[maxc] or [cdi]<[maxd] 
)r 
)--cte 
UPDATE t 
SET ColA = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE 
     (y,SPACE(1),''),'1',''),'2',''),'3',''),'4',''),'5',''),'6',''),'7',''),'8',''),'9',''),'0','') 
FROM cte 
JOIN TableA t ON (t.ID = cte.ID) 
WHERE di = (SELECT MAX(Depth) FROM TableA) 
AND cdi = (SELECT MAX(Depth) FROM TableA) 
AND ci = (SELECT MAX(c) FROM (SELECT COUNT(*) OVER(PARTITION BY ParentID) FROM TableA)f(c)) 
OPTION(maxrecursion 0) 

SELECT * FROM TableA 
DROP TABLE TableA 
Questions connexes