Ankündigung

Einklappen
Keine Ankündigung bisher.

Trigger mit Datenbankprüfung und Erstellung

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Trigger mit Datenbankprüfung und Erstellung

    Hallo zusammen,

    ich habe einen Trigger der erfolgreich täglich Millionen von Daten in unterschiedliche Datenbanken und Tabellen einträgt.
    Jetzt würde ich gerne eine Prüfung einbauen, die bei nicht existierenden Datenbanken und Tabellen diese automatisch
    erstellt.

    Ich verwende aktuell den MS SQL Server 2014.

    Ziel ist es den Eingangsstring "[datenbankname].[dbo].[tabelle]" entsprechend zu zerlegen
    und die DB und Tabelle auf existens zu prüfen, wobei die Performance nicht so sehr leiden sollte,
    deshalb auch erst eine Prüfung wenn der INSERT fehl schlägt.

    Beispiel:

    Code:
    INSTEAD OF INSERT
    AS
      DECLARE @wert float
      DECLARE @utczeit datetime
      DECLARE @table nvarchar(4000)
      DECLARE @table2 nvarchar(4000)
      DECLARE @quality bit
      SELECT @wert=LoggingValue, @quality=Quality, @utczeit=UTCTime, @table=LoggingTable, @table2=LTW_Table FROM INSERTED
      DECLARE @str nvarchar(4000)
      BEGIN
        SET @str='INSERT INTO '+@table+' (LoggingValue,LoggingValue2,UTCTime,LoggingQuality) VALUES (@wert,NULL,@utczeit,@quality)'
        EXEC sp_executesql @str, N'@utczeit datetime, @wert float, @quality bit', @utczeit, @wert, @quality
    
    Hier mein Gedanke
    
        IF EXEC == false
        BEGIN
    
          Das Zerlegen habe ich bisher leider noch nicht hinbekommen
          @dbtable = STRING_SPLIT(@table,'].[')
          @newdb = STRING_SPLIT(@dbtable[0],'[')[1]
          @newtable = STRING_SPLIT(@dbtable[2],']')[0]
    
          IF NOT EXISTS (SELECT 1 FROM sys.databases WHERE name = @dbtable)
          BEGIN
            CREATE DATABASE [DBNAME]
          END
          und das Gleiche mit der Tabelle,
          danach nochmal der INSERT von oben.
        END
      END
    Wie ihr seht brauche ich hier leider noch viel Hilfe...

    Vielen Dank für Eure Hilfe.

    Liebe Grüße
    Florian


  • #2
    Hallo Florian,

    STRING_SPLIT gibt es erst ab SQL Server 2016.

    Für 2014 kannst du die Funktion aber in deiner DB erstellen:

    Code:
    CREATE FUNCTION [dbo].[fnSplitString]
    (
    String NVARCHAR(MAX),
        @delimiter CHAR(1)
    )
    RETURNS @output TABLE(splitdata NVARCHAR(MAX)
    )
    BEGIN
        DECLARE @start INT, @end INT
        SELECT @start = 1, @end = CHARINDEX(@delimiter, String)
        WHILE @start < LEN(@string) + 1 BEGIN
            IF @end = 0  
                SET @end = LEN(@string) + 1
    
            INSERT INTO @output (splitdata)  
            VALUES(SUBSTRING(String, @start, @end - @start))
            SET @start = @end + 1
            SET @end = CHARINDEX(@delimiter, String, @start)
    
        END
        RETURN
    END
    Der Aufruf ist dann etwas "tricky":

    Code:
    SELECT splitdata FROM (
    SELECT splitdata, ROW_NUMBER() OVER (ORDER BY splitdata ASC) AS rownumber FROM dbo.fnSplitString('[datenbankname].[dbo].[tabelle]', '.')
    ) as split WHERE rownumber = 1
    (Rownumber 1 für den DB-Namen und 3 für den Tabellennamen)


    Beim Anlegen der Tabelle / DB empfehle ich dir, dies so zu tun wie bei deinem INSERT, da du ja variable Tabellennamen und Datenbanken hast.

    Dein ganzes Vorhaben, bzw. "INSTEAD OF" Trigger alleine schon, sind aber extrem unsauber. Du solltest immer erst prüfen, ob der Aufruf des ursprünglichen INSERTs nicht umgestellt werden kann.

    Kommentar


    • #3
      Zitat von Tetra1081 Beitrag anzeigen
      Jetzt würde ich gerne eine Prüfung einbauen, die bei nicht existierenden Datenbanken und Tabellen diese automatisch
      erstellt.

      Ich verwende aktuell den MS SQL Server 2014.

      Ziel ist es den Eingangsstring "[datenbankname].[dbo].[tabelle]" entsprechend zu zerlegen
      und die DB und Tabelle auf existens zu prüfen, wobei die Performance nicht so sehr leiden sollte,
      deshalb auch erst eine Prüfung wenn der INSERT fehl schlägt.
      Lass mich raten, Dein Ziel ist die Weltherrschaft!?

      Mal ernst, ich halte die Idee nicht für besonders erstrebenswert. Mag sein, dass Deine Datenbanken und Tabellen nur die Kanalisation von Paris verwalten, also Sch..egal. Aber vielleicht ist es auch anders.
      Es gibt verschiedenste Gründe, es nicht zu tun, wie Du planst, rein technische, administrative, ..
      Ein insert und ein create sind vom Aufbau recht unterschiedlich, du brauchst geeignete Typangaben.... Die musst Du irgendwo herbekommen, exakt nach Möglichkeit, unabhängig davon wer gerade eigentlich mit welchen Clientsettings und Spracheinstellungen das Insert aufruft.
      Das Scheitern eines Inserts kann 100e Gründe haben, zuletzt, dass die Tabelle oder DB fehlt. Unpassende Constraints, Drecksdaten, Festplatte voll, .. alles glatt nur leider Spaltentyp etwas zu klein, das musst Du alles analysieren, bevor Du ein Create (oder Alter) baust, damit am Ende die richtige Anweisung überhaupt erzeugt werden kann.
      Administrativ: eine Tabelle, geschweige eine DB erstellt sich nicht in der Luft, vielleicht ist das mit den Millionen etwas großspurig gewesen, wenn nicht, wo bitte sind in Deinem "Create Db" Statement irgendwelche Physikangaben und woher sollten sie kommen? KI? Template?
      Ursache/Wirkung: Was ist der Grund für Deinen Ansatz, schlecht gepflegte Datenmodelle, schlechte Versionierung, fehlende Admins. ..?
      Du wirst das Problem nicht lösen mit diesem Versuch.

      Ganz vielleicht ist ja das System nur dazu gedacht, in einer multi DB Enwicklungsumgebung irgendwelche Änderungen möglichst easy zu propagieren. Aber dazu gibt es eigentlich entsprechende Mechnismen in Persistenz Layern...

      Also, nichts für ungut, vielleicht habe ich den wahren Grund übersehen und Dein Anliegen ist total erstrebenswert. Dann vergiß meinen Beitrag einfach. (Aber sag mir gern wieso, damit ich nicht dumm sterbe)

      Und um was positives beizutragen: Log den Fehler und schick meinetwegen direkt ne Email an den DB Admin, der kann dann bei Bedarf umbauen, wenn nötig oder eben mit echter Intelligenz einfach reagieren.

      Kommentar


      • #4
        Zitat von Meister1900 Beitrag anzeigen
        Hallo Florian,

        STRING_SPLIT gibt es erst ab SQL Server 2016.

        Für 2014 kannst du die Funktion aber in deiner DB erstellen:

        Code:
        CREATE FUNCTION [dbo].[fnSplitString]
        (
        String NVARCHAR(MAX),
        @delimiter CHAR(1)
        )
        RETURNS @output TABLE(splitdata NVARCHAR(MAX)
        )
        BEGIN
        DECLARE @start INT, @end INT
        SELECT @start = 1, @end = CHARINDEX(@delimiter, String)
        WHILE @start < LEN(@string) + 1 BEGIN
        IF @end = 0
        SET @end = LEN(@string) + 1
        
        INSERT INTO @output (splitdata)
        VALUES(SUBSTRING(String, @start, @end - @start))
        SET @start = @end + 1
        SET @end = CHARINDEX(@delimiter, String, @start)
        
        END
        RETURN
        END
        Der Aufruf ist dann etwas "tricky":

        Code:
        SELECT splitdata FROM (
        SELECT splitdata, ROW_NUMBER() OVER (ORDER BY splitdata ASC) AS rownumber FROM dbo.fnSplitString('[datenbankname].[dbo].[tabelle]', '.')
        ) as split WHERE rownumber = 1
        (Rownumber 1 für den DB-Namen und 3 für den Tabellennamen)


        Beim Anlegen der Tabelle / DB empfehle ich dir, dies so zu tun wie bei deinem INSERT, da du ja variable Tabellennamen und Datenbanken hast.

        Dein ganzes Vorhaben, bzw. "INSTEAD OF" Trigger alleine schon, sind aber extrem unsauber. Du solltest immer erst prüfen, ob der Aufruf des ursprünglichen INSERTs nicht umgestellt werden kann.
        Danke für die hilfreiche und konstruktive Hilfe.
        Kann die Daten für die Datenbank und Tabelle auch getrennt erhalten,
        dann spare ich mir diese Funktion und bin was die Datenbank angeht
        flexibler.

        Kommentar


        • #5
          Zitat von Perry Staltic Beitrag anzeigen

          Lass mich raten, Dein Ziel ist die Weltherrschaft!?

          Mal ernst, ich halte die Idee nicht für besonders erstrebenswert. Mag sein, dass Deine Datenbanken und Tabellen nur die Kanalisation von Paris verwalten, also Sch..egal. Aber vielleicht ist es auch anders.
          Es gibt verschiedenste Gründe, es nicht zu tun, wie Du planst, rein technische, administrative, ..
          Ein insert und ein create sind vom Aufbau recht unterschiedlich, du brauchst geeignete Typangaben.... Die musst Du irgendwo herbekommen, exakt nach Möglichkeit, unabhängig davon wer gerade eigentlich mit welchen Clientsettings und Spracheinstellungen das Insert aufruft.
          Das Scheitern eines Inserts kann 100e Gründe haben, zuletzt, dass die Tabelle oder DB fehlt. Unpassende Constraints, Drecksdaten, Festplatte voll, .. alles glatt nur leider Spaltentyp etwas zu klein, das musst Du alles analysieren, bevor Du ein Create (oder Alter) baust, damit am Ende die richtige Anweisung überhaupt erzeugt werden kann.
          Administrativ: eine Tabelle, geschweige eine DB erstellt sich nicht in der Luft, vielleicht ist das mit den Millionen etwas großspurig gewesen, wenn nicht, wo bitte sind in Deinem "Create Db" Statement irgendwelche Physikangaben und woher sollten sie kommen? KI? Template?
          Ursache/Wirkung: Was ist der Grund für Deinen Ansatz, schlecht gepflegte Datenmodelle, schlechte Versionierung, fehlende Admins. ..?
          Du wirst das Problem nicht lösen mit diesem Versuch.

          Ganz vielleicht ist ja das System nur dazu gedacht, in einer multi DB Enwicklungsumgebung irgendwelche Änderungen möglichst easy zu propagieren. Aber dazu gibt es eigentlich entsprechende Mechnismen in Persistenz Layern...

          Also, nichts für ungut, vielleicht habe ich den wahren Grund übersehen und Dein Anliegen ist total erstrebenswert. Dann vergiß meinen Beitrag einfach. (Aber sag mir gern wieso, damit ich nicht dumm sterbe)

          Und um was positives beizutragen: Log den Fehler und schick meinetwegen direkt ne Email an den DB Admin, der kann dann bei Bedarf umbauen, wenn nötig oder eben mit echter Intelligenz einfach reagieren.
          Mit dem falschen Fuß aufgestanden?
          Scheitern eines Inserts kann viele Gründe haben... deshalb würde ich
          bevor ich einen Create ausführe auch erstmal prüfen ob die Datenbank existiert.
          Die Datenbanken und die Tabellen sind immer identisch... es geht nur um Trenddaten.
          Die Insert werden von einem Programm generiert und sind somit immer so wie sie sein sollten.
          Bezüglich den Millionen... ich habe hier ca. 400 Datenpunkten die ich regelmäßig aufzeichne, dass wären
          so am Tag ca. 400 x 1440 Minuten = 576000 Inserts, wobei das nur ein Daumenwert ist
          Es gibt auch Anlagen mit über 20.000 Datenpunkten und das hochgerechnet fürs Jahr
          wären es ein paar Milliarden, aber das ist nichts besonderes in diesem Bereich.
          Deshalb werden die Trenddaten auch in Jahresdatenbanekn gespeichert, damit
          man diese bei bedarf einfach aus dem Projekt nehmen und Archivieren kann.

          Mein Gedanke ist nun nicht bereits im Voraus die Datenbanken für das nächste Jahr
          automatisiert zu erstellen, sondern wirklich erst dann, wenn man sie auch benötigt.
          Dann bräuchte ich auch Programmtechnisch nicht die Überprüfung ob eine Datenbank
          mit der entsprechenden Tabelle bereits existiert, z.B. wenn man Rückwirkend virtuelle
          Daten auf bestehenden historischen Daten berechnet und diese Speichert.

          Deshalb möchte ich, wenn der Insert einen Fehler zurückgibt eine Überprüfung
          starten ob die Datenbank/ Tabelle existieren und bei Bedarf einfach diese erstellen.
          Scheitert auch dieses, dann ist der Insert einfach verloren oder ich versuche ihn dann
          im nächsten Schritt ins Dateisystem zu schreiben um ihn anschließend mit einem
          zusätzlichen Programmcode einzulesen, damit keine Daten verloren gehen.

          Kommentar


          • #6
            Ich glaube mir würde es reichen zu wissen, wie man im intern Trigger prüft ob der INSERT ausgeführt wurde
            um dann eine Aktion auszuführen z.B.

            BEGIN TRY
            PRINT 'A';
            INSERT INTO [Trigger_Test2] (Wert) VALUES (1)
            PRINT 'B';
            END TRY
            BEGIN CATCH
            PRINT 'Error: ' + ERROR_MESSAGE();
            END CATCH

            Mein Hauptproblem ist... das hier auch das TRY ... CATCH nicht funktioniert,
            weil nach dem INSERT das Skript abgebrochen wird, wenn z.B. die Tabelle
            nicht existiert.

            Hier noch die Ausgabe:
            A
            Nachricht 208, Stufe 16, Status 1, Prozedur Test, Zeile 20 [Batchstartzeile 0]
            Invalid object name 'Trigger_Test2'.

            Wie man sieht wird das PRINT 'B' schon nicht mehr ausgeführt.

            Kommentar


            • #7
              Zitat von Tetra1081 Beitrag anzeigen


              Bezüglich den Millionen... ich habe hier ca. 400 Datenpunkten die ich regelmäßig aufzeichne, dass wären
              so am Tag ca. 400 x 1440 Minuten = 576000 Inserts, wobei das nur ein Daumenwert ist
              Es gibt auch Anlagen mit über 20.000 Datenpunkten und das hochgerechnet fürs Jahr
              wären es ein paar Milliarden, aber das ist nichts besonderes in diesem Bereich.
              Deshalb werden die Trenddaten auch in Jahresdatenbanekn gespeichert, damit
              man diese bei bedarf einfach aus dem Projekt nehmen und Archivieren kann.
              Ich würde hier an Partitionierung denken, aber vielleicht meinen wir auch dasselbe. Falls Partitionen fehlen, kann man eine Default-Partition definieren. Das "Routing" der Daten kannst Du dann der DB überlassen.
              PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

              Kommentar


              • #8
                Zitat von akretschmer Beitrag anzeigen

                Ich würde hier an Partitionierung denken, aber vielleicht meinen wir auch dasselbe. Falls Partitionen fehlen, kann man eine Default-Partition definieren. Das "Routing" der Daten kannst Du dann der DB überlassen.
                Möglich... kenne mich noch nicht mit Partitionen aus, aber der Hauptgrund liegt an der Datenbankgröße... bei MS SQL Express gehen nur 10GB und die Trenddaten pro Anlage belaufen sich auf ca. 3-7 GB im Jahr.
                Deshalb möchte ich einfach eine Fehlerbehandlung wenn z.B. die Datenbank für 2019 noch nicht angelegt ist und die ersten Daten dafür kommen.
                Wie fange ich einen Insert ab bei dem die Tabelle oder Datenbank fehlt, z.B. wie oben beschrieben?

                Kommentar


                • #9
                  Zitat von Tetra1081 Beitrag anzeigen
                  bei MS SQL Express gehen nur 10GB und die Trenddaten pro Anlage belaufen sich auf ca. 3-7 GB im Jahr.
                  Die Entscheidung für ein falsches Tool kannst Du schwerlich uns anlasten. Alternativen gibt es. Daher gibt es meinerseits kein Mitleid. 3 oder 7 GB pro Jahr sind z.B. für PostgreSQL peanuts, typische Datenbanken unserer Kunden sind Faktor 100 oder 1000 größer. Für PostgreSQL XL noch einige Potenzen mehr, im PetaByte-Bereich.
                  Dein Problem, also "was tun, wenn die nötige Partition fehlt", ist keines. Dafür gibt es mehr als eine (simple) Lösung.
                  PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

                  Kommentar


                  • #10
                    Zitat von akretschmer Beitrag anzeigen

                    Die Entscheidung für ein falsches Tool kannst Du schwerlich uns anlasten. Alternativen gibt es. Daher gibt es meinerseits kein Mitleid. 3 oder 7 GB pro Jahr sind z.B. für PostgreSQL peanuts, typische Datenbanken unserer Kunden sind Faktor 100 oder 1000 größer. Für PostgreSQL XL noch einige Potenzen mehr, im PetaByte-Bereich.
                    Dein Problem, also "was tun, wenn die nötige Partition fehlt", ist keines. Dafür gibt es mehr als eine (simple) Lösung.
                    Ich verstehe ja das wenn man auf etwas eingeschossen ist alles andere schlecht ist, aber ich brauche da trotzdem eine Lösung, da ich da leider nicht drum herum komme und nicht so flexible sein kann auf PostgreSQL zu wechseln.
                    Man wird doch ein Insert der einen Fehler auswirft abfangen und entsprechend behandeln können, egal bei welcher Datenbank.
                    Auch wenn ich jetzt nicht eine Tabelle erstellen möchte... sondern rein zur Überwachung.

                    Kommentar


                    • #11
                      Es geht nicht darum, ob ich etwas gut oder schlecht finde, es geht darum, daß Deine Wahl der DB, welche nur 10GB verkraften kann, für Deine geplanten Datenmengen nicht geeignet ist. Wenn Du 10 Tonnen Nutzlast ins All bringen willst, reicht eine handelsübliche Silversterrakete halt nicht aus.
                      PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

                      Kommentar


                      • #12
                        akretschmer hat schon Recht. Wenn so viel Zeit und Energie in Workarounds gesteckt werden muss, sollte das ein deutliches Warnsignal sein. Denn im Endeffekt ist deine Arbeitszeit auch nicht gratis. Wer auch immer dir das aufgetragen hat so zu machen, der sollte mal abwägen was auf Dauer günstiger kommt. So ein Monster zu entwickeln und fortlaufend zu warten oder auf eine andere Plattform zu wechseln. Sei es SQL Server (ohne Express) oder ein freies DBMS (was im Endeffekt auch nicht gratis ist, weil irgendwer muss das ja aufsetzen und betreuen).

                        Kommentar


                        • #13
                          Zitat von Tetra1081 Beitrag anzeigen
                          Ich glaube mir würde es reichen zu wissen, wie man im intern Trigger prüft ob der INSERT ausgeführt wurde
                          um dann eine Aktion auszuführen z.B.

                          BEGIN TRY
                          PRINT 'A';
                          INSERT INTO [Trigger_Test2] (Wert) VALUES (1)
                          PRINT 'B';
                          END TRY
                          BEGIN CATCH
                          PRINT 'Error: ' + ERROR_MESSAGE();
                          END CATCH

                          Mein Hauptproblem ist... das hier auch das TRY ... CATCH nicht funktioniert,
                          weil nach dem INSERT das Skript abgebrochen wird, wenn z.B. die Tabelle
                          nicht existiert.

                          Hier noch die Ausgabe:
                          A
                          Nachricht 208, Stufe 16, Status 1, Prozedur Test, Zeile 20 [Batchstartzeile 0]
                          Invalid object name 'Trigger_Test2'.

                          Wie man sieht wird das PRINT 'B' schon nicht mehr ausgeführt.
                          Ist ja logisch, da er nach dem fehlerhaften INSERT direkt in den Catch-Block geht. Schreib mal das PRINT 'B' in den Catch-Block, dann wird es auch ausgeführt.

                          Kommentar


                          • #14
                            Zitat von Meister1900 Beitrag anzeigen

                            Ist ja logisch, da er nach dem fehlerhaften INSERT direkt in den Catch-Block geht. Schreib mal das PRINT 'B' in den Catch-Block, dann wird es auch ausgeführt.
                            Stimmt... aber der PRINT im Catch wird auch nicht ausgeführt.

                            Kommentar


                            • #15
                              Zitat von hellbringer Beitrag anzeigen
                              akretschmer hat schon Recht. Wenn so viel Zeit und Energie in Workarounds gesteckt werden muss, sollte das ein deutliches Warnsignal sein. Denn im Endeffekt ist deine Arbeitszeit auch nicht gratis. Wer auch immer dir das aufgetragen hat so zu machen, der sollte mal abwägen was auf Dauer günstiger kommt. So ein Monster zu entwickeln und fortlaufend zu warten oder auf eine andere Plattform zu wechseln. Sei es SQL Server (ohne Express) oder ein freies DBMS (was im Endeffekt auch nicht gratis ist, weil irgendwer muss das ja aufsetzen und betreuen).
                              Gebe euch ja Recht, sollte auf PostgreSQL umsteigen, auch für Linux, aber das geht aktuell nicht. Gibt es für mein Problem keine Lösung auf diesem Weg?

                              Kommentar

                              Lädt...
                              X