выберите первые N различных строк без внутреннего выбора в оракуле - PullRequest
1 голос
/ 11 января 2011

У меня есть что-то вроде следующей структуры: Таблица1 -> Таблица2 отношения 1: м

Мне нужно выполнить запросы, аналогичные следующим:

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

т.е. Я хочу первые 10 идентификаторов из таблицы 1, которая соответствует условиям в таблице 2. Проблема заключается в том, что я должен использовать разные, но условие «отличный» применяется после «rownum <11», так что результатом может быть, например, 5 записей, даже если их число больше 10. </p>

Очевидным решением является использование следующего:

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

Но я боюсь выполнения такого запроса. Если таблица Table1 содержит около 300 тыс. Записей, а таблица 2 содержит около 700 тыс. Записей, разве такой запрос не будет слишком медленным?

Есть ли другой запрос, но без внутреннего выбора? К сожалению, я хочу избежать использования внутренних селектов.

Ответы [ 3 ]

3 голосов
/ 11 января 2011

К сожалению, я хочу избежать использования внутренних выборок

Имея предложение WHERE в TABLE2, вы фильтруете выборку во ВНУТРЕННЕЕ СОЕДИНЕНИЕ (т. Е. Поскольку Table2.name IS пусто<> Table2.name наподобие '% a%', вы получите результаты только в том случае, если соединение INNER друг с другом. Кроме того,% a% без индекса на основе функций приведет к полному сканированию таблицы на каждой итерации.

но @lweller полностью корректен, для правильного выполнения запроса вам нужно будет использовать подзапрос. Имейте в виду, что без ORDER BY у вас нет гарантии порядка ваших лучших X записей (он всегда может «появляться»)что значения соответствуют первичному ключу или еще чему-то, но нет никакой гарантии.

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; 

- вы можете отбросить аналитическую функцию ROW_NUMBER () и заменить окончательный запрос как таковой (как вы изначально указали)

SELECT * 
  FROM sortable
 WHERE 
       ROWNUM <= 2
 ORDER BY sortable.NAME  --make sure to put in an order by!
 ;
2 голосов
/ 11 января 2011

Вам вообще не нужно DISTINCT, и в подзапросах как таковых нет ничего плохого.

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

Обратите внимание, что заказ не гарантирован.Чтобы гарантировать порядок, вы должны использовать вложенный запрос:

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

Нет способа сделать это без вложенных запросов (или CTE).

1 голос
/ 11 января 2011

Для меня нет причин бояться производительности.Я думаю, что суб-выбор - лучший способ решить вашу проблему.И если вы хотите, не доверяйте мне, посмотрите на план объяснения вашего запроса, и вы увидите, что он ведет себя не так плохо, как вы думаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...