我正在尝试创建一个简单的脚本,将复杂视图的结果转储到表中以进行报告。我使用同义词来简化调整视图和表名称。
这个想法是,脚本的用户可以将他们想要用作源的视图的名称以及目标报告表的名称放在开始和离开的地方。如果该表不存在,则脚本应创建它。如果表已经存在,那么脚本应该只从视图中复制表中尚不存在的记录。
下面的脚本涵盖了所有这些要求,但我找不到一个好的方法来检查同义词背后的表是否已经存在:
CREATE SYNONYM SourceView FOR my_view
CREATE SYNONYM TargetReportingTable FOR my_table
-- Here's where I'm having trouble, how do I check if the underlying table exists?
IF (SELECT COUNT(*) FROM information_schema.tables WHERE table_name = TargetReportingTable) = 0
BEGIN
-- Table does not exists, so insert into.
SELECT * INTO TargetReportingTable FROM SourceView
END
ELSE
BEGIN
-- Table already exists so work out the last record which was copied over
-- and insert only the newer records.
DECLARE @LastReportedRecordId INT;
SET @LastReportedRecordId = (SELECT MAX(RecordId) FROM TargetReportingTable)
INSERT INTO TargetReportingTable SELECT * FROM SourceView WHERE RecordId > @LastReportedRecordId
END
DROP SYNONYM SourceView
DROP SYNONYM TargetReportingTable
我知道我可以让脚本的用户将表名复制到“information_schema”行以及顶部的同义词中,但这留下了错误的范围。
我也知道我可以做一些肮脏的事情,比如将表名放入变量中并将 SQL 作为字符串输出,但这让我感觉有点恶心!
有没有一种优雅的 SQL 方式可以让我检查同义词后面的表是否存在?或者完全不同的方式来解决问题?
不是最优雅的解决方案,但您可以将
sys.synonyms
表连接到 sys.tables
表以检查该表是否存在。
如果表不存在,连接将失败,您将得到 0 行(因此
IF EXISTS
将为 false)。如果该表确实存在,则连接将成功,您将获得 1 行(且为 true):
IF EXISTS( SELECT *
FROM sys.synonyms s
INNER JOIN sys.tables t ON REPLACE(REPLACE(s.base_object_name, '[', ''), ']', '') = t.name
WHERE s.name = 'TargetReportingTable')
BEGIN
-- Does exist
END
ELSE
BEGIN
-- Does not exist
END
将
'TargetReportingTable'
替换为您要检查的同义词。
如果同义词引用另一个数据库,上述解决方案对我不起作用。我最近发现了函数 [fn_my_permissions],它对于显示特定数据库对象的权限很有用,所以我认为可以按如下方式使用它:
IF EXISTS
(
select *
from sys.synonyms sy
cross apply fn_my_permissions(sy.base_object_name, 'OBJECT')
WHERE sy.name = 'TargetReportingTable'
)
print 'yes - I exist!'
聚会迟到了,我创建了一个查询来测试
Synonyms
的存在并与您分享。
DECLARE @Synonyms table
(
ID int identity(1,1),
SynonymsDatabaseName sysname,
SynonymsSchemaName sysname,
SynonymsName sysname,
DatabaseName nvarchar(128),
SchemaName nvarchar(128),
ObjectName nvarchar(128),
Remark nvarchar(max),
IsExists bit default(0)
)
INSERT @Synonyms (SynonymsDatabaseName, SynonymsSchemaName, SynonymsName, DatabaseName, SchemaName, ObjectName)
SELECT
DB_NAME() AS SynonymsDatabaseName,
SCHEMA_NAME(schema_id) AS SynonymsSchemaName,
name AS SynonymsName,
PARSENAME(base_object_name,3) AS DatabaseName,
PARSENAME(base_object_name,2) AS SchemaName,
PARSENAME(base_object_name,1) AS ObjectName
FROM sys.synonyms
SET NOCOUNT ON
DECLARE @ID int = 1, @Query nvarchar(max), @Remark nvarchar(max)
WHILE EXISTS(SELECT * FROM @Synonyms WHERE ID = @ID)
BEGIN
SELECT
@Query = 'SELECT @Remark = o.type_desc FROM [' + DatabaseName + '].sys.objects o INNER JOIN sys.schemas s ON o.schema_id = s.schema_id WHERE s.name = ''' + SchemaName + ''' AND o.name = ''' + ObjectName + ''''
FROM @Synonyms WHERE ID = @ID
EXEC sp_executesql @Query, N'@Remark nvarchar(max) OUTPUT', @Remark OUTPUT;
UPDATE @Synonyms SET IsExists = CASE WHEN @Remark IS NULL THEN 0 ELSE 1 END, Remark = @Remark WHERE ID = @ID
SELECT @ID += 1, @Remark = NULL
END
SELECT * FROM @Synonyms
您可以使用动态 SQL 来完成此操作:
-- create synonym a for information_schema.tables
create synonym a for b
declare @exists int = 1;
begin try
exec('select top 0 * from a');
end try
begin catch
set @exists = 0;
end catch
select @exists;
这不适用于非动态 SQL,因为同义词引用是在编译时捕获的。这意味着代码只是失败并显示一条消息,并且不会被
try
/catch
块捕获。使用动态 SQL,该块会捕获错误。
您可以使用 SQL Server 中可用的 Object_Id 函数测试数据库中是否存在 Synonym
IF OBJECT_ID('YourDatabaseName..YourSynonymName') IS NOT NULL
PRINT 'Exist SYNONYM'
ELSE
PRINT 'Not Exist SYNONYM'
另一个更简单的解决方案:
IF (EXISTS (SELECT * FROM sys.synonyms WHERE NAME ='mySynonymName'))
BEGIN
UPDATE mySynonymName
SET [Win] = 1
END
在这种情况下,我首先进行数据库设置。我首先删除数据库 (database1) 中的所有同义词,然后运行 SPROC 为目标数据库 (database2) 中的所有表创建同义词。 database1 中的一些存储过程调用 DB2 中的表。如果 DB2 中不存在表,则 SPROC 会失败。如果 DB2 中不存在表,则在数据库设置时不会自动创建同义词。因此,我只是使用上面的内容来检查同义词是否存在,如果同义词不存在,则跳过 SPROC 的该部分。
所以,我正在修改上面 Martin 的回复,发现它不起作用,因为 base_object_name 是完全限定的。
我找到了一个似乎更灵活的替代方案...
OBJECT_ID()
适用于各种资格级别的名称,因此,如果您从 OBJECT_ID(s.base_object_name)
得到非空值,那么您正在谈论现有的东西。PARSENAME()
并结合 DB_ID()
和 SCHEMA_ID()
,您可以确定它是当前数据库的本地数据库、相邻数据库还是远程数据库。我还没有在远程服务器上测试同义词来查看 OBJECT_ID()
是否可以跨机器工作。select OBJECT_ID(s.base_object_name)
,DB_ID( parsename(s.base_object_name, 3))
,SCHEMA_ID(parsename(s.base_object_name, 2))
,parsename(s.base_object_name, 4)
FROM sys.synonyms s
WHERE s.name = 'SYNONYM_TEST'