オリジンサーバーのビューを介してリンクサーバーからデータをクエリしています。ビューには、Created
、Modified
、Deleted
などのいくつかの標準化された列を含める必要がありますが、この場合、ソースサーバーのテーブルには適切な列がありません。情報。したがって、列はそれぞれの型に明示的にキャストされます。列を変更してビューを更新しました
NULL AS Modified
に
CAST(NULL as DateTime) as Modified
ただし、この更新を実行した後、ビューは次のエラーメッセージをトリガーします。
メッセージ7341、レベル16、状態2、行3列=(ユーザーが生成した式)の現在の行の値を取得できませんOLEリンクサーバー ""のDBプロバイダー "SQLNCLI11"。
この「明示的なキャスト」の変更は、通常、Originサーバー全体で心配なく行われました。問題は、関連するサーバーのバージョンに関連しているのではないかと思います。実際にこのキャストを適用する必要はありませんが、よりクリーンに感じられます。今、私はなぜこれが起こっているのか知りたいだけです。
サーバーのバージョン(元):
Microsoft SQL Server 2012-11.0.5058.0(X64)May 14 2014 18:34:29 Copyright(c)Microsoft Corporation Enterprise Edition(64-bit)on Windows NT 6.1(Build 7601:Service Pack 1)(Hypervisor)
サーバーのバージョン(リンク):
Microsoft SQL Server 2008 R2(SP1)-10.50.2500.0(X64)Jun 17 2011 00:54:03 Copyright(c)Microsoft Corporation Enterprise Edition(64-bit)on Windows NT 6.1(Build 7601:Service Pack 1)(Hypervisor )
編集
問題の列をすべて投稿しないことで間違いを犯したことに気づきました。重要な詳細を省いたことをお詫びします。これに気づかなかった方法がすぐにわかりません。問題はまだ残っています。
誤ったキャストは、DateTimeへのキャストでは発生せず、UniqueIdentifierにキャストされる列で発生します。
これが原因です:
CAST(NULL AS UniqueIdentifier) AS [GUID]
UniqueIdentifiersはSQL Server 2008 R2でサポートされており、コメントに記載されているように、ビューによって実行されるクエリはリンクサーバーで正常に実行されます。
したがって、CAST
がリモートインスタンスではなくローカルで実行されていることを認識した後、エラーを再現できました。これを修正することを期待して、以前はSP3に移行することを推奨していました(一部はSP3でエラーを再現できないため、一部はそれが適切なアイデアであるためです)。ただし、これでエラーを再現できるようになったので、SP3に移行しても、おそらく良いアイデアではありますが、これを修正できないことは明らかです。また、SQL Server 2008 R2でエラーを再現しましたRTMおよび2014 SP1(3つのケースすべてで「ループバック」ローカルリンクサーバーを使用))。
この問題は、クエリが実行されているwhere、または少なくともpart(s)がどこにあるかに関係しているようです実行。これは、CAST
操作を機能させることができたためですが、ローカルDBオブジェクトへの参照を含めることによってのみ可能です。
_SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (SELECT TOP (1) 1 FROM [sys].[data_spaces]) tmp(dummy);
_
それは実際に機能します。ただし、以下は元のエラーを取得します。
_SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (VALUES (1)) tmp(dummy);
_
ローカル参照がない場合、クエリ全体がリモートシステムに送信されて実行され、何らかの理由でNULL
sをUNIQUEIDENTIFIER
に変換できないか、おそらくNULL
がOLE DBドライバが正しくありません。
私が行ったテストに基づくと、これはバグのように見えますが、バグがSQL Server内にあるのか、SQL Server Native Client/OLEDBドライバー内にあるのかはわかりません。ただし、変換エラーはOLEDBドライバー内で発生するため、ドライバーはSQL Serverを使用して変換を行っていないため(SQL ServerではSQL Serverでも使用できない変換)、INT
からUNIQUEIDENTIFIER
への変換の問題であるとは限りません(SQL ServerもINT
をDATE
に変換することはできませんが、OLEDBドライバは、テストの1つに示されているように、それを正常に処理します。
私は3つのテストを実行しました。成功した2つについては、リモートで実行されているクエリを示すXML実行プランを調べました。 3つすべてについて、SQLプロファイラーを介して例外またはOLEDBイベントをキャプチャしました。
イベント:
列フィルター:
テスト
テスト1
CAST(NULL AS UNIQUEIDENTIFIER)
_SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
, (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
_
XML実行プランの関連部分:
_ <DefinedValue>
<ColumnReference Column="Expr1002" />
<ScalarOperator ScalarString="NULL">
<Const ConstValue="NULL" />
</ScalarOperator>
</DefinedValue>
...
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT 1 FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
_
テスト2
CAST(NULL AS UNIQUEIDENTIFIER)
_SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
_
(注:私はサブクエリをそこに保持し、コメント化したので、XMLトレースファイルを比較したときの差が1つ少なくなります)
テスト3
CAST(NULL AS DATE)
_SELECT TOP (2) CAST(NULL AS DATE) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
_
(注:私はサブクエリをそこに保持し、コメント化したので、XMLトレースファイルを比較したときの差が1つ少なくなります)
XML実行プランの関連部分:
_ <DefinedValue>
<ColumnReference Column="Expr1002" />
<ScalarOperator ScalarString="[Expr1002]">
<Identifier>
<ColumnReference Column="Expr1002" />
</Identifier>
</ScalarOperator>
</DefinedValue>
...
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT TOP (2) NULL "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
_
テスト#3を見ると、「リモート」システムでSELECT TOP (2) NULL
を実行しています。 SQLプロファイラートレースは、このリモートフィールドのデータ型が実際にINT
であることを示しています。トレースは、クライアント側のフィールド(つまり、クエリを実行している場所)がDATE
であることも示しています。 SQL Serverでエラーが発生するINT
からDATE
への変換は、OLEDBドライバ内で正常に機能します。リモート値はNULL
であるため、直接返されるため、_<ColumnReference Column="Expr1002" />
_になります。
テスト#1を見ると、「リモート」システムで_SELECT 1
_を実行しています。 SQLプロファイラートレースは、このリモートフィールドのデータ型が実際にINT
であることを示しています。トレースは、クライアント側のフィールド(つまり、クエリを実行している場所)がGUID
であることも示しています。 INT
からGUID
への変換(これはドライバー内で行われ、OLEDBはこれを「GUID」と呼びます)は、SQL Serverでエラーが発生するもので、OLEDBドライバー内で正常に機能します。リモート値はnotNULL
であるため、リテラルNULL
に置き換えられるため、_<Const ConstValue="NULL" />
_になります。
テスト#2が失敗したため、実行計画はありません。ただし、「リモート」システムのクエリは成功しますが、結果セットを返すことができません。 SQLプロファイラがキャプチャしたクエリは次のとおりです。
_SELECT TOP (2) NULL "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001"
_
これは、テスト#1で実行されているのとまったく同じクエリですが、ここでは失敗しています。他にも小さな違いはありますが、OLEDBの通信を完全に解釈することはできません。ただし、リモートフィールドは引き続きINT
(wType = 3 = adInteger/4バイトの符号付き整数/ DBTYPE_I4)として表示されますが、「クライアント」フィールドは引き続きGUID
(wType = 72 = adGUID /グローバル一意識別子/ DBTYPE_GUID)として表示されます。 OLE DBドキュメントは GUIDデータ型変換 、 DBDATEデータ型変換 、および I4 Dataタイプ変換 は、I4からいずれかの[〜#〜] guid [〜#〜]への変換を示しますまたは[〜#〜] dbdate [〜#〜]はサポートされていませんが、DATE
クエリは機能します。
3つのテストのトレースXMLファイルはPastebinにあります。各テストが他のテストと異なる場所の詳細を確認する場合は、それらをローカルに保存してから、「差分」を実行できます。ファイルは次のとおりです。
ERGO?
それについて何をしますか? SQL Native Client-_SQLNCLI11
_-がSQL Server 2012で非推奨になっていることを考えると、おそらく、上のセクションで説明した回避策だけです。SQLServer Native Clientのトピックに関するほとんどのMSDNページには、上部の次の通知:
警告
SQL Server Native Client(SNAC)はSQL Server 2012以降ではサポートされていません。新しい開発作業ではSNACの使用を避け、現在それを使用しているアプリケーションの変更を計画してください。 Microsoft ODBC Driver for SQL Server は、WindowsからMicrosoft SQL ServerおよびMicrosoft Azure SQLデータベースへのネイティブ接続を提供します。
詳細については、以下を参照してください。
ODBC ??
ODBCリンクサーバーを次のように設定しました:
_EXEC master.dbo.sp_addlinkedserver
@server = N'LocalODBC',
@srvproduct=N'{my_server_name}',
@provider=N'MSDASQL',
@provstr=N'Driver={SQL Server};Server=(local);Trusted_Connection=Yes;';
EXEC master.dbo.sp_addlinkedsrvlogin
@rmtsrvname=N'LocalODBC',
@useself=N'True',
@locallogin=NULL,
@rmtuser=NULL,
@rmtpassword=NULL;
_
そして、試してみました:
_SELECT CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
FROM [LocalODBC].[tempdb].[sys].[objects] rmt;
_
次のエラーを受け取りました:
リンクサーバー "LocalODBC"のOLE DBプロバイダー "MSDASQL"がメッセージ "要求された変換はサポートされていません。"を返しました。
メッセージ7341、レベル16、状態2、行53
リンクサーバー "LocalODBC"のOLE DBプロバイダー "MSDASQL"から、列 "(ユーザー生成式).Expr1002"の現在の行の値を取得できません。
P.S。
リモートサーバーとローカルサーバー間のGUIDの転送に関連するため、NULL以外の値は特別な構文を介して処理されます。 CAST(0x00 AS UNIQUEIDENTIFIER)
を実行すると、SQLプロファイラトレースに次のOLE DBイベント情報が表示されます。
_<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT {guid'00000000-0000-0000-0000-000000000000'} "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
_
P.P.S。
また、次のクエリでOPENQUERY
を使用してテストしました。
_SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
--, (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM OPENQUERY([Local], N'SELECT 705 AS [dummy] FROM [TEMPTEST].[sys].[objects];') rmt;
_
ローカルオブジェクト参照がなくても成功しました。 SQLプロファイラのトレースXMLファイルは、Pastebinの次の場所に投稿されています。
XML実行プランは、テスト#1と同じように、NULL
定数を使用してそれを示しています。
醜い回避策しかありません-null
の代わりに'1900-01-01'
のような日付定数を使用してください。
CAST('1900-01-01' as DateTime) as Modified
インポート後、1900-01-01
で列を更新してNullに戻すことができます。
これは here によるSQL 2012の機能/バグの一種です。
編集:以下の@a_horse_with_no_nameコメントに従って、1900-00-00
を有効な日付1900-01-01
に置き換えます。
この問題は、データ型変換に関連しています(コメントでヒット)。
以下を検討してください。
SELECT NULL as NullColumn INTO SomeTable;
EXEC sp_help SomeTable;
DROP TABLE SomeTable;
NullColumn
のタイプはint
であることに注意してください。 SQL Serverは、int
値をuniqueidentifier
に変換することを好みません。このSELECT
ステートメントは、データ型変換で失敗します。
--Just a SELECT from nothing
SELECT CAST(CAST(NULL as int) as uniqueidentifier);
--
--or to see it from a physical table:
SELECT NULL as NullColumn INTO SomeTable;
SELECT CAST(NullColumn as uniqueidentifier) FROM SomeTable;
DROP TABLE SomeTable;
メッセージ529、レベル16、状態2、行3
データ型intからuniqueidentifierへの明示的な変換は許可されていません。
この特定の値(NULL)はGUIDにキャストできますが、SQL Serverは特定の値を調べる前に、データ型変換に基づいてエラーをスローします。代わりに、マルチステップCAST
操作を実行して、暗黙のint
をuniqueidentifer
にきれいに変換できるデータ型に変更する必要があります。 varchar
に、次にuniqueidentifier
に:
--Just a SELECT from nothing
SELECT CAST(CAST(CAST(NULL as int) as varchar) as uniqueidentifier);
--
--or to see it from a physical table:
SELECT NULL as NullColumn INTO SomeTable;
SELECT CAST(CAST(NullColumn as varchar(32)) as uniqueidentifier) FROM SomeTable;
DROP TABLE SomeTable;
OPは、これが適切な答えであるかどうかを最終的に決定できます。
私には「絶対的な」証明はありませんが、UniqueIdentiferはサーバーに依存しており、プロバイダーがこのuniqueidentifierを取得するサーバー(ローカルまたはリモート)を特定するのが困難であるという事実に問題があることが「疑い」ですヌル。このため、このシナリオではおそらく他のデータ型を正常にキャストできますが、uniqueidentifierはキャストできません。 UNIQUEIDENTIFIERSやDATETIMEOFFSETのように「サーバー」に依存するデータ型を使用すると、発生しているエラーが発生します。
4部構成の名前の代わりにOPENQUERYを使用しても機能します。
set nocount on
DECLARE @cmd nVARCHAR(max)
DECLARE @datatype SYSNAME
DECLARE _CURSOR CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT NAME
FROM sys.types
OPEN _CURSOR
FETCH NEXT
FROM _CURSOR
INTO @datatype
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
SET @cmd = 'select top 1 cast(null as ' + @Datatype + ') as CastedData from remoteserver.remotedatabase.remoteschema.remotetable'
PRINT @cmd
EXECUTE sp_executesql @cmd
END TRY
BEGIN CATCH
PRINT Error_message()
END CATCH
FETCH NEXT
FROM _CURSOR
INTO @datatype
END --End While
CLOSE _CURSOR
DEALLOCATE _CURSOR
回避策: 受け入れられた回答 は、OLEDBドライバーがサポートしていないため、ローカルで変換を行う必要があることを示しているようです。
したがって、簡単な回避策(少なくとも、再帰CTEの基本ケースでnull uniqueidentifier
を選択しているクエリの場合)はnull変数を宣言することです:
declare @nullGuid as uniqueidentifier = null;
--Instead of...
CAST(NULL AS UniqueIdentifier) AS [GUID]
--use
@nullGuid AS [GUID]