对于超过250条记录的批量插入,检查约束不起作用

Mah*_*vej 9 t-sql sql-server check-constraints user-defined-functions

我的查询:

INSERT into PriceListRows (PriceListChapterId,[No])
    SELECT TOP 250 100943 ,N'2'
    FROM #AnyTable
Run Code Online (Sandbox Code Playgroud)

此查询工作正常,并根据需要引发以下异常:

INSERT语句与CHECK约束"CK_PriceListRows_RowNo_Is_Not_Unqiue_In_PriceList"冲突.冲突发生在数据库"TadkarWeb",表"dbo.PriceListRows"中.

但是更改SELECT TOP 250SELECT TOP 251(是!只需更改250到251!),查询成功运行,没有任何检查约束异常!

为何这种奇怪的行为?

注意:

  1. 我的检查约束是一种检查某种唯一性的函数.它查询约4个表.

  2. 我检查了SQL Server 2012 SP2和SQL Server 2014 SP1

**编辑1**

检查约束函数:

ALTER FUNCTION [dbo].[CheckPriceListRows_UniqueNo] (
    @rowNo nvarchar(50),
    @rowId int,
    @priceListChapterId int,
    @projectId int)
RETURNS bit
AS
BEGIN
    IF EXISTS (SELECT 1 
               FROM RowInfsView 
               WHERE PriceListId = (SELECT PriceListId 
                                    FROM ChapterInfoView 
                                    WHERE Id = @priceListChapterId) 
                 AND (@rowID IS NULL OR Id <> @rowId) 
                 AND No = @rowNo 
                 AND (@projectId IS NULL OR 
                      (ProjectId IS NULL OR ProjectId = @projectId)))
        RETURN 0 -- Error

     --It is ok!
    RETURN 1
END
Run Code Online (Sandbox Code Playgroud)

**编辑2 **检查约束代码(SQL Server 2012生成的内容):

ALTER TABLE [dbo].[PriceListRows]  WITH NOCHECK ADD  CONSTRAINT [CK_PriceListRows_RowNo_Is_Not_Unqiue_In_PriceList] CHECK  (([dbo].[tfn_CheckPriceListRows_UniqueNo]([No],[Id],[PriceListChapterId],[ProjectId])=(1)))
GO

ALTER TABLE [dbo].[PriceListRows] CHECK CONSTRAINT [CK_PriceListRows_RowNo_Is_Not_Unqiue_In_PriceList]
GO
Run Code Online (Sandbox Code Playgroud)

**编辑3**

执行计划在这里:https://www.dropbox.com/s/as2r92xr14cfq5i/execution%20plans.zip?dl=0

**编辑4** RowInfsView定义是:

SELECT        dbo.PriceListRows.Id, dbo.PriceListRows.No, dbo.PriceListRows.Title, dbo.PriceListRows.UnitCode, dbo.PriceListRows.UnitPrice, dbo.PriceListRows.RowStateCode, dbo.PriceListRows.PriceListChapterId, 
                         dbo.PriceListChapters.Title AS PriceListChapterTitle, dbo.PriceListChapters.No AS PriceListChapterNo, dbo.PriceListChapters.PriceListCategoryId, dbo.PriceListCategories.No AS PriceListCategoryNo, 
                         dbo.PriceListCategories.Title AS PriceListCategoryTitle, dbo.PriceListCategories.PriceListClassId, dbo.PriceListClasses.No AS PriceListClassNo, dbo.PriceListClasses.Title AS PriceListClassTitle, 
                         dbo.PriceListClasses.PriceListId, dbo.PriceLists.Title AS PriceListTitle, dbo.PriceLists.Year, dbo.PriceListRows.ProjectId, dbo.PriceListRows.IsTemplate
FROM            dbo.PriceListRows INNER JOIN
                         dbo.PriceListChapters ON dbo.PriceListRows.PriceListChapterId = dbo.PriceListChapters.Id INNER JOIN
                         dbo.PriceListCategories ON dbo.PriceListChapters.PriceListCategoryId = dbo.PriceListCategories.Id INNER JOIN
                         dbo.PriceListClasses ON dbo.PriceListCategories.PriceListClassId = dbo.PriceListClasses.Id INNER JOIN
                         dbo.PriceLists ON dbo.PriceListClasses.PriceListId = dbo.PriceLists.Id
Run Code Online (Sandbox Code Playgroud)

Raz*_*col -1

您可能遇到了不正确的查询优化,但如果没有所有涉及的表中的数据,我们就无法重现该错误。

但是,对于此类检查,我建议使用触发器而不是基于函数的检查约束。在触发器中,您可以使用 SELECT 语句来调试它未按预期工作的原因。例如:

CREATE TRIGGER trg_PriceListRows_CheckUnicity ON PriceListRows
FOR INSERT, UPDATE
AS
IF @@ROWCOUNT>0 BEGIN
    /*
    SELECT * FROM inserted i
    INNER JOIN RowInfsView r
    ON r.PriceListId = (
        SELECT c.PriceListId 
        FROM ChapterInfoView c
        WHERE c.Id = i.priceListChapterId
    ) 
    AND r.Id <> i.Id
    AND r.No = i.No 
    AND (r.ProjectId=i.ProjectId OR r.ProjectId IS NULL AND i.ProjectId IS NULL)
    */
    IF EXISTS (
        SELECT * FROM inserted i
        WHERE EXISTS (
            SELECT * FROM RowInfsView r
            WHERE r.PriceListId = (
                SELECT c.PriceListId 
                FROM ChapterInfoView c
                WHERE c.Id = i.priceListChapterId
            ) 
            AND r.Id <> i.Id
            AND r.No = i.No 
            AND (r.ProjectId=i.ProjectId OR r.ProjectId IS NULL AND i.ProjectId IS NULL)
        )
    ) BEGIN
        RAISERROR ('Duplicate rows!',16,1)
        ROLLBACK
        RETURN
    END
END
Run Code Online (Sandbox Code Playgroud)

这样,您可以查看正在检查的内容并纠正您的视图和/或现有数据。