2017-12-15 1 views
0

Ich habe eine Tabelle, die einige Daten speichert. Jede Datenzeile hat eine Sequenznummer, die nach Organisation und Jahr gruppiert ist. Die Struktur dieser Tabelle untenReassign eine Sequenznummer, wenn Spalte durch Trigger geändert wird

| ID | Seq_num | Organisation | Year | 
|----|---------|--------------|------| 
| 1 | 1  | A   | 2017 | 
| 2 | 2  | A   | 2017 | 
| 3 | 3  | A   | 2017 | 
| 4 | 1  | A   | 2018 | 
| 5 | 2  | A   | 2018 | 
| 6 | 3  | A   | 2018 | 
| 7 | 1  | A   | 2019 | 
| 8 | 2  | A   | 2019 | 
| 9 | 4  | A   | 2017 | 

Ich habe 2 Fällen für diese Tabelle:
1) mit einer Sequenznummer an die neue Zeile
2) zuweisen einer Sequenznummer neu zuzuweisen, wenn die Spalten Jahr geändert wird

Für den ersten Fall wähle ich nur max von seq_num über Organisation und Jahr wie folgt aus:

UPDATE table 
SET Seq_num =isnull(CurrMaxNum+1,1) 
FROM table t 
    LEFT JOIN (SELECT DISTINCT Year, Organisation, 
    MAX(Seq_num) OVER (PARTITION BY Year, Organisation) AS CurrMaxNum 
       FROM table 
       GROUP BY Year,Organisation, Seq_num) as num 
       on num.Organisation=t.Organisation and num.Year=t.Year 
     WHERE (t.Seq_num IS Null OR t.Seq_num = 0 ) 
    and (t.Organisation=num.Organisation and num.Year=t.Year) 


Für den zweiten Fall muss ich neu zuweisen Seq_num wenn Jahr geändert wird. Zum Beispiel, wenn ich das Jahr für id=5 von 2018 bis 2017 ändere, sollte Seq_num von 2 zu 4 wechseln, da 4 gleich der maximalen Anzahl in 2017 Gruppe +1 ist.
Die anderen Werte müssen nicht neu zugewiesen werden. Wie soll ich bekommen, was ich brauche?

Antwort

2

Meine Lösung. Zwei Update-Anweisungen im Trigger nach Update.Ganz langsam, aber es funktioniert:

--for 2d case 
UPDATE table 
SET [Seq_num]= NULL 
FROM table Z 
    INNER JOIN Inserted I ON Z.Id = I.Id 
    INNER JOIN Deleted D ON Z.Id = D.Id    
WHERE 
    D.Year <> I.Year 

--for 1st case 
UPDATE table 
SET [Seq_num]= s_num 
FROM table Z 
    LEFT JOIN (select t.* , isnull(MAX([Seq_num]) over (partition by Year,Organisation 
       order by [Seq_num] desc)+1,1) as s_num 
       from table t 
       ) S on Z.ID=S.ID 
WHERE 
    Z.[Seq_num] IS NULL 

Also, wenn Year Änderungen, aktuelle Seq_num wird NULL, dann zweite UPDATE-Anweisung zuweisen neuen Wert für Seq_num

+0

Guter Job mit der maximalen Windowing. Ich muss mir angewöhnen, das als Anwendungsfall vorzuschlagen, wie er seit 2012 verfügbar ist. –

1

Dies ist zu lang für einen Kommentar.

Warum sollte die Sequenznummer gespeichert werden? Sie können es auf der Verwendung von fly generieren:

select t.*, 
     row_number() over (partition by organization, year order by id) as seq_num 
from t; 

Es gibt Gründe, warum Sie könnten die Daten in der Tabelle speichern - sagen, wenn die Tabelle sehr groß ist und die Leistung ist ein Problem. Oder wenn Sie Zeilen löschen und die ursprüngliche Sequenznummer beibehalten möchten.

Aber für eine Tabelle mit Tausenden von Zeilen (und Spalten wie "Jahr" und "Organisation" führen mich zu der Meinung, dass die Tabelle nicht so groß ist), Berechnung im laufenden Betrieb ist sehr machbar und viel einfacher.

+0

Die Tabelle ist sehr, sehr groß, und wir haben zu halten Original Sequenznummer –

1

Ich verabscheue Trigger, aber ein Anwendungsfall scheint immer aufzutauchen. (Ich würde immer noch sehen, ob dies etwas ist, das in der update stored procedure für Ihren Tisch gesteuert werden könnte).

Wenn Sie eine Trigger-Lösung wollen, dann würde ich einen UPDATE-Trigger nur erstellen und über alle eingefügten und aktualisieren die Datensätze, in denen der Jahr-Wert geändert wurde. Als Nebeneffekt haben Sie eine intransparente Geschäftsregel erstellt, und wenn sich jemand mit den Auslösern beschäftigt, könnten Sie in einen Auslöser geraten, der Auslöser auslöst, was schlecht ist.

CREATE TRIGGER [dbo].[trMyTableModified] 
    ON [dbo].[MyTable] 
    AFTER UPDATE 
AS BEGIN 
    SET NOCOUNT ON; 

    DECLARE @ID INT, @CurrentValueYear INT, @PriorValueYear INT, @OrganizationID INT 

    DECLARE X CURSOR FORWARD_ONLY READ_ONLY LOCAL FOR 
     SELECT ID,Year,OrganizationID FROM INSERTED 
     OPEN X 
     FETCH NEXT FROM X INTO @ID,@CurrentValueYear,@OrganizationID 
     WHILE @@FETCH_STATUS = 0 BEGIN 

      SELECT @PriorValueYear = Year FROM DELETED WHERE [email protected] 

      IF(@PriorValueYear<>@CurrentValueYear) BEGIN 

       UPDATE MyTable 
       SET 
        MyTable.Seq_Num = 
        (
         SELECT MAX(Seq_Num) 
         FROM MyTable T 
         WHERE 
          [email protected] 
          AND 
          [email protected] 
        ) + 1 
       WHERE 
        MyTable.ID= @ID 
      END 

     END 
    CLOSE X 
    DEALLOCATE X 
END 

Eine Aktualisierungsabfrage prüft und aktualisiert zu tun.

Dies wird nicht getestet, Sie können jedoch nach der Änderung suchen, die maximale Sequenz abrufen und das Update in einer Abfrage durchführen. Es könnte sich als effizienter erweisen. Die inserted und deleted sollte immer die gleiche Menge an Datensätzen enthalten, auch wenn sie von einem Merge-Anweisung ausgelöst, wie die Zusammenführung mehrere Trigger verursachen wird für jede Operation zu schießen, zu aktualisieren, löschen ...

WITH YearChanges AS 
    (
     SELECT I.ID,I.Year,I.OrganizationID 
     FROM 
      INSERTED I 
      INNER JOIN DELETED D ON D.ID=I.ID AND D.Year<>I.Year -- Ignore updates where year has not changed 
    ), 
    WITH MaxSeq AS 
    (
     SELECT Year,OrganizationID,NextSeqenceNumber = MAX(Seq_Num) + 1 
     FROM 
      MyTable T 
      INNER JOIN YearChanges YC ON YC.OrganizationID = T.OrganizationID AND YC.Year=T.Year 
     GROUP BY 
      Year, OrganizationID  
    ) 
    UPDATE 
     MyTable T 
    SET 
     Seq_Num = YC.NextSeqenceNumber 
    FROM 
     YearChanges YC 
     INNER JOIN MaxSeq MS ON MS.Year=YC.Year AND MS.OrganizationID=YC.OrganizationID  
    WHERE 
     T.ID=YC.ID 
Verwandte Themen