为什么在没有检测到更改时SQL会更新某些行?

时间:2016-08-02 05:26:01

标签: sql sql-server tsql azure-sql-database

我有一个用于添加或更新一行数据的语句。当没有明显的变化时,我正在观察SQL更新行。我正在试图解决为什么当SQL应该阻止它时它坚持要更新行。

我正在以这种方式更新大约1,000行(一个接一个),当我创建的掩码表明他们不应该更新时,只有2行似乎更新。

以下是相关的SQL代码段:

...
ELSE IF NOT EXISTS (
    SELECT PERMIT_STATUS, PERMIT_LOCATION, PERMIT_COMMODITY, PERMIT_TYPE_CODE, PERMIT_TYPE_DESCRIPTION, PERMIT_ALLOCATION_METHOD, PERMIT_OPERATION_NAME, OWNERS, SHARE_PERCENTAGE, OPERATOR, MINERALS, PERMIT_DURATION_YEARS, PERMIT_DURATION_MONTHS, PERMIT_AREA, PERMIT_AREA_UNIT, PERMIT_OFFSHORE_ONSHORE, PERMIT_STATUS_DATE, PERMIT_MINERAL_GROUP, PERMIT_MP, SUBSEQUENT_TO_PERMIT, PERMIT_COMMENCEMENT_DATE, PERMIT_EXPIRY_DATE, PERMIT_GRANT_DATE, PERMIT_NONEXCLUSIVE_YN, ShapeGeoJson, Removed
    FROM MyTable
    WHERE PERMIT_NUMBER = @PERMIT_NUMBER
    INTERSECT
    SELECT @PERMIT_STATUS, @PERMIT_LOCATION, @PERMIT_COMMODITY, @PERMIT_TYPE_CODE, @PERMIT_TYPE_DESCRIPTION, @PERMIT_ALLOCATION_METHOD, @PERMIT_OPERATION_NAME, @OWNERS, @SHARE_PERCENTAGE, @OPERATOR, @MINERALS, @PERMIT_DURATION_YEARS, @PERMIT_DURATION_MONTHS, @PERMIT_AREA, @PERMIT_AREA_UNIT, @PERMIT_OFFSHORE_ONSHORE, @PERMIT_STATUS_DATE, @PERMIT_MINERAL_GROUP, @PERMIT_MP, @SUBSEQUENT_TO_PERMIT, @PERMIT_COMMENCEMENT_DATE, @PERMIT_EXPIRY_DATE, @PERMIT_GRANT_DATE, @PERMIT_NONEXCLUSIVE_YN, @ShapeGeoJson, @Removed
)
BEGIN
    UPDATE MyTable
    SET PERMIT_STATUS = @PERMIT_STATUS
        ,PERMIT_LOCATION = @PERMIT_LOCATION
        ,PERMIT_COMMODITY = @PERMIT_COMMODITY
        ,PERMIT_TYPE_CODE = @PERMIT_TYPE_CODE
        ,PERMIT_TYPE_DESCRIPTION = @PERMIT_TYPE_DESCRIPTION
        ,PERMIT_ALLOCATION_METHOD = @PERMIT_ALLOCATION_METHOD
        ,PERMIT_OPERATION_NAME = @PERMIT_OPERATION_NAME
        ,OWNERS = @OWNERS
        ,SHARE_PERCENTAGE = @SHARE_PERCENTAGE
        ,OPERATOR = @OPERATOR
        ,MINERALS = @MINERALS
        ,PERMIT_DURATION_YEARS = @PERMIT_DURATION_YEARS
        ,PERMIT_DURATION_MONTHS = @PERMIT_DURATION_MONTHS
        ,PERMIT_AREA = @PERMIT_AREA
        ,PERMIT_AREA_UNIT = @PERMIT_AREA_UNIT
        ,PERMIT_OFFSHORE_ONSHORE = @PERMIT_OFFSHORE_ONSHORE
        ,PERMIT_STATUS_DATE = @PERMIT_STATUS_DATE
        ,PERMIT_MINERAL_GROUP = @PERMIT_MINERAL_GROUP
        ,PERMIT_MP = @PERMIT_MP
        ,SUBSEQUENT_TO_PERMIT = @SUBSEQUENT_TO_PERMIT
        ,PERMIT_COMMENCEMENT_DATE = @PERMIT_COMMENCEMENT_DATE
        ,PERMIT_EXPIRY_DATE = @PERMIT_EXPIRY_DATE
        ,PERMIT_GRANT_DATE = @PERMIT_GRANT_DATE
        ,PERMIT_NONEXCLUSIVE_YN = @PERMIT_NONEXCLUSIVE_YN
        ,ShapeGeoJson = @ShapeGeoJson
        ,UpdatedUtc = GETUTCDATE()
        ,Removed = @Removed
        ,UpdatedMask = (
            IIF ( PERMIT_STATUS != @PERMIT_STATUS, 4096, 0 ) +
            IIF ( PERMIT_LOCATION != @PERMIT_LOCATION, 256, 0 ) +
            IIF ( PERMIT_COMMODITY != @PERMIT_COMMODITY, 512, 0 ) +
            IIF ( PERMIT_TYPE_CODE != @PERMIT_TYPE_CODE, 64, 0 ) +
            IIF ( PERMIT_TYPE_DESCRIPTION != @PERMIT_TYPE_DESCRIPTION, 128, 0 ) +
            IIF ( PERMIT_ALLOCATION_METHOD != @PERMIT_ALLOCATION_METHOD, 1024, 0 ) +
            IIF ( PERMIT_OPERATION_NAME != @PERMIT_OPERATION_NAME, 2048, 0 ) +
            IIF ( OWNERS != @OWNERS, 1048576, 0 ) +
            IIF ( SHARE_PERCENTAGE != @SHARE_PERCENTAGE, 2097152, 0 ) +
            IIF ( OPERATOR != @OPERATOR, 4194304, 0 ) +
            IIF ( MINERALS != @MINERALS, 8388608, 0 ) +
            IIF ( PERMIT_DURATION_YEARS != @PERMIT_DURATION_YEARS, 32768, 0 ) +
            IIF ( PERMIT_DURATION_MONTHS != @PERMIT_DURATION_MONTHS, 65536, 0 ) +
            IIF ( PERMIT_AREA != @PERMIT_AREA, 67108864, 0 ) +
            IIF ( PERMIT_AREA_UNIT != @PERMIT_AREA_UNIT, 134217728, 0 ) +
            IIF ( PERMIT_OFFSHORE_ONSHORE != @PERMIT_OFFSHORE_ONSHORE, 262144, 0 ) +
            IIF ( PERMIT_STATUS_DATE != @PERMIT_STATUS_DATE, 8192, 0 ) +
            IIF ( PERMIT_MINERAL_GROUP != @PERMIT_MINERAL_GROUP, 131072, 0 ) +
            IIF ( PERMIT_MP != @PERMIT_MP, 524288, 0 ) +
            IIF ( SUBSEQUENT_TO_PERMIT != @SUBSEQUENT_TO_PERMIT, 33554432, 0 ) +
            IIF ( PERMIT_COMMENCEMENT_DATE != @PERMIT_COMMENCEMENT_DATE, 16, 0 ) +
            IIF ( PERMIT_EXPIRY_DATE != @PERMIT_EXPIRY_DATE, 32, 0 ) +
            IIF ( PERMIT_GRANT_DATE != @PERMIT_GRANT_DATE, 8, 0 ) +
            IIF ( PERMIT_NONEXCLUSIVE_YN != @PERMIT_NONEXCLUSIVE_YN, 16384, 0 ) +
            IIF ( ShapeGeoJson != @ShapeGeoJson, 137438953472, 0 ) +
            IIF ( Removed != @Removed, 274877906944, 0 )
        )
    WHERE PERMIT_NUMBER = @PERMIT_NUMBER;
