[AKTUELLE EDIT]
Meine ORIGINAL ANTWORT in Bezug auf den entsprechenden Index erstellen auf (Name, ID), den Index ersetzen auf (Name) ist unten. (Das war keine Antwort auf die ursprüngliche Frage, die Datenbankänderungen nicht zuließ.)
Hier sind Aussagen, die ich nicht noch getestet habe. Es gibt wahrscheinlich einen offensichtlichen Grund, warum das nicht funktionieren wird. Ich würde nie wirklich vorschlagen Schreiben Aussagen wie diese (auf die Gefahr für eine solche lächerlichen Vorschlag gründlich getrommelt werden.)
Wenn diese Abfragen auch Ergebnismengen zurückgeben, die ressult Satz enthält nur das Ergebnis ähnlich aus dem OP eingestellt Abfrage, fast durch Zufall, unter Ausnutzung einer schrulligen Garantie über die Daten, die Don uns zur Verfügung gestellt hat. Diese Anweisung entspricht NICHT dem ursprünglichen SQL. Diese Anweisungen sind für den von Don beschriebenen Sonderfall ausgelegt.
select m1.id
, m2.name
from (select min(t1.rowid) as min_rowid
, t1.id
from table1 t1
where t1.id is not null
group by t1.id
) m1
, (select min(t2.rowid) as min_rowid
, t2.name from table1 t2
where t2.name is not null
group by t2.name
) m2
where m1.min_rowid = m2.min_rowid
order
by m1.id
Lassen Sie uns das auspacken:
- m1 ist ein Inline-Ansicht, die uns eine Liste von unterschiedlichen ID-Werte erhält.
- m2 ist eine Inline-Ansicht, die uns eine Liste der eindeutigen Namen Werte erhält.
- materialisieren die Ansichten m1 und m2
- Spiel der ROWID von m1 und m2
id
mit name
jemand anderes die Idee eines Index merge vorgeschlagen übereinstimmen. Ich hatte zuvor diese Idee, einen Optimierer Plan, um 10 Millionen von rowids übereinstimmen, ohne sie zu beseitigen.
mit ausreichend niedrigen Kardinalität für ID und Namen, und mit dem richtigen Optimierungsprogramm Plan:
select m1.id
, (select m2.name
from table1 m2
where m2.id = m1.id
and rownum = 1
) as name
from (select t1.id
from table1 t1
where t1.id is not null
group by t1.id
) m1
order
by m1.id
sie auspacken, dass
- m1 ist eine Inline-Ansicht, die uns eine Liste von verschiedenen bekommt ID-Werte.
- materialisieren die Ansicht m1
- für jede Zeile in m1, Abfrage tabelle1 den Namen Wert aus einer einzelnen Zeile (stopkey)
WICHTIGER HINWEIS
Diese Aussagen zu erhalten sind grundsätzlich anders als die OP-Abfrage. Sie sind so konzipiert, dass sie ein UNTERSCHIEDLICHES Ergebnis als die OP-Abfrage zurückgeben. Die passieren, um die gewünschte Ergebnismenge wegen einer eigenartigen Garantie über die Daten zurückzugeben. Don hat uns gesagt, dass name
durch id
bestimmt wird. (Ist das Gegenteil wahr? Ist id
von name
bestimmt? Haben wir eine garantierte Garantie, nicht unbedingt durch die Datenbank durchgesetzt, aber eine Garantie, die wir nutzen können?) Für jede ID
Wert wird jede Zeile mit diesem ID
Wert haben das gleiche NAME
Wert. (Und wir werden garantiert auch das Gegenteil wahr ist, dass für jeden NAME
Wert, jede Zeile mit diesem Wert NAME
den gleichen ID
Wert?)
Wenn ja, vielleicht können wir Verwendung dieser Informationen machen. Wenn ID
und NAME
in verschiedenen Paaren auftreten, müssen wir nur eine bestimmte Zeile finden. Das "Paar" wird eine passende ROWID haben, die zufällig von jedem der vorhandenen Indizes verfügbar ist. Was ist, wenn wir für jede ID
die minimale ROWID erhalten und für jede NAME
die minimale ROWID erhalten. Könnten wir nicht die ID
mit der NAME
basierend auf der ROWID, die das Paar enthält, übereinstimmen? Ich denke, dass es funktionieren könnte, wenn eine Kardinalität niedrig genug ist. (Das heißt, wenn wir mit nur tun sind Hunderte von ROWIDs anstatt 10s von Millionen.)
[/ AKTUELLE EDIT]
[EDIT]
Die Frage ist nun mit Informationen über die aktualisierte Tabelle zeigt, dass die ID
Spalte und die NAME
Spalte beide NULL-Werte zulassen. Wenn Don ohne NULL-Werte leben kann, die in der Ergebnismenge zurückgegeben werden, kann das Hinzufügen des IS NOT NULL-Prädikats für diese beiden Spalten die Verwendung eines Indexes ermöglichen. (HINWEIS: In einem Oracle-Index (B-Tree) erscheinen NULL-Werte NICHT im Index.)
[/ EDIT]
ORIGINAL ANTWORT:
einen entsprechenden Index
create index table1_ix3 on table_1 (name,id) ... ;
Ordnung schaffen, das ist nicht die Antwort auf die Frage, die Sie gefragt, aber es ist die richtige Antwort auf die Behebung des Leistungsproblems. (Sie haben keine Änderungen an der Datenbank angegeben, aber in diesem Fall ist das Ändern der Datenbank die richtige Antwort.)
Beachten Sie, dass Sie (sehr wahrscheinlich) keinen Index benötigen, wenn Sie einen Index für (name,id)
definiert haben unter (name)
, wird der Optimierer die führende Spalte name
im anderen Index berücksichtigen.
(UPDATE: als jemand klüger als ich darauf hinwies, hatte ich nicht einmal die Möglichkeit in Betracht gezogen, dass die vorhandenen Indizes Bitmap-Indizes und nicht die B-Tree-Indizes waren ...)
Re- Bewerten Sie Ihren Bedarf für die Ergebnismenge ... müssen Sie id
zurückgeben, oder würde name
ausreichend sein.
select distinct name from table1 order by name;
Für einen bestimmten Namen, können Sie eine zweite Abfrage senden die zugehörige id
zu bekommen, ob und wann Sie es benötigt ...
select id from table1 where name = :b1 and rownum = 1;
Wenn Sie Sie wirklich Notwendigkeit In der angegebenen Ergebnismenge können Sie einige Alternativen ausprobieren, um zu sehen, ob die Leistung besser ist. Ich halte nicht viel Hoffnung für eine dieser aus:
select /*+ FIRST_ROWS */ DISTINCT id, name from table1 order by id;
oder
select /*+ FIRST_ROWS */ id, name from table1 group by id, name order by name;
oder
select /*+ INDEX(table1) */ id, min(name) from table1 group by id order by id;
UPDATE: wie andere astutely darauf hingewiesen haben, mit diesem Ansatz wir Erneutes Testen und Vergleichen der Leistung alternativer Abfragen, was eine Art "hit or miss" -Ansatz ist. (Ich stimme nicht zu, dass es zufällig ist, aber ich würde zustimmen, dass es Hit oder Fräulein ist).
UPDATE: tom schlägt den ALL_ROWS Hinweis vor. Ich hatte das nicht berücksichtigt, weil ich mich wirklich darauf konzentrierte, einen Abfrageplan mit einem INDEX zu erstellen. Ich vermute, dass die OP-Abfrage eine vollständige Tabelle scannen, und es ist wahrscheinlich nicht der Scan, der die Zeit nimmt, es ist die Art einzigartige Operation (< 10g) oder Hash-Operation (10gR2 +), die die Zeit braucht. (Abgesagte Zeitstatistiken und Ereignis 10046 Trace, ich rate nur hier.) Aber andererseits, vielleicht ist es der Scan, wer weiß, der High Watermark auf dem Tisch könnte weit draußen in einer Weite von leeren Blöcken sein.
Es ist fast selbstverständlich, dass die Statistiken in der Tabelle auf dem neuesten Stand sein sollten, und wir sollten SQL * Plus AUTOTRACE oder zumindest EXPLAIN PLAN verwenden, um die Abfragepläne zu betrachten.
Aber keiner der vorgeschlagenen alternativen Abfragen behandelt wirklich das Leistungsproblem.
Es ist möglich, dass Hinweise den Optimierer beeinflussen, um einen anderen Plan zu choozen, grundsätzlich die ORDER BY aus einem Index erfüllend, aber ich hege keine großen Hoffnungen dafür. (Ich glaube nicht, dass der FIRST_ROWS-Hinweis mit GROUP BY funktioniert, der INDEX-Hinweis könnte dazu führen.) Ich kann das Potenzial für einen solchen Ansatz in einem Szenario sehen, in dem Datenblöcke leer und dünn besetzt sind und auf die Daten zugegriffen wird Blöcke über einen Index, es könnten tatsächlich wesentlich weniger Datenblöcke in den Speicher gezogen werden ... aber dieses Szenario wäre eher die Ausnahme als die Norm.
UPDATE: Als Rob van Wijk weist darauf hin, die Nutzung der Oracle Trace-Einrichtung ist der effektivste Ansatz zur Identifizierung und Performance-Probleme zu lösen.
Ohne die Ausgabe eines EXPLAIN PLAN oder SQL * Plus AUTOTRACE-Ausgabe, rate ich hier nur.
Ich vermute, das Leistungsproblem, das Sie gerade haben, ist, dass die Tabellendatenblöcke referenziert werden müssen, um die angegebene Ergebnismenge zu erhalten.
keine Es gibt um es bekommt, kann die Abfrage nicht aus nur einem Index zufrieden sein, da es nicht ein Index ist, der enthält sowohl die NAME
und ID
Spalten, entweder mit den ID
oder NAME
Spalt als die führenden Spalte. Die anderen zwei "schnellen" OP-Abfragen können aus dem Index befriedigt werden, ohne dass auf die Zeile Bezug genommen werden muss (Datenblöcke).
Auch wenn der Optimierungsplan für die Abfrage einen der Indizes verwendet, muss er dennoch die zugehörige Zeile aus dem Datenblock abrufen, um den Wert für die andere Spalte zu erhalten. Und ohne Prädikat (keine WHERE-Klausel) entscheidet sich der Optimierer wahrscheinlich für einen vollständigen Tabellenscan und wahrscheinlich für eine Sortieroperation (< 10g). (Auch hier würde ein EXPLAIN-Plan den Optimierungsplan anzeigen, genauso wie AUTOTRACE.)
Ich nehme auch hier an (große Annahme), dass beide Spalten als NOT NULL definiert sind.
Sie können auch die Tabelle als organisierte Indextabelle (IOT) definieren, insbesondere wenn dies die einzigen beiden Spalten in der Tabelle sind. (Ein IOT ist kein Allheilmittel, es kommt mit einem eigenen Satz von Leistungsproblemen.)
Sie können versuchen, das Neuschreiben der Abfrage (es sei denn, das ist eine Datenbankänderung, die auch verboten ist) In unserer Datenbank Umgebungen betrachten wir eine Abfrage als einen Teil der Datenbank als die Tabellen und Indizes.)
Auch hier wird der Optimierer ohne Prädikat wahrscheinlich keinen Index verwenden. Es gibt eine Chance, dass Sie den Abfrage-Plan eine der vorhandenen Indizes verwenden, erhalten konnten die ersten Reihen schnell zurück zu bekommen, durch einen Hinweis hinzufügen, testen eine Kombination aus:
select /*+ INDEX(table1) */ ...
select /*+ FIRST_ROWS */ ...
select /*+ ALL_ROWS */ ...
distinct id, name from table1;
distinct id, name from table1 order by id;
distinct id, name from table1 order by name;
id, name from table1 group by id, name order by id;
id, min(name) from table1 group by id order by id;
min(id), name from table1 group by name order by name;
Mit einem Hinweis, können Sie möglicherweise zu Beeinflussen Sie den Optimierer, einen Index zu verwenden, und das kann die Sortieroperation vermeiden, aber insgesamt dauert es länger, bis die gesamte Ergebnismenge zurückgegeben wird.
(UPDATE: jemand anders wies darauf hin, dass der Optimierer möglicherweise zwei Indizes basierend auf ROWID zusammenführen würde.Das ist eine Möglichkeit, aber ohne ein Prädikat, um einige Zeilen zu eliminieren, wird das wahrscheinlich ein viel teurerer Ansatz sein (Übereinstimmung von 10 Millionen ROWIDs) aus zwei Indizes, insbesondere wenn keine der Zeilen auf der Grundlage der ausgeschlossen werden match.)
Aber alles, was theoretisieren ist nicht auf Kniebeugen ohne einige Leistungsstatistiken.
Abwesend irgendetwas anderes in der Datenbank zu verändern, die einzige andere Hoffnung (ich mich vorstellen kann) von der Abfrage zu beschleunigen ist der Sortiervorgang, um sicherzustellen, abgestimmt ist, so dass der (erforderlich) Sortiervorgang sein kann im Speicher ausgeführt, anstatt auf der Festplatte. Aber das ist nicht wirklich die richtige Antwort. Der Optimierer führt möglicherweise überhaupt keine Sortieroperation durch, sondern führt stattdessen eine Hash-Operation (10gR2 +) durch, in diesem Fall sollte diese optimiert werden. Der Sortiervorgang ist nur eine Vermutung meinerseits, basierend auf den Erfahrungen der Vergangenheit mit Oracle 7.3, 8, 8i, 9i.)
Eine ernsthafte DBA mehr Problem wird sich mit Ihnen mit dem SORT_AREA_SIZE
futzing und/oder HASH_AREA_SIZE
Parameter für Ihre Sitzung (en) als beim Erstellen der richtigen Indizes. (Und diese Session-Parameter sind "Old School" für Versionen vor 10g automatische Speicherverwaltung magic.)
Zeigen Sie Ihrem DBA die Spezifikation für die Ergebnismenge, lassen Sie den DBA tune es.
Könnten Sie möglicherweise eine WHERE-Klausel hinzufügen, um Ihre Ergebnismenge schnell einzugrenzen? Ich bin mir nicht sicher, ob Sie das schon tun, obwohl die gezeigten Beispiele keine Prädikatenlogik haben. :) – tom
Wie viele Zeilen werden von DISTINCT ID und DISTINCT NAME zurückgegeben? – Quassnoi
33 Reihen für jeden. –