5

身分証明書のリクエストを実行できるプログラムを作成しました。

身分証明書にはさまざまな種類があります ( 赤、青、緑 )

リクエスト中に、プログラムは識別番号を生成する必要があります。番号 (番号の範囲) は、要求されるカードによって異なります。

Red Card: 1 - 50000 
Blue Card: 50001 - 100000 
Green Card: 100001 - 150000

システムに新しい ID カードを追加すると、その新しい追加された ID カードの新しい範囲の番号がシーケンスによって自動的に作成されます。数字は繰り返されるべきではありません。1 つの番号は 1 回だけ使用する必要があります。

どうやってやるの?誰でもそれで私を助けることができますか?

4

10 に答える 10

2

デザインの観点から、特定の範囲にカードの色を割り当てるなど、追加のロジックを識別子にコーディングすることは強くお勧めしません。一意性と同時実行性を適切に処理し、ID を完全に代理し、特定の ID のカードの色情報を別の属性に格納する IDENTITY 列を使用したいと思います。その追加の属性にインデックスを作成して、特定の色のレコードを取得することができます。

また、たとえば赤いカードの所有者がそれを青いカードに変更するように要求した場合、何が必要になるか考えてみてください。範囲を使用して色の割り当てを保持するには、新しい ID を作成し、古い ID から新しい ID への順序に関する情報を別の場所に保存する必要があります。誰かがそれを複数回変更した場合はどうなりますか? サロゲート ID を使用すると、常に 1 つの ID を持つだけで、履歴全体を通じて同じ人物を追跡でき、テーブルに日付情報を追加して変更を順番に並べることができます。これは単純なシナリオの例にすぎません。

于 2013-09-28T11:24:18.193 に答える
1

これには SQL Server のIDENTITYメカニズムを利用できます。これは使いやすく、同時実行を適切に処理できるためです。

具体的には、次のIdスクリプトを使用して、ID (自動インクリメント) 列のみを含む 3 つのテーブルを作成できます。

create table RedCardIds(Id int identity(1, 1) primary key)
create table BlueCardIds(Id int identity(50001, 1) primary key)
create table GreenCardIds(Id int identity(100001, 1) primary key)
GO

3 つのテーブルの ID 値は、間隔の下限に一致するように設定されています。

次に、リクエストごとに適切なテーブルに挿入し、OUTPUT 句を使用して新しく生成された ID 値を取得します。

たとえば、リクエストがレッド カードの場合は、次のように記述できます。

insert RedCardIds 
output inserted.Id
default values

出力は次のとおりです。

Id
-----------
1

(1 row(s) affected)

次の実行では、 が返さ2れます。

同様に、ブルー カードの最初の要求は、次のステートメントをトリガーします。

insert BlueCardIds 
output inserted.Id
default values

結果:

Id
-----------
500001

(1 row(s) affected)
于 2013-09-23T08:56:39.213 に答える
0

そして、これが私のチャレンジへの貢献です。追加のテーブルは必要なく、同時実行セーフであり、一括更新を処理できます。最速ではないかもしれませんが、動作します。基本的に、挿入する行を別のテーブルにコピーし、色ごとに ID を作成し、最後にすべてを目的のテーブルに移動します。

Create Trigger Trg_CreateID ON  dbo.Cards instead of insert
as
begin
  set nocount on
  -- declare a working table holding intermediate results
  declare @Tmp Table (cardID int, cardColor char(1), cardNumber char(20))

  -- copy the data to be inserted in our working table
  insert into @Tmp select * from inserted

  declare @Id int
  -- fill in the Id's once per color    
  select @Id=coalesce (max(cardID),0) from dbo.Cards where cardColor='Red'
  update @Tmp set cardID = @Id, @Id=@id+1 where cardColor='Red'

  select @Id=coalesce(max(cardID),50000) from dbo.Cards where cardColor='Blue'
  update @Tmp set cardID = @Id, @Id=@id+1 where cardColor='Blue'

  select @Id=coalesce(max(cardID),100000) from dbo.Cards where cardColor='Gree'
  update @Tmp set cardID = @Id, @Id=@id+1 where cardColor='Green'

  -- do the actual insert here
  insert into dbo.Cards select * from @tmp
end

Cardsこのようなテーブルを想定しています

