0

我有一个从多台计算机运行的应用程序,并且必须在其内部数据库和 SQL 服务器中的一个数据库之间同步某些内容。

我使用一些临时表来插入内部数据库的数据,然后调用一个 SP 来同步数据,它将逐行处理数据,然后在 SQL 数据库中更新它们,插入新行或删除删除的行。因为我应该支持拥有 SQL server 2000 的客户,所以我应该有一个除MERGE.

问题是我的 SP 在 SSMS 中运行良好,但从我的应用程序调用时突然失败,我使用 C++ 本机代码并使用 ODBC 和 SQL Native Client 与 SQL 服务器连接。

这是我的数据库和 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

我认为答案是 ODBC 在收到 SQL 的第一个答案时会关闭或取消命令,所以如果我忘记使用SET NOCOUNT ON并且 SQL 发送计数通知,ODBC 将取消命令。可能有一些技术可以为 ODBC 中的 SQL 命令启用多个结果集,但我不知道这种技术

于 2012-10-02T20:49:02.943 回答