SQL Server에서 교착 상태없이 키 테이블에 대한 동시 액세스 처리


32

IDENTITY다양한 다른 테이블의 필드 대신 레거시 응용 프로그램에서 사용하는 테이블이 있습니다.

테이블의 각 행은에 LastID이름이 지정된 필드에 마지막으로 사용한 ID 를 저장합니다 IDName.

때로는 저장된 proc이 교착 상태를 겪습니다. 적절한 오류 처리기를 만들었습니다. 그러나이 방법론이 생각하는 것처럼 작동하는지 또는 여기 잘못된 트리를 짖고 있는지 확인하고 싶습니다.

교착 상태 없이이 테이블에 액세스 할 수있는 방법이 있어야한다고 확신합니다.

데이터베이스 자체는로 구성됩니다 READ_COMMITTED_SNAPSHOT = 1.

먼저 다음은 테이블입니다.

CREATE TABLE [dbo].[tblIDs](
    [IDListID] [int] NOT NULL 
        CONSTRAINT PK_tblIDs 
        PRIMARY KEY CLUSTERED 
        IDENTITY(1,1) ,
    [IDName] [nvarchar](255) NULL,
    [LastID] [int] NULL,
);

그리고 IDName필드 의 비 클러스터형 인덱스 :

CREATE NONCLUSTERED INDEX [IX_tblIDs_IDName] 
ON [dbo].[tblIDs]
(
    [IDName] ASC
) 
WITH (
    PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON
    , FILLFACTOR = 80
);

GO

일부 샘플 데이터 :

INSERT INTO tblIDs (IDName, LastID) 
    VALUES ('SomeTestID', 1);
INSERT INTO tblIDs (IDName, LastID) 
    VALUES ('SomeOtherTestID', 1);
GO

테이블에 저장된 값을 업데이트하고 다음 ID를 반환하는 데 사용되는 저장 프로 시저 :

CREATE PROCEDURE [dbo].[GetNextID](
    @IDName nvarchar(255)
)
AS
BEGIN
    /*
        Description:    Increments and returns the LastID value from tblIDs
        for a given IDName
        Author:         Max Vernon
        Date:           2012-07-19
    */

    DECLARE @Retry int;
    DECLARE @EN int, @ES int, @ET int;
    SET @Retry = 5;
    DECLARE @NewID int;
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    SET NOCOUNT ON;
    WHILE @Retry > 0
    BEGIN
        BEGIN TRY
            BEGIN TRANSACTION;
            SET @NewID = COALESCE((SELECT LastID 
                FROM tblIDs 
                WHERE IDName = @IDName),0)+1;
            IF (SELECT COUNT(IDName) 
                FROM tblIDs 
                WHERE IDName = @IDName) = 0 
                    INSERT INTO tblIDs (IDName, LastID) 
                    VALUES (@IDName, @NewID)
            ELSE
                UPDATE tblIDs 
                SET LastID = @NewID 
                WHERE IDName = @IDName;
            COMMIT TRANSACTION;
            SET @Retry = -2; /* no need to retry since the operation completed */
        END TRY
        BEGIN CATCH
            IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
                SET @Retry = @Retry - 1;
            ELSE
                BEGIN
                SET @Retry = -1;
                SET @EN = ERROR_NUMBER();
                SET @ES = ERROR_SEVERITY();
                SET @ET = ERROR_STATE()
                RAISERROR (@EN,@ES,@ET);
                END
            ROLLBACK TRANSACTION;
        END CATCH
    END
    IF @Retry = 0 /* must have deadlock'd 5 times. */
    BEGIN
        SET @EN = 1205;
        SET @ES = 13;
        SET @ET = 1
        RAISERROR (@EN,@ES,@ET);
    END
    ELSE
        SELECT @NewID AS NewID;
END
GO

저장된 proc의 샘플 실행 :

EXEC GetNextID 'SomeTestID';

NewID
2

EXEC GetNextID 'SomeTestID';

NewID
3

EXEC GetNextID 'SomeOtherTestID';

NewID
2

편집하다:

SP에서 기존 인덱스 IX_tblIDs_Name을 사용하지 않기 때문에 새 인덱스를 추가했습니다. 쿼리 프로세서는 LastID에 저장된 값이 필요하기 때문에 클러스터형 인덱스를 사용한다고 가정합니다. 어쨌든이 인덱스는 실제 실행 계획에서 사용됩니다.