CREATE TABLE [dbo].[Cards]
(
    [cardID] [int] NOT NULL,
    [cardColor] [char](1) NOT NULL,
    [cardNumber] [char](20) NOT NULL
) ON [PRIMARY]

cardID列に制約を追加して、挿入ステートメントで省略できるようにしました

ALTER TABLE [dbo].[Cards] 
  ADD CONSTRAINT [DF_Cards_cardID] DEFAULT ((0)) FOR [cardID]
于 2013-09-28T08:15:02.600 に答える
0

たくさんの答えがありますが、2セント追加します。元の投稿へのコメントに書いたことを想定していることに注意してください。

create table cardTypes(cardTypeName varchar(100) primary key, [50kSlot] int unique)

create table cards (identificationNumber bigint primary key);

--add slot if needed
declare @cardToBeAdded varchar(100) = 'green'
declare @tbl50kSlot table (i int)
merge into cardTypes as t
using (select @cardToBeAdded as newCard) as s
on t.[cardTypeName] = s.newCard
when not matched by target then
insert (cardTypeName, [50kSlot]) values (s.newCard, isnull((select max([50kSlot]) + 1 from cardTypes),1))
when matched then
update set [50kSlot] = [50kSlot]
output inserted.[50kSlot] into @tbl50kSlot;

declare @50kSlot int = (Select i from @tbl50kSlot)

insert into cards (identificationNumber) values (isnull(
    (select max(identificationNumber)+1 from cards where identificationNumber between ((@50kSlot-1)*50000+1) and @50kSlot*50000),
    (@50kSlot-1)*50000+1)
)

もちろん、実際のデータをいくつかカード テーブルに追加する必要があります。十分に有効なインデックスが存在する場合、最後のクエリは比較的高速に実行できることに注意してください。パフォーマンスに問題がある場合は、identificationNumber のインデックス作成を回避する価値があります。たとえば、多数の行がある場合は、この列にフィルター インデックスを作成することを検討してください。

または、 maxInt を cardTypes テーブルに保持して、マージ テーブルをもう少し複雑にすることもできます。欠点は、クエリ間に何らかのエラーが発生した場合、番号が使用されないため、私のソリューションはシーケンスをタイトに保つことです。

于 2013-09-24T14:32:24.403 に答える
0

理想的には、この情報を格納するテーブルを維持する必要があります。

CardCategry MinNumber MaxNumber RunningNumber

次に、SP を記述して次の番号を取得し、カード カテゴリをパラメーターとして渡すことができます。サンプルクエリは次のようになります。

SELECT @count=count(RunningNumber)
FROM IdentificationTable
WHERE CardCategry=@param

IF (@count=1)
    SELECT @RunningNumber=RunningNumber
    FROM IdentificationTable
    WHERE CardCategry=@param
ELSE
    SELECT TOP 1 @min=MinNumber,@max=MaxNumber
    FROM IdentificationTable
    ORDER BY MinNumber DESC

    INSERT INTO IdentificationTable VALUES (@param,@max+1,@max+(@max-@min),1)
    SET @RunningNumber=1

RETURN @RunningNumber

これは完全な作業ではありません。明らかに、チェック境界制限などのために何らかのエラー処理を行う必要があります。

于 2013-09-19T10:27:42.437 に答える
0

SQL フィドル

MS SQL Server 2008 スキーマのセットアップ:

CREATE TABLE Table1
    ([color] varchar(10), [id] int)
;

INSERT INTO Table1
    ([color], [id])
VALUES
    ('Red',(select isnull(case when (max(id)/50000)%3 = 1 and 
                                max(id)%50000 = 0 then max(id)+100000 else
                                max(id) end,0)+1 
              from Table1 where color = 'Red'));

INSERT INTO Table1  ([color], [id]) VALUES  ('Red',50000);

INSERT INTO Table1
    ([color], [id])
VALUES
    ('Red',(select isnull(case when (max(id)/50000)%3 = 1 and 
                                max(id)%50000 = 0 then max(id)+100000 else
                                max(id) end,0)+1 
              from Table1 where color = 'Red'));

INSERT INTO Table1
    ([color], [id])
