我有一个从多台计算机运行的应用程序,必须在其内部数据库和SQL Server中的一个数据库之间同步。
我使用一些临时表来插入内部数据库的数据,然后调用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的执行
我知道在大多数情况下,最好在SP的开头添加SET NOCOUNT ON
(性能),但为什么忘记添加它会破坏我的SP ??
答案 0 :(得分:0)
通过在SP前面添加#,您已将其设为临时。因此,当您从C ++程序中的其他会话中调用它时,它可能不存在
答案 1 :(得分:0)
我认为答案是ODBC在从SQL收到第一个答案时将关闭或取消命令,因此如果我忘记使用SET NOCOUNT ON
和SQL发送计数通知,ODBC将取消该命令。可能有一些技术可以在ODBC中为SQL命令启用多个结果集,但我不知道这样的技术