END
...

我在顶部使用INTERSECT方法来检测任何行数据列的更改。这是因为比较列中的NULL值更容易。

您可以看到UpdatedMask正在使用一个值进行更新,以指示哪些列已更新。出现问题时,将其设置为0(当按预期工作时,我会看到正确的UpdatedMask)。

我看不到任何拼写错误或明显的逻辑错误。所以我想知道是否可能是因为以某种方式使用相当大的ShapeGeoJson列一个接一个地更新这么多记录而加载SQL Azure?

以下是表定义,以防它有用:

CREATE TABLE [dbo].[MyTable](
    [PERMIT_NUMBER] [varchar](30) NOT NULL,
    [PERMIT_STATUS] [varchar](30) NULL,
    [PERMIT_LOCATION] [varchar](4000) NULL,
    [PERMIT_COMMODITY] [varchar](8) NULL,
    [PERMIT_TYPE_CODE] [varchar](4000) NULL,
    [PERMIT_TYPE_DESCRIPTION] [varchar](255) NULL,
    [PERMIT_ALLOCATION_METHOD] [varchar](4000) NULL,
    [PERMIT_OPERATION_NAME] [varchar](4000) NULL,
    [OWNERS] [varchar](4000) NULL,
    [SHARE_PERCENTAGE] [varchar](4000) NULL,
    [OPERATOR] [varchar](220) NULL,
    [MINERALS] [varchar](4000) NULL,
    [PERMIT_DURATION_YEARS] [varchar](4000) NULL,
    [PERMIT_DURATION_MONTHS] [varchar](4000) NULL,
    [PERMIT_AREA] [varchar](4000) NULL,
    [PERMIT_AREA_UNIT] [varchar](4) NULL,
    [PERMIT_OFFSHORE_ONSHORE] [varchar](4000) NULL,
    [PERMIT_STATUS_DATE] [date] NULL,
    [PERMIT_MINERAL_GROUP] [varchar](4000) NULL,
    [PERMIT_MP] [varchar](4000) NULL,
    [SUBSEQUENT_TO_PERMIT] [varchar](4000) NULL,
    [PERMIT_COMMENCEMENT_DATE] [date] NULL,
    [PERMIT_EXPIRY_DATE] [date] NULL,
    [PERMIT_GRANT_DATE] [date] NULL,
    [PERMIT_NONEXCLUSIVE_YN] [char](1) NULL,
    [ShapeGeoJson] [varchar](max) NOT NULL,
    [CreatedUtc] [datetime] NOT NULL,
    [UpdatedUtc] [datetime] NOT NULL,
    [Removed] [bit] NOT NULL,
    [UpdatedMask] [bigint] NULL,
 CONSTRAINT [PK_MyTable_1] PRIMARY KEY CLUSTERED 
(
    [PERMIT_NUMBER] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

有什么想法吗?

更新过程每晚运行,因此我会密切注意那些似乎违反规则的行,看看那里是否有模式。我现在已经看过几次,但忽略了保留以前调查的数据。

2 个答案:

答案 0 :(得分:0)

在您的代码中,您有代码重复。您有INTERSECT / EXISTS部分,您可以在其中检查某些内容是否已更新,然后您可以使用计算UpdatedMask的代码来重复逻辑。

我建议先删除重复的逻辑。然后,更容易找到问题并将其修复到一个地方。

下面的代码显示了一种更新记录的替代方法,并且只依赖于代码来计算UpdatedMask。

-- We will update MyTable from data in the NewData alias that contains
-- all of the new values and UpdatedMask
UPDATE U
SET U.PERMIT_STATUS = NewData.PERMIT_STATUS
    ,U.PERMIT_LOCATION = NewData.PERMIT_LOCATION
    .... all other columns ....
    ,U.UpdatedMask = NewData.UpdatedMask
FROM MyTable U
     INNER JOIN (
        SELECT T.PERMIT_STATUS = @PERMIT_STATUS
            ,T.PERMIT_LOCATION = @PERMIT_LOCATION
            ... all other columns ...
            ,UpdatedMask = (
                IIF ( T.PERMIT_STATUS != @PERMIT_STATUS, 4096, 0 ) +
                IIF ( T.PERMIT_LOCATION != @PERMIT_LOCATION, 256, 0 ) +
                ... all other columns ....
            )
        FROM MyTable T
        WHERE T.PERMIT_NUMBER = @PERMIT_NUMBER
     -- In the join clause we only take rows that are actually updated
     ) AS NewData ON NewData.UpdatedMask<>0
WHERE U.PERMIT_NUMBER = @PERMIT_NUMBER;

答案 1 :(得分:0)

我设法深入研究了这个问题。计算UpdatedMask值的逻辑在比较时不考虑NULL值。

因此更新有效。它计算的UpdatedMask无效。

所以现在我已经进一步了解了我的SQL:

UpdatedMask = (
    IIF ( ISNULL(PERMIT_STATUS, '') != ISNULL(@PERMIT_STATUS, ''), 4096, 0 ) +
    IIF ( ISNULL(PERMIT_LOCATION, '') != ISNULL(@PERMIT_LOCATION, ''), 256, 0 ) +
    ...
)