none
Bulk insert in mehrere Tabelle RRS feed

  • Frage

  • Hallo,

     

    ich möchte gerne Daten aus einem .NET Programm in eine Datenbank einfügen. TVPs scheinen dafür geeignet zu sein. Leider habe ich nicht ganz so viel Erfahrungen mit SQL und es trat folgendes Problem auf:

     

    Ich habe eine StoredProcedure geschrieben, die sich darum kümmert die Daten aus der flachen (TVP-)Tabelle in die 6 Tabellen der Datenbank zu verteilen. Das Problem dabei ist, dass ich nicht weiß wie ich Daten korrekt in die Verknüpfungstabelle einfügen soll, nachdem ich Daten in die einzelnen Tabellen eingefügt habe, da ich keine Verbindung zwischen den Schlüsseln hinbekomme.

     

    Ganz konkret:

    -- Tabelle Logs (Verknüpfungtabelle) --

    z.B.:

    • LogID
    • CategoryID

    -- Tabelle Categories --

    • ID
    • Name

    -- Tabelle Messages --

    • ID
    • LogID
    • Message

    -- Table Type TVPLogEntry --

    • ID
    • CategoryName
    • Message

    Als erstes füge ich alle nicht vorhandenen Kategorien in Categories ein. (Hier die IDs zu bekommen ist nicht das Problem, da Name eindeutig sein muss)

    INSERT Categories
    	SELECT V.[CategoryName] FROM @Values AS V
    	LEFT JOIN Categories ON Categories.Name = V.[CategoryName]
    	WHERE Categories.Name IS NULL
    	GROUP BY V.[CategoryName];
    


    (P.S.: Ich nehme gerne auch Verbesserungsvorschläge zu den Abfragen oder Tabellen an, da ich, wie gesagt, kaum Ahnung von SQL usw. habe)

    Als nächstes möchte ich die Einträge in die Logs-Tabelle machen.

    INSERT [Logs]
    	SELECT Severities.ID, V.[Timestamp], V.Priority, V.EventID, Categories.ID
    	FROM @Values AS V
    	LEFT JOIN Severities ON Severities.Name = V.Severity
    	LEFT JOIN Categories ON Categories.Name = V.CategoryName
    

    Danach müssten dann die Einträge in die Messages Tabelle gemacht werden, aber ich weiß nicht, wie ich an die Verbindung @Values.ID zu [Logs].LogID komme.

    Vielleicht mach ich auch vom Ansatz her etwas falsch. Ich bin für jede Hilfe dankbar...

    Donnerstag, 16. Juni 2011 09:01

