Как бы вы реализовали последовательности в Microsoft SQL Server? - PullRequest
33 голосов
/ 12 ноября 2008

У кого-нибудь есть хороший способ реализовать что-то вроде последовательности в SQL-сервере?

Иногда вы просто не хотите использовать GUID, кроме того факта, что они безобразны. Может быть, последовательность, которую вы хотите, не числовая? Кроме того, вставить строку, а затем спросить у БД, какое это число, кажется таким хакерским.

Ответы [ 16 ]

0 голосов
/ 25 января 2018

БЕЗОПАСНОСТЬ ТРАНЗАКЦИИ! Для версий SQLServer до 2012 года ... (спасибо Мэтту Дж.) В этом обсуждении упускается одна вещь - безопасность транзакций. Если вы получаете номер из последовательности, этот номер должен быть уникальным, и никакое другое приложение или код не может получить этот номер. В моем случае мы часто извлекаем уникальные номера из последовательностей, но фактическая транзакция может занимать значительное количество времени, поэтому мы не хотим, чтобы кто-либо еще получал такой же номер, прежде чем мы совершим транзакцию. Нам нужно было подражать поведению последовательностей оракула , где число было зарезервировано при извлечении. Мое решение состоит в том, чтобы использовать xp_cmdshell для получения отдельного сеанса / транзакции в базе данных, чтобы мы могли немедленно обновить последовательность для всей базы данных даже до завершения транзакции.

--it is used like this:
-- use the sequence in either insert or select:
Insert into MyTable Values (NextVal('MySequence'), 'Foo');

SELECT NextVal('MySequence');

--you can make as many sequences as you want, by name:
SELECT NextVal('Mikes Other Sequence');

--or a blank sequence identifier
SELECT NextVal('');

Для решения требуется одна таблица для хранения значений использованной последовательности и процедура , которая создает вторую автономную транзакцию , чтобы гарантировать, что параллельные сеансы не запутываются. Вы можете иметь столько уникальных последовательностей, сколько захотите, на них ссылаются по имени. Приведенный ниже пример кода изменен, чтобы не запрашивать пользователя и метку даты в таблице истории последовательности (для аудита), но я подумал, что менее сложный вариант лучше для примера; -).

  CREATE TABLE SequenceHolder(SeqName varchar(40), LastVal int);

