2011-01-11 8 views
1

ich so etwas wie die folgende Struktur haben: Tabelle 1 -> Table2 Beziehung 1: mersten N unterschiedlichen Zeilen ohne innere wählen Sie wählen in oracle

ich ähnlich wie die nächsten Abfragen durchführen müssen:

select Table1.id from Table1 left outer join Table2 on (Table1.id1 = Table2.id2) where Table2.name like '%a%' and rownum < 11 

dh ich möchte die ersten 10 IDs aus Tabelle 1, die die Bedingungen in Tabelle 2 erfüllt. Das Problem ist, dass ich distinct verwenden muss, aber die distinct-Klausel gilt nach 'rownum < 11', so dass das Ergebnis z. 5 Datensätze, auch wenn ihre Zahl mehr als 10.

Die scheinbare Lösung ist die folgende verwenden:

select id from (select Table1.id from Table1 left outer join Table2 on (Table1.id1 = Table2.id2) where Table2.name like '%a%') where rownum < 11 

Aber ich habe Angst vor Leistung einer solchen Abfrage. Wenn Tabelle1 etwa 300k Datensätze enthält und Tabelle2 etwa 700k Datensätze enthält, wäre eine solche Abfrage nicht wirklich langsam?

Gibt es eine andere Abfrage, aber ohne innere Auswahl? Unglücklicherweise möchte ich vermeiden, innere Auswahl zu verwenden.

+1

Wäre eine solche Abfrage nicht wirklich langsam? Wenn ich wüsste, wie langsam * wirklich * langsam ist, könnte ich die Frage beantworten. –

Antwort

1

Für mich gibt es keinen Grund, Angst vor der Leistung zu haben. Ich denke, die Subauswahl ist der beste Weg, um Ihr Problem zu lösen. Und wenn Sie mir nicht vertrauen wollen, schauen Sie sich den Plan Ihrer Anfrage an und Sie werden sehen, dass er sich nicht so schlecht benimmt, wie Sie vielleicht denken.

3

Unluckily, möchte ich mit Innen vermeiden wählt

Mit auf TABLE2 die WHERE-Klausel hat, Sie filtern die Auswahl zu einem INNER JOIN (dh. Seit Table2.name null IS <> Table2.name wie '% a%' erhalten Sie nur Ergebnisse, wo der Join INNER zu einander ist. Auch der% a% ohne einen funktionsbasierten Index führt zu einem vollständigen Tabellenscan bei jeder Iteration.

aber @lweller ist vollständig korrekt, um die Abfrage korrekt durchzuführen, müssen Sie eine Unterabfrage verwenden. Ohne ORDER BY haben Sie keine Garantie für die Reihenfolge Ihrer Top X-Datensätze (es kann immer so aussehen, als würden die Werte dem Primärschlüssel oder Ähnlichem entsprechen, aber es gibt keine Garantie.

WITH TABLE1 AS(SELECT 1 ID FROM DUAL 
       UNION ALL 
       SELECT 2 ID FROM DUAL 
       UNION ALL 
       SELECT 3 ID FROM DUAL 
       UNION ALL 
       SELECT 4 ID FROM DUAL 
       UNION ALL 
       SELECT 5 ID FROM DUAL) , 
    TABLE2 AS(SELECT 1 ID, 'AAA' NAME FROM DUAL 
       UNION ALL 
       SELECT 2 ID, 'ABB' NAME FROM DUAL 
       UNION ALL 
       SELECT 3 ID, 'ACC' NAME FROM DUAL 
       UNION ALL 
       SELECT 4 ID, 'ADD' NAME FROM DUAL 
       UNION ALL 
       SELECT 1 ID, 'BBB' NAME FROM DUAL 
       ) , 
    sortable as(--here is the subquery 
     SELECT 
      Table1.ID , 
      ROW_NUMBER() OVER (ORDER BY Table2.NAME NULLS LAST) ROWOverName , --this wil handle the sort 
      table2.name 
      from 
      Table1 
      LEFT OUTER JOIN --this left join it moot, pull the WHERE table2.name into the join to have it LEFT join as expected 
      Table2 
      on 
      (
       Table1.id = Table2.id 
      ) 
      WHERE 
      Table2.NAME LIKE '%A%') 
    SELECT * 
     FROM sortable 
    WHERE ROWOverName <= 2; 

- können Sie die ROW_NUMBER() analytische Funktion löschen und die letzte Abfrage als solche ersetzen (wie Sie zunächst angegeben)

SELECT * 
    FROM sortable 
WHERE 
     ROWNUM <= 2 
ORDER BY sortable.NAME --make sure to put in an order by! 
; 
2

Sie nicht DISTINCT hier überhaupt brauchen, und es ist in Unterabfragen als solches nichts Schlechtes.

SELECT id 
FROM Table1 
WHERE id IN 
     (
     SELECT id 
     FROM Table2 
     WHERE name LIKE '%a%' 
     ) 
     AND rownum < 11 

Beachten Sie, dass die Bestellung nicht garantiert ist. Um zu gewährleisten, um, müssen Sie eine verschachtelte Abfrage verwenden:

SELECT id 
FROM (
     SELECT id 
     FROM Table1 
     WHERE id IN 
       (
       SELECT id 
       FROM Table2 
       WHERE name LIKE '%a%' 
       ) 
     ORDER BY 
       id -- or whatever else 
     ) 
WHERE rownum < 11 

Es gibt keine Möglichkeit, es ohne verschachtelte Abfragen zu tun (oder die CTE).