Здравствуйте!
Сегодня столкнулся с проблемой, что создавая счета, в базу были внесены для двух разных счетов одинаковые номера.
Менеджеры работают через Web-сервисы. Когда посмотрел на дату создания счетов с одинаковым номером, увидел разницу в 1 секунду!
Как избежать в будущем дублирование нумерации счетов, которые создаются за короткий промежуток времени?
Нравится
Что бы не было дублирования номера должны генерится в одном месте. Логично в данном случае этим местом выбрать СУБД. Т.е. либо тригер, как предложил Александр Кравчук, но тогда не будет видно номера при редактировании. Либо слать запрос на получение номера до поста, но тогда в нумераци будут дырки.
Пока писал придумал еще простейший в реализации вариант - повесить уникальный ключ на поле :)
Варианты
"Александр Кравчук" написал:
сделать триггер
или
"Underscore a.k.a. _" написал:
повесить уникальный ключ на поле
неудобны: или уникальный индекс будет выдавать ошибку или Вы не будет видеть какой номер у документа, счета и т.п.
Думаю самым приемлемым способом, будет светить какой-то номер во время добавления - пусть это будет через системные настройки, а перед добавлением пересчитывать на корректный: запускать ХП с выходным параметром, которая в свою очередь вычитывает текущее значение системной настройки, увеличивает на единицу, сохраняет и возвращает результат для сохранения в номере. Можно для надежности в транзакции ее запускать. Можно эту логику и в триггер вынести. Но тогда немного рассредоточится логика по формированию номера: и в конфигурации, и в триггере. Неудобно будет сопровождать. А ХП можно сделать универсальной - допустим только для числовых последовательностей.
ЗЫ
"Underscore a.k.a. _" написал:
в нумераци будут дырки.
Для этого есть или ну очень надуманные способы или запретить удаление :)
Соглашусь, что для
"Виталий Ковалишин aka samael" написал:
Как избежать в будущем дублирование нумерации счетов, которые создаются за короткий промежуток времени?
уникальный индекс самое то - на уровне сервера будет проверка целостности данных.
Такой вот вариант надумал:
CREATE TRIGGER tr_tbl_Invoice_IIN ON [tbl_Invoice] AFTER INSERT AS BEGIN declare @ID uniqueidentifier, @NewNumber int, @Number nvarchar(250), @NumberMask varchar(10) SET NOCOUNT ON; select @ID = [ID], @Number = [InvoiceNumber] from inserted if (@Number is null) begin set @NumberMask = ( select [StringValue] from [tbl_SystemSetting] where [Code] = 'InvoiceMask' ) set @NewNumber = ( select [IntegerValue] +1 from [tbl_SystemSetting] where [Code] = 'InvoiceNumber' ) set @Number = REPLACE(@NumberMask, '%1', @NewNumber) update [tbl_SystemSetting] set [IntegerValue] = @NewNumber where [Code] = 'InvoiceNumber' update [tbl_Invoice] set [InvoiceNumber] = @Number where [ID] = @ID end END
--
Cogito, ergo sum
"Виталий Ковалишин aka samael" написал:
SELECT @ID = [ID], @Number = [InvoiceNumber] FROM inserted
Учтите, что inserted может содержать несколько записей.
"Осауленко Александр" написал:Думаю самым приемлемым способом, будет светить какой-то номер
А чем показывать в общем случае неправильный номер лучше, чем нек показывать его вообще.
"Осауленко Александр" написал:Учтите, что inserted может содержать несколько записей.
учтено:
ALTER TRIGGER tr_tbl_Invoice_IIN ON [tbl_Invoice] AFTER INSERT AS BEGIN DECLARE @ID uniqueidentifier, @NewNumber int, @Number nvarchar(250), @NumberMask varchar(10) SET NOCOUNT ON; DECLARE c_Invoices CURSOR FOR select [ID], [InvoiceNumber] from inserted OPEN c_Invoices WHILE 1 = 1 BEGIN FETCH c_Invoices INTO @ID, @Number IF @@fetch_status = -1 BREAK IF @@fetch_status = -2 CONTINUE if (@Number is null) begin set @NumberMask = ( select [StringValue] from [tbl_SystemSetting] where [Code] = 'InvoiceMask' ) set @NewNumber = ( select [IntegerValue] +1 from [tbl_SystemSetting] where [Code] = 'InvoiceNumber' ) set @Number = REPLACE(@NumberMask, '%1', @NewNumber) update [tbl_SystemSetting] set [IntegerValue] = @NewNumber where [Code] = 'InvoiceNumber' update [tbl_Invoice] set [InvoiceNumber] = @Number where [ID] = @ID end END CLOSE c_Invoices DEALLOCATE c_Invoices END GO
"Осауленко Александр" написал:Можно эту логику и в триггер вынести. Но тогда немного рассредоточится логика по формированию номера: и в конфигурации, и в триггере. Неудобно будет сопровождать.
Не нарушена, все из системных настроек :)
--
Cogito, ergo sum
"Underscore a.k.a. _" написал:
А чем показывать в общем случае неправильный номер лучше, чем нек показывать его вообще.
Я почти на 100% уверен, что такая ситуация будет очень редко, да еще и пользователь на это обратит внимание в 1 случае из 1000. Т.е. почти не реально :)
Обычно это решается созданием отдельной таблицы с хранением последнего номера. И генерацией номера в нужный момент (в момент создания счета или при сохранении - в зависимости от логики).
Самое главное - чтобы транзакция по получению номера и его увеличению была очень-очень короткая (намного короче, чем транзакция по сохранению всего счета). Возможна еще блокировки записи на время транзакции. Тогда одинаковых номеров не будет.
А "дырки" в такой нумерации - это практически неизбежное зло
"Владимир Соколов" написал:Обычно это решается созданием отдельной таблицы с хранением последнего номера. И генерацией номера в нужный момент (в момент создания счета или при сохранении - в зависимости от логики).
Владимир, я ничего не придумывал, просто использовал стандартную нумерацию счетов, которая:
1. хранит последний номер в таблице системных настроек
2. генерит номер при сохранении
:)
Все что нужно было, так это перенести логику со скриптов на уровень СУБД... Так и сделал!
--
Cogito, ergo sum
"Виталий Ковалишин aka samael" написал:ALTER TRIGGER tr_tbl_Invoice_IIN ON [tbl_Invoice] AFTER INSERT AS BEGIN DECLARE @ID uniqueidentifier, @NewNumber int, ...
Я бы еще добавил условие для репликации, даже если у Вас пока ее и нет, что бы потом не "шерстить" БД:
ALTER TRIGGER tr_tbl_Invoice_IIN ON [tbl_Invoice] AFTER INSERT AS BEGIN IF (SYSTEM_USER = 'TS_REPLICATION') RETURN DECLARE @ID uniqueidentifier, @NewNumber int, ...
Здравствуйте! В очередной раз поражаюсь возможностям T-SQL:
"Виталий Ковалишин aka samael" написал:... SET @NumberMask = ( SELECT [StringValue] FROM [tbl_SystemSetting] WHERE [Code] = 'InvoiceMask' ) SET @NewNumber = ( SELECT [IntegerValue] +1 FROM [tbl_SystemSetting] WHERE [Code] = 'InvoiceNumber' ) SET @Number = REPLACE(@NumberMask, '%1', @NewNumber) UPDATE [tbl_SystemSetting] SET [IntegerValue] = @NewNumber WHERE [Code] = 'InvoiceNumber' UPDATE [tbl_Invoice] SET [InvoiceNumber] = @Number WHERE [ID] = @ID ......
Можно заменить на:
... UPDATE [tbl_SystemSetting] SET @Number = (SELECT REPLACE([StringValue], '%1', [IntegerValue] + 1) FROM [tbl_SystemSetting] WHERE [Code] = 'InvoiceMask'), [IntegerValue] = [IntegerValue] + 1 WHERE [Code] = 'InvoiceNumber' UPDATE [tbl_Invoice] SET [InvoiceNumber] = @Number WHERE [ID] = @ID ...
Красота!:)