Default-Wert immer bei Insert setzen

Answered Default-Wert immer bei Insert setzen

  • Freitag, 27. April 2012 13:32
     
     

    Hallo

    In einer Tabelle habe ich eine Spalte mit DEFAULT NEWID() definiert. Damit gibt es zu jedem Datensatz eine eindeutige UID welche bei späteren Änderungen über das Programm einen neuen Wert erhält.

    Durch ein Partner-Modul, bei welchem ein Datensatz kopiert und neu eingefügt wird, wird diese Spalte nicht geändert und würde so die gleiche UID bekommen, wie der 1. Datensatz(wird verhindert, weil Index gesetzt).Ich müsste also beim Insert aus dem Partnermodul heraus auf DB-Ebene eine neue UID generieren können, welche beim Insert gesetzt wird und keine Indexverletzung bringt.

    Jemand einen Tipp, damit diese Spalte nicht nur initialisert wird, wenn kein Wert gesetzt wird, sondern immer?

    Gruss Christoph

Alle Antworten

  • Freitag, 27. April 2012 13:40
     
     

    Hallo Christoph,

    über ein INSTEAD OF Trigger kannst Du sozusagen die Schreibaktion abfangen, die ID nach Deiner Business Logik neu erstellen und dann das eigentlich Schreiben in die Tabelle selbst vornehmen.

    Siehe Entwerfen von INSTEAD OF-Triggern und Verwenden von INSTEAD OF-Triggern


    Olaf Helper
    * cogito ergo sum * errare humanum est * quote erat demonstrandum *
    Wenn ich denke, ist das ein Fehler und das beweise ich täglich
    Blog Xing



  • Freitag, 27. April 2012 13:46
    Moderator
     
     
    Ein INSTEAD OF INSERT-Trigger. INSTEAD OF da sonst der Unique Constraint zuschlägt. Im Trigger prüfst du, ob die UID schon existiert und überschreibst sie in diesem Fall mit einer neuen UID.

    Problem: Wenn ich Daten kopiere und ein OK bekomme erwarte ich im Besonderen bei solchen Spalten, das sich die Werte nicht ändern. Somit stellt sich die Frage, ob das Partnermodul dann überhaupt noch richtig funktionieren würde...

    Für deinen Tipp: Ein Trigger nach dem Einfügen, der per virtueller INSERTED-Tabelle allen betroffenen Zeilen einen neuen Wert verpasst.


  • Freitag, 27. April 2012 15:11
     
     

    Hallo zusammen

    Werde es prüfen. Das mit dem geänderten Wert ist für die anderen Module kein Problem. Diese Spalte wird für CalDav-Schnittstelle genutzt und da bedeutet ein geänderter Wert nur, dass der Datensatz geändert wurde.

    Gruss Christoph

  • Freitag, 27. April 2012 15:13
    Beantworter
     
     

    Hallo Christoph,

    neben der von Olaf und Stefan gegebenen Lösung via INSTEAD OF Trigger:

    Verwende beim INSERT anstatt die Spalte zu übergeben, den Platzhalter DEFAULT in der VALUES Klausel,
    womit die Default-Einschränkung (hier NEWID) verwendet wird -
    oder aber verwende NEWID() als Ausdruck.

    Gruß Elmar

  • Freitag, 27. April 2012 15:18
     
     

    Ich kann dem Partner schlecht vorschreiben, dass er das anders machen soll. Daher hätte ich lieber auf DB-Ebene das entsprechend umgesetzt

    Gruss Christoph

  • Freitag, 27. April 2012 17:17
     
     

    eine weitere Moeglichkeit ist, die Definition einer View und das Einfuegen der Daten ueber diese View fuer den Partner erzwingen.

    Da geht z.b. falls er ein spezieller SQL Login benutzt und Du ein Schema definiert welches auf die View statt die Tabelle zeigt. Der Trigger ist dann nur noch auf der View notwendig und die performance leidet bei normalen Insert/Updates nicht.

    siehe INSTEAD OF INSERT Triggers (erstes Beispiel "Create an INSTEAD OF INSERT trigger on the view")

    Eine besserer Ansatz waere unter Umstaenden die Verwendung von rowversion da dann die SQL DB Engine garantiert, dass der Wert bei jedem INSERT resp. UPDATE geaendert wird. (ueberdies wird damit auch weniger Platz in der DB beansprucht).

    Falls Du aber zwingend auf den Datentyp uniqueidentifier angewiesen bist, solltest Du klaeren, ob Du nicht besser newsequentialid anstelle von von newid verwenden kannst, da dann Index effizienter verwendet werden koennen.


    Please use Mark as Answer if my post solved your problem and use Vote As Helpful if a post was useful.

  • Montag, 30. April 2012 14:12
     
     

    Hallo Stefan

    Bin jetzt am Trigger und wollte das wie folgt machen:

      UPDATE inserted SET blcUID=NEWID()
       INSERT INTO tdAddresses
          SELECT * FROM inserted

    Das geht jetzt aber nicht: Die logischen Tabellen INSERTED und DELETED können nicht aktualisiert werden.

    Weil in dieser Tabelle weitere Spalten dazukommen können, kann ich hier nicht einfach alle Spalten aufführen und bei der blcUID-Spalte das NewID angeben.
    Gibt es eine Möglichkeit, dass ich den inserted-Wert ändern kann, bevor dann das INSERT selber gemacht wird?

    Gruss Christoph

  • Montag, 30. April 2012 14:58
     
     

    Gibt es eine Möglichkeit, dass ich den inserted-Wert ändern kann, bevor dann das INSERT selber gemacht wird?

    Hallo Christoph,

    Du hast den INSTEAD OF Trigger noch nicht ganz verstanden. Der wird anstelle des eigentlichen Vorgangs ausgelöst und wie ich beim ersten Mal schon schrieb, musst Du das eigentliche Schreiben selbst vor nehmen.


    Olaf Helper
    * cogito ergo sum * errare humanum est * quote erat demonstrandum *
    Wenn ich denke, ist das ein Fehler und das beweise ich täglich
    Blog Xing

  • Montag, 30. April 2012 18:36
     
     

    Hallo Olaf

    Denke, dass ich den Trigger schon verstanden habe. Wollte eben zuerst die Daten welche für das Einfügen bereitliegen anpassen und dann das Insert ausführen und da ich nicht weis, wie die Spalten später oder durch Partneranpassungen aussehen, müsste ich das Insert mit * machen.

    Gruss Christoph

  • Dienstag, 1. Mai 2012 03:13
     
     Beantwortet

    Hallo Christoph

    chmav wrote:

    Bin jetzt am Trigger und wollte das wie folgt machen:

    UPDATE inserted SET blcUID=NEWID()
    INSERT INTO tdAddresses
    SELECT * FROM inserted

    Das geht jetzt aber nicht: Die logischen Tabellen INSERTED und DELETED
    können nicht aktualisiert werden.

    Du solltest auch nicht in die Konzepttabelle INSERTED reinschreiben, sondern - das ist ja der Grund für den INSTEAD OF INSERT (Anstelle des Hinzufügens) Trigger den Datensatz direkt in die Tabelle wegschreiben. Es spricht aber (ausser der Performance) nichts dagegen, eine temporäre Tabelle anzulegen, welche genau der Tabelle INSERTED entspricht, deren Inhalt zu ändern und anschliessend dann das Ergebnis in die Zieltabelle wegzuschreiben.

    Hier ein Beispiel

    CREATE TRIGGER tdAdresses_InsteadOfInsert
     ON tdAdresses
     INSTEAD OF INSERT
    AS
    BEGIN
     SET NOCOUNT ON
     SELECT * INTO #TEMPTABLE FROM INSERTED
     UPDATE #TEMPTABLE SET blcUID = NEWID()
     INSERT INTO tdAdresses SELECT * FROM #TEMPTABLE
     DROP TABLE #TEMPTABLE
    END

    Gruss
    Henry

    • Als Antwort markiert chmav Dienstag, 1. Mai 2012 09:28
    •  
  • Dienstag, 1. Mai 2012 09:11
     
     

    Hallo Henry

    An eine solche Lösung habe ich auch geacht, wollte aber zuerst sicher gehen, dass es wirklich keine Möglichkeit gibt, das direkt über die INSERTED-Tabelle zu machen.

    Besten Dank Euch allen

    Gruss Christoph

  • Dienstag, 1. Mai 2012 09:18
     
     

    Hallo Christoph

    chmav wrote:

    An eine solche Lösung habe ich auch geacht, wollte aber zuerst sicher
    gehen, dass es wirklich keine Möglichkeit gibt, das direkt über die
    INSERTED-Tabelle zu machen.

    Da Du ja hier nicht mit riesigen Multirow Inserts, sondern wohl vorallem mit einzelnen Records konfrontiert wirst, sollte das soweit ok sein. Die lokale temporäre Tabelle wird - solange genügend Platz vorhanden ist - nur im Speicher angelegt und muss nicht physisch in die Temp DB geschrieben werden. Bezüglich Performance würde ich hier also wegen der lokalen temporären Tabelle keine allzugrossen Schwierigkeiten erwarten. Dafür bist Du sicher, dass du immer eine UID drin hast, auch wenn Deine Partner mit was anderem daher kommen. Diese könnten dann allerdings - abhängig vom System, das sie benutzen - mit der veränderten blcUID Probleme bekommen. Falls z.B. Access den Insert macht, müsste im Form_AfterUpdate sichergestellt werden, dass der Datensatz neu eingelesen wird, da sonst Access davon ausgeht, dass immer noch sein Schlüssel drin steht. Das sind dann aber eher die Probleme, mit denen die Partner kämpfen müssen, datenbankseitig sollte das so ok sein.

    Gruss
    Henry

  • Dienstag, 1. Mai 2012 09:19
     
     

    dass es wirklich keine Möglichkeit gibt, das direkt über die INSERTED-Tabelle zu machen

    Hallo Christoph,

    Nein, Du schreibst die Daten eben nicht in die virtuelle Tabellle INSERTED, sondern in die eigentlich Zieltabelle.


    Olaf Helper
    * cogito ergo sum * errare humanum est * quote erat demonstrandum *
    Wenn ich denke, ist das ein Fehler und das beweise ich täglich
    Blog Xing

  • Dienstag, 1. Mai 2012 09:26
     
     

    * cogito ergo sum * errare humanum est * quote erat demonstrandum *
    Wenn ich denke, ist das ein Fehler und das beweise ich täglich
    Blog Xing

    LOL

    BTW: quod erat demonstrandum (AKA "HX" = hesch g'seh)