我通过一系列Sprocs只允许与DB交互来保护数据库;相当普遍的票价。
我挖出并修改了一个循环遍历的脚本,并为所有非系统SProc分配用户EXECUTE权限。除了我理想地将它添加到主数据库以便我可以轻松地将它用于任何后续项目之外,它的工作方式。是的,我可以将简单保存为.sql文件,但我更喜欢这样。
问题是我不知道如何动态引用另一个DB中的对象。例如,我可以轻松查询MyDB.dbo.INFORMATION_SCHEMA.ROUTINES,但如果数据库名称是动态的(例如@MyDBName),我该如何查询此数据库中的对象?
编辑:感谢下面的海报,我现在有了一个有效的解决方案:
USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spGrantExec]
@User sysname,
@DB varchar(50),
@Target varchar(50)
AS
/*---------------------------- SQL 2005 + -------------------------------*/
SET NOCOUNT ON
-- 1 - Variable declarations
DECLARE @SQL varchar(8000)
-- 2 - Create temporary table
Set @SQL =
'USE @DB
DECLARE @MAXOID int
DECLARE @OwnerName varchar(128)
DECLARE @ObjectName varchar(128)
DECLARE @CMD1 varchar(8000)
CREATE TABLE #StoredProcedures
(OID int IDENTITY (1,1),
StoredProcOwner varchar(128) NOT NULL,
StoredProcName varchar(128) NOT NULL)
-- 3 - Populate temporary table
INSERT INTO #StoredProcedures (StoredProcOwner, StoredProcName)
SELECT ROUTINE_SCHEMA, ROUTINE_NAME
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_NAME LIKE ''' + @Target + '%''
AND ROUTINE_TYPE = ''PROCEDURE''
-- 4 - Capture the @MAXOID value
SELECT @MAXOID = MAX(OID) FROM #StoredProcedures
-- 5 - WHILE loop
WHILE @MAXOID > 0
BEGIN
-- 6 - Initialize the variables
SELECT @OwnerName = StoredProcOwner,
@ObjectName = StoredProcName
FROM #StoredProcedures
WHERE OID = @MAXOID
-- 7 - Build the string
SELECT @CMD1 = ''GRANT EXEC ON '' + ''['' + @OwnerName + '']'' + ''.'' + ''['' + @ObjectName + '']'' + '' TO @user''
-- 8 - Execute the string
Print @CMD1
EXEC(@CMD1)
-- 9 - Decrement @MAXOID
SET @MAXOID = @MAXOID - 1
END
-- 10 - Drop the temporary table
DROP TABLE #StoredProcedures'
Set @SQL = REPLACE(REPLACE(REPLACE(@SQL, '@DB', @DB), '@User', @User), '@Target', @Target)
--Select @SQL
--Print @SQL
Exec (@SQL)
SET NOCOUNT OFF
答案 0 :(得分:2)
与@ Cade的答案类似,这样做的方法是使用动态sql。在每次调用数据库表之前,添加“@DbName”。然后将@DbName替换为实际的数据库名称(数据库名称不能作为SQL中的变量传递,因此您必须进行替换)。
由于性能原因,游标通常被认为是邪恶的,但在这种情况下使用游标是有道理的。首先,它会大大简化程序,而且因为你只是在应用程序更新期间只运行一次,你可能不会注意到性能损失,即使它增加了一两秒(我怀疑它会添加到那附近的任何地方)。
ALTER PROCEDURE [dbo].[spGrantExec] @User SysName, @DbName VarChar(512) AS BEGIN DECLARE @Sql VarChar(1024)
SET @Sql = 'DECLARE @OwnerName varchar(128) DECLARE @ObjectName varchar(128) DECLARE @Cmd1 VarChar(128) DECLARE ProcCursor CURSOR FOR SELECT ROUTINE SCHEMA, ROUTINE NAME FROM @DbName.INFORMATION SCHEMA.ROUTINES WHERE ROUTINENAME NOT LIKE ''dt %'' AND ROUTINE TYPE = ''PROCEDURE'' OPEN ProcCursor FETCH NEXT FROM ProcCursor INTO @OwnerName, @ObjectName WHILE @@FETCH STATUS = 0 BEGIN SET @CMD1 = ''GRANT EXEC ON '' + ''['' + @OwnerName + '']'' + ''.'' + ''['' + @ObjectName + '']'' + '' TO '' + ''@user'' EXEC (@CMD1)
FETCH NEXT FROM ProcCursor INTO @OwnerName, @ObjectName END CLOSE ProcCursor DEALLOCATE ProcCursor '
SET @Sql = Replace(Replace(@Sql, '@DbName', @DbName), '@user', @User) EXEC (@Sql)
END
对不起,sp的间距有点偏。使用Sql 2005开发。
答案 1 :(得分:1)
您可以使用the double exec technique。
在您的情况下,而不仅仅是:
EXEC(@CMD1)
你会:
SET @CMD1 =
'USE OtherDatabase;
EXEC (''' + REPLACE(@CMD1, '''', '''''') + ''')'
EXEC(@CMD1)
答案 2 :(得分:1)
我找到another technique, which I think is cleaner:
SELECT @sql = 'CREATE VIEW ...'
SELECT @sp_executesql = quotename(@dbname) + '..sp_executesql'
EXEC @sp_executesql @sql
这依赖于通过在另一个数据库中调用sp_executesql来设置数据库上下文(就像可以在任何数据库中调用SP一样)。
在您的情况下,它将等同于:
SELECT @sp_executesql = quotename(@dbname) + '..sp_executesql'
EXEC @sp_executesql @CMD1