2011-01-27 11 views
1

私は親テーブルを持つcustomerテーブルを作成しています。 顧客表の既存のvarchar(4)列である会社IDの組み合わせであるcustomer表の主キーを作成することがあります(chagrin)。 customer.companySQL Server:カウンタと別の列の値に基づいてプライマリキーを生成

残りのvarchar(9)プライマリキーは、その会社内の顧客数を増やすゼロ埋め込みカウンタです。

など。 company = MSFTの場合、これはMSFTレコードの最初の挿入です。PKはMSFT00001、MSFT00002などの後続の挿入でMSFT00001 となります。 会社= INTLで最初のレコードが挿入されると、最初のレコードはbe INTL00001

私は他のstackoverflowレスポンスから作成したトリガーとudfの代わりに、まず始めました。

ALTER FUNCTION [dbo].[GetNextID] 
(
    @in varchar(9) 
) 
RETURNS varchar(9) AS 
BEGIN 
    DECLARE @prefix varchar(9); 
    DECLARE @res varchar(9); 
    DECLARE @pad varchar(9); 
    DECLARE @num int; 
    DECLARE @start int; 


if LEN(@in)<9 


begin 
    set @in = Left(@in + replicate('0',9) , 9) 
    end 

SET @start = PATINDEX('%[0-9]%',@in); 
SET @prefix = LEFT(@in, @start - 1); 


declare @tmp int; 
set @tmp = len(@in) 
declare @tmpvarchar varchar(9); 
set @tmpvarchar = RIGHT(@in, LEN(@in) - @start + 1) 
    SET @num = CAST( RIGHT(@in, LEN(@in) - @start + 1) AS int ) + 1 
    SET @pad = REPLICATE('0', 9 - LEN(@prefix) - CEILING(LOG(@num)/LOG(10))); 
    SET @res = @prefix + @pad + CAST(@num AS varchar); 

    RETURN @res 
END 

は、どのように私は私の代わりに値を挿入し、この主キーをインクリメントするトリガを書くでしょう。または私はそれをあきらめて、芝生の経営を始めなければなりませんか?

申し訳ありませんが、そのtmpvarchar変数SQLサーバーは、私にそれなしで奇妙な結果を与えていました。

+1

私はこれをしません。計算された列がサポートされている場合(2005+?)、IDENTITY値と接頭辞としての任意の任意の文字列を結合する計算列を追加します。 –

+0

@OMG Ponies:それは私の最初の考えでもありましたが、各社の顧客が1で始まるビジネス要件を満たすものではありません。 –

+0

@Joe:これまでに読んだことはありませんでした/だから、おそらくROW_NUMBERを使うだろうか? –

答えて

2

私は嫌いな人と同意しますが、「変更できないものを受け入れる」という原則は、ストレスレベルIMHO全体を下げる傾向があります。次の方法を試してみてください。

欠点

  • 単一列インサート。行を挿入するたびにストアドプロシージャを実行する必要があるため、新しい顧客テーブルに一括挿入を行うことはありません。
  • 鍵生成テーブルの競合が発生し、ブロックする可能性があります。
  • しかし、このアプローチはそれに関連する競合条件を持たず、本当に本当に私の感性を傷つけるためのハックでもありません。だから...

    まず、キー生成テーブルから始めます。それはあなたの会社の識別子と挿入が実行されるたびに浮上する整数カウンタを含む、各会社の1行を含みます。

    create table dbo.CustomerNumberGenerator 
    (
        company  varchar(8) not null , 
        curr_value int  not null default(1) , 
    
        constraint CustomerNumberGenerator_PK primary key clustered (company) , 
    
    ) 
    

    第二に、あなたはこのようなストアドプロシージャを(実際には、あなたが顧客レコードを挿入するための責任のストアドプロシージャにこのロジックを統合する場合があります。ビットのそれの詳細)必要があります。このストアドプロシージャは、唯一の引数として会社識別子(たとえば、 'MSFT')を受け入れます。このストアドプロシージャは、次の処理を実行します。

  • 企業IDを標準形式(たとえば、先頭と末尾の空白の大文字と小文字)にします。
  • キー生成テーブルに行を挿入します(アトミック操作)。
  • 単一のアトミック操作(更新ステートメント)では、指定された会社の現在のカウンタの値が取得され、インクリメントされます。
  • 顧客番号は、指定された方法で生成され、1行/ 1列のSELECTステートメントを介して呼び出し元に返されます。ここで

