Jadi, saya bisa mereproduksi kesalahan setelah menyadari bahwa CAST
itu dilakukan secara lokal, bukan pada contoh jarak jauh. Saya sebelumnya merekomendasikan pindah ke SP3 dengan harapan memperbaiki ini (sebagian karena tidak dapat mereproduksi kesalahan pada SP3, dan sebagian karena itu menjadi ide yang bagus terlepas). Namun, sekarang saya dapat mereproduksi kesalahan, jelas bahwa pindah ke SP3, sementara masih mungkin ide yang baik, tidak akan memperbaikinya. Dan saya juga mereproduksi kesalahan dalam SQL Server 2008 R2 RTM dan 2014 SP1 (menggunakan "loop-back" Server Linked lokal dalam ketiga kasus).
Tampaknya masalah ini berkaitan dengan di mana kueri mengeksekusi, atau setidaknya di mana sebagian dari itu mengeksekusi. Saya mengatakan ini karena saya bisa mendapatkan CAST
operasi untuk bekerja, tetapi hanya dengan memasukkan referensi ke objek DB lokal:
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);
Itu benar-benar berfungsi. Tetapi yang berikut mendapatkan kesalahan asli:
SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (VALUES (1)) tmp(dummy);
Saya menduga bahwa ketika tidak ada referensi lokal, seluruh permintaan dikirim ke sistem jarak jauh untuk dieksekusi, dan untuk beberapa alasan NULL
s tidak dapat dikonversi ke UNIQUEIDENTIFIER
, atau mungkin NULL
diterjemahkan oleh driver OLE DB salah.
Berdasarkan pengujian yang telah saya lakukan, ini akan muncul sebagai bug, tapi saya tidak yakin apakah bug itu ada di dalam SQL Server atau driver SQL Server Native Client / OLEDB. Namun, kesalahan konversi terjadi dalam pengandar OLEDB, dan karena itu belum tentu masalah konversi dari INT
ke UNIQUEIDENTIFIER
(konversi yang tidak diizinkan dalam SQL Server) karena pengandar tidak menggunakan SQL Server untuk melakukan konversi (SQL Server juga tidak memungkinkan untuk mengkonversi INT
ke DATE
, namun pegangan sopir OLEDB yang berhasil, seperti yang ditunjukkan dalam salah satu tes).
Saya menjalankan tiga tes. Untuk dua yang berhasil, saya melihat rencana eksekusi XML yang menunjukkan permintaan yang sedang dijalankan dari jarak jauh. Untuk ketiganya, saya menangkap Pengecualian atau peristiwa OLEDB melalui SQL Profiler:
Acara:
- Kesalahan dan Peringatan
- Perhatian
- Pengecualian
- Peringatan Eksekusi
- Pesan Kesalahan Pengguna
- OLEDB
- TSQL
- semua kecuali :
- SQL: StmtRecompile
- Jenis Statis XQuery
Filter Kolom:
- Nama aplikasi
- TIDAK SEPERTI % Intellisense%
- SPID
- Lebih besar dari atau sama dengan 50
UJI
Tes 1
CAST(NULL AS UNIQUEIDENTIFIER)
itu bekerja
SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
, (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
Bagian yang relevan dari rencana eksekusi XML:
<DefinedValue>
<ColumnReference Column="Expr1002" />
<ScalarOperator ScalarString="NULL">
<Const ConstValue="NULL" />
</ScalarOperator>
</DefinedValue>
...
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT 1 FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
Tes 2
CAST(NULL AS UNIQUEIDENTIFIER)
itu gagal
SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
(catatan: Saya menyimpan subquery di sana, berkomentar, sehingga akan ada satu perbedaan kurang ketika saya membandingkan file jejak XML)
Tes 3
CAST(NULL AS DATE)
itu bekerja
SELECT TOP (2) CAST(NULL AS DATE) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
(catatan: Saya menyimpan subquery di sana, berkomentar, sehingga akan ada satu perbedaan kurang ketika saya membandingkan file jejak XML)
Bagian yang relevan dari rencana eksekusi 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""
/>
Jika Anda melihat Tes # 3, itu sedang melakukan SELECT TOP (2) NULL
pada sistem "remote". Jejak SQL Profiler menunjukkan bahwa tipe data dari bidang jauh ini sebenarnya INT
. Jejak juga menunjukkan bahwa bidang di sisi klien (yaitu tempat saya menjalankan kueri dari) DATE
, seperti yang diharapkan. Konversi dari INT
ke DATE
, sesuatu yang akan mendapatkan kesalahan dalam SQL Server, berfungsi dengan baik dalam driver OLEDB. Nilai jarak jauh adalah NULL
, sehingga dikembalikan secara langsung, karenanya <ColumnReference Column="Expr1002" />
.
Jika Anda melihat Tes # 1, itu sedang melakukan SELECT 1
pada sistem "remote". Jejak SQL Profiler menunjukkan bahwa tipe data dari bidang jauh ini sebenarnya INT
. Jejak juga menunjukkan bahwa bidang di sisi klien (yaitu tempat saya menjalankan kueri dari) GUID
, seperti yang diharapkan. Konversi dari INT
ke GUID
(ingat, ini dilakukan di dalam driver, dan OLEDB menyebutnya "GUID"), sesuatu yang akan mendapatkan kesalahan dalam SQL Server, berfungsi dengan baik di dalam driver OLEDB. Nilai jarak jauh tidak NULL
, sehingga diganti dengan literal NULL
, karenanya <Const ConstValue="NULL" />
.
Tes # 2 gagal, jadi tidak ada rencana eksekusi. Namun, ia berhasil meng-query sistem "remote", tetapi tidak bisa mengembalikan set hasil. Kueri yang ditangkap SQL Profiler adalah:
SELECT TOP (2) NULL "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001"
Itu adalah permintaan yang sama persis yang sedang dilakukan di Tes # 1, namun di sini gagal. Ada perbedaan kecil lainnya, tetapi saya tidak dapat sepenuhnya menafsirkan komunikasi OLEDB. Namun, bidang jarak jauh masih ditampilkan sebagai INT
(wType = 3 = adInteger / integer bertanda empat-byte / DBTYPE_I4) sementara bidang "klien" masih ditampilkan sebagai GUID
(wType = 72 = adGUID / pengidentifikasi unik global / DBTYPE_GUID). Dokumentasi OLE DB tidak banyak membantu karena Konversi Tipe Data GUID , Konversi Tipe Data DBDATE , dan Konversi Tipe Data I4 menunjukkan bahwa konversi dari I4 ke GUID atau DBDATE tidak didukung, namun DATE
kueri berfungsi.
File Trace XML untuk tiga tes terletak di PasteBin. Jika Anda ingin melihat detail di mana masing-masing tes berbeda dari yang lain, Anda dapat menyimpannya secara lokal dan kemudian melakukan "perbedaan" pada mereka. File-file tersebut adalah:
- NullGuidSuccess.xml
- NullGuidError.xml
- NullDateSuccess.xml
JADI?
Apa yang harus dilakukan tentang hal itu? Mungkin hanya pekerjaan-sekitar yang saya catat di bagian atas, mengingat bahwa SQL Native Client - SQLNCLI11
- sudah usang pada SQL Server 2012. Sebagian besar halaman MSDN tentang topik SQL Server Native Client memiliki pemberitahuan berikut di teratas:
Peringatan
SQL Server Native Client (SNAC) tidak didukung di luar SQL Server 2012. Hindari menggunakan SNAC dalam pekerjaan pengembangan baru, dan rencanakan untuk memodifikasi aplikasi yang saat ini menggunakannya. The Microsoft ODBC SQL Server menyediakan konektivitas asli dari Windows ke Microsoft SQL Server dan Microsoft Azure SQL Database.
Untuk info lebih lanjut, silakan lihat:
ODBC ??
Saya mengatur ODBC Linked Server melalui:
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;
Dan kemudian mencoba:
SELECT CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
FROM [LocalODBC].[tempdb].[sys].[objects] rmt;
dan menerima kesalahan berikut:
Penyedia OLE DB "MSDASQL" untuk server yang terhubung "LocalODBC" mengembalikan pesan "Konversi yang diminta tidak didukung.".
Msg 7341, Level 16, State 2, Line 53
Tidak bisa mendapatkan nilai baris saat ini dari kolom "(ekspresi yang dibuat pengguna). EXPR1002" dari penyedia OLE DB "MSDASQL" untuk server yang terhubung "LocalODBC".
PS
Karena terkait dengan pengangkutan GUID antara server jauh dan lokal, nilai-nilai non-NULL ditangani melalui sintaks khusus. Saya perhatikan info acara OLE DB berikut di jejak SQL Profiler ketika saya berlari CAST(0x00 AS UNIQUEIDENTIFIER)
:
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT {guid'00000000-0000-0000-0000-000000000000'} "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
PPS
Saya juga menguji melalui OPENQUERY
dengan permintaan berikut:
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;
dan itu berhasil, bahkan tanpa referensi objek lokal. File XML jejak SQL Profiler telah diposting ke PasteBin di:
NullGuidSuccessOPENQUERY.xml
Paket eksekusi XML memperlihatkannya menggunakan NULL
konstanta, sama seperti pada Uji # 1.