如何在另一个数据库中创建引用调用者中正确 sys.objects 表的 UDF 或视图?

Posted

技术标签:

【中文标题】如何在另一个数据库中创建引用调用者中正确 sys.objects 表的 UDF 或视图?【英文标题】:How to create a UDF or View in another database that references the correct sys.objects table in the caller? 【发布时间】:2010-10-27 15:39:49 【问题描述】:

使用 SQL Server 2008,我想创建一个 UDF,它可以为我提供对象的创建日期。这是代码:

create function dbo.GetObjCreateDate(@objName sysname) returns datetime as
begin
    declare @result datetime
    select @result = create_date from sys.objects where name = @objname
    return @result
end
go

我想将此 UDF 放在主数据库或其他一些共享数据库中,以便可以从任何地方访问它,除非我这样做,然后 sys.objects 引用从 master 数据库中提取而不是我从中发起查询的数据库。我知道你可以这样做,因为 information_schema 视图位于 master 中,并且只需包装对 sys.objects 的本地实例的调用,所以我希望我的 UDF 也有一种简单的方法。

【问题讨论】:

information_schema 视图是在每个数据库中本地创建的。此外,作为一般规则,主数据库应单独放置。创建一个“实用程序”数据库来保存这样的东西。 你可以自己看看这个。在 SSMS 中,向下钻取 Databases -> YourDatabase -> Views -> System Views。 那行不通。 Utilities 中的 UDF 只会访问 Utilities 中的 sys.objects,而不是调用数据库。如果这是您的挂断,请忽略主控部分...我需要来自调用者的 sys.objects,而不是托管 UDF 的数据库。 【参考方案1】:

试试这个:

