2009-02-03 49 views
0

我通过一系列Sprocs仅通过与数据库的交互来保护数据库;很普通的票价。授予跨不同数据库(模式)的权限

我挖掘并修改了一个脚本,它循环并为所有非系统SProcs分配用户EXECUTE权限。除了理想地将其添加到主数据库以便我可以轻松地将其用于任何后续项目之外,它的工作原理是一种享受。是的,我可以简单地保存为.sql文件,但我更喜欢这种方式。

问题是我不知道如何动态引用另一个数据库中的对象。例如,我可以轻松地在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

你可能想看看一个更清洁,我发现了:SELECT @sp_executesql = QUOTENAME(@dbname)+ '..sp_executesql' EXEC @sp_executesql @ CMD1 – 2009-02-06 16:09:59

回答

2

类同@凯德的答案,要做到这一点的方法是使用动态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



可以使用称之为:EXEC [spGrantExec] '鲍勃', '罗斯文'

对不起的间距是在SP有点过。

SELECT @sql = 'CREATE VIEW ...' 
SELECT @sp_executesql = quotename(@dbname) + '..sp_executesql' 
EXEC @sp_executesql @sql 

这依赖于在其他数据库调用sp_executesql的(就像一个可以调用任何数据库的SP)设置数据库环境:使用SQL 2005

1

您可以使用the double exec technique

在你的情况,而不是只:

EXEC(@CMD1) 

你必须:

SET @CMD1 = 
    'USE OtherDatabase; 
    EXEC (''' + REPLACE(@CMD1, '''', '''''') + ''')' 
EXEC(@CMD1) 
+0

有趣。我可以看到原理,但还没有消化更精细的细节。但看起来很有希望...... – CJM 2009-02-03 16:01:10

+0

我希望我可以将已经接受的答案分为奖项 - 无论如何。 – CJM 2009-02-06 10:49:32