Триггер AFTER INSERT завершается неудачно, когда обновляется более одной строки - PullRequest
0 голосов
/ 14 января 2010

Долгое время читатель, впервые звонящий, и все такое ...

Вот бизнес-проблема: пользователь делает один или несколько запросов на документы. Через некоторое время документ загружается в систему. Если этот документ соответствует одному или нескольким запросам, запрос документа выполняется. Так, например, для Документа А может быть один или несколько запросов. Когда документ A загружен, все запросы на документ A выполняются.

И вот моя техническая проблема: У меня есть триггер AFTER INSERT на столе, где записывается загрузка документа. Он проверяет таблицу DocumentRequest и обновляет каждую строку, которая соответствует загруженному документу. Если соответствует только одна строка (только один запрос для Документа A), все будет просто. Однако, если более одного совпадения, инструкция UPDATE в триггере завершается неудачно - я вижу эту ошибку:

Подзапрос вернул более 1 значения. Это не разрешено, когда подзапрос следует =,! =, <, <=,>,> = или когда подзапрос используется как выражение.

Вот операторы CREATE для соответствующих таблиц:

CREATE TABLE [dbo].[DocumentRequest](
    [DocumentRequestId] [int] IDENTITY(1,1) NOT NULL,
    [BatchID] [int] NULL,
    [CertificateNum] [varchar](20) NOT NULL,
    [RequestedTypCd] [varchar](5) NULL,
    [RequestedReason] [varchar](60) NULL,
    [DestinationTypCd] [varchar](5) NULL,
    [DocumentPackageTypCd] [varchar](5) NULL,
    [RequestedDtm] [datetime] NOT NULL,
    [RequestNotes] [varchar](1000) NULL,
    [RequestStatusTypCd] [varchar](5) NOT NULL,
    [InactiveFlag] [char](1) NULL,
    [CreationDtm] [datetime] NOT NULL,
    [CreationUserID] [varchar](10) NOT NULL,
    [CompletedDtm] [datetime] NULL,
    [CompletedUserID] [varchar](10) NULL,
    [ModifiedDtm] [datetime] NULL,
    [ModifiedUserID] [varchar](10) NULL,
 CONSTRAINT [XPKDocumentRequest] PRIMARY KEY NONCLUSTERED 
(
    [DocumentRequestId] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

--------------
CREATE TABLE [dbo].[DocumentRequestContents](
    [DocumentTypCd] [varchar](5) NOT NULL,
    [CreationDtm] [datetime] NOT NULL,
    [CreationUserID] [varchar](10) NOT NULL,
    [DocumentRequestId] [int] NOT NULL,
    [DocumentReceivedDtm] [datetime] NULL,
    [DocumentReceivedFlag] [char](1) NULL,
    [DocumentIgnoreFlag] [char](1) NULL,
 CONSTRAINT [XPKDocumentRequestContents] PRIMARY KEY NONCLUSTERED 
(
    [DocumentRequestId] ASC,
    [DocumentTypCd] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

--------------
CREATE TABLE [dbo].[DocumentStorage](
    [DocumentStorageID] [int] IDENTITY(1,1) NOT NULL,
    [CertificateNum] [varchar](10) NULL,
    [DocumentHandleID] [int] NULL,
    [DocumentTypVal] [varchar](100) NULL,
    [CreationDtm] [datetime] NULL,
    [CreationUserID] [varchar](10) NULL,
    [lastupd_user] [varchar](8) NULL,
    [lastupd_stamp] [datetime] NULL,
    [DocumentNam] [varchar](100) NULL,
PRIMARY KEY NONCLUSTERED 
(
    [DocumentStorageID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]


--------------
CREATE TABLE [dbo].[DocumentProfile](
    [DocumentProfileID] [int] IDENTITY(1,1) NOT NULL,
    [DocumentTypCd] [varchar](5) NULL,
    [DocumentVersionNum] [varchar](10) NULL,
    [DocumentApproveInd] [varchar](1) NULL,
    [CreationDtm] [datetime] NULL,
    [CreationUserID] [varchar](10) NULL,
    [lastupd_stamp] [datetime] NULL,
    [lastupd_user] [varchar](8) NULL,
    [DocumentStorageTypVal] [varchar](100) NULL,
    [MIMETypVal] [varchar](50) NULL,
    [DocumentSourceTypCd] [varchar](5) NULL,
    [DocumentFormatTypCd] [varchar](5) NULL,
    [DocumentReceiveLocationTypCd] [varchar](5) NULL,
    [DocumentIndexTypCd] [varchar](5) NULL,
    [DocumentNam] [varchar](100) NULL,
    [DocumentTrackedInd] [char](1) NULL CONSTRAINT [DF_DocumentProfile_DocumentTrackedInd]  DEFAULT ('N'),
PRIMARY KEY NONCLUSTERED 
(
    [DocumentProfileID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

И курок ...

CREATE TRIGGER [trg_DMTDocumentReceived_DocumentStorage]
   ON  [dbo].[DocumentStorage]
   AFTER INSERT
AS 
BEGIN
    SET NOCOUNT ON;

    UPDATE DocumentRequestContents
    SET DocumentRequestContents.DocumentReceivedFlag = 'Y',
        DocumentRequestContents.DocumentReceivedDtm = getdate()
    FROM
        DocumentRequestContents
        INNER JOIN DocumentRequest
            ON DocumentRequestContents.DocumentRequestId = DocumentRequest.DocumentRequestId
        INNER JOIN DocumentProfile
            ON DocumentRequestContents.DocumentTypCd = DocumentProfile.DocumentTypCd
        INNER JOIN Inserted
            ON DocumentProfile.DocumentStorageTypVal = Inserted.DocumentTypVal
    WHERE
        (DocumentRequestContents.DocumentReceivedFlag <> 'Y' OR
            DocumentRequestContents.DocumentReceivedFlag IS NULL)
        AND (DocumentRequest.InactiveFlag IS NULL)
        AND (DocumentRequest.CertificateNum = Inserted.CertificateNum)
        AND (DocumentProfile.DocumentStorageTypVal = Inserted.DocumentTypVal)
END

Я немного растерялся относительно того, куда идти отсюда. Можешь помочь парню?

EDIT: Запрос может иметь более одного документа, поэтому таблица, которая обновляется в триггере (DocumentRequestContents), также имеет триггер, который определяет, был ли выполнен весь запрос. Насколько я могу судить, он также не имеет подзапроса, но вот он:

CREATE TRIGGER [trg_DMTDocumentReceived_CompletenessCheck]
   ON  [dbo].[DocumentRequestContents] 
   AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    DECLARE @TotalDocs int, @ReceivedDocs int

    IF UPDATE(DocumentReceivedFlag)
    BEGIN
        IF (SELECT DocumentReceivedFlag FROM Inserted) = 'Y' AND (SELECT DocumentReceivedFlag FROM Deleted) = 'N'
        BEGIN
            SELECT @TotalDocs = count(*)
            FROM DocumentRequestContents
                INNER JOIN Inserted
                    ON DocumentRequestContents.DocumentRequestID = Inserted.DocumentRequestID
            WHERE (DocumentRequestContents.DocumentIgnoreFlag <> 'Y' OR DocumentRequestContents.DocumentIgnoreFlag IS NULL)

            SELECT @ReceivedDocs = count(*)
            FROM DocumentRequestContents
                INNER JOIN Inserted
                    ON DocumentRequestContents.DocumentRequestID = Inserted.DocumentRequestID
            WHERE DocumentRequestContents.DocumentReceivedFlag = 'Y'

            IF (@ReceivedDocs = @TotalDocs)
            BEGIN
                UPDATE DocumentRequest
                SET RequestStatusTypCd = 'CMPLT',
                    CompletedDtm = getdate(),
                    CompletedUserID = 'SYSTEM',
                    ModifiedDtm = getdate(),
                    ModifiedUserID = 'SYSTEM'
                FROM DocumentRequest
                    INNER JOIN Inserted 
                        ON DocumentRequest.DocumentRequestId = Inserted.DocumentRequestId
            END
        END
    END
END

Спасибо, Jason

Ответы [ 5 ]

3 голосов
/ 15 января 2010

Кажется, что оскорбительная строка здесь:

IF (SELECT DocumentReceivedFlag FROM Inserted) = 'Y' AND
   (SELECT DocumentReceivedFlag FROM Deleted) = 'N' ...

Это по сути "подзапрос". Оператор IF написан с расчетом на то, что в inserted и deleted будет только одна строка. Не зная подробностей бизнес-логики, я могу только строить догадки, но, скорее всего, вы будете в порядке, если просто переписать это как:

IF EXISTS(SELECT 1 FROM inserted WHERE DocumentReceivedFlag = 'Y') AND
   EXISTS(SELECT 1 FROM deleted WHERE DocumentReceivedFlag = 'N'))

Но это может быть более сложным, чем это ... возможно, вам действительно нужно соединить вставленные и удаленные строки и проверить, действительно ли флаг изменился с 'Y' на 'N'.

1 голос
/ 14 января 2010

При повторном чтении очевидной проблемой является эта часть второго триггера:

IF (SELECT DocumentReceivedFlag FROM Inserted) = 'Y' 
    AND (SELECT DocumentReceivedFlag FROM Deleted) = 'N'

Это может произойти сбой, если вставлено несколько строк, поскольку SQL Server ожидает, что оба эти подзапроса вернут один или нетряды.

Кстати, я лично избегаю триггеров любой ценой.Они вводят слишком много сложности для меня, чтобы справиться.

1 голос
/ 14 января 2010

Проверьте, нет ли у вас триггера и для DocumentRequestContents.

0 голосов
/ 14 января 2010

Ваш триггер, похоже, нигде не содержит подзапроса - это единственный триггер на вашей таблице? Эта ошибка не вызвана объединением или многострочным обновлением, поэтому я не понимаю, почему ваш триггер будет генерировать эту ошибку.

Вы получаете ошибку при попытке вставить в саму таблицу? Возможно, на самом деле это не тот запрос, который вы используете для вставки - пожалуйста, опубликуйте также этот оператор вставки.

0 голосов
/ 14 января 2010

Возможно, вы захотите попробовать создать псевдоним таблицы Inserted в вашем соединении. Хотя я сам не сталкивался с этим, он может пытаться выполнить подзапрос в предложении WHERE вместо использования объединенной таблицы.

...