0

複数のコンピューターから実行されるアプリがあり、その内部データベースと SQL サーバーの 1 つのデータベースの間で何かを同期する必要があります。

いくつかの一時テーブルを使用して内部データベースのデータを挿入し、SP を呼び出してデータを同期します。データを行ごとに処理し、SQL データベースで更新するか、新しい行を挿入するか、削除された行を削除します。SQL Server 2000 を使用している顧客をサポートする必要があるため、 以外のソリューションが必要ですMERGE

問題は、SP が SSMS で非常にうまく機能することですが、アプリケーションから呼び出すと突然失敗します。C++ ネイティブ コードを使用し、SQL サーバーとの接続に ODBC と SQL Native Client を使用します。

これが私のデータベースとSPの定義です:

IF (NOT EXISTS(SELECT * FROM master.dbo.sysdatabases WHERE name='TestDB1'))
    CREATE DATABASE TestDB1;
GO
USE TestDB1;
GO
IF (NOT EXISTS( SELECT * FROM dbo.sysobjects WHERE name='Servers'))
BEGIN
    CREATE TABLE Servers(
        [ID]            uniqueidentifier NOT NULL PRIMARY KEY,
        [Name]          nvarchar(50)
        -- Other fields omitted
    );
END;
GO

IF (NOT EXISTS( SELECT * FROM dbo.sysobjects WHERE name='P'))
BEGIN
    CREATE TABLE [dbo].[P](
        [ID]            bigint              NOT NULL,
        [ServerID]      uniqueidentifier    NOT NULL
            CONSTRAINT KK_P_Servers FOREIGN KEY REFERENCES [Servers],
        [PName]         nvarchar(255)       NOT NULL,
        -- Other fields omitted

        CONSTRAINT PK_P PRIMARY KEY CLUSTERED ([ID], [ServerID])
    );
END;
GO
IF (NOT EXISTS( SELECT * FROM dbo.sysobjects WHERE name='C1'))
BEGIN
    CREATE TABLE [dbo].[C1](
        [ID]            bigint              NOT NULL,
        [ServerID]      uniqueidentifier    NOT NULL
            CONSTRAINT FK_C1_Servers FOREIGN KEY REFERENCES [Servers],
        [PID]           bigint              NOT NULL,
        [Type]          nvarchar(50)        NOT NULL
        -- Other fields omitted

        CONSTRAINT PK_C1 PRIMARY KEY CLUSTERED ([ID], [ServerID]),
        CONSTRAINT FK_C1_P
            FOREIGN KEY ([PID], [ServerID]) REFERENCES [P]
    );
END;
GO
IF (NOT EXISTS( SELECT * FROM dbo.sysobjects WHERE name='C2'))
BEGIN
    CREATE TABLE [dbo].[C2](
        [ID]            bigint              NOT NULL,
        [ServerID]      uniqueidentifier    NOT NULL
            CONSTRAINT FK_C2_Servers FOREIGN KEY REFERENCES [Servers],
        [PID]           bigint              NULL,
        [Name]          nvarchar(255)       NOT NULL
        -- Other fields omitted

        CONSTRAINT PK_C2 PRIMARY KEY CLUSTERED ([ID], [ServerID]),
        CONSTRAINT FK_C2_P FOREIGN KEY ([PID], [ServerID]) REFERENCES [P]
    );
END;
GO
IF (NOT EXISTS( SELECT * FROM dbo.sysobjects WHERE name='debug'))
BEGIN
    CREATE TABLE debug (
        [id] int identity(1, 1),
        [msg] nvarchar(255) NOT NULL,
        [cnt] int
    );
END;
GO

CREATE TABLE #C1(
    [ID]            bigint          NOT NULL PRIMARY KEY,
    [PID]           bigint          NOT NULL,
    [Type]          nvarchar(50)    NOT NULL
);
GO
CREATE TABLE #C2(
    [ID]            bigint          NOT NULL PRIMARY KEY,
    [PID]           bigint          NOT NULL,
    [Name]          nvarchar(255)   NOT NULL
);
GO


