2011-01-11 3 views
1

У меня есть что-то вроде следующую структуру: Table1 -> Table2 отношения 1: мвыбрать первые N различных строк без внутреннего выбора в оракула

мне нужно выполнять запросы, подобные следующему:

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.

Явное решение использовать следующее:

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 

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

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

+1

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

ответ

1

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

3

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

С имея ИНЕКЕ на TABLE2, вы фильтрование выберите на INNER JOIN (то есть., Так как Table2.name IS NULL <> 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

Вам не нужно 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).

Смежные вопросы