Fehler in database_scoped_configurations

9

Ich versuche, die Ergebnismenge einzufügen aus:

SELECT * FROM sys.database_scoped_configurations

in eine temporäre Tabelle, weil ich die Einstellungen für alle Datenbanken auf meinem Server überprüfen möchte. Also habe ich diesen Code geschrieben:

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

Aber dann gibt es nur eine Zeile pro Datenbank, nicht die vier Zeilen, die ich von einer einfachen Auswahl in jeder Datenbank erwarte.

Ich weiß, dass es bessere Möglichkeiten gibt, dies zu codieren, als sp_MSForEachDB zu verwenden, und ich habe mehrere ausprobiert. Aber ich bekomme immer noch nur eine Zeile pro Datenbank. Ich habe dies sowohl auf SQL Server 2016 RTM als auch auf SP1 versucht

Ist dies ein Fehler mit SQL Server 2016 oder mache ich etwas falsch?

Henrik Staun Poulsen
quelle
Fehler wurde behoben, zumindest in Microsoft SQL Server 2017 (RTM-CU15-DDR)
Henrik Staun Poulsen

Antworten:

8

Ist dies ein Fehler mit SQL Server 2016?

Ja. Dies ist definitiv kein korrektes Verhalten. Ich habe es hier gemeldet und ist in SQL Server 2016 SP2 CU9 behoben .

Wie Mikael Eriksson in den Kommentaren sagt sys.database_scoped_configurationsund sys.dm_exec_sessionsals Ansichten im Format implementiert sind

SELECT ...  
FROM OpenRowset(TABLE xxxx)  

Beim Vergleich der beiden folgenden Pläne ergibt sich jedoch ein offensichtlicher Unterschied.

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

Geben Sie hier die Bildbeschreibung ein

Die Ausgabe des Ablaufverfolgungsflags 8619 für diese beiden Abfragen wird angezeigt

Regel anwenden: EnforceHPandAccCard - x0-> Spool oder Top (x0)

SQL Server kann anscheinend nicht feststellen, dass die Quelle für die TVF nicht auch das Einfügeziel ist, sodass ein Halloween-Schutz erforderlich ist.

Im Sitzungsfall wurde dies als Spool implementiert, der zuerst alle Zeilen erfasst. In der database_scoped_configurationsdurch Hinzufügen eines a TOP 1zum Plan. Die Verwendung TOPfür den Halloween-Schutz wird in diesem Artikel erläutert . Der Artikel erwähnt auch ein undokumentiertes Trace-Flag, um eine Spool zu erzwingen, anstatt TOPdass dies wie erwartet funktioniert.

DECLARE @database_scoped_configurations TABLE(x INT);

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

Ein offensichtliches Problem bei der Verwendung TOP 1anstelle einer Spool besteht darin, dass die Anzahl der eingefügten Zeilen willkürlich begrenzt wird. Dies wäre also nur gültig, wenn die Anzahl der von der Funktion zurückgegebenen Zeilen <= 1 wäre.

Das erste Memo sieht so aus

Geben Sie hier die Bildbeschreibung ein

Vergleichen Sie dies mit dem ursprünglichen Memo für Abfrage 2

Geben Sie hier die Bildbeschreibung ein

Wenn ich das oben Gesagte richtig verstehe, denke ich, dass die erste TVF maximal eine Zeile zurückgeben kann und wendet daher eine falsche Optimierung an. Das Maximum für die zweite Abfrage wird auf 1.34078E+154( 2^512) gesetzt.

Ich habe keine Ahnung, woher diese maximale Zeilenanzahl stammt. Vielleicht Metadaten vom Autor der DMV? Es ist auch seltsam, dass die TOP(50)Problemumgehung nicht umgeschrieben wird, TOP(1)da TOP(50)dies das Auftreten des Halloween-Problems nicht verhindern würde (obwohl es auf unbestimmte Zeit fortgesetzt werden würde).

Martin Smith
quelle
6

Bitte hören Sie auf zu benutzen sp_MSForEachDB. Es wird nicht unterstützt, ist nicht dokumentiert und fehlerhaft - was hier das Problem sein kann. Mein Ersatz zeigt hier das gleiche Problem, aber im Allgemeinen ist es sicherer zu verwenden.

Für solche Dinge ziehe ich es vor, dynamisches SQL zu generieren, als einen einzelnen Befehl an eine Prozedur zu übergeben, um ihn mehrmals auszuführen (sogar meine Prozedur, der ich viel mehr vertraue). Auf diese Weise kann ich einfach die Befehle drucken, anstatt sie auszuführen, und Stellen Sie sicher, dass sie alle tun, was sie sagen.

Ausgehend von der Beobachtung, dass der der Systemansicht zugrunde liegende Code a implementiert TOP (1), können wir Folgendes versuchen:

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;

Beachten Sie, dass ich USEhier nicht verwende , sondern der sysKatalogansicht den Datenbanknamen voranstelle .

Warum die Ansicht auf magische Weise funktioniert, weiß ich nicht. Ich weiß nicht, dass Sie hier eine gute Antwort erhalten, da wahrscheinlich Kommentare von Microsoft (oder von Personen, die Zugriff auf den Quellcode haben oder bereit sind, einen Debugger zu starten) erforderlich sind.

Aaron Bertrand
quelle
Das war die erste der verschiedenen Methoden, die ich ausprobiert habe, aber ich dachte nicht, dass ich diesen Sproc im Beispiel verwenden könnte.
Henrik Staun Poulsen
6

Vielen Dank, dass Sie dieses Problem gemeldet haben!

Dies ist in der Tat ein Fehler in der Art und Weise, wie das Abfrageoptimierungsprogramm einen Plan für die sys.database_scoped_configurationsKatalogansicht generiert . Wir werden dies bei einem der nächsten Updates von SQL Server 2016 und in der Azure SQL-Datenbank beheben.

Um dieses Problem zu umgehen, können Sie eine TOPKlausel SELECTseitens Ihrer Einfügung hinzufügen , um den richtigen Plan zu erhalten, z.

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

Ich bin damit einverstanden, dass dies sehr seltsam und ein potenzieller Fehler ist, aber wenn Sie Ihrer Auswahl beispielsweise ein TOP (50) hinzufügen, werden tatsächlich alle Zeilen zurückgegeben, sodass Sie zumindest in Fahrt kommen. Das Ergebnis scheint von einer Systemtabellenwertfunktion ([DB_SCOPED_CONFIG]) zu stammen, daher kann ich nicht wirklich sagen, was los ist.

Ich werde diesen Thread im Auge behalten, um zu sehen, ob "klügere" Leute wissen, WARUM dies geschieht.

Scott Hodgin
quelle
Erhalten Sie nur die MAXDOP-Zeile für jede Datenbank?
Dan Guzman
@DanGuzman - ja Die Auswahl an sich funktioniert einwandfrei (ohne all das foreach-Zeug, nur in einer einzigen Datenbank). Es ist, wenn Sie die Einfügung hinzufügen, die das seltsame Verhalten erzeugt
Scott Hodgin