Сервер SQL: генерировать первичный ключ на основе счетчика и другого значения столбца - PullRequest
1 голос
/ 28 января 2011

Я создаю таблицу клиентов с родительской таблицей, которая является компанией.Было продиктовано (огорчено), что я создам первичный ключ для таблицы клиентов, которая представляет собой комбинацию идентификатора компании, который является существующим столбцом varchar (4) в таблице клиентов, например customer.company

Остальная часть первичного ключа varchar (9) должна быть счетчиком с добавлением нуля, увеличивающимся на число клиентов в этой компании.

Например, где company = MSFT, и это первая вставка записи MSFT: PKдолжен быть MSFT00001 на последующих вставках, PK будет MSFT00001, MSFT00002 и т. д. Затем, когда company = INTL и вставлена ​​его первая запись, первая запись будет INTL00001

.Я создал из других ответов stackoverflow.

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-сервер давал мне странные результаты без нее.

Ответы [ 2 ]

2 голосов
/ 28 января 2011

Хотя я согласен с скептиками, принцип «принятия того, что не может быть изменено», как правило, снижает общий уровень стресса, ИМХО.Попробуйте следующий подход.

Недостатки

  • Только однорядные вставки.Вы не будете выполнять массовые вставки в новую таблицу клиентов, поскольку вам нужно будет выполнять хранимую процедуру каждый раз, когда вы хотите вставить строку.
  • Определенное количество конфликтов для таблицы генерации ключей,следовательно, существует вероятность блокирования.

С другой стороны, у этого подхода нет никаких условий гонки, связанных с ним, и это не слишком вопиющий взлом, чтобы действительно и действительно оскорбить моючувства.Итак ...

Сначала начнем с таблицы генерации ключей.Он будет содержать по 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») в качестве единственного аргумента.Эта хранимая процедура выполняет следующие действия:

  • Переводит идентификатор компании в каноническую форму (например, в верхний регистр и обрезает начальные / конечные пробелы).
  • Вставляет строку в таблицу генерации ключей, еслион еще не существует (атомарная операция).
  • В одной атомарной операции (оператор обновления) извлекается текущее значение счетчика для указанной компании, а затем увеличивается.
  • Затем номер клиента генерируется указанным способом и возвращается вызывающему абоненту с помощью оператора 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

Причина, по которой вы можете захотеть интегрировать это в хранимую процедуру, вставляющую строку в таблицу customer, заключается в том, что она объединяет все это в одну транзакцию;без этого номера ваших клиентов могут / получат пробелы, если вставка не удастся, земля откатится.

0 голосов
/ 28 января 2011

Как говорили другие до меня, использование первичного ключа с вычисленными значениями автоинкремента звучит как очень плохая идея!

Если вам позволено и если вы можете жить с недостатками (см. Внизу), я бы предложил следующее:

Используйте обычную числовую клавишу автоинкремента и символ (4) столбец, который содержит только идентификатор компании.
Затем, когда вы выбираете из таблицы, вы используете row_number в столбце автоинкремента и объединяете его с идентификатором компании, чтобы у вас был дополнительный столбец с «ключом»выглядит так, как вы хотели (MSFT00001, MSFT00002, ...)

Пример данных:

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

Это создаст таблицу, которая выглядит следующим образом:

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

Теперь запустите следующий запрос:

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. Вам нужен хотя бы SQLServer 2005 для того, чтобы row_number работал.
  2. Числа в специальном ключе изменятся при удалении компаний из таблицы.Поэтому, если вы не хотите, чтобы числа менялись, вы должны убедиться, что из этой таблицы ничего не удалено.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...