调用存储过程时触发失败

时间:2014-08-04 13:45:47

标签: sql sql-server stored-procedures

我真的希望有人可以帮助我...

我有一个触发器来处理向表中插入新记录。正如您将在下面看到的,此触发器将一条记录插入到另一个表中,该表依次执行该表上的触发器,该表调用存储过程(我试图在触发器内执行此操作,但它失败并且很难测试它失败了,所以我把它移到了自己的小单位。)

在存储过程中,调用从Active Directory数据库(ADSI)中提取信息并更新新插入的记录。但是,这是触发器调用时失败的地方。当我通过简单地执行它来调用它,并传递记录进行更新时,它的效果很好......有人能指出我正确的方向吗?请!

在YYY中触发#1

USE [YYY]
GO
/****** Object:  Trigger [dbo].[NewCustodian]    Script Date: 08/04/2014 09:38:11 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[NewCustodian]
ON [YYY].[dbo].[Custodians]
AFTER INSERT
AS BEGIN
    SET NOCOUNT ON;

    DECLARE @CaseID varchar(20);
    DECLARE DBcursor CURSOR  FOR 
            SELECT [XXX].[dbo].[tblCase].CaseID from [XXX].[dbo].[tblCase] Where [XXX].[dbo].[tblCase].SQLSVR_Case_ID = 'YYY';
    Open DBcursor; FETCH DBCursor into @CaseID;
    CLOSE DBcursor; DEALLOCATE DBcursor;

    DECLARE @NAME varchar(255);
    DECLARE @TAG varchar(255);

    SELECT @NAME = name FROM inserted;
    SELECT @TAG = tag FROM inserted;

    IF NOT EXISTS (Select eID from [XXX].[dbo].[tblNames] 
                    WHERE eID = @TAG and CaseID = @CaseID)
    BEGIN
            INSERT INTO [XXX].[dbo].[tblNames] (CaseID, Name, eID) 
                Values (@CaseID, @NAME, @Tag);
        END
END

在XXX中触发#2

USE [XXX]
GO
/****** Object:  Trigger [dbo].[tblNames_New]    Script Date: 08/04/2014 08:56:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:       
-- Create date:  
-- Description:  
-- =============================================
ALTER TRIGGER [dbo].[tblNames_New]
ON [XXX].[dbo].[tblNames]
AFTER INSERT

AS BEGIN

    SET NOCOUNT ON;

DECLARE @NamesID varchar(10)
DECLARE @TAG varchar(10);
DECLARE @return_value int

SELECT @NamesID = namesID FROM inserted

EXEC dbo.UpdateNames @NamesID;
End

存储过程:

USE [XXX]
GO
/****** Object:  StoredProcedure [dbo].[UpdateNames]    Script Date: 08/04/2014 08:14:52 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:       
-- Create date: 
-- Description: 
-- =============================================
ALTER PROCEDURE [dbo].[UpdateNames] 
    @NamesID int 
AS
BEGIN
    SET FMTONLY OFF;
    SET NOCOUNT ON;

    DECLARE @eID varchar(10);
    DECLARE @TAG varchar(10);
    DECLARE @SQL nvarchar(555);
    DECLARE @DBresults as table (
        eID nvarchar(100),
        mobile nvarchar(100),
        mail nvarchar(100),
        phone nvarchar(100),
        name nvarchar(50),
        legacyExchangeDN nvarchar(100),
        Title nvarchar(100),
        homeDirectory nvarchar(100));
    DECLARE @mobile nvarchar(100)
    DECLARE @mail nvarchar(100)
    DECLARE @phone nvarchar(100) = 'Error'
    DECLARE @name nvarchar(100)
    DECLARE @legacyExchangeDN nvarchar(100)
    DECLARE @Title nvarchar(100) = 'Error'
    DECLARE @homeDirectory nvarchar(100)

    SET @eID = (Select eID from [XXX].[dbo].[tblNames] Where NamesID = @NamesID)

    SET @SQL = N'SELECT * FROM OpenQuery ( ADSI, ''SELECT homeDirectory,Title,legacyExchangeDN,displayName, telephoneNumber, mail, mobile,samAccountName
      FROM ''''LDAP://domain.com''''
      WHERE objectClass =  ''''User''''  and samAccountName = ''''' + @eID+ ''''''') As tblADSI'

    INSERT INTO @DBresults 
       EXEC sp_executesql @SQL

    DECLARE DBcursor CURSOR  FOR 
        SELECT * from @DBresults;
    Open DBcursor; FETCH DBCursor into @eID, @mobile, @mail, @phone, @Name, @legacyExchangeDN, @Title, @homeDirectory;
    CLOSE DBcursor; DEALLOCATE DBcursor;

    UPDATE XXX.dbo.tblNames
        SET Job_Title = @Title,
            Phone = @Phone
         Where NamesID = @NamesID;
END

1 个答案:

答案 0 :(得分:1)

正如我在评论中所说 - 触发器应该非常小,灵活,精简 - 不要在触发器内进行任何广泛且耗时的处理,并避免任何会导致性能瓶颈的事情,特别是游标!

原因是每当INSERT操作发生时触发器就会被触发,你无法控制它被调用的时间和次数。当触发器工作时,主应用程序将等待并挂起 - 因此,请不要长时间使用 - 从触发器快速返回以继续使用主应用程序。

我的方法是:

  • 创建一个新的单独表格,您可以从第一个原始触发器中插入一些关键信息

    CREATE TABLE NewCustodianInserted 
    (
        ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, 
        CaseID VARCHAR(20), 
        Tag VARCHAR(255),
        Handled BIT DEFAULT (0)
    );
    
  • 更改Custodians表上的原始触发器,只需将这些关键信息插入新的“命令”表中:

    CREATE TRIGGER [dbo].[NewCustodian]
    ON [YYY].[dbo].[Custodians]
    AFTER INSERT
    AS BEGIN
       SET NOCOUNT ON;
    
       -- insert key pieces about the new custodian into "command" table
       INSERT INTO dbo.NewCustodianInserted (CaseID, Tag)
          SELECT i.CaseId, i.Tag
          FROM Inserted i
          WHERE NOT EXISTS (SELECT * FROM [XXX].[dbo].[tblNames] WHERE eID = i.Tag AND CaseID = i.CaseID)
    END
    
  • 在一个单独的过程中,例如计划每隔5分钟(或对您的应用程序有意义的任何内容)运行的SQL Server代理作业,读取“命令”表,让新的保管人处理,调用长期运行的存储过程从中更新Active Directory。在这里,由于它从您的主应用程序异步运行,因此您可以使用几乎所需的游标,因为您要为新表中的每一行调用存储过程。

    CREATE PROCEDURE HandleNewCustodians
    AS 
    BEGIN
       SET NOCOUNT ON;
    
       DECLARE @CaseID VARCHAR(20);
       DECLARE @Tag VARCHAR(255);
       DECLARE @NamesID varchar(10);
    
       DECLARE CustodianCursor CURSOR FAST_FORWARD
       FOR 
          SELECT CaseID, Tag FROM dbo.NewCustodianInserted WHERE Handled = 0
    
       OPEN CustodianCursor
    
       FETCH NEXT FROM CustodianCursor INTO @CaseID, @Tag;
    
       WHILE @@FETCH_STATUS = 0
       BEGIN
           SELECT @NamesID = NameID 
           FROM [XXX].[dbo].[tblNames] WHERE eID = @Tag AND CaseID = @CaseID
    
           EXEC dbo.UpdateNames @NamesID;
    
           FETCH NEXT FROM CustodianCursor INTO @CaseID, @Tag;
       END
    
       CLOSE CustodianCursor;
       DEALLOCATE CustodianCursor;
     END