Frage: Ich portiere die folgende Abfrage (Auflisten von Tabellen nach Fremdschlüsselabhängigkeiten) nach PostGreSql.
WITH Fkeys AS (
SELECT DISTINCT
OnTable = OnTable.name
,AgainstTable = AgainstTable.name
FROM sysforeignkeys fk
INNER JOIN sysobjects onTable
ON fk.fkeyid = onTable.id
INNER JOIN sysobjects againstTable
ON fk.rkeyid = againstTable.id
WHERE 1=1
AND AgainstTable.TYPE = 'U'
AND OnTable.TYPE = 'U'
-- ignore self joins; they cause an infinite recursion
AND OnTable.Name <> AgainstTable.Name
)
,MyData AS (
SELECT
OnTable = o.name
,AgainstTable = FKeys.againstTable
FROM sys.objects o
LEFT JOIN FKeys
ON o.name = FKeys.onTable
WHERE (1=1)
AND o.type = 'U'
AND o.name NOT LIKE 'sys%'
)
,MyRecursion AS (
-- base case
SELECT
TableName = OnTable
,Lvl = 1
FROM MyData
WHERE 1=1
AND AgainstTable IS NULL
-- recursive case
UNION ALL
SELECT
TableName = OnTable
,Lvl = r.Lvl + 1
FROM MyData d
INNER JOIN MyRecursion r
ON d.AgainstTable = r.TableName
)
SELECT
Lvl = MAX(Lvl)
,TableName
--,strSql = 'delete from [' + tablename + ']'
FROM
MyRecursion
GROUP BY
TableName
ORDER BY lvl
/*
ORDER BY
2 ASC
,1 ASC
*/
Mit information_schema sieht die Abfrage folgendermaßen aus:
WITH Fkeys AS
(
SELECT DISTINCT
KCU1.TABLE_NAME AS OnTable
,KCU2.TABLE_NAME AS AgainstTable
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG
AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME
AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
WHERE (1=1)
AND KCU1.TABLE_NAME <> KCU2.TABLE_NAME
)
,MyData AS
(
SELECT
TABLE_NAME AS OnTable
,FKeys.againstTable AS AgainstTable
FROM INFORMATION_SCHEMA.TABLES
LEFT JOIN FKeys
ON TABLE_NAME = FKeys.onTable
WHERE (1=1)
AND TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME NOT IN ('sysdiagrams', 'dtproperties')
)
,MyRecursion AS
(
-- base case
SELECT
OnTable AS TableName
,1 AS Lvl
FROM MyData
WHERE 1=1
AND AgainstTable IS NULL
-- recursive case
UNION ALL
SELECT
OnTable AS TableName
,r.Lvl + 1 AS Lvl
FROM MyData d
INNER JOIN MyRecursion r
ON d.AgainstTable = r.TableName
)
SELECT
MAX(Lvl) AS Lvl
,TableName
--,strSql = 'delete from [' + tablename + ']'
FROM
MyRecursion
GROUP BY
TableName
ORDER BY lvl
/*
ORDER BY
2 ASC
,1 ASC
*/
Meine Frage ist jetzt:
In SQL Server (getestet auf 2008 R2): Warum springt die Abfrage beim Ersetzen von 1 Sekunde auf 11 Minuten?
SELECT DISTINCT
OnTable = OnTable.name
,AgainstTable = AgainstTable.name
FROM sysforeignkeys fk
INNER JOIN sysobjects onTable
ON fk.fkeyid = onTable.id
INNER JOIN sysobjects againstTable
ON fk.rkeyid = againstTable.id
WHERE 1=1
AND AgainstTable.TYPE = 'U'
AND OnTable.TYPE = 'U'
-- ignore self joins; they cause an infinite recursion
AND OnTable.Name <> AgainstTable.Name
mit
SELECT DISTINCT
KCU1.TABLE_NAME AS OnTable
,KCU2.TABLE_NAME AS AgainstTable
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG
AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME
AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
WHERE (1=1)
AND KCU1.TABLE_NAME <> KCU2.TABLE_NAME
???
Soweit ich das beurteilen kann, gibt es wirklich keinen signifikanten Geschwindigkeitsunterschied, wenn nur die Teilabfragen separat ausgeführt werden. Auch die Ergebnismenge ist völlig gleich (ich habe jede Zeile in Excel überprüft), obwohl die Reihenfolge unterschiedlich ist.
Unterhalb der funktionierenden PostGreSQL-Version (fertig in 35 ms bei genau demselben Datenbankinhalt [75 Tabellen] ...)
- Keine Garantie -
WITH RECURSIVE Fkeys AS
(
SELECT DISTINCT
KCU1.TABLE_NAME AS OnTable
,KCU2.TABLE_NAME AS AgainstTable
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG
AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME
AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
)
,MyData AS
(
SELECT
TABLE_NAME AS OnTable
,FKeys.againstTable AS AgainstTable
FROM INFORMATION_SCHEMA.TABLES
LEFT JOIN FKeys
ON TABLE_NAME = FKeys.onTable
WHERE (1=1)
AND TABLE_TYPE = 'BASE TABLE'
AND TABLE_SCHEMA = 'public'
--AND TABLE_NAME NOT IN ('sysdiagrams', 'dtproperties')
)
,MyRecursion AS
(
-- base case
SELECT
OnTable AS TableName
,1 AS Lvl
FROM MyData
WHERE 1=1
AND AgainstTable IS NULL
-- recursive case
UNION ALL
SELECT
OnTable AS TableName
,r.Lvl + 1 AS Lvl
FROM MyData d
INNER JOIN MyRecursion r
ON d.AgainstTable = r.TableName
)
SELECT
MAX(Lvl) AS Lvl
,TableName
--,strSql = 'delete from [' + tablename + ']'
FROM
MyRecursion
GROUP BY
TableName
ORDER BY lvl
/*
ORDER BY
2 ASC
,1 ASC
*/
Es scheint auch so
AND KCU1.TABLE_NAME <> KCU2.TABLE_NAME
ist überflüssig, wenn information_schema verwendet wird, daher sollte es eigentlich schneller sein.
quelle
where
.id
Feld (nicht überraschend) effizienter ist als das Verbinden auf den ZeichenfolgenNAME, SCHEMA
usw. Übrigens sollten Sie dassys.objects
usw. verwenden, nicht das veraltetesysobjects
Antworten:
Ich würde wahrscheinlich die
INFORMATION_SCHEMA
Ansichten hier aufgeben undsys.
stattdessen die neuen Ansichten (im Gegensatz zu den abwärtskompatiblen) verwenden oder zumindest die Ergebnisse der zuerstJOIN
in einer indizierten Tabelle materialisieren .Rekursive CTEs erhalten in SQL Server immer den gleichen Grundplan, bei dem jede Zeile einer Stapelspool hinzugefügt und einzeln verarbeitet wird. Dies bedeutet, dass die Verknüpfung zwischen
REFERENTIAL_CONSTRAINTS RC, KEY_COLUMN_USAGE KCU1, KEY_COLUMN_USAGE KCU2
so oft wie das Ergebnis der folgenden Abfrage erfolgtSELECT COUNT(*) FROM MyRecursion
.Was ich in Ihrem Fall (ab der Ausführungszeit von 11 Minuten) annehme, ist wahrscheinlich viele tausend Mal so, dass Sie den rekursiven Teil benötigen, um so effizient wie möglich zu sein. Ihre Abfrage wird die folgende Art von Dingen tausende Male ausführen.
(Randnotiz: Beide Versionen Ihrer Abfrage geben falsche Ergebnisse zurück, wenn derselbe Tabellenname in verschiedenen Schemas verwendet wird.)
Wie Sie sehen können, ist der Plan dafür ziemlich schrecklich.
Vergleichen Sie dies mit dem Plan für Ihre
sys
Abfrage, der etwas einfacher ist.Möglicherweise können Sie die Zwischenmaterialisierung fördern, ohne
#temp
explizit eine Tabelle zu erstellen, indem Sie die Definition vonMyData
in ändernBeim Testen
Adventureworks2008
auf meinem Computer wurde die Laufzeit von ca. 10 Sekunden auf 250 ms gesenkt (nachdem der erste Lauf aus dem Weg war, da das Kompilieren des Plans 2 Sekunden dauerte). Es fügt dem Plan eine eifrige Spool hinzu, die das Ergebnis des Joins beim ersten rekursiven Aufruf materialisiert und es dann bei nachfolgenden Aufrufen wiedergibt. Dieses Verhalten kann jedoch nicht garantiert werden, und Sie möchten möglicherweise die Anforderung für Connect-Elemente verbessern. Geben Sie einen Hinweis an, um die Zwischenmaterialisierung von CTEs oder abgeleiteten Tabellen zu erzwingenIch würde mich sicherer fühlen, wenn ich die
#temp
Tabelle explizit wie folgt erstelle, als mich auf dieses Verhalten zu verlassen.oder alternativ
quelle
In beiden Fällen fragen Sie Ansichten ab, die für die Kompatibilität übrig bleiben: Kompatibilitätsansichten und Informationsschemaansichten .
Verwenden Sie stattdessen Katalogansichten , um die beste Leistung zu erzielen (msdn: "Wir empfehlen, Katalogansichten zu verwenden, da diese die allgemeinste Schnittstelle zu den Katalogmetadaten darstellen und die effizienteste Möglichkeit bieten, benutzerdefinierte Formen dieser Informationen abzurufen, zu transformieren und darzustellen.") ..
quelle