VALUES
    ('Blue',(select isnull(case when (max(id)/50000)%3 = 2 and 
                                max(id)%50000 = 0 then max(id)+100000 else
                                max(id) end,50000)+1 
              from Table1 where color = 'Blue'));

INSERT INTO Table1
    ([color], [id])
VALUES
    ('Green',(select isnull(case when (max(id)/50000)%3 = 0 and 
                                max(id)%50000 = 0 then max(id)+100000 else
                                max(id) end,100000)+1 
                from Table1 where color = 'Green'));

クエリ 1 :

SELECT *
FROM Table1

結果

| COLOR |     ID |
|-------|--------|
|   Red |      1 |
|   Red |  50000 |
|   Red | 150001 |
|  Blue |  50001 |
| Green | 100001 |
于 2013-09-26T05:47:32.693 に答える
0

編集 #1: トリガー ( IF UPDATE)、ストアド プロシージャ、および最後の 2 つの例を更新しました。

CREATE TABLE dbo.CustomSequence
(
    CustomSequenceID INT IDENTITY(1,1) PRIMARY KEY,
    SequenceName NVARCHAR(128) NOT NULL, -- or SYSNAME
        UNIQUE(SequenceName),
    RangeStart INT NOT NULL,
    RangeEnd INT NOT NULL,
        CHECK(RangeStart < RangeEnd),
    CurrentValue INT NULL,
        CHECK(RangeStart <= CurrentValue AND CurrentValue <= RangeEnd)
);
GO
CREATE TRIGGER trgIU_CustomSequence_VerifyRange
ON dbo.CustomSequence
AFTER INSERT, UPDATE
AS
BEGIN
     IF (UPDATE(RangeStart) OR UPDATE(RangeEnd)) AND EXISTS
    (
        SELECT  *
        FROM    inserted i 
        WHERE   EXISTS
        (
            SELECT  * FROM dbo.CustomSequence cs 
            WHERE   cs.CustomSequenceID <> i.CustomSequenceID
            AND     i.RangeStart <= cs.RangeEnd
            AND     i.RangeEnd >= cs.RangeStart
        )
    )
    BEGIN
        ROLLBACK TRANSACTION;
        RAISERROR(N'Range overlapping error', 16, 1);
    END
END;
GO
--TRUNCATE TABLE dbo.CustomSequence
INSERT  dbo.CustomSequence (SequenceName, RangeStart, RangeEnd)
SELECT  N'Red Card',        1,  50000 UNION ALL
SELECT  N'Blue Card',   50001, 100000 UNION ALL
SELECT  N'Green Card', 100001, 150000;
GO
-- Test for overlapping range
INSERT  dbo.CustomSequence (SequenceName, RangeStart, RangeEnd)
VALUES  (N'Yellow Card', -100, +100);
GO
/*
Msg 50000, Level 16, State 1, Procedure trgIU_CustomSequence_VerifyRange, Line 20
Range overlapping error
Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted.
*/
GO

-- This procedure tries to reserve 
CREATE PROCEDURE dbo.SequenceReservation
(
    @CustomSequenceID INT, -- You could use also @SequenceName 
    @IDsCount INT, -- How many IDs do we/you need ? (Needs to be greather than 0)
    @LastID INT OUTPUT
)   
AS
BEGIN
    DECLARE @StartTranCount INT, @SavePoint VARCHAR(32);
    SET @StartTranCount = @@TRANCOUNT;
    IF @StartTranCount = 0 -- There is an active transaction ?
    BEGIN
        BEGIN TRANSACTION -- If not then it starts a "new" transaction
    END
    ELSE -- If yes then "save" a save point -- see http://technet.microsoft.com/en-us/library/ms188378.aspx
    BEGIN
        DECLARE @ProcID INT, @NestLevel INT;
        SET @ProcID = @@PROCID;
        SET @NestLevel = @@NESTLEVEL;
        SET @SavePoint = CONVERT(VARCHAR(11), @ProcID) + ',' + CONVERT(VARCHAR(11), @NestLevel);
        SAVE TRANSACTION @SavePoint;
    END

    BEGIN TRY
        UPDATE  dbo.CustomSequence
        SET     @LastID = CurrentValue = ISNULL(CurrentValue, 0) + @IDsCount
        WHERE   CustomSequenceID = @CustomSequenceID;

        IF @@ROWCOUNT = 0
            RAISERROR(N'Invalid sequence', 16, 1);

        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        IF @StartTranCount = 0
        BEGIN
            ROLLBACK TRANSACTION;
        END
        ELSE -- @StartTranCount > 0
        BEGIN
            ROLLBACK TRANSACTION @SavePoint
        END

        DECLARE @ErrorMessage NVARCHAR(2048), @ErrorSeverity INT, @ErrorState INT;
        SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
        RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState); 
    END CATCH;
