bug in database_scoped_configurations


9

Sto cercando di inserire il set di risultati da:

SELECT * FROM sys.database_scoped_configurations

in una tabella temporanea, perché voglio controllare le impostazioni di tutti i database sul mio server. Quindi ho scritto questo codice:

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

Ma poi ci sarà solo una riga per database, non le quattro righe che mi aspetto di eseguire una selezione semplice in ciascun database.

So che ci sono modi migliori per codificare questo che usare sp_MSForEachDB, e ho provato diversi. Ma ho ancora solo una riga per database. Ho provato questo su SQL Server 2016 RTM e SP1

Si tratta di un bug con SQL Server 2016 o sto facendo qualcosa di sbagliato?


il bug è stato corretto, almeno in Microsoft SQL Server 2017 (RTM-CU15-GDR)
Henrik Staun Poulsen,

Risposte:


8

È un bug con SQL Server 2016?

Sì. Sicuramente questo non è un comportamento corretto. L'ho segnalato qui ed è stato risolto in SQL Server 2016 SP2 CU9 .

Come dice Mikael Eriksson nei commenti sys.database_scoped_configurationse sys.dm_exec_sessionssono implementati come viste nel formato

SELECT ...  
FROM OpenRowset(TABLE xxxx)  

Tuttavia, confrontando i due piani seguenti c'è un'ovvia differenza.

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 );

inserisci qui la descrizione dell'immagine

Traccia flag 8619 output per entrambi questi spettacoli di query

Applica regola: EnforceHPandAccCard - x0-> Spool o Top (x0)

Apparentemente SQL Server non è in grado di accertare che l'origine per TVF non sia anche la destinazione di inserimento, quindi richiede la protezione di Halloween.

Nel caso delle sessioni questo è stato implementato come spool che cattura prima tutte le righe. Nel database_scoped_configurationsaggiungendo un TOP 1al piano. L'uso di TOPper la protezione di Halloween è discusso in questo articolo . L'articolo menziona anche un flag di traccia non documentato per forzare una bobina piuttosto TOPche funziona come previsto.

DECLARE @database_scoped_configurations TABLE(x INT);

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

Un ovvio problema nell'uso TOP 1piuttosto che in uno spool è che limiterà arbitrariamente il numero di righe inserite. Pertanto, ciò sarebbe valido solo se il numero di righe restituite dalla funzione fosse <= 1.

Il promemoria iniziale è simile al seguente

inserisci qui la descrizione dell'immagine

Confronta questo con il promemoria iniziale per la query 2

inserisci qui la descrizione dell'immagine

Se capisco correttamente quanto sopra, pensa che il primo TVF possa restituire un massimo di una riga e quindi applica un'ottimizzazione errata. Il valore massimo per la seconda query è impostato su 1.34078E+154( 2^512).

Non ho idea da dove derivi questo conteggio delle righe massimo. Forse i metadati forniti dall'autore del DMV? È anche strano che la TOP(50)soluzione alternativa non venga riscritta TOP(1)perché TOP(50)non impedirebbe il verificarsi del problema di Halloween (anche se impedirebbe che continui indefinitamente)


6

Si prega di smettere di usare sp_MSForEachDB. Non è supportato, è privo di documenti ed è difettoso, il che potrebbe essere il problema qui. La mia sostituzione dimostra lo stesso problema qui, ma in generale è una cosa più sicura da usare.

Per cose come questa preferisco generare SQL dinamico piuttosto che passare un singolo comando a una procedura da eseguire più volte (anche la mia procedura, di cui mi fido molto di più), in questo modo posso semplicemente stampare i comandi invece di eseguirli, e assicurati che facciano tutti quello che dicono.

Prendendo in prestito dall'osservazione che il codice alla base della vista di sistema implementa a TOP (1), possiamo provare in questo modo:

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;

Si noti che non uso USEqui, ma piuttosto prefisso la sysvista del catalogo con il nome del database.

Perché la vista funzioni in modo magico, non lo so; Non so che otterrai una buona risposta qui, poiché probabilmente richiede commenti da Microsoft (o da chiunque abbia accesso al codice sorgente o sia disposto ad avviare un debugger).


quello era il primo dei numerosi metodi che ho provato, ma non pensavo di poter usare questo sproc nell'esempio.
Henrik Staun Poulsen,

6

Grazie per aver segnalato questo problema!

Questo è davvero un bug nel modo in cui lo Strumento per ottimizzare le query genera un piano per la sys.database_scoped_configurationsvista del catalogo. Lo affronteremo su uno dei prossimi aggiornamenti di SQL Server 2016 e nel database SQL di Azure.

Per ovviare al problema, puoi aggiungere una TOPclausola da SELECTparte del tuo inserto per ottenere il piano corretto, ad esempio:

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

3

Sono d'accordo che questo è molto strano e un potenziale bug, ma l'aggiunta di un TOP (50), ad esempio, alla tua selezione in realtà restituisce tutte le righe, quindi almeno ti farebbe andare avanti. Il risultato sembra provenire da una funzione di valore di tabella del sistema ([DB_SCOPED_CONFIG]), quindi non posso davvero dire cosa sta succedendo.

Terrò d'occhio questo thread per vedere se le persone "più intelligenti" sanno PERCHÉ questo sta accadendo.


Ricevi solo la riga MAXDOP per ciascun database?
Dan Guzman,

@DanGuzman - sì La selezione di per sé funziona bene (senza tutte le cose foreach, solo su un singolo database). È quando aggiungi l'inserto che produce lo strano comportamento
Scott Hodgin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.