CREATE NONCLUSTERED INDEX IX_tblIDs_IDName_LastID 
ON dbo.tblIDs
(
    IDName ASC
) 
INCLUDE
(
    LastID
)
WITH (FILLFACTOR = 100
    , ONLINE=ON
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON);

편집 # 2 :

@AaronBertrand가 제공 한 조언을 약간 수정했습니다. 여기서 일반적인 아이디어는 불필요한 잠금을 제거하고 전체적으로 SP를보다 효율적으로 만들기 위해 명령문을 수정하는 것입니다.

코드는 아래에서 위의 코드 대신 BEGIN TRANSACTION에를 END TRANSACTION:

BEGIN TRANSACTION;
SET @NewID = COALESCE((SELECT LastID 
        FROM dbo.tblIDs 
        WHERE IDName = @IDName), 0) + 1;

IF @NewID = 1
    INSERT INTO tblIDs (IDName, LastID) 
    VALUES (@IDName, @NewID);
ELSE
    UPDATE dbo.tblIDs 
    SET LastID = @NewID 
    WHERE IDName = @IDName;

COMMIT TRANSACTION;

코드가 0으로이 테이블에 레코드를 추가하지 LastID않으므로 @NewID가 1이면 의도가 목록에 새 ID를 추가한다고 가정 할 수 있습니다. 그렇지 않으면 목록의 기존 행을 업데이트합니다.


RCSI를 지원하도록 데이터베이스를 구성한 방법은 관련이 없습니다. 의도적으로 SERIALIZABLE여기로 이관하고 있습니다.
Aaron Bertrand

예, 방금 모든 관련 정보를 추가하고 싶었습니다. 관련이 없음을 확인하게되어 기쁩니다.
맥스 버논

sp_getapplock을 교착 상태 피해자로 만드는 것은 매우 쉽지만 트랜잭션을 시작한 경우 sp_getapplock을 한 번 호출하여 독점 잠금을 획득하고 수정을 계속하십시오.
AK

1
IDName은 고유합니까? 그런 다음 " 고유 비 클러스터형 인덱스 만들기"를 권장하십시오 . 그러나 null 값이 필요한 경우 인덱스도 필터링 해야합니다 .
crokusek

답변:


15

먼저 모든 값에 대해 데이터베이스를 왕복하는 것을 피합니다. 예를 들어, 애플리케이션에 20 개의 새 ID가 필요하다는 것을 알고 있으면 20 번의 왕복 여행을하지 마십시오. 하나의 저장 프로 시저 호출 만 수행하고 카운터를 20 씩 늘리십시오. 또한 테이블을 여러 테이블로 분할하는 것이 좋습니다.

교착 상태를 완전히 피할 수 있습니다. 시스템에 교착 상태가 전혀 없습니다. 이를 달성하기위한 몇 가지 방법이 있습니다. sp_getapplock을 사용하여 교착 상태를 제거하는 방법을 보여 드리겠습니다. SQL Server가 닫힌 소스이므로 소스 코드를 볼 수 없으므로 가능한 모든 사례를 테스트했는지 여부를 알 수 없기 때문에 이것이 효과가 있는지 전혀 모르겠습니다.

다음은 나를 위해 무엇이 작동하는지 설명합니다. YMMV.

먼저, 항상 많은 양의 교착 상태가 발생하는 시나리오부터 시작하겠습니다. 둘째, sp_getapplock을 사용하여 제거합니다. 여기서 가장 중요한 점은 솔루션 스트레스 테스트입니다. 솔루션은 다를 수 있지만 나중에 설명 하겠지만 높은 동시성에 노출해야합니다.

전제 조건

테스트 데이터가 포함 된 테이블을 설정해 보겠습니다.

CREATE TABLE dbo.Numbers(n INT NOT NULL PRIMARY KEY); 
GO 

INSERT INTO dbo.Numbers 
    ( n ) 
        VALUES  ( 1 ); 
GO 
DECLARE @i INT; 
    SET @i=0; 
WHILE @i<21  
    BEGIN 
    INSERT INTO dbo.Numbers 
        ( n ) 
        SELECT n + POWER(2, @i) 
        FROM dbo.Numbers; 
    SET @i = @i + 1; 
    END;  
GO

SELECT n AS ID, n AS Key1, n AS Key2, 0 AS Counter1, 0 AS Counter2
INTO dbo.DeadlockTest FROM dbo.Numbers
GO

