2つのデータベースを作成します。2番目のデータベースにテーブルを作成し、最初のデータベースにストアドプロシージャを作成します。ストアドプロシージャクロスデータベースがテーブルにアクセスします。 SQLサーバーログインを作成し、このログインを各データベースのユーザーにマップします。ユーザーにdb_owner権限を与えます。これを実行するスクリプトは次のとおりです(スクリプトを実行すると、SQL sysadminとして接続されます)。
USE [master]
GO
CREATE DATABASE [TestDatabase1] ON PRIMARY
( NAME = N'TestDatabase1', FILENAME = N'd:\database\TestDatabase1.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
LOG ON
( NAME = N'TestDatabase1_log', FILENAME = N'd:\database\TestDatabase1_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO
CREATE DATABASE [TestDatabase2] ON PRIMARY
( NAME = N'TestDatabase2', FILENAME = N'd:\database\TestDatabase2.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
LOG ON
( NAME = N'TestDatabase2_log', FILENAME = N'd:\database\TestDatabase2_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO
USE [TestDatabase2]
GO
CREATE TABLE [dbo].[TestTable](
[Test] [int] NULL
) ON [PRIMARY]
GO
USE [master]
GO
CREATE LOGIN [TestUser] WITH PASSWORD=N'password', DEFAULT_DATABASE=[TestDatabase1], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
GO
USE [TestDatabase1]
GO
CREATE USER [TestUser] FOR LOGIN [TestUser]
GO
USE [TestDatabase1]
GO
ALTER USER [TestUser] WITH DEFAULT_SCHEMA=[dbo]
GO
USE [TestDatabase2]
GO
CREATE USER [TestUser] FOR LOGIN [TestUser]
GO
USE [TestDatabase2]
GO
ALTER USER [TestUser] WITH DEFAULT_SCHEMA=[dbo]
GO
USE [TestDatabase2]
GO
EXEC sp_addrolemember N'db_owner', N'TestUser'
GO
USE [TestDatabase1]
GO
EXEC sp_addrolemember N'db_owner', N'TestUser'
GO
これが行われると、TestUserのIDでSSMSを使用してサーバーに接続します。 SSMSでTestSpストアドプロシージャを実行すると、成功します。
次に、同じストアドプロシージャを実行するジョブを作成します。私は次のようにしています(スクリプトを実行するときにSQL sysadminとして接続しています)。
USE [msdb]
GO
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
END
DECLARE @jobId BINARY(16)
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'TestJob',
@enabled=1,
@notify_level_eventlog=0,
@notify_level_email=0,
@notify_level_netsend=0,
@notify_level_page=0,
@delete_level=0,
@description=N'No description available.',
@category_name=N'[Uncategorized (Local)]',
@owner_login_name=N'sa', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'TestStep',
@step_id=1,
@cmdexec_success_code=0,
@on_success_action=1,
@on_success_step_id=0,
@on_fail_action=2,
@on_fail_step_id=0,
@retry_attempts=0,
@retry_interval=0,
@os_run_priority=0, @subsystem=N'TSQL',
@command=N'SELECT TOP 1 * FROM TestDatabase2.dbo.TestTable',
@database_name=N'TestDatabase1',
@database_user_name=N'TestUser',
@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:
GO
ジョブが作成されたら、SSMSから実行します(これを行うと、SQLシステム管理者として接続されます)。ジョブは次のエラーで失敗します。
Date 10/04/2012 3:26:31 p.m.
Log Job History (TestJob)
Step ID 1
Server obfuscated
Job Name TestJob
Step Name TestStep
Duration 00:00:00
Sql Severity 14
Sql Message ID 916
Operator Emailed
Operator Net sent
Operator Paged
Retries Attempted 0
Message
Executed as user: TestUser. The server principal "TestUser" is not able to access the database "TestDatabase2" under the current security context. [SQLSTATE 08004] (Error 916). The step failed.
上記のスクリプトによって作成されたデータベースオブジェクトを削除するクリーンアップスクリプトを次に示します。
USE [master]
GO
alter database TestDatabase1 set single_user with rollback immediate
GO
alter database TestDatabase1 set multi_user
GO
alter database TestDatabase2 set single_user with rollback immediate
GO
alter database TestDatabase2 set multi_user
GO
drop database TestDatabase1
GO
drop database TestDatabase2
GO
USE [msdb]
GO
declare @job_id uniqueidentifier
SELECT @job_id = job_id FROM msdb.dbo.sysjobs_view WHERE name = N'TestJob'
EXEC msdb.dbo.sp_delete_job @job_id=@job_id, @delete_unused_schedule=1
GO
DROP LOGIN [TestUser]
GO
質問:
更新1
高度なgoogle-fuを使用して、質問2の回答の1つがこれである可能性があると判断できました。
ALTER DATABASE TestDatabase1 SET TRUSTWORTHY ON;
GO
RECONFIGURE WITH OVERRIDE;
GO
質問1は未回答のままです
更新2
わかりました。わかったので、以下に回答を投稿しました。ただし、1つはまだ明確ではありません。ジョブからのこのエラー。SQL2005では発生しません。SQL2008では発生しますが、SQL 2005では発生しません。明らかにsomethingが変更されました。誰が変更が正確に何だったか知っていますか?
誰も答えとしてフィードバックを投稿しないので:
ジョブとSSMSの結果が異なるのはなぜですか?
どうやらSQLエージェントは、@database_user_name
パラメータをsp_add_jobstep
ストアドプロシージャに指定した場合、 EXECUTE AS USER を使用してジョブステップを実行します。上記の私の例では、sysadminとしてログインしてこのスクリプトを実行することにより、この動作をSSMSで再現できます。
use TestDatabase1;
GO
execute as user = 'TestUser';
GO
select top 1 * from TestDatabase2.dbo.TestTable;
GO
REVERT
GO
このコードスニペットでexecute as user
をexecute as login
に変更すると、エラーはなくなりますが、SQLエージェントはexecute as user
を使用しているようです。
MSDNによると:データベースユーザーへの[EXECUTE AS USER]コンテキストスイッチがアクティブである間、データベース外のリソースにアクセスしようとすると、ステートメントが失敗します。これには、USEデータベースステートメント、分散クエリ、3つまたは4つの部分からなる識別子を使用する別のデータベースを参照するクエリが含まれます。
詳細については、@ RemusRusanuのリンクを参照してください。 sp_send_mailのデータベースアクセス許可に関する問題
(失敗するのではなく)どうやってジョブを機能させるのですか?
ALTER DATABASE TestDatabase1 SET TRUSTWORTHY ON;
GO
RECONFIGURE WITH OVERRIDE;
GO
上記のRemus Rusanuのリンクでは、他のオプションも検討されています。
私は同じ問題に直面し、最初に、データベースのTRUSTWORTHY
プロパティをオンにすることで問題を解決しました。しかし、私はそれがセキュリティの観点から安全な方法ではないことを発見しました。
この問題に取り組むためのベストプラクティスは、ジョブステップ「-> Advanced->」に移動して、「ユーザーとして実行」の下で空白にすることです。 「ユーザーとして実行」でログインアカウントを指定すると、SQLエージェントにそのアカウントを偽装するように指示することになります。これが、このエラー応答を受け取る主な理由です。
マイクロソフトの設計により、偽装は無効になっています。偽装を有効にするには、次に示すように信頼できるものを有効にします。
ALTER DATABASE TestDatabase1 SET TRUSTWORTHY ON;
GO
RECONFIGURE WITH OVERRIDE;
GO
これがお役に立てば幸いです。
ジョブを実行していて、ジョブを実行するユーザーを指定していない場合、ジョブはSQL Serverサービスの下で実行されます。SQLServerサービスは、データベースに対する権限を持っている場合と持っていない場合があります。
SSMSでスクリプトを実行しているときは、ログインしているユーザーとして実行されます。これには、データベースに対する権限がある場合とない場合があります。
これが結果の違いです。ジョブで使用するユーザー資格情報を指定する必要があります。