bug dalam konfigurasi database_scoped_

9

Saya mencoba menyisipkan set hasil dari:

SELECT * FROM sys.database_scoped_configurations

ke tabel temp, karena saya ingin memeriksa pengaturan untuk semua database di server saya. Jadi saya menulis kode ini:

DROP TABLE IF EXISTS #h
CREATE TABLE #h(dbname sysname, configuration_id INT, name sysname,     value SQL_VARIANT,  value_for_secondary SQL_VARIANT)
EXEC sys.sp_MSforeachdb 'USE ?; insert into #h(dbname, configuration_id, name, value,value_for_secondary)  SELECT ''?'' as dbname, * FROM sys.database_scoped_configurations  D'
SELECT * FROM #h H

Tapi kemudian hanya akan ada satu baris per basis data, bukan empat baris yang saya harapkan dari menjalankan pilihan polos di setiap basis data.

Saya tahu ada cara yang lebih baik untuk kode ini daripada menggunakan sp_MSForEachDB, dan saya mencoba beberapa. Tapi saya masih hanya mendapatkan satu baris per basis data. Saya sudah mencoba ini pada SQL Server 2016 RTM dan SP1

Apakah ini bug dengan SQL Server 2016, atau apakah saya melakukan sesuatu yang salah?

Henrik Staun Poulsen
sumber
bug telah diperbaiki, setidaknya di Microsoft SQL Server 2017 (RTM-CU15-GDR)
Henrik Staun Poulsen

Jawaban:

8

Apakah ini bug dengan SQL Server 2016?

Iya. Jelas ini bukan perilaku yang benar. Saya telah melaporkannya di sini dan diperbaiki di SQL Server 2016 SP2 CU9 .

Seperti yang dikatakan Mikael Eriksson dalam komentar sys.database_scoped_configurationsdan sys.dm_exec_sessionsdiimplementasikan sebagai tampilan dalam format

SELECT ...  
FROM OpenRowset(TABLE xxxx)  

Namun membandingkan dua rencana di bawah ini ada perbedaan yang jelas.

DBCC TRACEON(3604);

DECLARE @database_scoped_configurations TABLE(x INT);

INSERT INTO @database_scoped_configurations
SELECT configuration_id
FROM   sys.database_scoped_configurations
OPTION (QUERYTRACEON 8608, QUERYTRACEON 8615, QUERYTRACEON 8619, QUERYTRACEON 8620 );


DECLARE @dm_exec_sessions TABLE(x INT);

INSERT INTO @dm_exec_sessions
SELECT session_id
FROM   sys.dm_exec_sessions
OPTION (QUERYTRACEON 8608, QUERYTRACEON 8615, QUERYTRACEON 8619, QUERYTRACEON 8620 );

masukkan deskripsi gambar di sini

Lacak flag 8619 output untuk kedua queri ini

Terapkan Aturan: EnforceHPandAccCard - x0-> Spool atau Top (x0)

SQL Server tampaknya tidak dapat memastikan bahwa sumber untuk TVF tidak juga target penyisipan sehingga membutuhkan perlindungan Halloween.

Dalam kasus sesi ini diimplementasikan sebagai gulungan yang menangkap semua baris terlebih dahulu. Di database_scoped_configurationsdengan menambahkan TOP 1ke rencana. Penggunaan TOPuntuk perlindungan Halloween dibahas dalam artikel ini . Artikel itu juga menyebutkan bendera jejak yang tidak berdokumen untuk memaksa spool daripada TOPyang berfungsi seperti yang diharapkan.

DECLARE @database_scoped_configurations TABLE(x INT);

INSERT INTO @database_scoped_configurations
SELECT configuration_id
FROM   sys.database_scoped_configurations
OPTION (QUERYTRACEON 8692)

Masalah yang jelas dengan menggunakan TOP 1daripada spool adalah bahwa itu akan secara sewenang-wenang membatasi jumlah baris yang dimasukkan. Jadi ini hanya akan valid jika jumlah baris yang dikembalikan oleh fungsi adalah <= 1.

Memo awal terlihat seperti ini

masukkan deskripsi gambar di sini

Bandingkan ini dengan memo awal untuk kueri 2

masukkan deskripsi gambar di sini

Jika saya memahami hal di atas dengan benar, ia berpikir bahwa TVF pertama dapat mengembalikan maksimum satu baris dan menerapkan pengoptimalan yang salah. Maks untuk kueri kedua diatur ke 1.34078E+154( 2^512).