あなたが行く:

create procedure dbo.GetNewCustomerNumber 

    @company   varchar(8) 

as 

    set nocount     on 
    set ansi_nulls    on 
    set concat_null_yields_null on 
    set xact_abort    on 

    declare 
    @customer_number varchar(32) 

    -- 
    -- put the supplied key in canonical form 
    -- 
    set @company = ltrim(rtrim(upper(@company))) 

    -- 
    -- if the name isn't already defined in the table, define it. 
    -- 
    insert dbo.CustomerNumberGenerator (company) 
    select id = @company 
    where not exists (select * 
        from dbo.CustomerNumberGenerator 
        where company = @company 
        ) 

    -- 
    -- now, an interlocked update to get the current value and increment the table 
    -- 
    update CustomerNumberGenerator 
    set @customer_number = company + right('00000000' + convert(varchar,curr_value) , 8) , 
     curr_value  = curr_value + 1 
    where company = @company 

    -- 
    -- return the new unique value to the caller 
    -- 
    select customer_number = @customer_number 
    return 0 

go 

あなたは顧客テーブルに行を挿入するストアドプロシージャにこれを統合する場合があります理由は、それが1つのトランザクションにすべて一緒にそれをグロブなることです。それがなければ、あなたの顧客番号は、インサートが土地の転落に失敗した場合にギャップを生ずることがあります。

0

他の人が私の前で言ったように、計算された自動インクリメント値を持つプライマリキーを使用することは非常に悪い考えです。

あなたが(一番下を参照)に、あなたが欠点と一緒に暮らすことができる場合は許可されている場合は、私は次のことを示唆している:

通常の数値自動インクリメントキーと文字を使用します(4)列会社のIDのみが含まれています。
次に、テーブルから選択するときに、自動インクリメント列にrow_numberを使用し、それを会社IDと組み合わせることで、希望のように見える「キー」列(MSFT00001、MSFT00002 ,. ...)

例データ:

Id Company CustomerName 
------------------------------------ 
1 MSFT First MSFT customer 
2 MSFT Second MSFT customer 
3 ABCD First ABCD customer 
4 MSFT Third MSFT customer 
5 ABCD Second ABCD customer 

は今それで次のクエリを実行します:

create table customers 
(
    Id int identity(1,1) not null, 
    Company char(4) not null, 
    CustomerName varchar(50) not null 
) 

insert into customers (Company, CustomerName) values ('MSFT','First MSFT customer') 
insert into customers (Company, CustomerName) values ('MSFT','Second MSFT customer') 
insert into customers (Company, CustomerName) values ('ABCD','First ABCD customer') 
insert into customers (Company, CustomerName) values ('MSFT','Third MSFT customer') 
insert into customers (Company, CustomerName) values ('ABCD','Second ABCD customer') 

これは、このように見えるテーブルを作成します。

select 
    Company + right('00000' + cast(ROW_NUMBER() over (partition by Company order by Id) as varchar(5)),5) as SpecialKey, 
    * 
from 
    customers 

これは、同じテーブルを返しますが、あなたの「特殊キー」を持つ追加の列で:

SpecialKey Id Company CustomerName 
--------------------------------------------- 
ABCD00001 3 ABCD First ABCD customer 
ABCD00002 5 ABCD Second ABCD customer 
MSFT00001 1 MSFT First MSFT customer 
MSFT00002 2 MSFT Second MSFT customer 
MSFT00003 4 MSFT Third MSFT customer 

あなたはこのクエリでビューを作成し、誰もが見ていることを確認するために、誰もがそのビューを使用してみましょうことができ「特別なキー」列。

  1. あなたは、少なくともSQL Server 2005の動作するrow_numberため ために必要があります。

    しかし、この解決策は2つの欠点を持っています。

  2. テーブルから会社を削除すると、特殊キーの番号が変更されます。したがって、数値を変更したくない場合は、そのテーブルから何も削除されていないことを確認する必要があります。
関連する問題