包含数据库整理错误

Tom*_*Tom 6 sql-server collation sql-server-2012 contained-database except

将数据库更改为部分包含时,出现以下错误:

无法解决 EXCEPT 操作中“Latin1_General_CI_AS”和“Latin1_General_100_CI_AS_KS_WS_SC”之间的排序规则冲突。

在编译 > 对象期间,过程“RSExecRole.DeleteExtensionModuleDDL”中遇到错误。数据库“VeeamOne”的包含选项已更改,或者该对象存在于模型 db 中并且用户尝试创建新的包含数据库。ALTER DATABASE 语句失败。无法更改数据库“VeeamOne”的包含选项,因为在验证 SQL 模块期间遇到编译错误。请参阅以前的错误。ALTER DATABASE 语句失败。(.Net SqlClient 数据提供程序)

我认为这是报告的对象来自 SSRS。但是,我正在更改排序规则的 DB 是一个完全独立的应用程序。

有没有人对如何解决这个问题有任何建议?

================================================== ====================== 好的,这是 proc 的代码,但不确定它是什么导致它无法被包含

USE [VeeamOne]
GO
/****** Object:  StoredProcedure [reporter].[DeleteExtensionModuleDDL]    Script Date: 02/12/2015 12:06:19 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [reporter].[DeleteExtensionModuleDDL]
@EMID int
AS
BEGIN
SET NOCOUNT ON;
declare @Debug bit;
set @Debug = 0;
declare @Emulate bit;
set @Emulate = 0;
declare @reportPackDestructorFunctionName nvarchar(max)
exec @reportPackDestructorFunctionName = [reporter].GenerateExtensionModuleDestructorName @EMID
if exists(select * from sys.objects where (object_id = OBJECT_ID(@reportPackDestructorFunctionName) and type in (N'P', N'PC')))
begin
exec @reportPackDestructorFunctionName
declare @objectsToDelete as table (Name nvarchar(2048), Type nvarchar(2048))
insert @objectsToDelete exec @reportPackDestructorFunctionName
if @Debug = 1
begin
select * from @objectsToDelete
end
declare @TablesToDelete    as table(ObjectID int, Name varchar(max))
declare @FunctionsToDelete   as Table(Name nvarchar(max))
declare @StoredProceduresToDelete as Table(Name nvarchar(max))
declare @AssembliesToDelete   as Table(Name nvarchar(max))
declare @ViewsToDelete    as Table(Name nvarchar(max))
insert into @TablesToDelete
select object_id(Name), Name
from @objectsToDelete
where Type = 'Table'
insert into @FunctionsToDelete
select Name
from @objectsToDelete
where Type = 'Function'
insert into @StoredProceduresToDelete
select Name
from @objectsToDelete
where Type = 'Procedure'
union
select @reportPackDestructorFunctionName
insert into @AssembliesToDelete
select Name
from @objectsToDelete
where Type = 'Assembly'
insert into @ViewsToDelete
select Name
from @objectsToDelete
where Type = 'View'
declare @DependencyTree as Table(ForeignKeyObjectID int, ForeignKeyObjectName nvarchar(max),
ParentTableID int, ParentTableName nvarchar(max),
ChildTableID int, ChildTableName nvarchar(max), Generation int)
declare @Generation int;
set @Generation = 0;
insert into @DependencyTree
select  distinct(fk.object_id) as ForeignKeyObjectID, fk.name as ForeignKeyObjectName,
fk.referenced_object_id as ParentTableID, parent.name as ParentTableName,
fk.parent_object_id as ChildTableID, child.name as ChildTableName, @Generation
from  sys.foreign_keys as fk
inner join sys.objects as parent
on   fk.referenced_object_id = parent.object_id
inner join sys.objects as child
on   fk.parent_object_id = child.object_id
where  fk.referenced_object_id in (select ObjectID from @TablesToDelete)
while @@ROWCOUNT > 0
begin
set @Generation = @Generation + 1
insert into @DependencyTree
select  fk.object_id as ForeignKeyObjectID, fk.name as ForeignKeyObjectName,
fk.referenced_object_id as ParentTableID, parent.name as ParentTableName,
fk.parent_object_id as ChildTableID, child.name as ChildTableName, @Generation
from  @DependencyTree dt
inner join sys.foreign_keys as fk
on   fk.referenced_object_id = dt.ChildTableID
inner join sys.objects as parent
on   fk.referenced_object_id = parent.object_id
inner join sys.objects as child
on   fk.parent_object_id = child.object_id
except
select  ForeignKeyObjectID, ForeignKeyObjectName,
ParentTableID, ParentTableName,
ChildTableID, ChildTableName, @Generation
from  @DependencyTree
end
declare @clearScript as table(ID int primary key identity (0,1), ScriptText nvarchar(max))
insert into @clearScript
select  'alter table [reporter].[' + ChildTableName +
'] drop constraint [' + ForeignKeyObjectName + ']'
from  @DependencyTree
where  ParentTableName in (select Name from @TablesToDelete)
insert into @clearScript
select 'drop table [reporter].[' + Name + ']' from @TablesToDelete
insert into @clearScript
select 'drop function [reporter].[' + Name + ']'
from @FunctionsToDelete
insert into @clearScript
select 'drop procedure [reporter].[' + Name + ']'
from @StoredProceduresToDelete
insert into @clearScript
select 'drop assembly [reporter].[' + Name + ']'
from @AssembliesToDelete
insert into @clearScript
select 'drop view [reporter].[' + Name + ']'
from @ViewsToDelete
if @Debug = 1
begin
select * from @clearScript
end
declare @str nvarchar(max)
declare @ID int;
set @ID  = 0;
declare @MaxID int
select @MaxID = MAX(ID) from @clearScript
print ''
while @ID <= @MaxID
begin
select @str = ScriptText from @clearScript where ID = @ID
if @Emulate = 1
print(@str)
else
exec sp_executesql @statement = @str
set @ID = @ID + 1
end
end
END
Run Code Online (Sandbox Code Playgroud)

Sol*_*zky 6

您所看到的问题是,在系统视图中的元数据的排序规则之间存在冲突-sys.foreign_keyssys.objects-和表变量@DependencyTree

正如@RLF 的回答中指出的那样,在将数据库更改为“包含”时,数据库元数据的整理从 DATABASE_DEFAULT(在您的情况下Latin1_General_CI_AS)更改为 CATALOG_DEFAULT(始终Latin1_General_100_CI_AS_WS_KS_SC)。这会影响name此查询中返回的字段:

SELECT fk.object_id AS [ForeignKeyObjectID], fk.name AS [ForeignKeyObjectName],
       fk.referenced_object_id AS [ParentTableID], parent.name AS [ParentTableName],
       fk.parent_object_id AS ChildTableID, child.name AS [ChildTableName], @Generation
FROM   @DependencyTree dt
INNER JOIN sys.foreign_keys fk
        ON fk.referenced_object_id = dt.ChildTableID
INNER JOIN sys.objects parent
        ON fk.referenced_object_id = parent.[object_id]
INNER JOIN sys.objects  child
        ON fk.parent_object_id = child.[object_id]

EXCEPT

SELECT ForeignKeyObjectID, ForeignKeyObjectName,
       ParentTableID, ParentTableName,
       ChildTableID, ChildTableName, @Generation
FROM   @DependencyTree
Run Code Online (Sandbox Code Playgroud)

fk.nameparent.namechild.name领域都开始整理为Latin1_General_CI_AS后来改为Latin1_General_100_CI_AS_WS_KS_SC当你修改数据库,使其“载”。

抛出错误是因为两个部分中的字符串字段都EXCEPT需要匹配的排序规则。但另一部分EXCEPT是使用定义为的表变量:

DECLARE @DependencyTree as Table(ForeignKeyObjectID INT,
     ForeignKeyObjectName NVARCHAR(MAX), ParentTableID INT, ParentTableName NVARCHAR(MAX),
ChildTableID INT, ChildTableName NVARCHAR(MAX), Generation INT)
Run Code Online (Sandbox Code Playgroud)

没有为NVARCHAR(MAX)字段指定排序规则(技术上应该声明为sysname——对于那个字段总是全部小写——因为这是源系统 Views of sys.objectsand的数据类型sys.foreign_keys)。虽然在包含数据库排序规则表MSDN 页面中没有提到它,但与临时表不同,表变量从数据库中获取默认排序规则,而不是从tempdb(这就是为什么您过去没有看到此错误,因为您的tempdb排序规则应该是SQL_Latin1_General_CP1_CI_AS从这就是实例整理;如果此表是临时表,您之前就会收到此错误)。因此,用于整理ForeignKeyObjectNameParentTableNameChildTableName领域是Latin1_General_CI_AS 并且在“包含”数据库时仍然是相同的排序规则。

将该表变量声明更改为以下内容应该可以解决此问题:

DECLARE @DependencyTree Table
(
  ForeignKeyObjectID INT,
  ForeignKeyObjectName sysname COLLATE CATALOG_DEFAULT,
  ParentTableID INT,
  ParentTableName sysname COLLATE CATALOG_DEFAULT,
  ChildTableID INT,
  ChildTableName sysname COLLATE CATALOG_DEFAULT,
  Generation INT
);
Run Code Online (Sandbox Code Playgroud)

UsingCOLLATE CATALOG_DEFAULT将在数据库不包含时以及当它们被更改为包含时使用,因为CATALOG_DEFAULT解析为非包含数据库中的数据库默认值。说明此行为的另一种方式是,由于数据库元数据CATALOG_DEFAULT在数据库的任一状态下进行整理,因此它将在数据库的任一状态下在表变量(和临时表)中工作。


RLF*_*RLF 5

包含数据库排序规则的 MSDN 页面有一些指导,其中包括:

  • 在包含的数据库中,目录排序规则 Latin1_General_100_CI_AS_WS_KS_SC。此排序规则对于 SQL Server 的所有实例上的所有包含的数据库都是相同的,并且无法更改

因此,您的问题在于目录排序规则。当您更改为包含时,它将数据库的目录排序规则更改为Latin1_General_100_CI_AS_WS_KS_SC,这是您的问题的根源。

也许关于整理的评论,特别是CATALOG_DEFAULT可以为您提供一些帮助:

  • 数据库核对被保留,而是仅作为缺省归类用户数据
  • COLLATE 子句中提供了一个新关键字CATALOG_DEFAULT。这用作当前包含和非包含数据库中元数据整理的快捷方式

包含和非包含上下文之间的交叉

  • 只要包含数据库中的会话保持包含状态,它就必须保留在它所连接的数据库中。在这种情况下,行为非常简单。但是,如果会话在包含和非包含上下文之间交叉,则行为会变得更加复杂,因为这两组规则必须桥接。

此链接以结论结尾

  • 包含数据库的整理行为与非包含数据库中的整理行为略有不同。这种行为通常是有益的,提供了实例独立性和简单性。某些用户可能会遇到问题,特别是当会话访问包含和非包含数据库时。

关于数据整理问题

如果您也有关于决心整理问题的数据使用COLLATE DATABASE_DEFAULT。可能您的两个数据库对数据具有相同的排序规则。但如果没有,您可以使用以下技术:

select NameValue COLLATE DATABASE_DEFAULT from MyDatabase.Schema.Table
EXCEPT
select NameValue COLLATE DATABASE_DEFAULT from TheirDatabase.Schema.Table
Run Code Online (Sandbox Code Playgroud)

这种方法的价值在于您不需要指定特定的排序规则,而是COLLATE DATABASE_DEFAULT允许您使用当前数据库的排序规则。这将解决数据整理问题