ALTER TABLE dbo.DeadlockTest ADD CONSTRAINT PK_DeadlockTest PRIMARY KEY(ID);
GO

CREATE INDEX DeadlockTestKey1 ON dbo.DeadlockTest(Key1);
GO

CREATE INDEX DeadlockTestKey2 ON dbo.DeadlockTest(Key2);
GO

다음 두 절차는 교착 상태에 빠질 가능성이 높습니다.

CREATE PROCEDURE dbo.UpdateCounter1 @Key1 INT
AS
SET NOCOUNT ON ;
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION ;
UPDATE dbo.DeadlockTest SET Counter1=Counter1+1 WHERE Key1=@Key1;
SET @Key1=@Key1-10000;
UPDATE dbo.DeadlockTest SET Counter1=Counter1+1 WHERE Key1=@Key1;
COMMIT;
GO

CREATE PROCEDURE dbo.UpdateCounter2 @Key2 INT
AS
SET NOCOUNT ON ;
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION ;
SET @Key2=@Key2-10000;
UPDATE dbo.DeadlockTest SET Counter2=Counter2+1 WHERE Key2=@Key2;
SET @Key2=@Key2+10000;
UPDATE dbo.DeadlockTest SET Counter2=Counter2+1 WHERE Key2=@Key2;
COMMIT;
GO

교착 상태 재현

다음 루프는 실행할 때마다 20 개 이상의 교착 상태를 재현해야합니다. 20보다 작 으면 반복 횟수를 늘리십시오.

한 탭에서 이것을 실행하십시오.

DECLARE @i INT, @DeadlockCount INT;
SELECT @i=0, @DeadlockCount=0;

WHILE @i<5000 BEGIN ;
  BEGIN TRY 
    EXEC dbo.UpdateCounter1 @Key1=123456;
  END TRY
  BEGIN CATCH
    SET @DeadlockCount = @DeadlockCount + 1;
    ROLLBACK;
  END CATCH ;
  SET @i = @i + 1;
END;
SELECT 'Deadlocks caught: ', @DeadlockCount ;

다른 탭에서이 스크립트를 실행하십시오.

DECLARE @i INT, @DeadlockCount INT;
SELECT @i=0, @DeadlockCount=0;

WHILE @i<5000 BEGIN ;
  BEGIN TRY 
    EXEC dbo.UpdateCounter2 @Key2=123456;
  END TRY
  BEGIN CATCH
    SET @DeadlockCount = @DeadlockCount + 1;
    ROLLBACK;
  END CATCH ;
  SET @i = @i + 1;
END;
SELECT 'Deadlocks caught: ', @DeadlockCount ;

몇 초 안에 두 가지를 모두 시작하십시오.

sp_getapplock을 사용하여 교착 상태 제거

두 절차를 모두 변경하고 루프를 다시 실행하고 더 이상 교착 상태가 없는지 확인하십시오.

ALTER PROCEDURE dbo.UpdateCounter1 @Key1 INT
AS
SET NOCOUNT ON ;
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION ;
EXEC sp_getapplock @Resource='DeadlockTest', @LockMode='Exclusive';
UPDATE dbo.DeadlockTest SET Counter1=Counter1+1 WHERE Key1=@Key1;
SET @Key1=@Key1-10000;
UPDATE dbo.DeadlockTest SET Counter1=Counter1+1 WHERE Key1=@Key1;
COMMIT;
GO

ALTER PROCEDURE dbo.UpdateCounter2 @Key2 INT
AS
SET NOCOUNT ON ;
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION ;
EXEC sp_getapplock @Resource='DeadlockTest', @LockMode='Exclusive';
SET @Key2=@Key2-10000;
UPDATE dbo.DeadlockTest SET Counter2=Counter2+1 WHERE Key2=@Key2;
SET @Key2=@Key2+10000;
UPDATE dbo.DeadlockTest SET Counter2=Counter2+1 WHERE Key2=@Key2;
COMMIT;
GO

교착 상태를 제거하기 위해 하나의 행이있는 테이블 사용

sp_getapplock을 호출하는 대신 다음 테이블을 수정할 수 있습니다.

CREATE TABLE dbo.DeadlockTestMutex(
ID INT NOT NULL,
CONSTRAINT PK_DeadlockTestMutex PRIMARY KEY(ID),
Toggle INT NOT NULL);
GO

