Выберите различное ... внутреннее объединение против выбора ... где идентификатор в (...) - PullRequest
3 голосов
/ 14 апреля 2010

Я пытаюсь создать подмножество таблицы (как материализованное представление), определяемое как те записи, у которых есть совпадающая запись в другом материализованном представлении.

Например, допустим, у меня есть таблица Users с столбцами user_id и name и таблица Log с столбцами entry_id, user_id, activity и timestamp.

Сначала я создаю материализованное представление таблицы Log, выбирая только те строки с меткой времени> some_date. Теперь я хочу получить materliazed представление о пользователях, на которые есть ссылка в моем снимке таблицы журнала. Я могу либо создать его как

select * from Users where user_id in (select user_id from Log_mview)

или я могу сделать

select distinct u.* from Users u inner join Log_mview l on u.user_id = l.user_id

(нужен отчет, чтобы избежать нескольких обращений пользователей с несколькими записями журнала).

Первый выглядит чище и элегантнее, но занимает гораздо больше времени. Я что-то пропустил? Есть ли лучший способ сделать это?

Редактировать: Предложение where exists очень помогло, за исключением случая, когда условие использует OR. Например, предположим, что в приведенной выше таблице журнала также имеется столбец user_name, и правильный способ сопоставления записи журнала с записью Users - это когда один из столбцов (идентификатор пользователя или имя пользователя) совпадает. Я обнаружил, что

select distinct u.* from Users u
    inner join Log_mview l
        on u.user_id = l.user_id or u.name = l.user_name

намного быстрее, чем

select * from Users u where exists
    (select id from Log_mview l 
        where l.user_id = u.user_id or l.user_name = u.name)

Любая помощь?

(Что касается плана объяснения ... Позвольте мне поработать над его дезинфекцией, или, скорее, ... Я опубликую их через некоторое время.)

Редактировать: объяснить планы: Для запроса с внутренним объединением:

Plan hash value: 436698422

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name                | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                     |  4539K|   606M|       |   637K  (3)| 02:07:25 |
|   1 |  HASH UNIQUE                    |                     |  4539K|   606M|  3201M|   637K  (3)| 02:07:25 |
|   2 |   CONCATENATION                 |                     |       |       |       |            |          |
|*  3 |    HASH JOIN                    |                     |  4206K|   561M|    33M|   181K  (4)| 00:36:14 |
|   4 |     BITMAP CONVERSION TO ROWIDS |                     |   926K|    22M|       |  2279   (1)| 00:00:28 |
|   5 |      BITMAP INDEX FAST FULL SCAN| I_M_LOG_MVIEW_4     |       |       |       |            |          |
|*  6 |     TABLE ACCESS FULL           | USERS               |    15M|  1630M|       | 86638   (6)| 00:17:20 |
|*  7 |    HASH JOIN                    |                     |  7646K|  1020M|    33M|   231K  (4)| 00:46:13 |
|   8 |     BITMAP CONVERSION TO ROWIDS |                     |   926K|    22M|       |  2279   (1)| 00:00:28 |
|   9 |      BITMAP INDEX FAST FULL SCAN| I_M_LOG_MVIEW_4     |       |       |       |            |          |
|  10 |     TABLE ACCESS FULL           | USERS               |    23M|  2515M|       | 87546   (7)| 00:17:31 |
---------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("U"."NAME"="L"."USER_NAME")
   6 - filter("U"."NAME" IS NOT NULL)
   7 - access("U"."USER_ID"=TO_NUMBER("L"."USER_ID"))
       filter(LNNVL("U"."NAME"="L"."USER_NAME") OR LNNVL("U"."NAME" IS NOT NULL))

Note
-----
   - dynamic sampling used for this statement

Для тех, кто использует where exists:

Plan hash value: 2786958565

-----------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name                | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                     |     1 |   114 |    21M  (1)| 70:12:13 |
|*  1 |  FILTER                       |                     |       |       |            |          |
|   2 |   TABLE ACCESS FULL           | USERS               |    23M|  2515M| 87681   (7)| 00:17:33 |
|   3 |   BITMAP CONVERSION TO ROWIDS |                     |  7062 |   179K|     1   (0)| 00:00:01 |
|*  4 |    BITMAP INDEX FAST FULL SCAN| I_M_LOG_MVIEW_4     |       |       |            |          |
-----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter( EXISTS (SELECT /*+ */ 0 FROM "MYSCHEMA"."LOG_MVIEW" 
              "LOG_MVIEW" WHERE ("USER_NAME"=:B1 OR TO_NUMBER("USER_ID")=:B2) AND 
              ("USER_NAME"=:B3 OR TO_NUMBER("USER_ID")=:B4) AND ("USER_NAME"=:B5 OR 
              TO_NUMBER("USER_ID")=:B6)))
   4 - filter("USER_NAME"=:B1 OR TO_NUMBER("USER_ID")=:B2)

Note
-----
   - dynamic sampling used for this statement

Имена объектов БД изменены, чтобы защитить невинных. : Р

Ответы [ 3 ]

1 голос
/ 14 апреля 2010

Попробуйте это

select * from Users u
where exists 
   ( select user_id 
     from Log_mview l
     where l.user_id = u.user_id )
/

Если подзапрос возвращает большое количество строк, WHERE EXISTS может быть значительно быстрее, чем WHERE ... IN.

1 голос
/ 14 апреля 2010

Второй запрос, вероятно, работает более жестко, чем первый (join + distinc).

Первый запрос, вероятно, будет выглядеть примерно так:

для каждой строки в таблице Log найдите соответствующую строку в таблице User (в памяти). База данных, вероятно, достаточно умна, чтобы создавать в памяти структуры для таблицы User, которая, вероятно, намного меньше таблицы журналов. Я считаю, что запрос один (объединение + отдельный) потребует только одного прохода в таблице журнала. Отличное, вероятно, выполняется в памяти.

Второй запрос, вероятно, заставляет базу данных делать кратные чтения по таблице Log.

Итак, во втором запросе вы, вероятно, получите:

Для каждой строки в таблице пользователь читает все строки таблицы Log (с диска), чтобы соответствовать условию.

Вы также должны учитывать, что некоторые запросы могут испытывать резкое различие в скорости из-за изменений в доступности памяти, загрузке и увеличении таблицы.

1 голос
/ 14 апреля 2010

Это будет зависеть от данных, которые у вас есть, но использование Distinct в объединении может улучшить вашу производительность:

Select u.*
From Users u
Join ( Select Distinct user_id
       From log_mview ) l On u.user_id = l.user_id
...