GO
CREATE function NextVAL(@SEQname varchar(40))
returns int
as
begin
    declare @lastval int
    declare @barcode int;

    set @lastval = (SELECT max(LastVal) 
                      FROM SequenceHolder
                     WHERE SeqName = @SEQname);

    if @lastval is null set @lastval = 0

    set @barcode = @lastval + 1;

    --=========== USE xp_cmdshell TO INSERT AND COMMINT NOW, IN A SEPERATE TRANSACTION =============================
    DECLARE @sql varchar(4000)
    DECLARE @cmd varchar(4000)
    DECLARE @recorded int;

    SET @sql = 'INSERT INTO SequenceHolder(SeqName, LastVal) VALUES (''' + @SEQname + ''', ' + CAST(@barcode AS nvarchar(50)) + ') '
    SET @cmd = 'SQLCMD -S ' + @@servername +
              ' -d ' + db_name() + ' -Q "' + @sql + '"'
    EXEC master..xp_cmdshell @cmd, 'no_output'

    --===============================================================================================================

    -- once submitted, make sure our value actually stuck in the table
    set @recorded = (SELECT COUNT(*) 
                       FROM SequenceHolder
                      WHERE SeqName = @SEQname
                        AND LastVal = @barcode);

    --TRIGGER AN ERROR 
    IF (@recorded != 1)
        return cast('Barcode was not recorded in SequenceHolder, xp_cmdshell FAILED!! [' + @cmd +']' as int);

    return (@barcode)

end

GO

COMMIT;

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

--- LOOSEN SECURITY SO THAT xp_cmdshell will run 
---- To allow advanced options to be changed.
EXEC sp_configure 'show advanced options', 1
GO
---- To update the currently configured value for advanced options.
RECONFIGURE
GO
---- To enable the feature.
EXEC sp_configure 'xp_cmdshell', 1
GO
---- To update the currently configured value for this feature.
RECONFIGURE
GO

—-Run SQLServer Management Studio as Administrator,
—- Login as domain user, not sqlserver user.

--MAKE A DATABASE USER THAT HAS LOCAL or domain LOGIN! (not SQL server login)
--insure the account HAS PERMISSION TO ACCESS THE DATABASE IN QUESTION.  (UserMapping tab in User Properties in SQLServer)

—grant the following
GRANT EXECUTE on xp_cmdshell TO [domain\user] 

—- run the following:
EXEC sp_xp_cmdshell_proxy_account 'domain\user', 'pwd'

--alternative to the exec cmd above: 
create credential ##xp_cmdshell_proxy_account## with identity = 'domain\user', secret = 'pwd'


-—IF YOU NEED TO REMOVE THE CREDENTIAL USE THIS
EXEC sp_xp_cmdshell_proxy_account NULL;


-—ways to figure out which user is actually running the xp_cmdshell command.
exec xp_cmdshell 'whoami.exe'  
EXEC xp_cmdshell 'osql -E -Q"select suser_sname()"'
EXEC xp_cmdshell 'osql -E -Q"select * from sys.login_token"'
0 голосов
/ 21 октября 2017

По SQL вы можете использовать эту стратегию;

CREATE SEQUENCE [dbo].[SequenceFile]
AS int
START WITH 1
INCREMENT BY 1 ;

и прочитайте уникальное следующее значение с этим SQL

SELECT NEXT VALUE FOR [dbo].[SequenceFile]
0 голосов
/ 17 октября 2011

Другая проблема со столбцами идентификаторов состоит в том, что если у вас несколько таблиц, в которых порядковые номера должны быть уникальными, столбец идентификаторов не будет работать. И, как упоминает Corey Trager, реализация последовательности типа «сам по себе» может представлять некоторые проблемы с блокировкой.

Наиболее простыми эквивалентными решениями, похоже, является создание таблицы SQL Server с одним столбцом для идентификатора, который заменяет отдельный тип объекта «sequence». Например, если в Oracle у вас будет две таблицы из одной последовательности, такие как Dogs <- sequence object -> Cats, то в SQL Server вы создадите три объекта базы данных, все таблицы, такие как Dogs <- Pets with identity column -> Кошки. Вы вставляете строку в таблицу «Домашние животные», чтобы получить порядковый номер, где вы обычно используете NEXTVAL, а затем вставляете в таблицу «Собаки или кошки», как обычно, когда вы получаете фактический тип домашнего животного от пользователя. Любые дополнительные общие столбцы могут быть перемещены из таблиц Dogs / Cats в таблицу супертипов Pets, с некоторыми последствиями, что 1) будет одна строка для каждого порядкового номера, 2) любые столбцы, которые не могут быть заполнены при получении порядкового номера, будут необходимо иметь значения по умолчанию и 3) для получения всех столбцов потребуется объединение.

0 голосов
/ 15 декабря 2008

Если вы используете SQL Server 2005, вы можете использовать Row_Number

0 голосов
/ 12 ноября 2008

Если вы хотите вставить данные с последовательным ключом, но не хотите снова запрашивать базу данных, чтобы получить только что вставленный ключ, я думаю, что вы можете выбрать только два варианта:

  1. Выполнить вставку с помощью хранимой процедуры, которая возвращает вновь вставленное значение ключа
  2. Реализация последовательности на стороне клиента (чтобы вы знали новый ключ перед вставкой)

Если я делаю генерацию ключей на стороне клиента, я люблю GUID. Я думаю, что они чертовски красивы.

row["ID"] = Guid.NewGuid();

Эта линия должна лежать где-то на капоте спортивного автомобиля.

0 голосов
/ 12 ноября 2008

Я полностью согласен и сделал это в прошлом году на проекте.

Я только что создал таблицу с именем последовательности, текущего значения и суммы приращения.

Затем я создал 2 процесса, чтобы добавить и удалить их. И 2 функции, чтобы получить следующий, и получить текущий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...