web-dev-qa-db-ja.com

条件付きの一意の識別子フィールド

本番環境にないデータベースがあるため、メインテーブルはCustodyDetailsであり、このテーブルにはID int IDENTITY(1,1) PRIMARY KEY列があり、他のテーブルで参照されていない別の一意の識別子を追加する方法を探しています。これを考慮に入れると、列のコンテンツはIDキーとは完全に一致しないと思います。

ただし、この新しいID列にはいくつかの具体的な詳細があり、ここから問題が始まります。形式は次のとおりです。_XX/YY_ここで[〜#〜] xx [〜#〜]は、毎年新しいリセット/再起動する自動インクリメント可能な値で、[〜# 〜] yy [〜#〜]は、現在の年の下2桁ですSELECT RIGHT(YEAR(GETDATE()), 2)

したがって、たとえば、1つのレコードが28/12/2015から始まる日03/01から追加されたとしましょう/ 2016の場合、列は次のようになります。

_ID    ID2     DATE_ADDED
1     1/15    2015-12-28
2     2/15    2015-12-29
3     3/15    2015-12-30
4     4/15    2015-12-31
5     1/16    2016-01-01
6     2/16    2016-01-02
7     3/16    2016-01-03
_

フロントエンドを使用して複合ID(例ではID2)を解析し、最後の2桁を取得して現在の年の最後の2桁と比較し、新しい相関を開始するかどうかを決定することを考えました。もちろん、データベース側ですべてを実行できるようになることはすばらしいことです。

編集1:ところで、並列IDキーを格納するためだけに別のテーブルを使用している人もいるので、1つのテーブルIDキーが2番目のテーブルの2次キーになります。そのような実装は整っていますか?

編集2:このextraIDは、すべてのファイル/レコードにラベルを付けるレガシードキュメント参照です。私はそれをメインIDの特別なエイリアスと考えることができると思います。

このデータベースが毎年処理するレコードの数されていない過去20年間で100を超えており、もちろん99を超える場合は非常に(実際には、非常に)非常にありそうにありません。フィールドは追加の桁で続行でき、フロントエンド/プロシージャは99を超えることができるため、状況が変化するわけではありません。

もちろん、最初に言及しなかったこれらの詳細の一部は、特定のニーズに合わせてソリューションの可能性を絞り込むだけなので、問題の範囲をより広く保つことを試みました。

8
Nelz

キーテーブルを使用して、2番目のID列の増分部分を格納できます。このソリューションは、クライアント側のコードに依存せず、自動的に複数年認識されます。 _@DateAdded_パラメータが未使用の年に渡されると、その年に基づいて_ID2_列の新しい値のセットの使用が自動的に開始されます。その結果、procが前の年の行を挿入するために使用される場合、それらの行は、増分の「正しい」値で挿入されます。 GetNextID() procは、可能なデッドロックを適切に処理するように調整されており、tblIDsテーブルを更新しようとしたときに5つの順次デッドロックが発生した場合にのみ、呼び出し元にエラーを渡します。

現在使用されているID値を含む年に1行を格納するテーブルと、使用する新しい値を返すストアドプロシージャを作成します。

_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

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 / Mike Defehr
        Date:           2012-07-19

    */
    SET NOCOUNT ON;

    DECLARE @Retry int;
    DECLARE @EN int, @ES int, @ET int;
    SET @Retry = 5;
    DECLARE @NewID int;
    WHILE @Retry > 0
    BEGIN
        SET @NewID = NULL;
        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
_

テーブルと、それに行を挿入するためのプロシージャ:

_CREATE TABLE dbo.Cond
(
    CondID INT NOT NULL
        CONSTRAINT PK_Cond
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , CondID2 VARCHAR(30) NOT NULL
    , Date_Added DATE NOT NULL
);

GO
CREATE PROCEDURE dbo.InsertCond
(
    @DateAdded DATE
)
AS
BEGIN
    DECLARE @NextID INT;
    DECLARE @Year INT;
    DECLARE @IDName NVARCHAR(255);
    SET @Year = DATEPART(YEAR, @DateAdded);
    DECLARE @Res TABLE
    (
        NextID INT NOT NULL
    );
    SET @IDName = 'Cond_' + CONVERT(VARCHAR(30), @Year, 0);
    INSERT INTO @Res (NextID)
    EXEC dbo.GetNextID @IDName;

    INSERT INTO dbo.Cond (CondID2, Date_Added)
    SELECT CONVERT(VARCHAR(30), NextID) + '/' + 
        SUBSTRING(CONVERT(VARCHAR(30), @Year), 3, 2), @DateAdded
    FROM @Res;
END
GO
_

いくつかのサンプルデータを挿入します。

_EXEC dbo.InsertCond @DateAdded = '2015-12-30';
EXEC dbo.InsertCond @DateAdded = '2015-12-31';
EXEC dbo.InsertCond @DateAdded = '2016-01-01';
EXEC dbo.InsertCond @DateAdded = '2016-01-02';
_

両方のテーブルを表示:

_SELECT *
FROM dbo.Cond;

SELECT *
FROM dbo.tblIDs;
_

結果:

enter image description here

キーテーブルとストアドプロシージャは this 質問から取得されます。

6
Max Vernon