如何在另一个数据库中创建引用调用者中正确 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,只要对数据库执行CREATE
、ALTER
或DROP
语句,就会触发它。然后触发器将在步骤#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部署的微服务架构(二): 服务提供者和调用者