Почему выполнение этого оператора выбора оракула занимает минуты?

В этих таблицах менее 20 полей, в свойствах — около 9 миллионов строк, а в списках — 3 миллиона строк, но это не должно быть проблемой. Для этого и нужны базы данных...

listing_id имеют тип Number. Наше лучшее предположение на данный момент заключается в том, что, поскольку в таблице свойств есть еще 6 миллионов строк с идентификаторами списков, которые на самом деле не указывают на список, Oracle тратит много времени на поиск списков, которых не существует. Это вообще имеет смысл?

Select  count(*) 
from listings.rfs_listings listings  
    join listings.rfs_properties properties 
        on listings.listing_id= properties.listing_id        
where listings.display = 1  
    and properties.city= 'New York'
    and rownum <= 10;

Я запустил план объяснения запроса и получил следующую информацию:

PLAN_TABLE_OUTPUT

Plan hash value: 772088252

-------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name                        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                             |    10 |  4110 |   283   (0)| 00:00:04 |
|*  1 |  COUNT STOPKEY                |                             |       |       |            |          |
|   2 |   NESTED LOOPS                |                             |       |       |            |          |
|   3 |    NESTED LOOPS               |                             |    10 |  4110 |   283   (0)| 00:00:04 |
|*  4 |     TABLE ACCESS FULL         | RFS_LISTINGS                |   140 |  2940 |     3   (0)| 00:00:01 |
|*  5 |     INDEX UNIQUE SCAN         | RFS_PROPERTIES_LD730_UNIQUE |     1 |       |     1   (0)| 00:00:01 |
|*  6 |    TABLE ACCESS BY INDEX ROWID| RFS_PROPERTIES              |     1 |   390 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------

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

   1 - filter(ROWNUM<=10)
   4 - filter("LISTINGS"."DISPLAY"=1)
   5 - access("LISTINGS"."LISTING_ID"="PROPERTIES"."LISTING_ID")
   6 - filter(NLSSORT("PROPERTIES"."CITY",'nls_sort=''BINARY_CI''')=HEXTORAW('6E657720796F726
      B00') )

РЕДАКТИРОВАТЬ: Схемы таблиц:

rfs.rfs_listings:
Name                            Null?    Type
------------------------------- -------- ------------------
DETAIL_ID                       NOT NULL NUMBER
LISTING_ID                      NOT NULL NUMBER
DETAIL_CHECKSUM                 NOT NULL VARCHAR2(32 CHAR)
PRICE                                    NUMBER
IN_CONTRACT                     NOT NULL NUMBER
CREATED                         NOT NULL DATE
PROPERTY_WEB_ID                 NOT NULL VARCHAR2(100)
SOURCE_ID                       NOT NULL NUMBER
LISTING_CREATED                 NOT NULL DATE
ARCHIVE_NAME                             VARCHAR2(100)
DUPLICATES_GROUP_ID                      NUMBER
FILENAME                                 VARCHAR2(80)
DISPLAY                                  NUMBER


rfs.rfs_properties:
Name                             Null?    Type
------------------------------- -------- -----------------
PROPERTY_ID                     NOT NULL NUMBER
LISTING_ID                      NOT NULL NUMBER
BLDG_PROPKEY                             NUMBER
UNIT_PROPKEY                             NUMBER
ADDRESSKEY                               NUMBER
HOUSE_NUMBER                             VARCHAR2(32)
STREET_ADDRESS                           VARCHAR2(200)
UNIT_NUMBER                              VARCHAR2(32)
UNIT_NUMBER_PARSED                       VARCHAR2(16 CHAR)
PARSED_ADDRESS                           VARCHAR2(255)
DISPLAY_ADDRESS                          VARCHAR2(150)
CROSS_STREET                             VARCHAR2(200)
NEIGHBORHOOD                             VARCHAR2(150)
NEIGHBORHOOD_CODE                        NUMBER
NEIGHBORHOOD_REG_CODE                    NUMBER
SCHOOL_DISTRICT                          VARCHAR2(100)
BOROUGH_CITY                             VARCHAR2(100 CHAR)
METRO_AREA                               VARCHAR2(100 CHAR)
ZIP_CODE                                 NUMBER
COUNTY                                   NUMBER
STATE                                    VARCHAR2(4 CHAR)
ROOMS                                    NUMBER
BEDROOMS                                 NUMBER
BATHROOMS                                NUMBER
SQFT                                     NUMBER
LOT_SIZE                                 NUMBER
STUDIO                                   NUMBER
LOFT                                     NUMBER
MAINT_CC                                 NUMBER
RE_TAX                                   NUMBER
PROPERTY_TYPE_ID                         NUMBER
PROPERTY_TYPE                            VARCHAR2(255)
BLDG_NAME                                VARCHAR2(255)
BLDG_TYPE                                VARCHAR2(32 CHAR)
BLDG_NEW_DEVEL                           NUMBER
BLDG_FEATURES                            VARCHAR2(256)
MANUALLY_BLDG_FEAT                       VARCHAR2(255)
APT_FEATURES                             VARCHAR2(256)
OUTDOOR_SPACE                            VARCHAR2(32 CHAR)
YEAR_BUILT                               NUMBER
LISTING_RANK                    NOT NULL NUMBER
LOCATION_CHECKED                         NUMBER
PROPKEY_SOURCE                           VARCHAR2(15)
WEB_BUG_URL                              VARCHAR2(255)
EMAIL_LEAD_GENERATION                    VARCHAR2(100)
LISTING_URL                              VARCHAR2(255)
BROKER_NAME                              VARCHAR2(100)
BROKER_URL                               VARCHAR2(256)
LISTING_TEXT                             CLOB
IS_UPDATED                               NUMBER
CENTROID_X                               NUMBER(20,10)
CENTROID_Y                               NUMBER(20,10)
CENTROID                                 MDSYS.SDO_GEOMETRY
COUNTY_GEO_ID                            NUMBER
CX                                       NUMBER
CY                                       NUMBER

Некоторая обновленная статистика после обновления статистики оракула:

Статистика

     31  recursive calls
      2  db block gets
  63053  consistent gets
  15474  physical reads
      0  redo size
   2890  bytes sent via SQL*Net to client
    524  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
     10  rows processed

Новый план выполнения после обновления статистики:

Execution Plan
----------------------------------------------------------
Plan hash value: 3213592672

--------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                             |    10 |  4110 |   236   (0)| 00:00:03 |
|*  1 |  COUNT STOPKEY                 |                             |       |       |            |          |
|   2 |   NESTED LOOPS                 |                             |       |       |            |          |
|   3 |    NESTED LOOPS                |                             |    10 |  4110 |   236   (0)| 00:00:03 |
|   4 |     TABLE ACCESS BY INDEX ROWID| RFS_LISTINGS                |   224 |  4704 |    11   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | RFS_LISTINGS_DISPLAY        |       |       |     3   (0)| 00:00:01 |
|*  6 |     INDEX UNIQUE SCAN          | RFS_PROPERTIES_LD730_UNIQUE |     1 |       |     1   (0)| 00:00:01 |
|*  7 |    TABLE ACCESS BY INDEX ROWID | RFS_PROPERTIES              |     1 |   390 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------

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

   1 - filter(ROWNUM<=10)
   5 - access("LISTINGS"."DISPLAY"=1)
   6 - access("LISTINGS"."LISTING_ID"="PROPERTIES"."LISTING_ID")
   7 - filter(NLSSORT("PROPERTIES"."BOROUGH_CITY",'nls_sort=''BINARY_CI''')=HEXTORAW('6E657720796F726B
          00') )

person Jared    schedule 26.07.2011    source источник
comment
Нужна дополнительная информация о схеме, индексах и т. д.   -  person n8wrl    schedule 26.07.2011
comment
Ваш план запроса указывает, что Oracle начинает с таблицы RFS_LISTINGS, а затем ищет RFS_PROPERTIES, поэтому он не ищет списки, которых не существует, как вы подозревали. Однако модель данных с внешним ключом, в которой 6 М из 9 М строк имеют недопустимую ссылку, дефектна!   -  person Tony Andrews    schedule 26.07.2011
comment
Нам все еще нужно знать, какие индексы существуют. Кроме того, сколько есть объектов недвижимости в Нью-Йорке и сколько объявлений с отображением = 1?   -  person Tony Andrews    schedule 26.07.2011
comment
Для rfs_listings есть индекс: цена, listing_id и отображение. Для rfs_properties есть индекс для 39 полей, я могу перечислить их все, если хотите, но listing_id включен.   -  person Jared    schedule 26.07.2011
comment
Хорошо, но (а) сколько есть объектов недвижимости в Нью-Йорке? (б) сколько объявлений с отображением = 1 (в) есть ли индекс на properties.city?   -  person Tony Andrews    schedule 26.07.2011
comment
1952 г. Ряды в «Нью-Йорке», 2028 855 Рядов с отображением = 1, да   -  person Jared    schedule 26.07.2011


Ответы (4)


Ваш план запроса предполагает, что Oracle считает, что в RFS_LISTINGS есть 140 строк, где DISPLAY=1, а не миллионы. Чтобы получить лучшую оптимизацию, вам нужно собрать лучшую статистику.

Чтобы уточнить, я имею в виду, что вы должны запустить DBMS_STATS, чтобы Oracle (а не только вы) знали, с каким объемом данных он имеет дело, например:

exec dbms_stats.gather_schema_stats ('LISTINGS');

Сначала поговорите со своим администратором баз данных.

person Tony Andrews    schedule 26.07.2011
comment
В rfs_listings 2527011, а в rfs_properties 9024600. Как обновить статистику? EXEC dbms_stats.gather_schema_stats('списки', cascade=›TRUE); ? Сколько времени это должно занять? - person Jared; 26.07.2011
comment
Да, но Oracle считает, что у него 140, и ему нужно дать другой совет - см. мой обновленный ответ. - person Tony Andrews; 26.07.2011
comment
Это звучит правильно. Насколько я понимаю, оракул использует эту статистику для построения наиболее эффективного метода выбора, и если они неверны, он не выберет правильно? - person Jared; 26.07.2011
comment
Верный. Если он думает, что есть только 140 строк, то он, вероятно, думает, что для полного сканирования таблицы потребуется только пара операций чтения, и, следовательно, это лучший способ найти любую строку в этой таблице, и что любые индексы не приносят пользы. - person Tony Andrews; 26.07.2011
comment
После разговора с администратором баз данных он сказал, что число 140 показывает только количество строк, выбранных в рамках этой части процесса, а не полное количество строк в таблице. Мы запустили команду collect_schema_stats, и это не помогло в нашем случае. Теперь он отображает 246 строк в плане. Сейчас запрос занимает 2:46:00. - person Jared; 26.07.2011
comment
Да, он прав, я ошибся насчет того, что означают эти 140, извините. Итак, Oracle считает, что в листинге около 246 строк, соответствующих условию DISPLAY=1. Правильно ли это, или ответ действительно исчисляется миллионами? - person Tony Andrews; 26.07.2011
comment
1952 строк в «Нью-Йорке» 2028 855 строк с отображением = 1 - person Jared; 26.07.2011
comment
Итак, ваш запрос призывает использовать индекс в properties.city, чтобы получить свойства Нью-Йорка 1952 года, а затем выполнить поиск индекса 1952 года в списках. Но это не так, потому что он считает, что существует только 246 объявлений с display=1, и, по-видимому, считает, что списки намного меньше, чем есть на самом деле. Почему? Что это возвращает: select num_rows from all_tables where owner='LISTINGS' and table_name = 'RFS_LISTINGS';? - person Tony Andrews; 26.07.2011

вам понадобится индексация столбцов WHERE...

listings.display = 1  
properties.city= 'New York'
person Randy    schedule 26.07.2011
comment
Я уточню: вы можете захотеть.... Если 2 М из 3 М строк в списках имеют display=1, то это, вероятно, не поможет. - person Tony Andrews; 26.07.2011
comment
Это определенно помогло, теперь сократилось примерно до 30 секунд. Но это по-прежнему неприемлемо. - person Jared; 26.07.2011
comment
Вы создали один индекс с этими двумя полями или два отдельных индекса? Если вы создали один индекс, убедитесь, что наиболее ограничительное предложение (poperties.city) является первым элементом. Не помню, нужно ли заново собирать статистику после создания индекса в Oracle, но не помешало бы. - person TMN; 26.07.2011
comment
@ Джаред, я предлагаю вам опубликовать новый план выполнения после создания дополнительных индексов. - person Dave Costa; 26.07.2011
comment
добавлен новый план выполнения в OP - person Jared; 26.07.2011

Возможно, потому что ROWNUM оценивается после выбора всех данных. К тому же, как сказал Рэнди, вам понадобятся индексы, но по плану похоже, что они у вас есть.

person TC1    schedule 26.07.2011
comment
нет, rownum обычно не включается и здесь не имеет значения. Я не должен был включать это. - person Jared; 26.07.2011

Я думаю, что вам нужен индекс для LISTING_ID в каждой таблице. Это должно предотвратить полное сканирование таблицы.

person Lost in Alabama    schedule 26.07.2011
comment
Если бы вы перечислили свои текущие индексы, то мы все могли бы перестать гадать, какие из них вам могут понадобиться. - person Lost in Alabama; 26.07.2011