Antworten

  • Sorry, wir reden aneinander vorbei:

    Leider habe ich nicht ganz so viel Erfahrungen mit SQL und es trat folgendes Problem auf:
    Vielleicht mach ich auch vom Ansatz her etwas falsch.

    Das ist was ich denke, daher meine vorige Frage:
    Sprechen wir also davon, das du ein neues Datenmodell brauchst oder nur das alte umbauen willst?

    Das Datenbankmodell habe ich meiner Meinung nach fertig (Siehe oben). Meine Frage ist eigentlich nur, wie ich jetzt die Daten da rein bekomme.

    Nein, das glaube ich nicht. Es ist weder für das eine noch das andere geeignet. Das siehst du alleine daran, das du die Daten nicht rein bekommst.

    Außerdem fehlt noch die Definition deiner flachen Tabelle, TVPLogEntry oder @Values, welche du schon erwähnt hast.

    Wenn es sich um nur ein Protokoll handelt, brauchst du die Messags-Tabelle nicht und die SystemInformations nicht in dieser Form.

    Imho sollte es so aussehen:

    USE tempdb ;
    GO
    
    CREATE TABLE AppDomains
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          AppDomainName NVARCHAR(255) NOT NULL
                                      UNIQUE
        ) ;
    
    CREATE TABLE Categories
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          CategoryName NVARCHAR(255) NOT NULL
                                     UNIQUE
        ) ;
    
    CREATE TABLE [Events]
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          EventName NVARCHAR(255) NOT NULL
                                  UNIQUE ,
          EventDescription NVARCHAR(MAX) NULL
        ) ;
    
    CREATE TABLE Machines
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          MachineName NVARCHAR(255) NOT NULL
                                    UNIQUE
        ) ;
    
    CREATE TABLE Processes
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          ProcessName NVARCHAR(255) NOT NULL
                                    UNIQUE
        ) ;
    
    CREATE TABLE dbo.Severities
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          SeverityName NVARCHAR(255) NOT NULL
                                     UNIQUE
        ) ;
    
    CREATE TABLE dbo.Logs
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          idAppDomain INT NOT NULL ,
          idCategory INT NOT NULL ,
          idEvent INT NULL ,
          idMachine INT NULL ,
          idProcess INT NULL ,
          idSeverity INT NOT NULL ,
          MessageShort NVARCHAR(255) NOT NULL ,
          MessageLong NVARCHAR(MAX) NOT NULL ,
          Priority INT NOT NULL ,
          [TimeStamp] DATETIME NOT NULL ,
          CONSTRAINT FK_Logs_AppDomain FOREIGN KEY ( idAppDomain ) REFERENCES AppDomains ( ID ) ,
          CONSTRAINT FK_Logs_Category FOREIGN KEY ( idCategory ) REFERENCES Categories ( ID ) ,
          CONSTRAINT FK_Logs_Event FOREIGN KEY ( idEvent ) REFERENCES [Events] ( ID ) ,
          CONSTRAINT FK_Logs_Machine FOREIGN KEY ( idMachine ) REFERENCES Machines ( ID ) ,
          CONSTRAINT FK_Logs_Process FOREIGN KEY ( idProcess ) REFERENCES Processes ( ID ) ,
          CONSTRAINT FK_Logs_Severity FOREIGN KEY ( idSeverity ) REFERENCES Severities ( ID )
        ) ;
    
    CREATE INDEX IX_Logs_MessageShort ON Logs (MessageShort) ;
    
    -- oder 
    
    CREATE TABLE [Messages]
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          MessageShort NVARCHAR(255) NOT NULL ,
          MessageLong NVARCHAR(MAX) NOT NULL
        ) ;
    
    CREATE INDEX IX_Messages_MessageShort ON [Messages] (MessageShort) ;
    
    CREATE TABLE dbo.Logs2
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          idAppDomain INT NOT NULL ,
          idCategory INT NOT NULL ,
          idEvent INT NULL ,
          idMachine INT NULL ,
          idMessage INT NULL ,
          idProcess INT NULL ,
          idSeverity INT NOT NULL ,
          Priority INT NOT NULL ,
          [TimeStamp] DATETIME NOT NULL ,
          CONSTRAINT FK_Logs2_AppDomain FOREIGN KEY ( idAppDomain ) REFERENCES AppDomains ( ID ) ,
          CONSTRAINT FK_Logs2_Category FOREIGN KEY ( idCategory ) REFERENCES Categories ( ID ) ,
          CONSTRAINT FK_Logs2_Event FOREIGN KEY ( idEvent ) REFERENCES [Events] ( ID ) ,
          CONSTRAINT FK_Logs2_Machine FOREIGN KEY ( idMachine ) REFERENCES Machines ( ID ) ,
          CONSTRAINT FK_Logs2_Messsage FOREIGN KEY ( idMessage ) REFERENCES [Messages] ( ID ) ,
          CONSTRAINT FK_Logs2_Process FOREIGN KEY ( idProcess ) REFERENCES Processes ( ID ) ,
          CONSTRAINT FK_Logs2_Severity FOREIGN KEY ( idSeverity ) REFERENCES Severities ( ID )
        ) ;
    GO

    Für dein eigentliches Einfügeproblem fehlt mir wie gesagt die Information über deine flachen Tabelle, TVPLogEntry oder @Values. Aber im Grunde sieht es so aus:

    DECLARE @Flat TABLE ( Col1 INT, Col2 INT ) ;
    
    INSERT  INTO @Flat
    VALUES  ( 0, 0 ),
            ( 1, 1 ),
            ( 2, 2 ),
            ( 3, 1 ) ;
    
    DECLARE @NormalizedCol1 TABLE
        (
          ID INT IDENTITY(1, 1) ,
          Col1 INT
        ) ;
    
    DECLARE @NormalizedCol2 TABLE
        (
          ID INT IDENTITY(1, 1) ,
          Col2 INT
        ) ;
    
    DECLARE @NormlizedFlat TABLE ( idCol1 INT, idCol2 INT ) ;
    
    INSERT  INTO @NormalizedCol1
            SELECT DISTINCT
                    Col1
            FROM    @Flat ;
    
    INSERT  INTO @NormalizedCol2
            SELECT DISTINCT
                    Col2
            FROM    @Flat ;
    
    INSERT  INTO @NormlizedFlat
            SELECT  C1.ID ,
                    C2.ID
            FROM    @Flat F
                    INNER JOIN @NormalizedCol1 C1 ON F.Col1 = C1.Col1
                    INNER JOIN @NormalizedCol2 C2 ON F.Col2 = C2.Col2 ;
    
    SELECT  C1.Col1 ,
            C2.Col2
    FROM    @NormlizedFlat F
            INNER JOIN @NormalizedCol1 C1 ON F.idCol1 = C1.ID
            INNER JOIN @NormalizedCol2 C2 ON F.idCol2 = C2.ID ;

    Microsoft MVP Office Access
    https://mvp.support.microsoft.com/profile/Stefan.Hoffmann
    • Als Antwort markiert SACO2 Donnerstag, 16. Juni 2011 14:12
    Donnerstag, 16. Juni 2011 11:56
    Moderator

