web-dev-qa-db-ja.com

含まれるDB照合エラー

データベースを部分的包含に変更すると、次のエラーが発生します。

EXCEPT操作での「Latin1_General_CI_AS」と「Latin1_General_100_CI_AS_KS_WS_SC」の間の照合の競合を解決できません。

>オブジェクトのコンパイル中に、プロシージャ 'RSExecRole.DeleteExtensionModuleDDL'でエラーが発生しました。データベース 'VeeamOne'の包含オプションが変更されたか、このオブジェクトがモデルdbに存在し、ユーザーが新しい包含データベースを作成しようとしました。 ALTER DATABASEステートメントが失敗しました。 SQLモジュールの検証中にコンパイルエラーが発生したため、データベース 'VeeamOne'の包含オプションを変更できませんでした。以前のエラーを参照してください。 ALTER DATABASEステートメントが失敗しました。 (.Net SqlClientデータプロバイダー)

これが報告しているオブジェクトはSSRSからのものだと思います。ただし、照合を変更するDBは完全に別のアプリケーションです。

これを解決する方法について何か提案はありますか?

================================================== ======================= OKこれは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
5
Tom

表示されている問題は、システムビューのメタデータの照合-_sys.foreign_keys_および_sys.objects_-とテーブル変数_@DependencyTree_の間の競合です。

@RLFの answer で指摘されているように、データベースを「」に変更すると、データベースメタデータの照合順序が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
_

_fk.name_、_parent.name_、および_child.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)
_

NVARCHAR(MAX)フィールドには照合順序が指定されていません(技術的にはsysnameとして宣言する必要があります-常にすべて小文字です-これはソースシステムのデータ型であるためです。 _sys.objects_および_sys.foreign_keys_)。 Contained Database Collat​​ions Table MSDNページでは言及されていませんが、一時テーブルとは異なり、テーブル変数はtempdbではなくデータベースからデフォルトの照合順序を取得します(これが理由です) tempdb照合順序は_SQL_Latin1_General_CP1_CI_AS_である必要があるため、過去にこのエラーが表示されます。これはインスタンスの照合順序であるためです。このテーブルが一時テーブルである場合は、このエラーが発生するはずです)したがって、ForeignKeyObjectNameParentTableName、およびChildTableNameフィールドに使用される照合は_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
);
_

_COLLATE CATALOG_DEFAULT_は非包含データベースのデフォルトに解決されるため、_CATALOG_DEFAULT_を使用すると、データベースが含まれていない場合、およびデータベースが含まれるように変更された場合に機能します。この動作を述べるもう1つの方法は、データベースのメタデータがデータベースのどちらの状態でも_CATALOG_DEFAULT_として照合されるため、データベースのどちらの状態でもテーブル変数(および一時テーブル)で機能するということです。

4
Solomon Rutzky

Contained Database Collat​​ions のMSDNページには、次のようなガイダンスがあります。

  • 包含データベースでは、カタログの照合順序はLatin1_General_100_CI_AS_WS_KS_SCです。この照合はSQL Serverのすべてのインスタンスに含まれるすべてのデータベースで同じであり、変更できませんです。

したがって、問題はカタログ照合にあります。包含に変更すると、データベースのカタログ照合Latin1_General_100_CI_AS_WS_KS_SCに変更され、これが問題の原因です。

おそらく、照合に関するコメント、特にCATALOG_DEFAULTは、いくつかの支援を提供する可能性があります。

  • データベースの照合は保持されますですが、ユーザーデータのデフォルトの照合としてのみ使用されます。
  • 新しいキーワードCATALOG_DEFAULTがCOLLATE句で使用できます。これは、包含データベースと非包含データベースの両方でメタデータの現在の照合へのショートカットとして使用されます

包含コンテキストと非包含コンテキストの交差

  • 含まれているデータベースのセッションが含まれている限り、接続先のデータベース内にある必要があります。この場合、動作は非常に簡単です。しかし、セッションが包含コンテキストと非包含コンテキストの間で交差する場合、2つのルールセットをブリッジする必要があるため、動作はより複雑になります。

そして、このリンクは結論:で終わります

  • 包含データベースの照合動作は、非包含データベースの照合動作とは微妙に異なります。この動作は一般に有益であり、インスタンスに依存せず、単純です。 一部のユーザーは、特にセッションが包含データベースと非包含データベースの両方にアクセスする場合に問題が発生する可能性があります。

データ照合の問題について

あなたがもする必要がある場合は、 _COLLATE DATABASE_DEFAULT_を使用してdataの照合問題を解決します。おそらく、2つのデータベースのデータの照合順序は同じです。ただし、そうでない場合は、次の手法を使用できます。

_select NameValue COLLATE DATABASE_DEFAULT from MyDatabase.Schema.Table
EXCEPT
select NameValue COLLATE DATABASE_DEFAULT from TheirDatabase.Schema.Table
_

このアプローチの価値は、特定の照合を指定する必要がないことですが、_COLLATE DATABASE_DEFAULT_を使用すると、現在のデータベースの照合を使用できます。これは、データ照合問題を解決します。

5
RLF

これは、私が実装した回避策を示すためです-両手が縛られていて、比較エラーの原因となっているソーステーブルを変更できない場合、これは機能します。 。

 /* This is an interesting problem I ran across while trying to do some work in a collated database. 
The SQLLogin column on the LoginsReference table is nvarchar(50) (yes, it should be sysname)
When trying to see if [name] from the sys.sysusers table existed in the LoginsReference table, I got the following error:

--Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_100_CI_AS_KS_WS_SC" in the equal to operation.

My temporary solution is to leverage implicit conversion to insert the SQLLogins FROM LoginsReference into a temp table that is properly collated, and use it for the comparison operator.
Long term solution is to properly collate things in tables.  However, there is a lot of dynamic SQL involved and I'm worried there will be many more issues with other comparisons made in stored procedures elsewhere.  We'll see.
*/
--this is a script to clean out users created in testing and also reset all tables involved in testing.
USE [_Contained Database]
DECLARE @UUID smallint
DECLARE @SQL nvarchar(max)
DECLARE @loginname nvarchar (20)
DECLARE @stop bit
IF OBJECT_ID('tempdb..#usernames') is NOT NULL
DROP TABLE #usernames
CREATE TABLE #usernames
([sqlLogin] nvarchar (50) COLLATE CATALOG_DEFAULT)
INSERT INTO #userNames
SELECT [sqlLogin] FROM [dbo].[LoginsReference]


WHILE EXISTS (SELECT TOP 1 [UID] FROM sys.sysusers WHERE [uid] BETWEEN 5 and 16000 and [name] in (SELECT [sqlLogin] FROM #usernames))
BEGIN
    SELECT TOP 1 @loginname = [name] FROM sys.sysusers WHERE [uid] BETWEEN 5 and 16000 and [name] in (SELECT [sqlLogin] FROM #usernames)
    SELECT @SQL = 'DROP USER ' + @loginname
    exec sp_executesql @sql
END
TRUNCATE TABLE [dbo].[LoginRoles]
TRUNCATE TABLE [dbo].[LoginsReference]

FROM: https://github.com/scorellis/SQLTutorials/blob/master/COLLATE_Comparison_problem

0
RelativitySQL