Trường định danh duy nhất có điều kiện


8

Tôi có một cơ sở dữ liệu không được sản xuất, vì vậy bảng chính là CustodyDetails, bảng này có một ID int IDENTITY(1,1) PRIMARY KEYcột và tôi đang tìm cách thêm một mã định danh duy nhất khác mà nó không được tham chiếu trong bất kỳ bảng nào khác, tôi sẽ nghĩ bằng cách lấy nó trong tài khoản nội dung của cột sẽ không chính xác là một khóa nhận dạng.

Cột nhận dạng mới này có một vài chi tiết cụ thể và đây là vấn đề của tôi bắt đầu. Định dạng như sau: XX/YYtrong đó XX là giá trị gia tăng tự động đặt lại / khởi động lại mỗi năm mới và YY là 2 chữ số cuối của năm hiện tại SELECT RIGHT(YEAR(GETDATE()), 2).

Vì vậy, ví dụ: giả sử một bản ghi được thêm vào một ngày bắt đầu từ ngày 28/12/2015 kết thúc ngày 03/01/2016 , cột sẽ có dạng:

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

Tôi đã nghĩ đến việc sử dụng frontend để phân tích ID tổng hợp (ví dụ ID2) lấy 2 chữ số cuối và so sánh với 2 chữ số cuối của năm hiện tại và sau đó quyết định có bắt đầu tương quan mới hay không. Tất nhiên sẽ là tuyệt vời khi có thể làm tất cả về phía cơ sở dữ liệu.

EDIT 1: btw, tôi cũng đã thấy mọi người sử dụng các bảng riêng biệt chỉ để lưu các khóa nhận dạng song song, do đó, một bảng Khóa nhận dạng trở thành Khóa phụ của bảng thứ hai, điều này nghe có vẻ hơi khó hiểu nhưng có lẽ đây là trường hợp triển khai như vậy?

EDIT 2: Đây thêm ID là một tài liệu tham khảo tài liệu di sản mà nhãn mỗi tập tin / hồ sơ. Tôi đoán người ta có thể nghĩ về nó như một bí danh đặc biệt cho ID chính.

Số lượng hồ sơ mà cơ sở dữ liệu này xử lý hàng năm đã không còn trong số 100 trong 20 năm qua và rất khó có thể thực hiện được, tất nhiên là nếu nó vượt quá 99 thì trường sẽ có thể tiếp tục với chữ số phụ và giao diện / thủ tục sẽ có thể vượt quá 99, vì vậy nó không giống như nó thay đổi mọi thứ.

Tất nhiên, một số chi tiết tôi đã không đề cập ngay từ đầu vì chúng sẽ chỉ thu hẹp khả năng giải pháp để đáp ứng nhu cầu cụ thể của tôi, đã cố gắng để phạm vi vấn đề rộng hơn.


Phiên bản SQL Server này là về cái gì?
Max Vernon

Tại sao điều này cần phải được lưu trữ trong bảng, nếu nó không được sử dụng làm tài liệu tham khảo ở bất cứ đâu? Tại sao nó không thể là một cột được tính toán (được duy trì hoặc tính toán trong một truy vấn, khi cần thiết)? Điều gì sẽ xảy ra nếu bạn có hơn 100 hàng trong một năm?
ypercubeᵀᴹ

1
Với ID= 5, 6 và 7, DATE_ADDED nên 2016-01-01 và v.v.
Kin Shah

@Kin có vẻ như nó. Tôi đã sửa mẫu.
ypercubeᵀᴹ

Cảm ơn đã sửa chữa, vâng, chúng là 2016 recs và SQL Server 2005 hiện đang sử dụng. @ YperSillyCubeᵀᴹ Chủ yếu là vấn đề tìm kiếm một giải pháp tốt hơn , vì vậy thực sự bất kỳ đề xuất nào cũng sẽ được đưa ra.
Nelz

Câu trả lời:


6

Bạn có thể sử dụng bảng chính để lưu trữ phần tăng dần của cột ID thứ hai. Giải pháp này không dựa trên bất kỳ mã phía khách hàng nào và được tự động nhận biết nhiều năm; khi @DateAddedtham số vượt qua trong một năm chưa sử dụng trước đó, nó sẽ tự động bắt đầu sử dụng một bộ giá trị mới cho ID2cột, dựa trên năm đó. Do đó, Proc được sử dụng để chèn các hàng từ các năm trước, các hàng đó sẽ được chèn với các giá trị "chính xác" cho mức tăng. Các GetNextID()proc là hướng xử lý bế tắc có thể duyên dáng, chỉ đi qua một lỗi cho người gọi nếu 5 bế tắc tuần tự xảy ra khi cố gắng cập nhật các tblIDsbảng.

Tạo bảng để lưu trữ một hàng mỗi năm có chứa giá trị ID hiện đang được sử dụng, cùng với quy trình được lưu trữ để trả về giá trị mới sẽ sử dụng:

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

Bảng của bạn, cùng với một Proc để chèn các hàng vào đó:

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

Chèn một số dữ liệu mẫu:

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';

Hiển thị cả hai bảng:

SELECT *
FROM dbo.Cond;

SELECT *
FROM dbo.tblIDs;

Các kết quả:

nhập mô tả hình ảnh ở đây

Bảng chính và lưu trữ Proc đến từ câu hỏi này .


Bạn đang đặt mức cô lập giao dịch nhưng không rõ ràng mở giao dịch. Ngoài ra, nếu hai phiên đồng thời cố gắng chèn cùng một (IDName, LastID)hàng, điều đó có dẫn đến bế tắc hoặc một trong các giao dịch vi phạm PK không? Nếu sau này, có lẽ sẽ có ý nghĩa khi trao cho giao dịch đó một cơ hội khác (để cuối cùng nó sẽ có được ID là 2).
Andriy M

Và một điều nữa, có lẽ tôi sẽ đặt @NewIDrõ ràng thành null ở đầu vòng lặp: nếu giao dịch cố gắng chèn một hàng trở thành nạn nhân bế tắc, nó sẽ không cố gắng chèn một hàng ở lần lặp tiếp theo, vì @NewIDsẽ có được đặt thành 1 (không phải là NULL và do đó nhánh INSERT sẽ bị bỏ qua).
Andriy M

Trên thực tế, mức độ cô lập giao dịch hoàn toàn không cần thiết lập; Tôi sẽ xóa nó. Tôi không thấy làm thế nào hai phiên đồng thời có thể chèn cùng một giá trị vào tblIDsbảng vì bảng đó được cập nhật bởi một hoạt động nguyên tử duy nhất; cập nhật "kỳ quặc".
Max Vernon

Một ý tưởng không tồi về việc thiết lập @NewID = NULLở đầu vòng lặp.
Max Vernon

Tôi cho rằng về mặt lý thuyết, hành động đầu tiên cho một năm mới thể dễ bị bế tắc.
Max Vernon
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.