Alle Antworten

  • Servus,

    Das haut nicht hin, du kannst nicht von CategoryName auf LogID für deine Messages-Tabelle schließen, da entweder dann

    a) CategoryID unique sein muss, damit ist die Categories-Tabelle im Grunde nutzlos.

    b) du schreibst deine Message in jedes Log mit der gleiche Kategorie, dann erschließt sich mir die Logik nicht.

    Laut deinem "ganz konkreten" Modell müsste es so aussehen:

    DECLARE @Category TABLE
        (
          ID INT IDENTITY(1, 1) ,
          Name NVARCHAR(255)
        ) ;
    
    DECLARE @Log TABLE
        (
          LogID INT ,
          CategoryID INT
        ) ;
    
    DECLARE @LogEntry TABLE
        (
          CategoryName NVARCHAR(255) ,
          Message NVARCHAR(255)
        ) ;
    
    DECLARE @Message TABLE
        (
          LogID INT ,
          Message NVARCHAR(255)
        ) ;
    
    INSERT  INTO @Category
    VALUES  ( 'CatA' ),
            ( 'CatB' ) ;
    
    INSERT  INTO @Log
    VALUES  ( 1, 1 ),
            ( 2, 2 ),
            ( 3, 1 ) ;
    
    INSERT  INTO @LogEntry
    VALUES  ( 'CatA', 'MessageA' ),
            ( 'CatB', 'MessageB' ) ;
    
    INSERT  INTO @Message
            SELECT  L.LogID ,
                    LE.Message
            FROM    @LogEntry LE
                    INNER JOIN @Category C ON LE.CategoryName = C.Name
                    INNER JOIN @Log L ON C.ID = L.CategoryID ;
    
    SELECT  *
    FROM    @Message ;

    Was ich nicht verstehe ist dein INSERT auf die Logs-Tabelle. Da kommen Dinge vor, welche du oben nicht beschrieben hast. Auch kommen da Werte aus einer Tabelle @Values. Was ist dass für eine?


    Microsoft MVP Office Access
    https://mvp.support.microsoft.com/profile/Stefan.Hoffmann
    Donnerstag, 16. Juni 2011 09:19
    Moderator
  • Wie gesagt habe ich 6 Tabellen. Die aufgelisteten sind nur Beispiele, weil ich hier nicht alles vollposten wollte. Der Grund wieso ich die Categories-Tabelle verwende ist ein Performance-technischer. Die Datenbank kann schon mal ganz schnell > 5 Millionen Einträge beinhalten. Es soll eine Oberfläche geben mit der man die Einträge Anzeigen und filtern kann. Das Problem bei der alten Version (eine große flache Tabelle) war, dass z.B. das Befüllen der auswählbaren Kategorien ca eine halbe Minute gedauert hat. Das ist definitiv zu lang. Das filtern auf den Key der Category ist, so denke ich, auch schneller, als, wenn ich auf den Namen filtern muss.

    Hier nun alle vorhandenen Tabellen und Beziehungen:

    ---- System Informations ----

     

    CREATE TABLE [dbo].[SystemInformations](
    	[ID] [int] IDENTITY(1,1) NOT NULL,
    	[ProcessID] [int] NOT NULL,
    	[MachineName] [nvarchar](32) NOT NULL,
    	[AppDomainName] [nvarchar](512) NOT NULL,
    	[ProcessName] [nvarchar](512) NOT NULL,
    	[LogID] [int] NOT NULL,
     CONSTRAINT [PK_SystemInformations] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    
    ALTER TABLE [dbo].[SystemInformations] WITH CHECK ADD CONSTRAINT [FK_SystemInformations_Logs] FOREIGN KEY([LogID])
    REFERENCES [dbo].[Logs] ([LogID])
    GO
    
    ALTER TABLE [dbo].[SystemInformations] CHECK CONSTRAINT [FK_SystemInformations_Logs]
    GO
    

     

    ---- Severites (beinhaltet nur feste Werte, diese werden sich vorraussichtlich nicht ändern) ----

    CREATE TABLE [dbo].[Severities](
    	[ID] [tinyint] IDENTITY(1,1) NOT NULL,
    	[Name] [nchar](25) NOT NULL,
     CONSTRAINT [PK_Severities] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    

     

     

    ---- Messages ----

    CREATE TABLE [dbo].[Messages](
    	[ID] [int] IDENTITY(1,1) NOT NULL,
    	[LogID] [int] NOT NULL,
    	[Message] [ntext] NOT NULL,
    	[Title] [nvarchar](256) NOT NULL
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
    
    ALTER TABLE [dbo].[Messages] WITH CHECK ADD CONSTRAINT [FK_Messages_Logs] FOREIGN KEY([LogID])
    REFERENCES [dbo].[Logs] ([LogID])
    GO
    
    ALTER TABLE [dbo].[Messages] CHECK CONSTRAINT [FK_Messages_Logs]
    GO
    

     


    ---- Logs ----

     

    CREATE TABLE [dbo].[Logs](
    	[LogID] [int] IDENTITY(1,1) NOT NULL,
    	[SeverityID] [tinyint] NOT NULL,
    	[Timestamp] [datetime] NOT NULL,
    	[Priority] [tinyint] NOT NULL,
    	[EventID] [int] NULL,
    	[CategoryID] [int] NOT NULL,
     CONSTRAINT [PK_Logs] PRIMARY KEY CLUSTERED 
    (
    	[LogID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    
    ALTER TABLE [dbo].[Logs] WITH CHECK ADD CONSTRAINT [FK_Logs_Categories] FOREIGN KEY([CategoryID])
    REFERENCES [dbo].[Categories] ([ID])
    GO
    
    ALTER TABLE [dbo].[Logs] CHECK CONSTRAINT [FK_Logs_Categories]
    GO
    
    ALTER TABLE [dbo].[Logs] WITH CHECK ADD CONSTRAINT [FK_Logs_Events] FOREIGN KEY([EventID])
    REFERENCES [dbo].[Events] ([ID])
    GO
    
    ALTER TABLE [dbo].[Logs] CHECK CONSTRAINT [FK_Logs_Events]
    GO
    
    ALTER TABLE [dbo].[Logs] WITH CHECK ADD CONSTRAINT [FK_Logs_Severities] FOREIGN KEY([SeverityID])
    REFERENCES [dbo].[Severities] ([ID])
    GO
    
    ALTER TABLE [dbo].[Logs] CHECK CONSTRAINT [FK_Logs_Severities]
    GO
    

     


    ---- Events (es wird sichergestellt, dass die Events vor dem Eintragen der Logs schon existieren) ----

     

    CREATE TABLE [dbo].[Events](
    	[ID] [int] IDENTITY(1,1) NOT NULL,
    	[Description] [nvarchar](1024) NOT NULL,
    	[CatalogID] [uniqueidentifier] NULL,
     CONSTRAINT [PK_Events] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    

     


    ---- Categories ----

     

    CREATE TABLE [dbo].[Categories](
    	[ID] [int] IDENTITY(1,1) NOT NULL,
    	[Name] [nvarchar](64) NOT NULL,
     CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    

    Ich habe die Tabelle mit meinem sehr bescheidenen Wissen und nach "Gefühl" erstellt. Es geht halt hauptsächlich darum, dass beim Abfragen der Daten auch noch eine halbwegs vernünftige Performance gegeben ist.

    P.S.:

     

    Die StoredProcedure wie ich sie eigentlich gedacht hatte

    ALTER PROCEDURE [dbo].[TVP_AddLogEntries]
    	-- Add the parameters for the stored procedure here
    	@Values TVPLogEntry READONLY
    AS
    BEGIN
    	-- SET NOCOUNT ON added to prevent extra result sets from
    	-- interfering with SELECT statements.
    	SET NOCOUNT ON;
    	
    	-- add all categories which are not already in Categories
      INSERT Categories
    			SELECT V.[CategoryName] FROM @Values AS V
    			LEFT JOIN Categories ON Categories.Name = V.[CategoryName]
    			WHERE Categories.Name IS NULL
    			GROUP BY V.[CategoryName];
    	
    	-- suggest all severities exist
    	-- events where created manually before
    	
    	DECLARE @LogIDs TABLE
    	(
    		LogID int,
    		ID int
    	);
    	
    	-- add all logs
    	INSERT [Logs]
    	OUTPUT inserted.[LogID] INTO @LogIDs (LogID)
    			SELECT Severities.ID, V.[Timestamp], V.Priority, V.EventID, Categories.ID
    			FROM @Values AS V
    			LEFT JOIN Severities ON Severities.Name = V.Severity
    			LEFT JOIN Categories ON Categories.Name = V.CategoryName
    			
    	INSERT @LogIDs (ID)
    		SELECT V.ID FROM @Values AS V
    				
    	-- add messages and titles
    	INSERT [Messages]
    		SELECT L.[LogID], V.[Message], V.[Title] FROM @Values AS V
    		LEFT JOIN @LogIDs AS L ON L.ID = V.ID
    END
    


    • Bearbeitet SACO2 Donnerstag, 16. Juni 2011 09:58 StoredProcedure hinzugefügt
    Donnerstag, 16. Juni 2011 09:54
  • Sprechen wir also davon, das du ein neues Datenmodell brauchst oder nur das alte umbauen willst?

    Für ersteres musst du ganz normal dein Datenmodell entwicklen, bzw. ausgehend von der jetzigen Tabelle normalisieren.

    Für zweiteres sollte es ausreichend sein, deine flache Tabelle zu behalten und sie lediglich mittles DKNF zu zerlegen.


    Microsoft MVP Office Access
    https://mvp.support.microsoft.com/profile/Stefan.Hoffmann
    Donnerstag, 16. Juni 2011 10:30
    Moderator
  • Das Datenbankmodell habe ich meiner Meinung nach fertig (Siehe oben). Meine Frage ist eigentlich nur, wie ich jetzt die Daten da rein bekomme.
    Donnerstag, 16. Juni 2011 10:49
  • Sorry, wir reden aneinander vorbei:

    Leider habe ich nicht ganz so viel Erfahrungen mit SQL und es trat folgendes Problem auf:
    Vielleicht mach ich auch vom Ansatz her etwas falsch.

    Das ist was ich denke, daher meine vorige Frage:
    Sprechen wir also davon, das du ein neues Datenmodell brauchst oder nur das alte umbauen willst?

    Das Datenbankmodell habe ich meiner Meinung nach fertig (Siehe oben). Meine Frage ist eigentlich nur, wie ich jetzt die Daten da rein bekomme.

    Nein, das glaube ich nicht. Es ist weder für das eine noch das andere geeignet. Das siehst du alleine daran, das du die Daten nicht rein bekommst.

    Außerdem fehlt noch die Definition deiner flachen Tabelle, TVPLogEntry oder @Values, welche du schon erwähnt hast.

    Wenn es sich um nur ein Protokoll handelt, brauchst du die Messags-Tabelle nicht und die SystemInformations nicht in dieser Form.

    Imho sollte es so aussehen:

    USE tempdb ;
    GO
    
    CREATE TABLE AppDomains
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          AppDomainName NVARCHAR(255) NOT NULL
                                      UNIQUE
        ) ;
    
    CREATE TABLE Categories
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          CategoryName NVARCHAR(255) NOT NULL
                                     UNIQUE
        ) ;
    
    CREATE TABLE [Events]
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          EventName NVARCHAR(255) NOT NULL
                                  UNIQUE ,
          EventDescription NVARCHAR(MAX) NULL
        ) ;
    
    CREATE TABLE Machines
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          MachineName NVARCHAR(255) NOT NULL
                                    UNIQUE
        ) ;
    
    CREATE TABLE Processes
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          ProcessName NVARCHAR(255) NOT NULL
                                    UNIQUE
        ) ;
    
    CREATE TABLE dbo.Severities
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          SeverityName NVARCHAR(255) NOT NULL
                                     UNIQUE
        ) ;
    
    CREATE TABLE dbo.Logs
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          idAppDomain INT NOT NULL ,
          idCategory INT NOT NULL ,
          idEvent INT NULL ,
          idMachine INT NULL ,
          idProcess INT NULL ,
          idSeverity INT NOT NULL ,
          MessageShort NVARCHAR(255) NOT NULL ,
          MessageLong NVARCHAR(MAX) NOT NULL ,
          Priority INT NOT NULL ,
          [TimeStamp] DATETIME NOT NULL ,
          CONSTRAINT FK_Logs_AppDomain FOREIGN KEY ( idAppDomain ) REFERENCES AppDomains ( ID ) ,
          CONSTRAINT FK_Logs_Category FOREIGN KEY ( idCategory ) REFERENCES Categories ( ID ) ,
          CONSTRAINT FK_Logs_Event FOREIGN KEY ( idEvent ) REFERENCES [Events] ( ID ) ,
          CONSTRAINT FK_Logs_Machine FOREIGN KEY ( idMachine ) REFERENCES Machines ( ID ) ,
          CONSTRAINT FK_Logs_Process FOREIGN KEY ( idProcess ) REFERENCES Processes ( ID ) ,
          CONSTRAINT FK_Logs_Severity FOREIGN KEY ( idSeverity ) REFERENCES Severities ( ID )
        ) ;
    
    CREATE INDEX IX_Logs_MessageShort ON Logs (MessageShort) ;
    
    -- oder 
    
    CREATE TABLE [Messages]
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          MessageShort NVARCHAR(255) NOT NULL ,
          MessageLong NVARCHAR(MAX) NOT NULL
        ) ;
    
    CREATE INDEX IX_Messages_MessageShort ON [Messages] (MessageShort) ;
    
    CREATE TABLE dbo.Logs2
        (
          ID INT IDENTITY(1, 1)
                 NOT NULL
                 PRIMARY KEY ,
          idAppDomain INT NOT NULL ,
          idCategory INT NOT NULL ,
          idEvent INT NULL ,
          idMachine INT NULL ,
          idMessage INT NULL ,
          idProcess INT NULL ,
          idSeverity INT NOT NULL ,
          Priority INT NOT NULL ,
          [TimeStamp] DATETIME NOT NULL ,
          CONSTRAINT FK_Logs2_AppDomain FOREIGN KEY ( idAppDomain ) REFERENCES AppDomains ( ID ) ,
          CONSTRAINT FK_Logs2_Category FOREIGN KEY ( idCategory ) REFERENCES Categories ( ID ) ,
          CONSTRAINT FK_Logs2_Event FOREIGN KEY ( idEvent ) REFERENCES [Events] ( ID ) ,
          CONSTRAINT FK_Logs2_Machine FOREIGN KEY ( idMachine ) REFERENCES Machines ( ID ) ,
          CONSTRAINT FK_Logs2_Messsage FOREIGN KEY ( idMessage ) REFERENCES [Messages] ( ID ) ,
          CONSTRAINT FK_Logs2_Process FOREIGN KEY ( idProcess ) REFERENCES Processes ( ID ) ,
          CONSTRAINT FK_Logs2_Severity FOREIGN KEY ( idSeverity ) REFERENCES Severities ( ID )
        ) ;
    GO

    Für dein eigentliches Einfügeproblem fehlt mir wie gesagt die Information über deine flachen Tabelle, TVPLogEntry oder @Values. Aber im Grunde sieht es so aus:

    DECLARE @Flat TABLE ( Col1 INT, Col2 INT ) ;
    
    INSERT  INTO @Flat
    VALUES  ( 0, 0 ),
            ( 1, 1 ),
            ( 2, 2 ),
            ( 3, 1 ) ;
    
    DECLARE @NormalizedCol1 TABLE
        (
          ID INT IDENTITY(1, 1) ,
          Col1 INT
        ) ;
    
    DECLARE @NormalizedCol2 TABLE
        (
          ID INT IDENTITY(1, 1) ,
          Col2 INT
        ) ;
    
    DECLARE @NormlizedFlat TABLE ( idCol1 INT, idCol2 INT ) ;
    
    INSERT  INTO @NormalizedCol1
            SELECT DISTINCT
                    Col1
            FROM    @Flat ;
    
    INSERT  INTO @NormalizedCol2
            SELECT DISTINCT
                    Col2
            FROM    @Flat ;
    
    INSERT  INTO @NormlizedFlat
            SELECT  C1.ID ,
                    C2.ID
            FROM    @Flat F
                    INNER JOIN @NormalizedCol1 C1 ON F.Col1 = C1.Col1
                    INNER JOIN @NormalizedCol2 C2 ON F.Col2 = C2.Col2 ;
    
    SELECT  C1.Col1 ,
            C2.Col2
    FROM    @NormlizedFlat F
            INNER JOIN @NormalizedCol1 C1 ON F.idCol1 = C1.ID
            INNER JOIN @NormalizedCol2 C2 ON F.idCol2 = C2.ID ;

    Microsoft MVP Office Access
    https://mvp.support.microsoft.com/profile/Stefan.Hoffmann
    • Als Antwort markiert SACO2 Donnerstag, 16. Juni 2011 14:12
    Donnerstag, 16. Juni 2011 11:56
    Moderator
  • Der Tabellen-Typ sieht wie folgt aus:

     

    CREATE TYPE [dbo].[TVPLogEntry] AS TABLE(
    	[ID] [int] IDENTITY(1,1) NOT NULL,
    	[Severity] [nchar](25) NOT NULL,
    	[Timestamp] [datetime] NOT NULL,
    	[Priority] [tinyint] NOT NULL,
    	[EventID] [int] NULL,
    	[CategoryName] [nvarchar](64) NOT NULL,
    	[Message] [ntext] NOT NULL,
    	[Title] [nvarchar](256) NOT NULL,
    	[ProcessID] [int] NOT NULL,
    	[MachineName] [nvarchar](32) NOT NULL,
    	[AppDomainName] [nvarchar](512) NOT NULL,
    	[ProcessName] [nvarchar](512) NOT NULL
    )
    
    


    Die ganze Datenbank an sich ist eigentlich eine Abwandlung der Microsoft Enterprise Logging Datenbank. Ich habe mit der MS-DB angefangen, mir überlegt, was für meine Anforderungen am meisten Sinn macht und dann das daraus gemacht, was ich bereits gepostet habe.

    "Sprechen wir also davon, das du ein neues Datenmodell brauchst oder nur das alte umbauen willst?"

    Also Ziel des Ganzen: eine neue Datenbank mit der man Daten in einer akzeptablen Zeit hinein (über .NET) und auch wieder heraus bekommt. D.h. das von mir gepostet ist eigentlich das "Neue". Ich brauche eines, was beim auslesen der Daten nicht so langsam ist, wie das von dem MS Enterprise Logging Application.

     

     



    • Bearbeitet SACO2 Donnerstag, 16. Juni 2011 12:42
    Donnerstag, 16. Juni 2011 12:19
  • > Die ganze Datenbank an sich ist eigentlich eine Abwandlung der Microsoft Enterprise Logging Datenbank.
    Okay, vergleiche die Richtung der Foreign Keys bei meinem Beispiel (Logs2).

    Bei dir ist diese falsch für die Messages-Tabelle. Was ursprünglich TVPLogEntry ist, wurde bei dir zu Logs. Die Messages-Tabelle ist eine Lookup-Tabelle, kann auch DKNF sein, muss aber nicht.


    Microsoft MVP Office Access
    https://mvp.support.microsoft.com/profile/Stefan.Hoffmann
    Donnerstag, 16. Juni 2011 12:39
    Moderator
  • Erstmal vielen Dank, dass du die so viel Mühe machst mir zu helfen! :)

    Ok, ich werde mal mein Modell mit deinem abgleichen. Kannst du mir eventuell noch Erklären, wieso du welche Entscheidung bei dem Modell getroffen hast, damit ich beim nächsten mal weiß, wieso das ganze so gemacht wurde?

    Wieso hast du z.B. eine MessageShort-Spalte in der Messages-Tabelle? Aus welchem Grund sollte ich welche der beiden Lösungen (Logs oder Logs2) bevorzugen? Macht es überhaupt Sinn alle Daten mit einer einzigen StoredProcedure einzufügen? Ich bin einfach davon ausgegangen, dass es das schnellste ist...

    Zu deiner Antwort zu dem "Einfügeproblem": Du gehst davon aus, dass z.B. der Name bei Severites, Categories usw. eindeutig ist, richtig? Das Problem ist, dass das bei Messages aber nicht der Fall sein muss. Es ist nur sehr wahrscheinlich... Das heißt, der einzige Weg ist die Lösung "Logs" zu nehmen, oder?

    Donnerstag, 16. Juni 2011 13:20
  • Kannst du mir eventuell noch Erklären, wieso du welche Entscheidung bei dem Modell getroffen hast, damit ich beim nächsten mal weiß, wieso das ganze so gemacht wurde?

    Nun ja, im Grunde habe ich - anstatt mir Gedanken über die korrekten Entitäten - einfach die Domain Key Normal Form zu Nutze gemacht. Diese ähnelt in eingen Fällen einer normalen Normalisierung, aber eben nicht ganz. Die Idee dahinter ist, das jeder möglich Wert per Relation in eine Domänenwerttabelle abgebildet wird.
     > Wieso hast du z.B. eine MessageShort-Spalte in der Messages-Tabelle?
    Da man NTEXT - was veraltet ist - und [N]VARCHAR(MAX) nicht indizieren kann. Für eine normale Suche reichen auch die ersten N-Zeichen. Ich nehme da halt immer 255 Zeichen, da ich diese auch z.B. in MS Access ohne Probleme weiterverarbeiten kann.

    Aus welchem Grund sollte ich welche der beiden Lösungen (Logs oder Logs2) bevorzugen?

    Das kommt auf die Meldungen an. Logs2 mit der extra Messages-Tabelle kann Platz sparen. Aber im Grunde würde mir schon Logs reichen.

    Macht es überhaupt Sinn alle Daten mit einer einzigen StoredProcedure einzufügen?

    Nicht unbedingt. Allerdings kann man mittels Stored Procedure die Fiktion der flachen Tabelle aufrecht erhalten.

    Das Problem ist, dass das bei Messages aber nicht der Fall sein muss. Es ist nur sehr wahrscheinlich...

    Na ja, das kommt auf die notwendige Performance beim Einfügen an und auf die Ähnlichkeit der Messages.

    Das heißt, der einzige Weg ist die Lösung "Logs" zu nehmen, oder?

    Nein, Logs2 macht das Einfügen halt ein bischen komplizierter.

    Ich würde erstmal mit Logs arbeiten. Und für das Einfügen im alten Stil einfach eine Sicht mit einem INSTEAD OF INSERT-Trigger nutzen, der dann alles sauber aufteilt.

    btw, ich habe die Spalte Title vergessen. Wird wie AppDomainName behandelt.


    Microsoft MVP Office Access
    https://mvp.support.microsoft.com/profile/Stefan.Hoffmann
    Donnerstag, 16. Juni 2011 13:38
    Moderator