CREATE TABLE #P(
    [ID]            bigint          NOT NULL PRIMARY KEY,
    [PName]         nvarchar(255)   NOT NULL UNIQUE
    -- Table have other fields that is not important here
);
GO
CREATE TABLE #C1(
    [ID]            bigint          NOT NULL PRIMARY KEY,
    [PID]            bigint            NOT NULL,
    [Type]            nvarchar(50)    NOT NULL
);
GO
CREATE TABLE #C2(
    [ID]            bigint            NOT NULL PRIMARY KEY,
    [PID]            bigint            NOT NULL,
    [Name]            nvarchar(255)    NOT NULL
);
GO

CREATE PROCEDURE #RegisterServer
    @ServerId            uniqueidentifier,
    @ServerName            nvarchar(128)
AS
BEGIN
    BEGIN TRANSACTION
    UPDATE [Servers]
    SET [ServerName]=@ServerName
    WHERE [ID]=@ServerId;
    IF @@ROWCOUNT = 0
        INSERT INTO [Servers](
            [ID], [ServerName]
        ) VALUES (
            @ServerId, @ServerName
        );
    COMMIT TRANSACTION
END
GO
CREATE PROCEDURE #DropP
    @ServerID            uniqueidentifier,
    @PId                bigint
AS
BEGIN
    DELETE FROM C1
    WHERE PID=@PId AND ServerID=@ServerID;

    UPDATE C2 SET PID=NULL
    WHERE PID=@PId AND ServerID=@ServerID;

    DELETE FROM P
    WHERE ID=@PId AND ServerID=@ServerID;
END
GO
CREATE PROCEDURE #SynchronizeP
    @ServerID            uniqueidentifier