INSERT INTO dbo.DeadlockTestMutex(ID, Toggle)
VALUES(1,0);

이 테이블을 만들고 채우면 다음 줄을 바꿀 수 있습니다

EXEC sp_getapplock @Resource='DeadlockTest', @LockMode='Exclusive';

이 두 가지 절차 모두에서

UPDATE dbo.DeadlockTestMutex SET Toggle = 1 - Toggle WHERE ID = 1;

스트레스 테스트를 다시 실행할 수 있으며 교착 상태가 없는지 직접 확인할 수 있습니다.

결론

앞에서 본 것처럼 sp_getapplock을 사용하여 다른 리소스에 대한 액세스를 직렬화 할 수 있습니다. 따라서 교착 상태를 제거하는 데 사용할 수 있습니다.

물론 이로 인해 수정 속도가 크게 느려질 수 있습니다. 이를 해결하기 위해 배타적 잠금에 적합한 세분성을 선택하고 가능할 때마다 개별 행 대신 세트로 작업해야합니다.

이 방법을 사용하기 전에 직접 스트레스 테스트를해야합니다. 먼저, 원래의 접근 방식으로 최소한 수십 개의 교착 상태를 확보해야합니다. 둘째, 수정 된 저장 프로 시저를 사용하여 동일한 재현 스크립트를 다시 실행할 때 교착 상태가 발생하지 않아야합니다.

일반적으로 T-SQL을 보거나 실행 계획을보고 교착 상태로부터 안전한지 확인할 수있는 좋은 방법은 없다고 생각합니다. 코드가 교착 상태가 발생하기 쉬운 지 확인하는 유일한 방법은 IMO가 코드를 높은 동시성에 노출시키는 것입니다.

교착 상태를 제거하여 행운을 빕니다! 우리는 시스템에 교착 상태가 전혀 없으며 이는 일과 삶의 균형에 좋습니다.


2
sp_getapplock은 +1로 잘 알려져 있지 않은 유용한 도구입니다. 분리하는 데 시간이 걸릴 수있는 끔찍한 혼란을 감안할 때 교착 상태에있는 프로세스를 직렬화하는 편리한 트릭입니다. 그러나 쉽게 이해하고 표준 잠금 메커니즘으로 처리 할 수있는 이와 같은 경우에 대한 첫 번째 선택이어야합니까?
Mark Storey-Smith

2
@ MarkStorey-Smith 그것은 한 번만 연구하고 스트레스 테스트를했기 때문에 나의 첫 번째 선택이며, 어떤 상황에서도 재사용 할 수 있습니다-직렬화가 이미 수행되었으므로 sp_getapplock 이후에 발생하는 모든 것이 결과에 영향을 미치지 않습니다. 표준 잠금 메커니즘을 사용하면 확신을 가질 수 없습니다. 인덱스를 추가하거나 다른 실행 계획을 얻는 것만으로는 교착 상태가 발생할 수 있습니다. 내가 어떻게 알아?
AK

나는 분명한 것을 놓치고 있다고 생각하지만 어떻게 UPDATE dbo.DeadlockTestMutex SET Toggle = 1 - Toggle WHERE ID = 1;교착 상태 를 방지합니까?
데일 K

9

접근 방식이나 다음 XLOCK에 힌트를 사용 하면 이러한 유형의 교착 상태에 영향을받지 않아야합니다.SELECTUPDATE

DECLARE @Output TABLE ([NewId] INT);
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

BEGIN TRANSACTION;

UPDATE
    dbo.tblIDs WITH (XLOCK)
SET 
    LastID = LastID + 1
OUTPUT
    INSERTED.[LastId] INTO @Output
WHERE
    IDName = @IDName;

IF(@@ROWCOUNT = 1)
BEGIN
    SELECT @NewId = [NewId] FROM @Output;
END
ELSE
BEGIN
    SET @NewId = 1;

    INSERT dbo.tblIDs
        (IDName, LastID)
    VALUES
        (@IDName, @NewId);
END

SELECT [NewId] = @NewId ;

COMMIT TRANSACTION;

두 가지 다른 변종과 함께 돌아갑니다 (구타하지 않으면!).


XLOCK기존 카운터가 여러 연결에서 업데이트되는 것을 방지 할 수 있지만 TABLOCKX여러 연결이 동일한 새 카운터를 추가하지 못하게 해야 합니까?
Dale K

