环境:SQL SERVER 2016 我在SQL中使用execute as user函数来创建特定于会话的表而不使用动态SQL。当我将代码作为单个查询运行时,它的执行完全符合预期。工作代码是:
DECLARE @strSQL NVARCHAR(MAX)
, @strSession AS NVARCHAR(256)
SET @strSession = 'SESSION_ABCD'
SET @strSQL = 'CREATE SCHEMA ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'CREATE LOGIN ' + @strSession + ' WITH PASSWORD = ''' + CAST(NEWID() AS NVARCHAR(36)) + ''' '
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'CREATE USER ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'ALTER USER ' + @strSession + ' WITH DEFAULT_SCHEMA = ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'GRANT ALTER ON SCHEMA::' + @strSession + ' TO ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'GRANT CREATE TABLE TO ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'GRANT SELECT,INSERT,UPDATE,DELETE TO ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
EXECUTE AS USER = 'SESSION_ABCD'
-- Create table without including default schema - OK
-- Creates table SESSION_ABCD.Test as expected
CREATE TABLE Test (MyValue INT)
-- Select from table including schema name - OK
SELECT *
FROM SESSION_ABCD.Test
-- Select from table excluding schema name - Works outside of stored procedure
SELECT *
FROM Test
REVERT
但是,如果我在存储的[程序中运行相同的代码,我会得到意想不到的结果。代码是:
CREATE PROC dbo.SetupUser
AS
BEGIN
DECLARE @strSession AS NVARCHAR(256)
SET @strSession = 'SESSION_ABCD'
DECLARE @strSQL NVARCHAR(MAX)
SET @strSQL = 'CREATE SCHEMA ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'CREATE LOGIN ' + @strSession + ' WITH PASSWORD = ''' + CAST(NEWID() AS NVARCHAR(36)) + ''' '
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'CREATE USER ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'ALTER USER ' + @strSession + ' WITH DEFAULT_SCHEMA = ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'GRANT ALTER ON SCHEMA::' + @strSession + ' TO ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'GRANT CREATE TABLE TO ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
SET @strSQL = 'GRANT SELECT,INSERT,UPDATE,DELETE TO ' + @strSession
PRINT (@strSQL)
EXEC (@strSQL)
END
GO
CREATE PROC dbo.SetupTable
AS
BEGIN
EXECUTE AS USER = 'SESSION_ABCD'
-- Create table without including default schema - OK
-- Creates table SESSION_ABCD.Test as expected
CREATE TABLE Test (MyValue INT)
-- Select from table including schema name - OK
SELECT *
FROM SESSION_ABCD.Test
-- Select from table excluding schema name - Fails
-- Expecting same result as above
PRINT SCHEMA_NAME()
SELECT *
FROM Test
REVERT
END
GO
EXEC dbo.SetupUser
EXEC dbo.SetupTable
当我执行上面的脚本时,users / sessions / etc正确配置。 (请注意,您需要从系统中删除它们以再次运行它们。)在SetupTable过程中,create table命令针对默认模式执行,但是select命令需要使用模式名称进行限定。虽然我可以使用Dynamic SQL构建查询以插入模式名称,但此项目完全是删除旧的动态sql代码。
我会诚实地说这不是我之前使用的方法,但似乎是解决另一个问题的最佳解决方案。我希望有一些我缺少的基本内容,但此刻我完全被难倒了。
非常感谢。
答案 0 :(得分:1)
您的第二个过程是在dbo
架构中创建的,如果您未在其中使用完全限定名称,则默认架构为存储过程架构(dbo
case),而不是用户的默认架构。
例如,您的用户A
包含默认架构a
,架构B,
和架构dbo
。
您创建了一个proc B.sp_test:
create proc B.sp_test as select * from test_tbl
现在用户A
执行此sp。
表test_tbl
不是模式限定的,因此服务器首先查找B.test_tbl
。
如果表存在,则从中进行选择。如果不是,则查找dbo.test_tbl
。
如果找不到该表,则表示您输入了错误。
这不是用户的已验证的默认架构,但它是过程的架构,然后验证了dbo
架构。
这与表创建不同。 当用户创建表而不使用模式对其进行限定时,将使用它的默认scema。这与创建表的位置无关,在sp内或不在。
如何向自己证明?
只需在不同的架构中创建一个过程。 创建3个具有相同名称test_tbl的表,将它们放在默认模式,dbo和sp模式中。 在每个表中插入它的模式名称。
在sp中从test_tbl创建一个SELECT,在所有3个表存在时运行它。 您将从sp的架构中的表中获取数据。
现在删除此表,再次运行sp。
这次你将从dbo.test_tbl获取数据。
最后,删除dbo.test_tbl,运行sp。这次你会收到错误。