AS
BEGIN
    DECLARE @rc int, @e int;
    DECLARE @AllP TABLE (
        [num]            bigint            IDENTITY(1, 1) PRIMARY KEY,
        [ID]            bigint            NOT NULL,
        [PName]            nvarchar(255)    NOT NULL
    );
    DECLARE @AllC1 TABLE (
        [num]            bigint            IDENTITY(1, 1) PRIMARY KEY,
        [ID]            bigint            NOT NULL,
        [PID]            bigint            NOT NULL,
        [Type]            nvarchar(50)    NOT NULL
    );
    DECLARE @AllC2 TABLE (
        [num]            bigint            IDENTITY(1, 1) PRIMARY KEY,
        [ID]            bigint            NOT NULL,
        [PID]            bigint            NOT NULL,
        [Name]            nvarchar(255)    NOT NULL
    );

    DELETE FROM debug;

    INSERT INTO @AllP( [ID], [PName] )
    SELECT [ID], [PName]
    FROM #ServerP;
    SELECT @rc = @@ROWCOUNT;
    INSERT INTO debug VALUES( 'CREATE @AllP', @rc );

    INSERT INTO @AllC1( [ID], [PID], [Type] )
    SELECT [ID], [PID], [Type]
    FROM #ServerC1;
    SELECT @rc = @@ROWCOUNT;
    INSERT INTO debug VALUES( 'CREATE @AllC1', @rc );

    INSERT INTO @AllC2( [ID], [PID], [Name] )
    SELECT [ID], [PID], [Name]
    FROM #ServerC2;
    SELECT @rc = @@ROWCOUNT;
    INSERT INTO debug VALUES( 'CREATE @AllC2', @rc );

    DECLARE @PCount int
    SELECT @PCount = COUNT(*) FROM @AllP
    INSERT INTO debug VALUES( 'Read count of @AllP', @PCount );

    BEGIN TRANSACTION;

    DECLARE @PId bigint, @PName nvarchar(255);

    -- find dropped c1 and delete them
    DELETE FROM [C1]
    WHERE [ServerID]=@ServerID AND ([ID] NOT IN (SELECT a.[ID] FROM @AllC1 a));
    SELECT @rc = @@ROWCOUNT;
    INSERT INTO debug VALUES( 'Delete invalid c1', @rc );

    -- find dropped c2 and abandon them
    UPDATE [C2] SET [PID]=NULL
    WHERE [ServerID]=@ServerID AND ([ID] NOT IN (SELECT a.[ID] FROM @AllC2 a));
    SELECT @rc = @@ROWCOUNT;
    INSERT INTO debug VALUES( 'Abandon invalid c2', @rc );

    -- find dropped p and delete them
    DELETE FROM [P]
    WHERE [ServerID]=@ServerID AND ([ID] NOT IN (SELECT a.[ID] FROM @AllP a));
    SELECT @rc = @@ROWCOUNT;
    INSERT INTO debug VALUES( 'Delete invalid p', @rc );

    -- insert or update server p into database
    DECLARE @p int
    SET @p = 1
    WHILE @p <= @PCount
    BEGIN
        SELECT    @PId=[ID], @PName=[PName]
        FROM    @AllP
        WHERE    [num] = @p;
        SELECT @rc = @@ROWCOUNT;
        INSERT INTO debug VALUES( 'Select a p ' +
            CASE @PId WHEN NULL THEN 'NULL' ELSE CONVERT(nvarchar(5), @PId) END + '|' +
            CASE @PName WHEN NULL THEN 'NULL' ELSE @PName END, @rc );

        -- update or add this processor
        UPDATE dbo.[P]
        SET [PName]=@PName
        WHERE [ServerID]=@ServerID AND [ID]=@PId;
        SELECT @rc = @@ROWCOUNT;
        INSERT INTO debug VALUES( 'Update p', @rc );
        IF @rc = 0
        BEGIN
            INSERT INTO dbo.[P](
                [ID], [ServerID], [PName]
            ) VALUES(
                @PId, @ServerID, @PName
            );
            SELECT @rc = @@ROWCOUNT;
            INSERT INTO debug VALUES( 'Insert p', @rc );
        END;

        -- Now update list of c1 that belong to this processor
        DECLARE @TmpC1 TABLE (
            [num]                    bigint identity(1, 1) primary key,
            [ID]                    bigint            NOT NULL,
            [Type]                    nvarchar(50)    NOT NULL
        );
        INSERT INTO @TmpC1( [ID], [Type] )
        SELECT [ID], [Type]
        FROM @AllC1
        WHERE [PID] = @PId;
        SELECT @rc = @@ROWCOUNT;
        INSERT INTO debug VALUES( 'Create @TmpC1', @rc );

        DECLARE @Test nvarchar(4000);
        SELECT @Test = '';
        SELECT @Test = @Test + CONVERT(nvarchar(5), [ID]) + ', '
        FROM @TmpC1;
        SELECT @rc = @@ROWCOUNT;
        INSERT INTO debug VALUES( '@TmpC1: ' + @Test, @rc );

        DECLARE @C1Count int, @C1 int;
        SELECT @C1Count = COUNT(*) FROM @TmpC1;
        INSERT INTO debug VALUES( '@TmpC1.Count', @C1Count );

        SET @C1 = 1
        WHILE @C1 <= @C1Count
        BEGIN
            DECLARE @C1Id bigint, @C1Type nvarchar(50);

            SELECT @C1Id=[ID], @C1Type=[Type]
            FROM @TmpC1
            WHERE [num] = @C1;
            SELECT @rc = @@ROWCOUNT;
            INSERT INTO debug VALUES( 'Read c1: ' +
                CASE @C1Id WHEN NULL THEN 'NULL' ELSE CONVERT(nvarchar(5), @C1Id) END + '|' +
                CASE @C1Type WHEN NULL THEN 'NULL' ELSE @C1Type END, @rc );

            UPDATE C1
            SET [PID]=@PId, [Type]=@C1Type
            WHERE [ID]=@C1Id AND [ServerID]=@ServerID;
            SELECT @rc = @@ROWCOUNT;
            INSERT INTO debug VALUES( 'Update c1', @rc );
            IF @rc = 0
            BEGIN
                INSERT INTO C1(
                    [ID], [ServerID], [PID], [Type]
                ) VALUES (
                    @C1Id, @ServerID, @PId, @C1Type
                );
                SELECT @rc = @@ROWCOUNT;
                INSERT INTO debug VALUES( 'Insert c1', @rc );
            END;

            SET @C1 = @C1 + 1;
        END;

        DELETE FROM @TmpC1;

        -- And at last insert or update c2 of this processor
        DECLARE @TmpC2 TABLE (
            [num]                    bigint identity(1, 1) primary key,
            [ID]                    bigint            NOT NULL,
            [Name]                    nvarchar(255)    NOT NULL
        );
        INSERT INTO @TmpC2( [ID], [Name] )
        SELECT [ID], [Name]
        FROM @AllC2
        WHERE [PID] = @PId;
        SELECT @rc = @@ROWCOUNT;
        INSERT INTO debug VALUES( 'Create @TmpC2', @rc );

        SELECT @Test = '';
        SELECT @Test = @Test + CONVERT(nvarchar(5), [ID]) + ', '
        FROM @TmpC2;
        SELECT @rc = @@ROWCOUNT;
        INSERT INTO debug VALUES( '@TmpC2: ' + @Test, @rc );

        DECLARE @C2Count int, @C2 int;
        SELECT @C2Count = COUNT(*) FROM @TmpC2;
        INSERT INTO debug VALUES( '@TmpC2.Count', @C2Count );

        SET @C2 = 1
        WHILE @C2 <= @C2Count
        BEGIN
            DECLARE @C2Id bigint, @C2Name nvarchar(255);

            SELECT @C2Id=[ID], @C2Name=[Name]
            FROM @TmpC2
            WHERE [num] = @C2;
            SELECT @rc = @@ROWCOUNT;
            INSERT INTO debug VALUES( 'Read c2: ' +
                CASE @C2Id WHEN NULL THEN 'NULL' ELSE CONVERT(nvarchar(5), @C2Id) END + '|' +
                CASE @C2Name WHEN NULL THEN 'NULL' ELSE @C2Name END, @rc );

            UPDATE [C2]
            SET [PID]=@PId, [Name]=@C2Name
            WHERE [ID]=@C2Id AND ServerID=@ServerID;
            SELECT @rc = @@ROWCOUNT;
            INSERT INTO debug VALUES( 'Update c2', @rc );
            IF @rc = 0
            BEGIN
                INSERT INTO debug VALUES( 'Inserting channel: ' +
                    CONVERT(nvarchar(5), @C2Id) + '|' +
                    CONVERT(nvarchar(50), @ServerId) + '|' +
                    CONVERT(nvarchar(5), @PId), 0 );
                INSERT INTO [C2] (
                    [ID], [ServerID], [PID], [Name]
                ) VALUES (
                    @C2Id, @ServerID, @PId, @C2Name
                );
                SELECT @rc = @@ROWCOUNT;
                INSERT INTO debug VALUES( 'Insert c2', @rc );
            END;

            INSERT INTO debug VALUES( 'To next c2', @C2 );
            SET @C2 = @C2 + 1;
            INSERT INTO debug VALUES( 'Next c2', @C2 );
        END;

        DELETE FROM @TmpC2;

        SET @p = @p + 1;
    END;

    COMMIT TRANSACTION;