1
@DaleBurrell 아니요, IDName에 PK 또는 고유 제약 조건이 있습니다.
Mark Storey-Smith

7

Mike Defehr는 매우 가벼운 방법으로 이것을 달성하는 우아한 방법을 보여주었습니다.

ALTER PROCEDURE [dbo].[GetNextID](
    @IDName nvarchar(255)
)
AS
BEGIN
    /*
        Description:    Increments and returns the LastID value from tblIDs for a given IDName
        Author:         Max Vernon / Mike Defehr
        Date:           2012-07-19

    */

    DECLARE @Retry int;
    DECLARE @EN int, @ES int, @ET int;
    SET @Retry = 5;
    DECLARE @NewID int;
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    SET NOCOUNT ON;
    WHILE @Retry > 0
    BEGIN
        BEGIN TRY
            UPDATE dbo.tblIDs 
            SET @NewID = LastID = LastID + 1 
            WHERE IDName = @IDName;

            IF @NewID IS NULL
            BEGIN
                SET @NewID = 1;
                INSERT INTO tblIDs (IDName, LastID) VALUES (@IDName, @NewID);
            END
            SET @Retry = -2; /* no need to retry since the operation completed */
        END TRY
        BEGIN CATCH
            IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
                SET @Retry = @Retry - 1;
            ELSE
                BEGIN
                SET @Retry = -1;
                SET @EN = ERROR_NUMBER();
                SET @ES = ERROR_SEVERITY();
                SET @ET = ERROR_STATE()
                RAISERROR (@EN,@ES,@ET);
                END
        END CATCH
    END
    IF @Retry = 0 /* must have deadlock'd 5 times. */
    BEGIN
        SET @EN = 1205;
        SET @ES = 13;
        SET @ET = 1
        RAISERROR (@EN,@ES,@ET);
    END
    ELSE
        SELECT @NewID AS NewID;
END
GO

(완벽 성을 위해 다음은 저장된 proc과 관련된 테이블입니다.)

CREATE TABLE [dbo].[tblIDs]
(
    IDName nvarchar(255) NOT NULL,
    LastID int NULL,
    CONSTRAINT [PK_tblIDs] PRIMARY KEY CLUSTERED 
    (
        [IDName] ASC
    ) WITH 
    (
        PAD_INDEX = OFF
        , STATISTICS_NORECOMPUTE = OFF
        , IGNORE_DUP_KEY = OFF
        , ALLOW_ROW_LOCKS = ON
        , ALLOW_PAGE_LOCKS = ON
        , FILLFACTOR = 100
    ) 
);
GO

최신 버전의 실행 계획입니다.

여기에 이미지 설명을 입력하십시오

그리고 이것은 원본 버전의 실행 계획입니다 (교착 상태에 취약).

여기에 이미지 설명을 입력하십시오

분명히 새 버전이 승리합니다!

비교를 위해 (XLOCK)etc 와 같은 중간 버전 은 다음 계획을 생성합니다.

여기에 이미지 설명을 입력하십시오

나는 그것이 승리라고 말할 것입니다! 모두의 도움에 감사드립니다!


2
실제로 작동하지만 적용 할 수없는 곳에서 SERIALIZABLE을 사용하고 있습니다. 팬텀 행은 여기에 존재할 수 없습니다. 왜 그들을 방지하기 위해 존재하는 격리 수준을 사용합니까? 또한 누군가 다른 프로 시저 또는 외부 트랜잭션이 시작된 연결에서 프로 시저를 호출하면 시작한 추가 조치는 SERIALIZABLE에서 진행됩니다. 지저분해질 수 있습니다.
Mark Storey-Smith

2
SERIALIZABLE팬텀을 방지하기 위해 존재하지 않습니다. 자료를 제공하기 위해 존재 직렬화 격리의 의미 , 데이터베이스에 대한 즉, 동일한 영속 효과 처럼 관련된 거래는 일부 지정되지 않은 순서대로 순차적으로 실행했다입니다.
폴 화이트 GoFundMonica 말한다

6

Mark Storey-Smith의 천둥을 훔치지 말고 위의 게시물과 함께 무언가에 있습니다 (실수로 가장 많은 찬사를 받았습니다). Max에 제공 한 조언은 정말 멋지다고 생각되는 "UPDATE set @variable = column = column + value"구문을 중심으로 작성되었지만 문서화되지 않은 것으로 생각됩니다 (TCP를 위해 특별히 있기 때문에 지원해야 함) 벤치 마크).