Saya tidak tahu dari mana asal jumlah baris maksimum ini. Mungkin metadata disediakan oleh penulis DMV? Juga aneh bahwa TOP(50)penyelesaiannya tidak ditulis ulang TOP(1)karena TOP(50)tidak akan mencegah masalah Halloween terjadi (meskipun akan menghentikannya berlanjut tanpa batas waktu)

Martin Smith
sumber
6

Tolong berhenti menggunakan sp_MSForEachDB. Ini tidak didukung, tidak berdokumen, dan bermasalah - yang mungkin menjadi masalah di sini. Pengganti saya menunjukkan masalah yang sama di sini, tetapi secara umum itu adalah hal yang lebih aman untuk digunakan.

Untuk hal-hal seperti ini saya lebih suka untuk menghasilkan SQL dinamis daripada menyerahkan satu perintah ke prosedur untuk dieksekusi beberapa kali (bahkan prosedur saya, yang saya percayai lebih banyak), dengan cara ini saya bisa mencetak perintah daripada menjalankannya, dan pastikan mereka semua akan melakukan apa yang mereka katakan.

Meminjam dari pengamatan bahwa kode yang mendasari tampilan sistem mengimplementasikan a TOP (1), kita dapat mencoba cara ini:

DROP TABLE IF EXISTS #h;

CREATE TABLE #h(dbname sysname, configuration_id INT, name sysname, 
  value SQL_VARIANT,  value_for_secondary SQL_VARIANT);

DECLARE @sql nvarchar(max) = N'', @base nvarchar(max) = N'insert into #h
  (dbname, configuration_id, name, value,value_for_secondary)  SELECT TOP ($c$) 
  $db$ as dbname, * FROM $qdb$.sys.database_scoped_configurations;';

SELECT @sql += REPLACE(REPLACE(REPLACE(@base, N'$qdb$', QUOTENAME(name)), 
  N'$db$', CHAR(39) + name + CHAR(39)), N'$c$', RTRIM(COUNT(*) OVER()))
FROM sys.databases WHERE state = 0;

PRINT @sql;
EXEC sys.sp_executesql @sql;
SELECT * FROM #h;

Perhatikan bahwa saya tidak menggunakan di USEsini, melainkan awalan tampilan syskatalog dengan nama database.

Mengapa pandangan bekerja dengan cara yang ajaib, saya tidak tahu; Saya tidak tahu bahwa Anda akan mendapatkan jawaban yang baik di sini, karena kemungkinan membutuhkan komentar dari Microsoft (atau siapa pun yang memiliki akses ke kode sumber, atau bersedia menjalankan debugger).

Aaron Bertrand
sumber
itu adalah yang pertama dari beberapa metode yang saya coba, tetapi saya tidak berpikir saya bisa menggunakan sproc itu dalam contoh.
Henrik Staun Poulsen
6

Terima kasih telah melaporkan permasalahan ini!

Ini memang bug dalam cara Pengoptimal Kueri membuat rencana untuk tampilan sys.database_scoped_configurationskatalog. Kami akan membahas ini pada salah satu pembaruan SQL Server 2016 dan Azure SQL Database berikutnya.

Sebagai solusinya, Anda bisa menambahkan TOPklausa pada SELECTbagian sisipan Anda untuk mendapatkan paket yang benar, misalnya:

DECLARE @database_scoped_configurations TABLE(x INT); 
INSERT INTO @database_scoped_configurations 
SELECT **TOP 100** configuration_id 
FROM sys.database_scoped_configurations 
Panagiotis Antonopoulos
sumber
3

Saya setuju bahwa ini sangat aneh dan berpotensi bug, tetapi menambahkan TOP (50), misalnya, ke pilihan Anda benar-benar mengembalikan semua baris, sehingga setidaknya membuat Anda maju. Hasilnya tampaknya berasal dari fungsi Table Value Function sistem ([DB_SCOPED_CONFIG]), jadi saya tidak bisa mengatakan apa yang sebenarnya terjadi.

Saya akan mengawasi utas ini untuk melihat apakah orang-orang 'pintar' tahu MENGAPA ini terjadi.

Scott Hodgin
sumber
Apakah Anda hanya mendapatkan baris MAXDOP untuk setiap database?
Dan Guzman
@DanGuzman - ya Memilih dengan sendirinya berfungsi dengan baik (tanpa semua hal foreach, hanya pada satu database). Saat Anda menambahkan Sisipan ke dalam yang menghasilkan perilaku aneh
Scott Hodgin