END;
GO

SELECT * FROM dbo.CustomSequence;
GO

-- Example usage #1
DECLARE @LastID INT;
EXEC dbo.SequenceReservation  
        @CustomSequenceID = 1, -- Red Card
        @IDsCount = 2, -- How many IDs ?
        @LastID = @LastID OUTPUT;
SELECT @LastID - 2 + 1 AS [FirstID], @LastID AS [LastID];
GO

-- Example usage #2
DECLARE @LastID INT;
EXEC dbo.SequenceReservation  
        @CustomSequenceID = 1, -- Red Card
        @IDsCount = 7, -- How many IDs ?
        @LastID = @LastID OUTPUT;
SELECT @LastID - 7 + 1 AS [FirstID], @LastID AS [LastID];

SELECT * FROM dbo.CustomSequence;
GO

結果:

CustomSequenceID SequenceName RangeStart  RangeEnd    CurrentValue
---------------- ------------ ----------- ----------- ------------
1                Red Card     1           50000       9
2                Blue Card    50001       100000      NULL
3                Green Card   100001      150000      NULL
于 2013-09-28T15:18:43.543 に答える
-1

*このソリューションは単一の行の挿入で機能しますが、複数の挿入の同時実行には別のアプローチが必要です。詳細についてはコメントで説明します*


テーブルを作成するためのオプションがない場合は、トリガーの代わりに使用できます (オラクルのトリガーの前のように微調整します)。

トリガー内で特定の条件を使用して、Identity列の範囲を設定します。ソリューションを実装する方法のサンプルを次に示します。

テーブル

CREATE TABLE REQUEST_TABLE(
      REQ_ID numeric(8, 0) NOT NULL,
      REQ_COLOR VARCHAR(30) NOT NULL
 ); -- I have used this sample table

トリガーの代わりに

CREATE TRIGGER tg_req_seq ON REQUEST_TABLE
INSTEAD OF INSERT AS
DECLARE @REQ_ID INT
DECLARE @REQ_COLOR VARCHAR(30)
DECLARE @REQ_START INT 
BEGIN  
  SELECT @REQ_COLOR= (SELECT ISNULL(REQ_COLOR,'NA') FROM INSERTED)

  SELECT @REQ_START = (SELECT CASE WHEN @REQ_COLOR = 'Red' THEN 0
                    WHEN @REQ_COLOR = 'Blue' THEN 50000 
                    ELSE 100000 END)

  SELECT @REQ_ID = ISNULL(MAX(REQ_ID),@REQ_START)+1 FROM REQUEST_TABLE   
    WHERE REQ_COLOR = @REQ_COLOR  

  INSERT INTO REQUEST_TABLE (REQ_ID,REQ_COLOR)
   VALUES (@REQ_ID,@REQ_COLOR)
END;

いくつかの挿入ステートメントの後

INSERT INTO REQUEST_TABLE VALUES(NULL,'Red');
INSERT INTO REQUEST_TABLE VALUES(NULL,'Red');
INSERT INTO REQUEST_TABLE VALUES(NULL,'Red');

INSERT INTO REQUEST_TABLE VALUES(NULL,'Blue');
INSERT INTO REQUEST_TABLE VALUES(NULL,'Blue');
INSERT INTO REQUEST_TABLE VALUES(NULL,'Blue');

INSERT INTO REQUEST_TABLE VALUES(NULL,'Yellow');
INSERT INTO REQUEST_TABLE VALUES(NULL,'Yellow');
INSERT INTO REQUEST_TABLE VALUES(NULL,'Yellow');

SqlFiddleに同じ結果を追加しました。含めるものを見逃した場合はお知らせください。

編集

柔軟な要件に対応するためにFiddleを更新しました。

于 2013-09-23T10:55:06.467 に答える