CREATE FUNCTION dbo.GetObjCreateDate(@objName sysname, @dbName sysname) 
RETURNS datetime AS
BEGIN
    DECLARE @createDate datetime;
    DECLARE @params nvarchar(50);
    DECLARE @sql nvarchar(500);

    SET @params = '@createDate datetime OUTPUT';

    SELECT @sql = 'SELECT @createDate = create_date FROM ' + @dbName + '.sys.objects WHERE name = ''' + @objname + '''';

    EXEC sp_executesql @sql, @params, @createDate = @createDate OUTPUT;         

     RETURN @createDate
END
;

【讨论】:

那不行,因为你不能从函数中使用动态SQL,也不能调用存储过程。它应该是存储过程。【参考方案2】:

为什么不这样做呢?

    创建一个存储过程,该过程在主数据库中创建一个视图,其中包含来自服务器上每个数据库的sys.objects 中的所有信息。 创建一个DDL Trigger,只要对数据库执行CREATEALTERDROP 语句,就会触发它。然后触发器将在步骤#1 中执行存储过程。这样可以自动更新视图。 (可选) 创建一个用户定义的函数,用于查询视图以获取给定对象的创建日期。

存储过程 DDL:

USE [master];
GO

CREATE PROCEDURE dbo.BuildAllServerObjectsView
AS

SET NOCOUNT ON;

IF OBJECT_ID('master.dbo.AllServerObjects') IS NOT NULL
    EXEC master..sp_SQLExec 'DROP VIEW dbo.AllServerObjects;';

IF OBJECT_ID('tempdb..Databases') IS NOT NULL
    DROP TABLE #Databases;

DECLARE @CreateView varchar(8000);
SET @CreateView = 'CREATE VIEW dbo.AllServerObjects AS' + CHAR(13)+CHAR(10) + CHAR(13)+CHAR(10);

SELECT name COLLATE SQL_Latin1_General_CP1_CI_AS AS 'name'
  INTO #Databases
  FROM sys.databases
 ORDER BY name;

DECLARE @DatabaseName nvarchar(100);
WHILE (SELECT COUNT(*) FROM #Databases) > 0
BEGIN
    SET @DatabaseName = (SELECT TOP 1 name FROM #Databases ORDER BY name);
    SET @CreateView +='SELECT N'+QUOTENAME(@DatabaseName, '''')+' AS ''database_name''' + CHAR(13)+CHAR(10)
                    + '      ,name COLLATE SQL_Latin1_General_CP1_CI_AS AS ''object_name''' + CHAR(13)+CHAR(10)
                    + '      ,object_id' + CHAR(13)+CHAR(10)
                    + '      ,principal_id' + CHAR(13)+CHAR(10)
                    + '      ,schema_id' + CHAR(13)+CHAR(10)
                    + '      ,parent_object_id' + CHAR(13)+CHAR(10)
                    + '      ,type' + CHAR(13)+CHAR(10)
                    + '      ,type_desc' + CHAR(13)+CHAR(10)
                    + '      ,create_date' + CHAR(13)+CHAR(10)
                    + '      ,modify_date' + CHAR(13)+CHAR(10)
                    + '      ,is_ms_shipped' + CHAR(13)+CHAR(10)
                    + '      ,is_published' + CHAR(13)+CHAR(10)
                    + '      ,is_schema_published' + CHAR(13)+CHAR(10)
                    + '  FROM ' + QUOTENAME(@DatabaseName) + '.sys.objects';
    IF (SELECT COUNT(*) FROM #Databases) > 1
        SET @CreateView += CHAR(13)+CHAR(10) + CHAR(13)+CHAR(10) + ' UNION' + CHAR(13)+CHAR(10);
    ELSE
        SET @CreateView += ';';  

    DELETE #Databases
     WHERE name = @DatabaseName;
END;

--PRINT @CreateView --<== Uncomment this to see the DDL for the view.

EXEC master..sp_SQLExec @CreateView;

IF OBJECT_ID('tempdb..Databases') IS NOT NULL
    DROP TABLE #Databases;

GO

函数 DDL:

USE [master];
GO

CREATE FUNCTION dbo.GetObjCreateDate(@DatabaseName sysname, @objName sysname) RETURNS DATETIME AS
BEGIN
    DECLARE @result datetime;

    SELECT @result = create_date
      FROM master.dbo.AllServerObjects
     WHERE [database_name] = @DatabaseName
       AND [object_name] = @objname;

    RETURN @result;
END
GO

示例用法:

SELECT master.dbo.GetObjCreateDate('MyDatabase', 'SomeObject') AS 'Created';
SELECT master.dbo.GetObjCreateDate(DB_NAME(), 'spt_monitor') AS 'Created';

【讨论】:

这绝对是精巧的。我遇到的唯一问题是 @CreateView 变量有太多数据库来保存整个脚本,但是对于我想做的事情,这将解决它。谢谢! 太好了,很高兴它有帮助!如果您需要额外的容量,您应该能够将 @CreateView 变量从 varchar(8000) 更改为 nvarchar(max)【参考方案3】:

它必须是一个函数吗?如果你只是想让它在任何地方都可以访问,一个技巧是将你的代码放在一个 varchar 和 sp_executesql 中:

create procedure dbo.GetObjCreateDate(@objName sysname) 
as
    declare @sql nvarchar(max)
    select @sql = 'select create_date from sys.objects where name = ''' + @objname + ''''
    EXEC sp_executesql @sql 
go

【讨论】:

不幸的是,我希望得到一个专门针对 UDF 的答案,因为我对此有更多的用途,而不仅仅是我在这里介绍的简单案例。我只是选择了一个简单的例子,看看我是否可以为更大的目的获得帮助。【参考方案4】:

似乎有一个未记录的存储过程允许您创建自己的系统对象:sp_ms_marksystemobject

您可以在http://www.mssqltips.com/tip.asp?tip=1612阅读更多内容

【讨论】:

如此接近!!!这似乎对存储过程很有效,但不适用于 UDF。还有其他建议吗? 这就是我所拥有的。对不起:)【参考方案5】:

看看How to Write Your Own System Functions。相信对你有帮助

【讨论】:

@mattmc3 你在这方面取得了成功吗?如果你准备好了,请分享你的结果:-]

以上是关于如何在另一个数据库中创建引用调用者中正确 sys.objects 表的 UDF 或视图?的主要内容,如果未能解决你的问题,请参考以下文章

如何在AngularJS中创建一个对话框,但只加载来自服务器的对话框内容

如何在另一个数据库中的多个数据库中创建多个表的单个视图?

基于docker部署的微服务架构(二): 服务提供者和调用者

如何正确引用/检索在 AppData 中创建的临时文件以将文件上传到服务器?

使用在另一个 Swift VC 文件中创建的对象?

spring di