誰かがSQLサーバーのシーケンスのようなものを実装する良い方法を持っていますか?
GUIDがuseいだけでなく、GUIDを使いたくない場合もあります。たぶんあなたが望むシーケンスは数値ではありませんか?その上、行を挿入してから、DBにその数字が何であるかを尋ねるのは、とてもハッキングのようです。
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/
sqljunkieshare が正しく述べているように、SQL Server 2012以降では、組み込みの SEQUENCE
機能があります。
元の質問は明確ではありませんが、シーケンスの要件は次のとおりです。
元の質問の声明にコメントしたい:
「さらに、行を挿入してから、DBにその番号が非常にハッキングされているように見えるかを尋ねます。」
さて、ここでできることはあまりありません。 DBは連続番号のプロバイダーであり、DBは、自分では処理できないこれらの同時実行性の問題をすべて処理します。シーケンスの次の値をDBに尋ねるのに代わるものはありません。 atomic操作「シーケンスの次の値を与える」が必要で、DBのみがそのようなを提供できます。 atomic操作。クライアントコードは、シーケンスで作業する唯一のクライアントであることを保証できません。
「シーケンスをどのように実装しますか」というタイトルの質問に答えるために、SEQUENCE
機能を持たない2008を使用しているため、このトピックを読んだ後、次のようになりました。
必要なシーケンスごとに、1つのIDENTITY
列を持つ個別のヘルパーテーブルを作成します(2012年と同じ方法で、個別のSequenceオブジェクトを作成します)。
CREATE TABLE [dbo].[SequenceContractNumber]
(
[ContractNumber] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_SequenceContractNumber] PRIMARY KEY CLUSTERED ([ContractNumber] ASC)
)
開始値を指定して、増分することができます。次に、シーケンスの次の値を返すストアドプロシージャを作成します。プロシージャはトランザクションを開始し、ヘルパーテーブルに行を挿入し、生成されたID値を記憶し、トランザクションをロールバックします。したがって、ヘルパーテーブルは常に空のままです。
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
手順に関するいくつかのメモ。
まず、ID列が1つしかないテーブルに行を挿入する方法は明らかではありませんでした。答えはDEFAULT VALUES
です。
次に、別のトランザクション内で呼び出された場合にプロシージャが正しく機能するようにしたかった。単純な ROLLBACK
は、ネストされたトランザクションがある場合、すべてをロールバックします。私の場合、ヘルパーテーブルにINSERT
だけをロールバックする必要があるため、 SAVE TRANSACTION
を使用しました。
Savepoint_nameまたはtransaction_nameを指定しないROLLBACK TRANSACTIONは、トランザクションの先頭にロールバックします。トランザクションをネストする場合、この同じステートメントはすべての内部トランザクションを最も外側のBEGIN TRANSACTIONステートメントにロールバックします。
これは私が手順を使用する方法です(たとえば、新しいコントラクトを作成する他の大きな手順の内部):
DECLARE @VarContractNumber int;
EXEC @VarContractNumber = dbo.GetNewContractNumber;
シーケンス値を1つずつ生成する必要がある場合は、すべて正常に機能します。契約の場合、各契約は個別に作成されるため、このアプローチは完全に機能します。すべての契約には常に一意の契約番号が付いていると確信できます。
NB:考えられる質問を防ぐためだけです。これらの契約番号は、Contractsテーブルにある代理IDキーに追加されます。代理キーは、参照整合性のために使用される内部キーです。生成された契約番号は、契約書に印刷されている人間に優しい番号です。また、同じ契約表には最終契約とプロポーザルの両方が含まれており、これらは契約になることもあれば、プロポーザルとして永久に残ることもあります。プロポーザルとコントラクトはどちらも非常に類似したデータを保持しているため、同じテーブルに保持されます。プロポーザルは、1行のフラグを変更するだけで契約になります。プロポーザルには、2番目のテーブルSequenceProposalNumber
と2番目のプロシージャGetNewProposalNumber
がある個別の番号シーケンスを使用して番号が付けられます。
しかし、最近、私は問題に遭遇しました。シーケンス値を1つずつではなくバッチで生成する必要がありました。
特定の四半期に受け取ったすべての支払いを一度に処理する手順が必要です。そのような処理の結果は、Transactions
テーブルに記録したい〜20,000トランザクションになる可能性があります。ここには似たようなデザインがあります。 Transactions
テーブルには、エンドユーザーが決して見ることのない内部IDENTITY
列があり、ステートメントに印刷される人間にわかりやすいトランザクション番号があります。そのため、バッチで特定の数の一意の値を生成する方法が必要です。
基本的に、私は同じアプローチを使用しましたが、特徴はほとんどありません。
まず、1つのIDENTITY
列のみを持つテーブルに複数の行を挿入する直接的な方法はありません。 (ab)using MERGE
による回避策がありますが、最終的には使用しませんでした。ダミーのFiller
列を追加する方が簡単だと判断しました。シーケンステーブルは常に空になるので、余分な列は実際には関係ありません。
ヘルパーテーブルは次のようになります。
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に拡張します。
一括挿入の結果をどこかに保存し、何らかの方法で呼び出し元に渡す必要があります。ストアドプロシージャの外部でテーブルを渡す1つの方法は、一時テーブルを使用することです。残念ながら読み取り専用であるため、ここではテーブル値パラメーターを使用できません。また、生成されたシーケンス値を一時テーブル#NewS2TransactionNumbers
に直接挿入することはできません。 OUTPUT
句では#NewS2TransactionNumbers
を使用できません。ROLLBACK
がクリーンアップするためです。幸いなことに、テーブル変数はROLLBACK
の影響を受けません。
そのため、OUTPUT
句の宛先としてテーブル変数@TableTransactionNumbers
を使用します。次に、トランザクションをROLLBACK
して、シーケンステーブルをクリーンアップします。次に、生成されたシーケンス値をテーブル変数@TableTransactionNumbers
から一時テーブル#NewS2TransactionNumbers
にコピーします。これは、一時テーブル#NewS2TransactionNumbers
のみがストアドプロシージャの呼び出し元に表示されるためです。テーブル変数@TableTransactionNumbers
は、ストアドプロシージャの呼び出し元には表示されません。
また、OUTPUT
句を使用して、生成されたシーケンスを呼び出し元に直接送信することもできます(MERGE
を使用するコメント付きバリアントで確認できます)。それ自体は問題なく動作しますが、呼び出し元のストアドプロシージャでさらに処理するために、いくつかのテーブルに生成された値が必要でした。私がこのようなことを試みたとき:
INSERT INTO @TableTransactions (ID)
EXEC dbo.GetNewS2TransactionNumbers @ParamCount = @VarTransactionCount;
エラーが発生しました
INSERT-EXECステートメント内でROLLBACKステートメントを使用することはできません。
しかし、ROLLBACK
内にEXEC
が必要です。これが、非常に多くの一時テーブルを持つことになった理由です。
このすべての後、適切なSEQUENCE
オブジェクトを持つSQLサーバーの最新バージョンに切り替えるとしたら、どれほどいいでしょう。
ID列 は、おおよそシーケンスに似ています。
単純な古いテーブルを使用し、それらをシーケンスとして使用することができます。つまり、挿入は常に次のようになります。
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の「シーケンス」スキームはハックのように見えました。私はあなたが他の方向からあなたに来ていると推測し、scope_identity()はハックのように見えます。
それを乗り越えます。ローマにいるときは、ローマ人と同じようにします。
私がこの問題を解決するために使用した方法は、すべてのシーケンスを格納するテーブル「Sequences」と「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 name=@name
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';
シーケンステーブルから次の値を取得するC#コード、
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;
}
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)
Oracleによって実装されるシーケンスでは、挿入前にデータベースを呼び出す必要があります。 SQL Serverによって実装されたIDでは、挿入後にデータベースへの呼び出しが必要です。
1つはもう1つ以上のハックではありません。最終的な効果は同じです。データストアへの依存/依存により、一意の人工キー値と(ほとんどの場合)ストアへの2つの呼び出しが提供されます。
私はあなたのリレーショナルモデルが人工的なキーに基づいていると仮定しています、そしてこの文脈で、私は次の観察を提供します:
人工的なキーに意味を吹き込むことを決して試みるべきではありません。それらの唯一の目的は、関連するレコードをリンクすることです。
データの注文に関連するニーズは何ですか?ビュー(プレゼンテーション)で処理できますか、それとも永続化する必要があるデータの真の属性ですか?
次のスニペットを検討してください。
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
識別子を含むステージテーブルを作成します。
ステージテーブルを読み込む前に、識別子を切り捨てて再シードし、1から開始します。
テーブルをロードします。各行には1からNまでの一意の値があります。
シーケンス番号を保持するテーブルを作成します。これは、各シーケンスに1つずつ、複数の行になる場合があります。
作成したシーケンステーブルからシーケンス番号を検索します。ステージテーブルの行数をシーケンス番号に追加して、シーケンス番号を更新します。
検索したシーケンス番号を追加して、ステージテーブル識別子を更新します。これは簡単な1ステップのプロセスです。またはターゲットテーブルをロードし、ETLでロードするときにシーケンス番号を識別子に追加します。これは、バルクローダーを利用して、他の変換を可能にします。
sqljunkiesshare states のように、SQL Server 2012にシーケンスが追加されました。GUIでこれを行う方法を次に示します。これは次のものと同等です。
CREATE SEQUENCE Schema.SequenceName
AS int
INCREMENT BY 1 ;
ノート:
デフォルトの開始値、最小値、および最大値は、この場合intであるデータ型の範囲によって決定されました。 int以外のものを使用する場合のデータ型の範囲については、こちらをご覧ください 。
シーケンスを1から開始し、最小値を1にすることもできます。
昨年、プロジェクトでこれに同意しました。
シーケンスの名前、現在の値、および増分量を含むテーブルを作成しました。
次に、2つのプロシージャを作成して追加および削除しました。そして、次を取得し、現在を取得するための2つの関数。
ID列のもう1つの問題は、シーケンス番号を一意にする必要があるテーブルが複数ある場合、ID列が機能しないことです。また、Corey Tragerが言及しているように、独自のタイプのシーケンス実装では、ロックの問題が発生する場合があります。
最も単純な同等のソリューションは、IDの単一の列を持つSQL Serverテーブルを作成することであると思われます。これは、別のタイプの「シーケンス」オブジェクトの代わりになります。たとえば、OracleでDogs <-sequence object-> Catsなどの1つのシーケンスから2つのテーブルがあり、SQL ServerでDogs <-Pets with identity column->のような3つのデータベースオブジェクトを作成する場合猫。通常はNEXTVALを使用するシーケンス番号を取得するために、Petsテーブルに行を挿入し、ユーザーから実際のタイプのペットを取得した後に通常のようにDogsまたはCatsテーブルに挿入します。追加の一般的な列は、犬/猫の表からペットのスーパータイプの表に移動できます。その結果、1)各シーケンス番号に1つの行があり、2)シーケンス番号を取得するときに入力できない列があります。デフォルト値が必要であり、3)すべての列を取得するには結合が必要です。
TRANSACTION SAFE!2012年以前のSQLServerバージョンの場合...(Matt G.に感謝)この議論で欠けていることの1つは、トランザクションの安全性です。シーケンスから番号を取得する場合、その番号は一意である必要があり、他のアプリやコードはその番号を取得できないはずです。私の場合、シーケンスから一意の番号を取得することがよくありますが、実際のトランザクションにはかなりの時間がかかる可能性があるため、トランザクションをコミットする前に同じ番号を他の人が取得することは望ましくありません。Oracleシーケンスの振る舞いを模倣する必要がありました。ここでは、プルされたときに番号が予約されていました。私の解決策は、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('');
このソリューションには、使用済みのシーケンス値を保持する単一のテーブルと、2番目の自律型トランザクションを作成するプロシージャが必要です同時セッションが混乱しないようにします。一意のシーケンスはいくつでも持つことができ、名前で参照されます。以下のサンプルコードは、(監査用の)シーケンス履歴テーブルでのユーザーと日付スタンプの要求を省略するように変更されていますが、この例では複雑さが少ない方が良いと考えました;-)。
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を有効にする必要があります。その方法については、多くの優れた説明があります。ここに、機能させるために作成した個人的なメモを示します。基本的な考え方は、SQLServer Surface Areでxp_cmdshellを有効にする必要があり、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"'
シーケンシャルキーを使用してデータを挿入したいが、挿入したばかりのキーを取得するためにデータベースを再度照会する必要がない場合は、2つの選択肢しかないと思います。
クライアント側のキー生成を行う場合、love GUID。私は彼らが一体のように美しいと思います。
row["ID"] = Guid.NewGuid();
その線は、どこかでスポーツカーのボンネットの上に敷設する必要があります。
SQL Server 2005を使用している場合、Row_Numberを使用するオプションがあります
SQLにより、この戦略を使用できます。
CREATE SEQUENCE [dbo].[SequenceFile]
AS int
START WITH 1
INCREMENT BY 1 ;
このSQLで一意の次の値を読み取ります
SELECT NEXT VALUE FOR [dbo].[SequenceFile]