Как реализовать последовательности в Microsoft SQL Server?
У кого-нибудь есть хороший способ реализовать что-то вроде последовательности на SQL-сервере?
Иногда вы просто не хотите использовать GUID, кроме того, что они уродливы, как черт. Может быть, последовательность, которую вы хотите, не является числовой? Кроме того, вставляя строку, а затем спрашивая БД, что число просто кажется таким хакерским.
Ответы
Ответ 1
Sql Server 2012 представил SEQUENCE
объекты, которые позволяют генерировать последовательные числовые значения, не связанные с какой-либо таблицей.
Создание их легко:
CREATE SEQUENCE Schema.SequenceName
AS int
INCREMENT BY 1 ;
Пример использования их перед вставкой:
DECLARE @NextID int ;
SET @NextID = NEXT VALUE FOR Schema.SequenceName;
-- Some work happens
INSERT Schema.Orders (OrderID, Name, Qty)
VALUES (@NextID, 'Rim', 2) ;
См. мой блог для углубленного изучения того, как использовать последовательности:
http://sqljunkieshare.com/2011/12/11/sequences-in-sql-server-2012-implementingmanaging-performance/
Ответ 2
Как sqljunkieshare правильно сказано, начиная с SQL Server 2012 существует встроенный SEQUENCE
.
Оригинальный вопрос не уточняет, но я предполагаю, что требования к последовательности следующие:
- Он должен предоставить набор уникальных растущих чисел
- Если несколько пользователей запрашивают следующее значение последовательности одновременно, все они должны получать разные значения. Другими словами, уникальность сгенерированных значений гарантируется независимо от того, что.
- Из-за возможности откат некоторых транзакций возможно, что конечный результат сгенерированных номеров будет иметь пробелы.
Я хотел бы прокомментировать выражение в исходном вопросе:
"Кроме того, вставляя строку, а затем спрашивая БД, какой номер просто кажется настолько хаки".
Ну, здесь мы мало что можем сделать. БД является поставщиком последовательных номеров, а БД обрабатывает все эти проблемы concurrency, с которыми вы не справитесь. Я не вижу альтернативы просить БД для следующего значения последовательности. Должна быть атомная операция "дать мне следующее значение последовательности", и только такая БД может обеспечить такую атомную операцию. Клиентский код не может гарантировать, что он единственный, кто работает с этой последовательностью.
Чтобы ответить на вопрос в заголовке "Как бы вы реализовали последовательности" - мы используем 2008, который не имеет функции SEQUENCE
, поэтому после некоторого чтения в этом разделе я получил следующее.
Для каждой необходимой последовательности я создаю отдельную вспомогательную таблицу только с одним столбцом IDENTITY
(таким же образом, как и в 2012 году, вы создадите отдельный объект Sequence).
CREATE TABLE [dbo].[SequenceContractNumber]
(
[ContractNumber] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_SequenceContractNumber] PRIMARY KEY CLUSTERED ([ContractNumber] ASC)
)
Вы можете указать начальное значение и прирост для него.
Затем я создаю хранимую процедуру, которая возвращает следующее значение последовательности.
Процедура запускает транзакцию, вставляет строку в вспомогательную таблицу, запоминает полученное значение идентификации и откатывает транзакцию. Таким образом, вспомогательная таблица всегда остается пустой.
CREATE PROCEDURE [dbo].[GetNewContractNumber]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @Result int = 0;
IF @@TRANCOUNT > 0
BEGIN
-- Procedure is called when there is an active transaction.
-- Create a named savepoint
-- to be able to roll back only the work done in the procedure.
SAVE TRANSACTION ProcedureGetNewContractNumber;
END ELSE BEGIN
-- Procedure must start its own transaction.
BEGIN TRANSACTION ProcedureGetNewContractNumber;
END;
INSERT INTO dbo.SequenceContractNumber DEFAULT VALUES;
SET @Result = SCOPE_IDENTITY();
-- Rollback to a named savepoint or named transaction
ROLLBACK TRANSACTION ProcedureGetNewContractNumber;
RETURN @Result;
END
Несколько заметок о процедуре.
Во-первых, было не очевидно, как вставить строку в таблицу, которая имеет только один столбец идентификатора. Ответ: DEFAULT VALUES
.
Затем я хотел, чтобы процедура работала правильно, если она была вызвана внутри другой транзакции. Простой ROLLBACK
откатывает все, если есть вложенные транзакции. В моем случае мне нужно откат только INSERT
в вспомогательную таблицу, поэтому я использовал SAVE TRANSACTION
.
ROLLBACK TRANSACTION без имени savepoint или имени транзакции возвращается к началу транзакции. Когда гнездование транзакций, этот же оператор возвращает все внутренние транзакции в самый внешний оператор BEGIN TRANSACTION.
Вот как я использую эту процедуру (внутри некоторой другой большой процедуры, которая, например, создает новый контракт):
DECLARE @VarContractNumber int;
EXEC @VarContractNumber = dbo.GetNewContractNumber;
Все работает отлично, если вам нужно генерировать значения последовательности по одному. В случае контрактов каждый контракт создается индивидуально, поэтому этот подход работает отлично. Я могу быть уверен, что все контракты всегда имеют уникальные номера контрактов.
NB: просто чтобы не допустить возможных вопросов. Эти номера контрактов в дополнение к суррогатной идентификационной информации, которую имеет таблица моих контрактов. Суррогатный ключ - это внутренний ключ, который используется для ссылочной целостности. Срочный номер контракта - это номер для человека, который указан в контракте. Кроме того, в той же таблице контрактов содержатся как окончательные контракты, так и предложения, которые могут стать контрактами или могут оставаться в качестве предложений навсегда. Оба предложения и контракты содержат очень похожие данные, поэтому они хранятся в одной таблице. Предложение может стать контрактом, просто изменив флаг в одной строке. Предложения нумеруются с использованием отдельной последовательности чисел, для которой у меня есть вторая таблица SequenceProposalNumber
и вторая процедура GetNewProposalNumber
.
В последнее время я столкнулся с проблемой.
Мне нужно было генерировать значения последовательности в пакете, а не один за другим.
Мне нужна процедура, которая обрабатывала бы все платежи, которые были получены в течение данного квартала за один раз. Результатом такой обработки может быть ~ 20 000 транзакций, которые я хочу записать в таблице Transactions
. У меня такой же дизайн здесь. В таблице Transactions
есть внутренний столбец IDENTITY
, который пользователь никогда не видит и имеет удобный для транзакции номер транзакции, который будет напечатан в инструкции. Итак, мне нужен способ генерации заданного количества уникальных значений в пакете.
По сути, я использовал один и тот же подход, но есть несколько особенностей.
Во-первых, нет прямого способа вставить несколько строк в таблицу только с одним столбцом IDENTITY
. Хотя существует способ обхода (ab) с помощью MERGE
, я не использовал его в конце. Я решил, что добавить фиктивный столбец Filler
было проще. Таблица My Sequence будет всегда пустой, поэтому дополнительный столбец не имеет значения.
Таблица помощников выглядит так:
CREATE TABLE [dbo].[SequenceS2TransactionNumber]
(
[S2TransactionNumber] [int] IDENTITY(1,1) NOT NULL,
[Filler] [int] NULL,
CONSTRAINT [PK_SequenceS2TransactionNumber]
PRIMARY KEY CLUSTERED ([S2TransactionNumber] ASC)
)
Процедура выглядит следующим образом:
-- Description: Returns a list of new unique S2 Transaction numbers of the given size
-- The caller should create a temp table #NewS2TransactionNumbers,
-- which would hold the result
CREATE PROCEDURE [dbo].[GetNewS2TransactionNumbers]
@ParamCount int -- not NULL
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET XACT_ABORT ON;
IF @@TRANCOUNT > 0
BEGIN
-- Procedure is called when there is an active transaction.
-- Create a named savepoint
-- to be able to roll back only the work done in the procedure.
SAVE TRANSACTION ProcedureGetNewS2TransactionNos;
END ELSE BEGIN
-- Procedure must start its own transaction.
BEGIN TRANSACTION ProcedureGetNewS2TransactionNos;
END;
DECLARE @VarNumberCount int;
SET @VarNumberCount =
(
SELECT TOP(1) dbo.Numbers.Number
FROM dbo.Numbers
ORDER BY dbo.Numbers.Number DESC
);
-- table variable is not affected by the ROLLBACK, so use it for temporary storage
DECLARE @TableTransactionNumbers table
(
ID int NOT NULL
);
IF @VarNumberCount >= @ParamCount
BEGIN
-- the Numbers table is large enough to provide the given number of rows
INSERT INTO dbo.SequenceS2TransactionNumber
(Filler)
OUTPUT inserted.S2TransactionNumber AS ID INTO @TableTransactionNumbers(ID)
-- save generated unique numbers into a table variable first
SELECT TOP(@ParamCount) dbo.Numbers.Number
FROM dbo.Numbers
OPTION (MAXDOP 1);
END ELSE BEGIN
-- the Numbers table is not large enough to provide the given number of rows
-- expand the Numbers table by cross joining it with itself
INSERT INTO dbo.SequenceS2TransactionNumber
(Filler)
OUTPUT inserted.S2TransactionNumber AS ID INTO @TableTransactionNumbers(ID)
-- save generated unique numbers into a table variable first
SELECT TOP(@ParamCount) n1.Number
FROM dbo.Numbers AS n1 CROSS JOIN dbo.Numbers AS n2
OPTION (MAXDOP 1);
END;
/*
-- this method can be used if the SequenceS2TransactionNumber
-- had only one identity column
MERGE INTO dbo.SequenceS2TransactionNumber
USING
(
SELECT *
FROM dbo.Numbers
WHERE dbo.Numbers.Number <= @ParamCount
) AS T
ON 1 = 0
WHEN NOT MATCHED THEN
INSERT DEFAULT VALUES
OUTPUT inserted.S2TransactionNumber
-- return generated unique numbers directly to the caller
;
*/
-- Rollback to a named savepoint or named transaction
ROLLBACK TRANSACTION ProcedureGetNewS2TransactionNos;
IF object_id('tempdb..#NewS2TransactionNumbers') IS NOT NULL
BEGIN
INSERT INTO #NewS2TransactionNumbers (ID)
SELECT TT.ID FROM @TableTransactionNumbers AS TT;
END
END
И вот как он используется (внутри некоторой большой хранимой процедуры, которая вычисляет транзакции):
-- Generate a batch of new unique transaction numbers
-- and store them in #NewS2TransactionNumbers
DECLARE @VarTransactionCount int;
SET @VarTransactionCount = ...
CREATE TABLE #NewS2TransactionNumbers(ID int NOT NULL);
EXEC dbo.GetNewS2TransactionNumbers @ParamCount = @VarTransactionCount;
-- use the generated numbers...
SELECT ID FROM #NewS2TransactionNumbers AS TT;
Здесь есть несколько вещей, требующих объяснения.
Мне нужно вставить заданное количество строк в таблицу SequenceS2TransactionNumber
. Для этого я использую таблицу-помощник Numbers
. Эта таблица просто содержит целые числа от 1 до 100 000. Он также используется в других местах в системе. Я проверяю, достаточно ли строк в таблице Numbers
и расширяет его до 100 000 * 100 000 путем перекрестного соединения с самим собой, если это необходимо.
Мне нужно сохранить результат массовой вставки и передать его вызывающему. Один из способов передачи таблицы за пределами хранимой процедуры - использовать временную таблицу. Я не могу использовать параметр table-valued здесь, потому что он доступен только для чтения. Кроме того, я не могу напрямую вставить созданные значения последовательности во временную таблицу #NewS2TransactionNumbers
. Я не могу использовать #NewS2TransactionNumbers
в предложении OUTPUT
, потому что ROLLBACK
очистит его. К счастью, переменные таблицы не влияют на ROLLBACK
.
Итак, я использую переменную таблицы @TableTransactionNumbers
как пункт назначения OUTPUT
. Затем я ROLLBACK
транзакция для очистки таблицы Sequence. Затем скопируйте сгенерированные значения последовательности из переменной таблицы @TableTransactionNumbers
во временную таблицу #NewS2TransactionNumbers
, так как для вызывающей стороны хранимой процедуры может быть видна только временная таблица #NewS2TransactionNumbers
. Переменная таблицы @TableTransactionNumbers
не отображается вызывающей стороне хранимой процедуры.
Кроме того, можно использовать предложение OUTPUT
для отправки сгенерированной последовательности непосредственно вызывающему абоненту (как вы можете видеть в комментарии, который использует MERGE
). Он отлично работает сам по себе, но мне нужны сгенерированные значения в некоторой таблице для дальнейшей обработки в вызывающей хранимой процедуре. Когда я попробовал что-то вроде этого:
INSERT INTO @TableTransactions (ID)
EXEC dbo.GetNewS2TransactionNumbers @ParamCount = @VarTransactionCount;
Я получаю сообщение об ошибке
Невозможно использовать оператор ROLLBACK в инструкции INSERT-EXEC.
Но мне нужно ROLLBACK
внутри EXEC
, поэтому у меня получилось так много временных таблиц.
В конце концов, как бы неплохо было перейти на последнюю версию SQL-сервера, у которой есть правильный SEQUENCE
объект.
Ответ 3
Столбец Identity примерно аналогичен последовательности.
Ответ 4
Вы можете просто использовать простые старые таблицы и использовать их как последовательности. Это означает, что ваши вставки всегда будут:
BEGIN TRANSACTION
SELECT number from plain old table..
UPDATE plain old table, set the number to be the next number
INSERT your row
COMMIT
Но не делайте этого. Блокировка будет плохой...
Я начал на SQL Server, и для меня схема Oracle "sequence" выглядела как взлома. Я предполагаю, что вы исходите из другого направления и к вам, а scope_identity() выглядит как хак.
Поверните. Когда в Риме делайте, как делают римляне.
Ответ 5
То, что я использовал для решения этой проблемы, - это таблица "Последовательности", в которой хранятся все мои последовательности и хранимая процедура "nextval".
Таблица Sql:
CREATE TABLE Sequences (
name VARCHAR(30) NOT NULL,
value BIGINT DEFAULT 0 NOT NULL,
CONSTRAINT PK_Sequences PRIMARY KEY (name)
);
PK_Sequences используется, чтобы быть уверенным, что никогда не будет последовательностей с тем же именем.
Сохраненная процедура Sql:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'nextVal') AND type in (N'P', N'PC')) DROP PROCEDURE nextVal;
GO
CREATE PROCEDURE nextval
@name VARCHAR(30)
AS
BEGIN
DECLARE @value BIGINT
BEGIN TRANSACTION
UPDATE Sequences
SET @value=value=value + 1
WHERE name = @name;
-- SELECT @value=value FROM Sequences WHERE [email protected]
COMMIT TRANSACTION
SELECT @value AS nextval
END;
Вставьте несколько последовательностей:
INSERT INTO Sequences(name, value) VALUES ('SEQ_Workshop', 0);
INSERT INTO Sequences(name, value) VALUES ('SEQ_Participant', 0);
INSERT INTO Sequences(name, value) VALUES ('SEQ_Invoice', 0);
Наконец, получим следующее значение последовательности,
execute nextval 'SEQ_Participant';
Некоторый код С#, чтобы получить следующее значение из таблицы Sequence,
public long getNextVal()
{
long nextval = -1;
SqlConnection connection = new SqlConnection("your connection string");
try
{
//Connect and execute the select sql command.
connection.Open();
SqlCommand command = new SqlCommand("nextval", connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@name", SqlDbType.NVarChar).Value = "SEQ_Participant";
nextval = Int64.Parse(command.ExecuteScalar().ToString());
command.Dispose();
}
catch (Exception) { }
finally
{
connection.Dispose();
}
return nextval;
}
Ответ 6
Последовательности, реализованные Oracle, требуют вызова базы данных перед вставкой.
идентификаторы, реализованные SQL Server, требуют вызова базы данных после вставки.
Один не более хакерский, чем другой. Сетевой эффект один и тот же - зависимость/зависимость от хранилища данных для предоставления уникальных значений искусственных ключей и (в большинстве случаев) двух вызовов в хранилище.
Я предполагаю, что ваша реляционная модель основана на искусственных ключах, и в этом контексте я предложу следующее наблюдение:
Мы никогда не должны пытаться наполнить искусственные ключи смыслом; их единственная цель - связать связанные записи.
Каковы ваши потребности в заказе данных? может ли он обрабатываться в представлении (представлении) или это истинный атрибут ваших данных, который должен сохраняться?
Ответ 7
В SQL Server 2012 вы можете просто использовать
CREATE SEQUENCE
В 2005 и 2008 годах вы можете получить произвольный список последовательных чисел, используя общее табличное выражение.
Вот пример (обратите внимание, что параметр MAXRECURSION важен):
DECLARE @MinValue INT = 1;
DECLARE @MaxValue INT = 1000;
WITH IndexMaker (IndexNumber) AS
(
SELECT
@MinValue AS IndexNumber
UNION ALL SELECT
IndexNumber + 1
FROM
IndexMaker
WHERE IndexNumber < @MaxValue
)
SELECT
IndexNumber
FROM
IndexMaker
ORDER BY
IndexNumber
OPTION
(MAXRECURSION 0)
Ответ 8
Создайте таблицу сцен с идентификатором на ней.
Перед загрузкой таблицы сцен обрезайте и повторно введите идентификатор, начинающийся с 1.
Загрузите таблицу. Каждая строка теперь имеет уникальное значение от 1 до N.
Создайте таблицу, содержащую порядковые номера. Это может быть несколько строк, по одному для каждой последовательности.
Найдите номер последовательности из созданной последовательности.
Обновите номер последовательности, добавив количество строк в таблице этапа к порядковому номеру.
Обновите идентификатор таблицы сцен, добавив номер последовательности, который вы просмотрели. Это простой одноэтапный процесс.
или
Загрузите целевую таблицу, добавьте порядковый номер в идентификатор при загрузке в ETL. Это может использовать преимущества загрузчика и допускать другие преобразования.
Ответ 9
Рассмотрим следующий фрагмент.
CREATE TABLE [SEQUENCE](
[NAME] [varchar](100) NOT NULL,
[NEXT_AVAILABLE_ID] [int] NOT NULL,
CONSTRAINT [PK_SEQUENCES] PRIMARY KEY CLUSTERED
(
[NAME] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE PROCEDURE CLAIM_IDS (@sequenceName varchar(100), @howMany int)
AS
BEGIN
DECLARE @result int
update SEQUENCE
set
@result = NEXT_AVAILABLE_ID,
NEXT_AVAILABLE_ID = NEXT_AVAILABLE_ID + @howMany
where Name = @sequenceName
Select @result as AVAILABLE_ID
END
GO
Ответ 10
Как sqljunkiesshare states, последовательности были добавлены в SQL Server 2012. Вот как это сделать в графическом интерфейсе. Это равнозначно:
CREATE SEQUENCE Schema.SequenceName
AS int
INCREMENT BY 1 ;
- В Проводнике объектов разверните папку Programmability
- В папке Programmability щелкните правой кнопкой мыши по последовательностям
как показано ниже:
![введите описание изображения здесь]()
- Подчеркнуты значения, которые вы обновили, чтобы получить
эквивалент вышеприведенного оператора SQL, однако я бы рассмотрел
изменяя их в зависимости от ваших потребностей (см. примечания ниже).
![введите описание изображения здесь]()
Примечания:
Ответ 11
Я полностью согласен и сделал это в прошлом году по проекту.
Я только что создал таблицу с именем последовательности, текущего значения и суммы приращения.
Затем я создал 2 procs для их добавления и удаления. И 2 функции для получения следующего и получения тока.
Ответ 12
Если вы хотите вставить данные с помощью последовательного ключа, но вы не хотите снова запрашивать базу данных, чтобы получить только что вставленный ключ, я думаю, что ваши два варианта:
- Выполните вставку с помощью хранимой процедуры, которая возвращает вновь вставленное значение ключа
- Внедрить последовательность на стороне клиента (чтобы вы знали новый ключ перед вставкой)
Если я выполняю генерацию ключей на стороне клиента, я люблю GUID. Я думаю, что они красивы, как черт.
row["ID"] = Guid.NewGuid();
Эта линия должна укладываться на капот спортивного автомобиля где-то.
Ответ 13
Если вы используете SQL Server 2005, у вас есть возможность использовать Row_Number
Ответ 14
Другая проблема с столбцами идентификаторов заключается в том, что если у вас более одной таблицы, где порядковые номера должны быть уникальными, столбец идентификаторов не работает. И, как упоминает Кори Трейгер, реализация последовательности ваших действий в рулонах может вызвать некоторые проблемы с блокировкой.
Наиболее справедливо эквивалентные решения, похоже, состоят в том, чтобы создать таблицу SQL Server с одним столбцом для идентификатора, который заменяет отдельный тип объекта "sequence". Например, если в Oracle у вас будет две таблицы из одной последовательности, такие как Dogs < - sequence object → Cats, тогда в SQL Server вы создадите три объекта базы данных, все таблицы, такие как Dogs < - Pets с идентификационным столбцом, → Кошки. Вы вставляете строку в таблицу "Домашние животные", чтобы получить порядковый номер, где вы обычно используете NEXTVAL, а затем вставляете в таблицу "Собаки или кошки", как обычно, как только вы получите фактический тип домашнего животного от пользователя. Любые дополнительные общие столбцы можно перенести из таблиц Dogs/Cats в таблицу супертипов Pets, с некоторыми последствиями: 1) для каждой последовательности будет одна строка, 2) любые столбцы, которые не могут быть заполнены при получении номера последовательности, будут должны иметь значения по умолчанию и 3) для соединения всех столбцов потребуется объединение.
Ответ 15
По SQL вы можете использовать эту стратегию;
CREATE SEQUENCE [dbo].[SequenceFile]
AS int
START WITH 1
INCREMENT BY 1 ;
и прочитайте уникальное следующее значение с этим SQL
SELECT NEXT VALUE FOR [dbo].[SequenceFile]
Ответ 16
СДЕЛОК БЕЗОПАСНОСТИ! Для версий 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"'