다음은 Mark의 답변 변형입니다. 새 ID 값을 레코드 세트로 반환하기 때문에 스칼라 변수를 완전히 제거 할 수 있으며 명시적인 트랜잭션이 필요하지 않으며 격리 수준을 망칠 필요가 없다는 데 동의합니다. 게다가. 결과는 매우 깨끗하고 매끄 럽습니다 ...

ALTER PROC [dbo].[GetNextID]
  @IDName nvarchar(255)
  AS
BEGIN
SET NOCOUNT ON;

DECLARE @Output TABLE ([NewID] INT);

UPDATE dbo.tblIDs SET LastID = LastID + 1
OUTPUT inserted.[LastId] INTO @Output
WHERE IDName = @IDName;

IF(@@ROWCOUNT = 1)
    SELECT [NewID] FROM @Output;
ELSE
    INSERT dbo.tblIDs (IDName, LastID)
    OUTPUT INSERTED.LastID AS [NewID]
    VALUES (@IDName,1);
END

3
이는 교착 상태에 영향을받지 않아야하지만 트랜잭션을 생략하면 삽입시 경쟁 조건이 발생하기 쉽습니다.
Mark Storey-Smith

4

작년에 다음과 같이 변경하여 비슷한 교착 상태를 해결했습니다.

IF (SELECT COUNT(IDName) FROM tblIDs WHERE IDName = @IDName) = 0 
  INSERT INTO tblIDs (IDName, LastID) VALUES (@IDName, @NewID)
ELSE
  UPDATE tblIDs SET LastID = @NewID WHERE IDName = @IDName;

이에:

UPDATE tblIDs SET LastID = @NewID WHERE IDName = @IDName;
IF @@ROWCOUNT = 0
BEGIN
  INSERT ...
END

일반적으로 COUNT존재 유무를 결정하기위한 방법을 선택하는 것은 상당히 낭비입니다. 이 경우 그것은 많은 작업이지만, (a)는 그 버릇이 어디 다른 경우에 피가 수처럼 그렇지 않은 0 또는 1이므로 것이다 (이러한 경우에, 사용 훨씬 비싼 될 IF NOT EXISTS대신 IF COUNT() = 0), 그리고 (b) 추가 스캔은 완전히 필요하지 않습니다. UPDATE본질적 수행 동일한 체크.

또한 이것은 심각한 코드 냄새처럼 보입니다.

SET @NewID = COALESCE((SELECT LastID FROM tblIDs WHERE IDName = @IDName),0)+1;

요점이 뭐야? 왜 ROW_NUMBER()쿼리 열을 사용하여 ID 열을 사용하거나 해당 시퀀스를 파생시키지 않습니까?


우리가 사용하는 대부분의 테이블은 IDENTITY. 이 테이블은 개조와 관련이있는 MS Access로 작성된 일부 레거시 코드를 지원합니다. SET @NewID=라인은 단순히 (하지만 당신은 이미 알고) 지정된 ID의 테이블에 저장된 값을 증가시킨다. 내가 어떻게 사용할 수 있는지 확장 할 수 ROW_NUMBER()있습니까?
맥스 버논

@MaxVernon LastID은 모델에서 실제로 무엇을 의미 하는지 모릅니다 . 그 목적은 무엇입니까? 그 이름은 정확히 설명이 아닙니다. Access는 그것을 어떻게 사용합니까?
Aaron Bertrand

Access의 함수는 IDENTITY가없는 주어진 테이블에 행을 추가하려고합니다. First Access GetNextID('WhatevertheIDFieldIsCalled')는 다음에 사용할 ID를 얻기 위해 호출 한 다음 필요한 데이터와 함께 새 행에 삽입합니다.
Max Vernon

변경 사항을 이행하겠습니다. "더 적은 것"의 순수한 사례!
맥스 버논

1
고정 교착 상태가 다시 발생할 수 있습니다. 두 번째 패턴도 취약합니다 : sqlblog.com/blogs/alexander_kuznetsov/archive/2010/01/12/… 교착 상태를 제거하려면 sp_getapplock 을 사용합니다. 수백 명의 사용자가 혼합 된로드 시스템에 교착 상태가 없을 수 있습니다.
AK
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.