Эффективно вставить в несколько уровней родительских дочерних таблиц - PullRequest
0 голосов
/ 26 марта 2012

У меня есть следующий образец данных для вставки в таблицы (от родителя к потомку, все отношения многие к одному): программа, модуль, класс и член. Каждая таблица использует автоматическую идентификацию в качестве первичного ключа. Это для SQL Server 2008.

Program Module  Class   Member
Program1    M1  C1  Func1
Program1    M1  C1  Func2
Program1    M1  C2  Func3
Program1    M2  C3  Func4
Program1    M2  C4  Func5
Program2    M3  C5  Func6
Program2    M3  C5  Func7
Program2    M3  C6  Func8
Program2    M4  C7  Func9
Program2    M4  C7  Func10
Program2    M4  C8  Func11

Нужно ли разбивать это на несколько хранимых процедур (сначала вставьте таблицу программ, затем модуль и т. Д.), Или есть более простые и эффективные способы?

1 Ответ

1 голос
/ 26 марта 2012

Таблица определений:

CREATE TABLE Programs (
    ID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
    Name varchar(50)
);
CREATE TABLE Modules (
    ID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
    ProgramID INT,
    Name varchar(50)
);
CREATE TABLE Classes (
    ID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
    ModuleID INT,
    Name varchar(50)
);
CREATE TABLE Functions (
    ID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
    ClassID INT,
    Name varchar(50)
);

Определение типа:

CREATE TYPE ProgramTableType AS TABLE
(
    Program varchar(50),
    Module varchar(50),
    Class varchar(50),
    Func varchar(50)
);

Определение процедуры:

CREATE PROCEDURE dbo.sp_insert_definitions
    @NewRows ProgramTableType READONLY
AS
    SET NOCOUNT ON;

    DECLARE cur_new CURSOR FOR
        SELECT Program, Module, Class, Func
        FROM @NewRows
        ORDER BY Program, Module, Class, Func;

    DECLARE
        -- Cache variables
        @ProgramID int,
        @ProgramName varchar(50) = NULL,
        @ModuleID int,
        @ModuleName varchar(50) = NULL,
        @ClassID int,
        @ClassName varchar(50) = NULL,
        -- Iteration variables
        @Program varchar(50),
        @Module varchar(50),
        @Class varchar(50),
        @Func varchar(50);

    OPEN cur_new;

    FETCH NEXT FROM cur_new
    INTO @Program, @Module, @Class, @Func;

    -- Loop trough the cursor
    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- Get or create program
        IF @ProgramName IS NULL OR @Program <> @ProgramName
        BEGIN
            IF NOT EXISTS (SELECT NULL FROM Programs WHERE Name = @Program)
                INSERT INTO Programs (Name)
                VALUES (@Program);

            SELECT
                @ProgramID = ID,
                @ProgramName = Name,
                @ModuleID = NULL,
                @ModuleName = NULL,
                @ClassID = NULL,
                @ClassName = NULL
            FROM Programs
            WHERE Name = @Program;
        END

        -- Get or create module
        IF @ModuleName IS NULL OR @Module <> @ModuleName
        BEGIN
            IF NOT EXISTS (SELECT NULL FROM Modules WHERE Name = @Module AND ProgramID = @ProgramID)
                INSERT INTO Modules (ProgramID, Name)
                VALUES (@ProgramID, @Module);

            SELECT
                @ModuleID = ID,
                @ModuleName = Name,
                @ClassID = NULL,
                @ClassName = NULL
            FROM Modules
            WHERE Name = @Module
            AND ProgramID = @ProgramID;
        END;

        -- Get or create class
        IF @ClassName IS NULL OR @Class <> @ClassName
        BEGIN
            IF NOT EXISTS (SELECT NULL FROM Classes WHERE Name = @Class AND ModuleID = @ModuleID)
                INSERT INTO Classes (ModuleID, Name)
                VALUES (@ModuleID, @Class);

            SELECT
                @ClassID = ID,
                @ClassName = Name
            FROM Classes
            WHERE Name = @Class
            AND ModuleID = @ModuleID;
        END;

        -- Create function if it doesn't exists
        IF NOT EXISTS (SELECT NULL FROM Functions WHERE Name = @Func AND ClassID = @ClassID)
            INSERT INTO Functions (ClassID, Name)
            VALUES (@ClassID, @Func);

        FETCH NEXT FROM cur_new
        INTO @Program, @Module, @Class, @Func;
    END

    CLOSE cur_new;
    DEALLOCATE cur_new;
GO

Пример:

BEGIN TRAN;
DECLARE @NewData AS ProgramTableType;
INSERT INTO @NewData (Program, Module, Class, Func)
VALUES ('Program1', 'M1', 'C1', 'Func1'),
('Program1', 'M1', 'C1', 'Func2'),
('Program1', 'M1', 'C2', 'Func3'),
('Program1', 'M2', 'C3', 'Func4'),
('Program1', 'M2', 'C4', 'Func5'),
('Program2', 'M3', 'C5', 'Func6'),
('Program2', 'M3', 'C5', 'Func7'),
('Program2', 'M3', 'C6', 'Func8'),
('Program2', 'M4', 'C7', 'Func9'),
('Program2', 'M4', 'C7', 'Func10'),
('Program2', 'M4', 'C8', 'Func11');
EXEC sp_insert_definitions @NewData;
COMMIT TRAN;

Вернуть исходный ввод:

SELECT p.Name AS Program, m.Name AS Module, c.Name AS Class, f.Name AS [Function]
FROM Functions f
INNER JOIN Classes c ON c.ID = f.ClassID
INNER JOIN Modules m ON m.ID = c.ModuleID
INNER JOIN Programs p ON p.ID = m.ProgramID;

См. Также

Редактировать: Я переписал весь ответ, так как он был неверным.

...