我可以在存储过程中设置默认架构吗?

Posted

技术标签:

【中文标题】我可以在存储过程中设置默认架构吗?【英文标题】:Can I set a default schema for within a stored procedure? 【发布时间】:2010-12-06 21:10:27 【问题描述】:

我正在为 StackQL 进行下一次更新。

我想做的一件事是能够查询多个版本。因此,例如,当我加载 10 月份的数据时,我并没有删除旧的 9 月份数据库。它还在外面。事实上,你甚至可以通过像这样包含数据库名称来查询它:

select top 10 * from SO_Sept09..Posts

当他们开始为 ServerFault 和 SuperUser 提供数据时,这将变得更加重要。

但我不喜欢有一大堆数据库来支持这一点。我更愿意将所有数据放在同一个数据库中,并将每个不同的集合分开到它自己的模式中。但是为了使这成为可能,我需要能够将默认模式设置为运行查询的存储过程的一部分,基于传递给存储过程的参数,该参数告诉它用户从未来的下拉列表中选择了哪个数据库出现在工具栏中。

StackQL 中的查询最终只是传递给 exec() 函数,如下所示:

exec(@QueryText)

我可以在存储过程中或在 QueryText 字符串 (ala USE [DatabaseName]) 之前做些什么来设置查询中使用的默认模式吗?

【问题讨论】:

【参考方案1】:

这里的各个地方都有一些如何做到这一点,但不是全部。这样做的方法是:

    为每个架构创建一个唯一的登录名和用户

    让这些用户成为每个不同架构的所有者。

    将每个此类用户的默认架构设置为他们拥有的架构。

    使用语法 EXECUTE ('sql commands') AS USER = 'schema-owner' 在该默认架构的上下文中执行您的 SQL 命令。

以下脚本演示了这一点:

--====== Create the Login for the User:
CREATE LOGIN [UserTest1] WITH PASSWORD='whatever', DEFAULT_DATABASE=[TestUsers], DEFAULT_LANGUAGE=[us_english]
GO

--====== Make a User for the Login:
CREATE USER [UserTest1] FOR LOGIN [UserTest1]
GO

--====== Make a Schema owned by the User and default to it:
--        (I assume that you already have the schemas)
CREATE SCHEMA [UserTest1] AUTHORIZATION [UserTest1]
GO
ALTER USER [UserTest1] WITH DEFAULT_SCHEMA=[UserTest1]
GO

--====== Make a sProc in dbo
CREATE PROCEDURE [dbo].[TestSchema_Exec] AS
    SELECT 'executing in schema [dbo]'
GO
--====== Make a similar sProc in New Schema
CREATE PROCEDURE [UserTest1].[TestSchema_Exec] AS
    SELECT 'executing in schema [UserTest1]'
GO

--========= Demonstrate that we can switch Default Schemas:
EXEC('TestSchema_Exec')

EXEC('TestSchema_Exec') AS USER = 'UserTest1'

【讨论】:

【参考方案2】:

除了修改@QueryText本身,我唯一能想到的就是用户的默认架构:

ALTER USER SO_Sept09_Reader WITH DEFAULT_SCHEMA = SO_Sept09

...然后为您要使用的每个架构以不同的用户身份连接。黑客攻击。

但是,如果您的查询无论如何都是动态构建的(我相信您知道为什么这通常不是一个好主意),那么最简单的方法可能是在查询文本中添加一个模式占位符,然后将模式名称与对替换函数的查询。

【讨论】:

添加模式占位符绝对不是一种选择。该站点允许任何人再次编写和运行 SQL 查询 *** 公共数据转储,他们几乎可以编写任何内容。更改用户也已退出,因为这会导致并发问题。但它确实让我想到了可能有几个用户并即时选择连接字符串。 是的,后者是我的建议;每个模式一个或多个(静态)用户;即时切换用户(通过匹配的连接字符串连接)。【参考方案3】:

另一种可能性是在每个架构中生成每个 SP 的副本,SP 中未修改的表名指的是同一架构中的表。

请注意,这不适用于此类 SP 中的动态 SQL:

CREATE PROCEDURE schema_a.SP
    @somesql AS varchar(MAX)
AS
BEGIN
    EXEC ( @somesql )
END

CREATE PROCEDURE schema_b.SP
    @somesql AS varchar(MAX)
AS
BEGIN
    EXEC ( @somesql )
END

不起作用,因为架构关联性在 EXEC 中丢失了。

此时:

CREATE PROCEDURE schema_a.SP
AS
BEGIN
    SELECT * FROM tbl -- Will use schema_a.tbl first
END

CREATE PROCEDURE schema_b.SP
AS
BEGIN
    SELECT * FROM tbl -- Will use schema_b.tbl first
END

工作正常。

同样:

EXEC ( 'EXEC schema_a.SP' )

显然可以正常工作。

【讨论】:

仍然是一个杂牌,但我更喜欢它而不是创建额外的用户。 我会像 RBArryYoung 的回答那样使用 EXECUTE AS,因为您已经在使用动态 SQL。架构亲和力对您不起作用的原因是,一旦您点击 exec,您就会从调用 exec 的 SP 中丢失架构的上下文,就像它丢失了其他所有内容一样。【参考方案4】:

未尝试过,但是:您能否将不同架构的数据合并到一个视图中,并添加一列调用架构名称?

CREATE VIEW AllPosts AS
  SELECT Data1, Data2, 'Sept09' AS Partition FROM SO_Sept09..Posts
    UNION ALL
  SELECT Data1, Data2, 'Oct09' AS Partition FROM SO_Oct09..Posts
  ...

SELECT * FROM AllPosts WHERE Partition = 'Sept09'
SELECT * FROM dbo.AllPosts('Sept09') -- if use table-valued function instead

【讨论】:

这是一个想法,但它会破坏我的很多索引。 您可以为视图编制索引,但有很多注意事项。 msdn.microsoft.com/en-us/library/dd171921.aspxmsdn.microsoft.com/en-us/library/ms191432.aspx【参考方案5】:

好的,我有一种新的方法可以做到这一点,可能对我来说效果更好。这是我对 Michael Petrotta 回答的评论的变体:

但它确实让我想到了可能有几个用户并即时选择连接字符串。

我要做的只是让一个用户来执行这些查询,但我会即时换出连接字符串以指定正确的初始目录。

【讨论】:

以上是关于我可以在存储过程中设置默认架构吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL 中插入一行并在存储过程中设置变量?

《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集 (转)

在存储过程中设置 RAISERROR 的编号和消息

如何在存储过程中设置日期时间可选参数?

在存储过程中设置 QUOTED_IDENTIFIER

MYSQL,使用单个选择语句在存储过程中设置两个变量