END
GO

C++ アプリから #SynchronizeP を実行するたびに、SP とトランザクションの間のどこかで突然エラーが発生しますが、SSMS でコードを実行するのは完璧です。いろいろ試してみましたが答えが出ます!!

これが私がそれを扱う私のサンプルデータです

INSERT INTO #P( [ID, [Name] ) VALUES
    ( 1, 'p1' )
    ( 2, 'p2' )
    ( 3, 'p3' )
GO

INSERT INTO #C1( [ID], [PID], [Type] ) VALUES
    ( 1, 1, 'T1' )
    ( 2, 1, 'T2' )
    ( 3, 2, 'T3' )
    ( 4, 2, 'T4' )
    ( 5, 3, 'T5' )
    ( 6, 3, 'T6' )
GO

INSERT INTO #C2( [ID], [PID], [Name] ) VALUES
    ( 1, 1, 'C2_01' )
    ( 2, 1, 'C2_02' )
    ( 3, 1, 'C2_03' )
    ( 4, 1, 'C2_04' )
    ( 5, 1, 'C2_05' )
    ( 6, 1, 'C2_06' )
    ( 7, 1, 'C2_07' )
    ( 8, 1, 'C2_08' )
    ( 9, 1, 'C2_09' )
    (10, 1, 'C2_10' )
    (11, 1, 'C2_11' )
    (12, 1, 'C2_12' )
    (13, 1, 'C2_13' )
    (14, 1, 'C2_14' )
    (15, 1, 'C2_15' )
    (16, 1, 'C2_16' )
    (17, 1, 'C2_17' )
    (18, 1, 'C2_18' )
    (19, 1, 'C2_19' )
    (20, 1, 'C2_20' )
    (21, 1, 'C2_21' )
    (22, 1, 'C2_22' )
    (23, 1, 'C2_23' )
    (24, 1, 'C2_24' )
    (25, 1, 'C2_25' )
    (26, 1, 'C2_26' )
    (27, 1, 'C2_27' )
    (28, 1, 'C2_28' )
    (29, 1, 'C2_29' )
    (30, 1, 'C2_30' )

    (31, 2, 'C2_31' )
    (32, 2, 'C2_32' )
    (33, 2, 'C2_33' )
    (34, 2, 'C2_34' )
    (35, 2, 'C2_35' )
    (36, 2, 'C2_36' )
    (37, 2, 'C2_37' )
    (38, 2, 'C2_38' )
    (39, 2, 'C2_39' )
    (40, 2, 'C2_40' )
    (41, 2, 'C2_41' )
    (42, 2, 'C2_42' )
    (43, 2, 'C2_43' )
    (44, 2, 'C2_44' )
    (45, 2, 'C2_45' )
    (46, 2, 'C2_46' )
    (47, 2, 'C2_47' )
    (48, 2, 'C2_48' )
    (49, 2, 'C2_49' )
    (50, 2, 'C2_50' )
    (51, 2, 'C2_51' )
    (52, 2, 'C2_52' )
    (53, 2, 'C2_53' )
    (54, 2, 'C2_54' )
    (55, 2, 'C2_55' )
    (56, 2, 'C2_56' )
    (57, 2, 'C2_57' )
    (58, 2, 'C2_58' )
    (59, 2, 'C2_59' )
    (60, 2, 'C2_60' )

    (61, 3, 'C2_61' )
    (62, 3, 'C2_62' )
    (63, 3, 'C2_63' )
    (64, 3, 'C2_64' )
    (65, 3, 'C2_65' )
    (66, 3, 'C2_66' )
    (67, 3, 'C2_67' )
    (68, 3, 'C2_68' )
    (69, 3, 'C2_69' )
    (70, 3, 'C2_70' )
    (71, 3, 'C2_71' )
    (72, 3, 'C2_72' )
    (73, 3, 'C2_73' )
    (74, 3, 'C2_74' )
    (75, 3, 'C2_75' )
    (76, 3, 'C2_76' )
    (77, 3, 'C2_77' )
    (78, 3, 'C2_78' )
    (79, 3, 'C2_79' )
    (80, 3, 'C2_80' )
    (81, 3, 'C2_81' )
    (82, 3, 'C2_82' )
    (83, 3, 'C2_83' )
    (84, 3, 'C2_84' )
    (85, 3, 'C2_85' )
    (86, 3, 'C2_86' )
    (87, 3, 'C2_87' )
    (88, 3, 'C2_88' )
    (89, 3, 'C2_89' )
    (90, 3, 'C2_90' )
GO

EXEC #SynchronizeP
GO


編集:なんてこった!! 信じられないSET NOCOUNT ON、SP の開始に追加すると、すべてが期待どおりに進みます!! 誰かが理由を知っていますか?? SP の影響を受ける行ブレーク実行の数を示すメッセージが表示されるのはなぜですか。
ほとんどの場合、SET NOCOUNT ON(パフォーマンスのために) SP の先頭に追加することをお勧めしますが、追加するのを忘れると SP が破損するのはなぜですか??

4

2 に答える 2

0

SPの前に#を付けることで、一時的なものになります。したがって、C++プログラムの別のセッションから呼び出す場合はおそらく存在しません。

于 2012-08-22T11:30:00.380 に答える
0

答えは、SQLから最初の回答を受け取ったときにODBCがコマンドを閉じるかキャンセルすることだと思います。したがって、SET NOCOUNT ONSQLがカウント通知を送信するのを忘れると、ODBCはコマンドをキャンセルします。ODBCでSQLコマンドの複数の結果セットを有効にするためのいくつかのテクニックがあるかもしれませんが、私はそのようなテクニックを知りません

于 2012-10-02T20:49:02.943 に答える