2016-09-09 5 views
9

У меня есть два запроса MySQL, которые один за другим, бежать очень быстро:Как объединить эти два запроса mysql, чтобы сохранить его скорость?

QUERY 1

SELECT Ads.AdId FROM Ads, AdsGeometry WHERE 
     AdsGeometry.AdId = Ads.AdId AND 
     (ST_CONTAINS(GeomFromText('Polygon((
     -4.9783515930176 36.627100703563, 
     -5.0075340270996 36.61222072018, 
     -4.9896812438965 36.57638676015, 
     -4.965991973877 36.579419508882, 
     -4.955005645752 36.617732160006, 
     -4.9783515930176 36.627100703563 
    ))'), AdsGeometry.GeomPoint)) 
GROUP BY Ads.AdId 

Этот запрос выполняется в 0,0013 секунды, и возвращает 4 строки.

QUERY 2

SELECT Ads.AdId FROM Ads, AdsHierarchy WHERE 
     Ads.AdId = AdsHierarchy.ads_AdId AND 
     AdsHierarchy.locations_LocationId = 148022797 
GROUP BY Ads.AdId 

Этот запрос выполняется в 0,0094 секунд, и возвращает 67 строк (3 из них одинаковы вышеуказанного запроса).

Я пытаюсь объединить эти два запроса в один запрос, потому что позже набор результатов из двух запросов должен быть упорядочен вместе, и я хотел бы сделать заказ с использованием MySQL. Это то, что я пытался, и под ним, вы найдете это объяснить также:

SELECT Ads.AdId FROM Ads, AdsHierarchy, AdsGeometry WHERE 
     Ads.AdId = AdsHierarchy.ads_AdId AND 
     AdsGeometry.AdId = Ads.AdId AND ( 
      ST_CONTAINS(GeomFromText('Polygon((
      -4.9783515930176 36.627100703563, 
      -5.0075340270996 36.61222072018, 
      -4.9896812438965 36.57638676015, 
      -4.965991973877 36.579419508882, 
      -4.955005645752 36.617732160006, 
      -4.9783515930176 36.627100703563 
     ))'), AdsGeometry.GeomPoint) OR 
      AdsHierarchy.locations_LocationId = 148022797 
    ) 
GROUP BY Ads.AdId 

id select_type  table   type     possible_keys         key    key_len ref      rows  Extra 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
1 SIMPLE   AdsGeometry  ALL     PRIMARY,GeomPoint,sx_adsgeometry_geompoint NULL    NULL  NULL      682848  Using temporary; Using filesort 
1 SIMPLE   Ads    eq_ref    PRIMARY          PRIMARY   4  dbname.AdsGeometry.AdId 1   Using where; Using index 
1 SIMPLE   AdsHierarchy ref     Ads_AdsHierarchy,locations_LocationId   Ads_AdsHierarchy 4  dbname.Ads.AdId   1   Using where 

Хотя этот запрос возвращает правильный набор результатов (68 строк), он нуждается в 6.5937 секунд для запуска. Если я это правильно понимаю, таблица AdsHierarchy не использует его индексы, а также AdsGeometry.

Есть ли способ объединить два запроса (или, возможно, даже больше местоположения или запросы на основе многоугольника, подобные этим), и поддерживать разумную скорость для его запуска?

Спасибо!

EDIT: Некоторая информация, о том, как индексы 3 таблицы в вопросе

AdsGeometry таблица MyISAM и первичный ключ AdId.

Результат SHOW INDEXES FROM AdsGeometry является:

Table   Non_unique Key_name     Seq_in_index Column_name  Collation Cardinality  Sub_part Packed  Null Index_type Comment Index_comment 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
AdsGeometry  0   PRIMARY      1    AdId   A   682848   NULL  NULL  BTREE  
AdsGeometry  1   Latitude     1    Latitude  A   NULL   NULL  NULL  BTREE  
AdsGeometry  1   Longitude     1    Longitude  A   NULL   NULL  NULL  BTREE  
AdsGeometry  1   GeomPoint     1    GeomPoint  A   NULL   32   NULL  SPATIAL   
AdsGeometry  1   sx_adsgeometry_geompoint 1    GeomPoint  A   NULL   32   NULL  SPATIAL   
AdsGeometry  1   Latitude_2     1    Latitude  A   NULL   NULL  NULL  BTREE  
AdsGeometry  1   Latitude_2     2    Longitude  A   NULL   NULL  NULL  BTREE  

AdsHierarchy тип таблицы InnoDB, первичный ключ AdsHierarchyId.

Результат SHOW INDEXES FROM AdsHierarchy является:

Table   Non_unique Key_name     Seq_in_index Column_name   Collation  Cardinality  Sub_part Packed  Null Index_type Comment Index_comment 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
AdsHierarchy 0   PRIMARY      1    AdsHierarchyId  A    2479044   NULL  NULL    BTREE  
AdsHierarchy 1   Ads_AdsHierarchy   1    ads_AdId    A    2479044   NULL  NULL    BTREE  
AdsHierarchy 1   locations_LocationId  1    locations_LocationId A    123952   NULL  NULL    BTREE 

Ads тип таблицы InnoDB, первичный ключ AdId.

Результат SHOW INDEXES FROM Ads является:

Table   Non_unique Key_name     Seq_in_index Column_name   Collation  Cardinality  Sub_part Packed  Null Index_type Comment Index_comment 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

Ads    0   PRIMARY      1    AdId     A   705411   NULL  NULL    BTREE  
Ads    1   Accounts_Ads    1    accounts_AccountId  A   2    NULL  NULL    BTREE  
Ads    1   Ads_Locations    1    locations_LocationId A   88176   NULL  NULL    BTREE  
Ads    1   Categories_Ads    1    categories_CategoryId A   16    NULL  NULL    BTREE  
Ads    1   Currencies_Ads    1    currencies_Currency A   2    NULL  NULL    BTREE  
Ads    1   countries_CountryId   1    countries_CountryId A   204    NULL  NULL    BTREE  
Ads    1   ExternalId     1    ExternalId    A   705411   NULL  NULL    BTREE  
Ads    1   ExternalId     2    accounts_AccountId  A   705411   NULL  NULL    BTREE  
Ads    1   xml_XMLId     1    xml_XMLId    A   4    NULL  NULL    BTREE  
Ads    1   streets_StreetId   1    streets_StreetId  A   2    NULL  NULL  YES  BTREE 

EDIT 2: Запрос переписан с неявным присоединяется, и объяснил:

Это запрос, переписанный с использованием неявной соединения, но он все еще работает очень медленно (5.503 сек)

SELECT a.AdId FROM Ads AS a 
    JOIN AdsHierarchy AS ah ON a.AdId = ah.ads_AdId 
    JOIN AdsGeometry AS ag ON a.AdId = ag.AdId 
    WHERE 
     ST_CONTAINS(GeomFromText('Polygon((
      -4.9783515930176 36.627100703563, 
      -5.0075340270996 36.61222072018, 
      -4.9896812438965 36.57638676015, 
      -4.965991973877 36.579419508882, 
      -4.955005645752 36.617732160006, 
      -4.9783515930176 36.627100703563 
    ))'), ag.GeomPoint) 
     OR ah.locations_LocationId = 148022797 
    GROUP BY a.AdId 

id select_type  table   type     possible_keys         key    key_len ref      rows  Extra 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
1 SIMPLE   a    index     PRIMARY          PRIMARY   4  NULL      627853  Using index 
1 SIMPLE   ag    eq_ref    PRIMARY,GeomPoint,sx_adsgeometry_geompoint PRIMARY   8  micasa_dev.a.AdId  1   Using index condition 
1 SIMPLE   ah    ref     Ads_AdsHierarchy,locations_LocationId   Ads_AdsHierarchy 4  micasa_dev.a.AdId  1   Using where 

EDIT 3: Испытание UNION-ки два запроса

Также пробовали UNION метод, предоставленный @RobertKoch.

Хотя нижеследующее UNION запрос выполняется очень быстро (0,06 секунд)

SELECT Ads.AdId FROM Ads, AdsGeometry 
WHERE 
    AdsGeometry.AdId = Ads.AdId AND 
    ST_CONTAINS(GeomFromText('Polygon(( 
     -4.9783515930176 36.627100703563, 
     -5.0075340270996 36.61222072018, 
     -4.9896812438965 36.57638676015, 
     -4.965991973877 36.579419508882, 
     -4.955005645752 36.617732160006, 
     -4.9783515930176 36.627100703563 
    ))'), AdsGeometry.GeomPoint) 
GROUP BY Ads.AdId 
UNION 
SELECT Ads.AdId FROM Ads, AdsHierarchy WHERE 
    Ads.AdId = AdsHierarchy.ads_AdId AND 
    AdsHierarchy.locations_LocationId = 148022797 
GROUP BY Ads.AdId 

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

И если я пытаюсь сделать следующее дело, запрос снова становится экстремально медленно (3,7 секунды):

SELECT Ads.AdId FROM Ads WHERE Ads.AdId IN (
    SELECT Ads.AdId FROM Ads, AdsGeometry 
    WHERE 
     AdsGeometry.AdId = Ads.AdId AND 
     ST_CONTAINS(GeomFromText('Polygon(( 
     -4.9783515930176 36.627100703563, 
     -5.0075340270996 36.61222072018, 
     -4.9896812438965 36.57638676015, 
     -4.965991973877 36.579419508882, 
     -4.955005645752 36.617732160006, 
     -4.9783515930176 36.627100703563 
    ))'), AdsGeometry.GeomPoint) 
    GROUP BY Ads.AdId 
    UNION 
    SELECT Ads.AdId FROM Ads, AdsHierarchy WHERE 
    Ads.AdId = AdsHierarchy.ads_AdId AND 
    AdsHierarchy.locations_LocationId = 148022797 
    GROUP BY Ads.AdId 
) WHERE Ads.AdId > 100000 
ORDER BY Ads.ModifiedDate ASC 

EDIT 4: Изменение где UNION постоянно находится, по-видимому, чтобы решить эту проблему

Если я изменить выше UNION запрос

SELECT Ads.AdId 
FROM Ads, 
(SELECT Ads.AdId 
    FROM Ads, 
    AdsGeometry 
    WHERE AdsGeometry.AdId = Ads.AdId 
    AND ST_CONTAINS(GeomFromText('Polygon(( 
     -4.9783515930176 36.627100703563, 
     -5.0075340270996 36.61222072018, 
     -4.9896812438965 36.57638676015, 
     -4.965991973877 36.579419508882, 
     -4.955005645752 36.617732160006, 
     -4.9783515930176 36.627100703563 
    ))'), AdsGeometry.GeomPoint) 
    GROUP BY Ads.AdId 
    UNION SELECT Ads.AdId 
    FROM Ads, 
     AdsHierarchy 
    WHERE Ads.AdId = AdsHierarchy.ads_AdId 
    AND AdsHierarchy.locations_LocationId = 148022797 
    GROUP BY Ads.AdId) AS nt 
WHERE Ads.AdId = nt.AdId 
    AND Ads.AdId > 1000000 
ORDER BY Ads.ModifiedDate ASC 

запрос затем выполняется быстро снова (~ 0.0 007 секунд).

Если никакое решение не приходит без UNION, я готов дать награду любому, кто может объяснить разницу между этими двумя UNION версий (это один и тот в EDIT 3), и объясните мне , почему запрос выполняется быстро, когда он написан в следующем порядке и работает медленно, когда он написан в указанном выше порядке.

Если вам нужна дополнительная информация, просьба указать в комментариях, и я стараюсь их предоставить! Спасибо

* ПРИМЕЧАНИЕ: * Я добавил ORDER к двум запросам UNION, чтобы сделать его более ясным, что, хотя я только выбираю AdId из таблиц, мне все еще нужны другие поля из таблицы Ads.

EDIT 5: запрос @bovko

1 SIMPLE Ads  index NULL    countries_CountryId  2 NULL     627853 Using index; Using temporary 
1 SIMPLE ag  eq_ref PRIMARY    PRIMARY     8 micasa_dev.Ads.AdId  1  Using where; Distinct 
1 SIMPLE ah  ref  Ads_AdsHierarchy Ads_AdsHierarchy  4 micasa_dev.Ads.AdId  1  Using where; Distinct 
+0

Вы посмотрели план запроса, чтобы узнать, откуда идет замедление. ? – Doon

+5

Попробуйте «СОЮЗ» оба запроса. Он должен автоматически отфильтровывать дубликаты. –

+0

@RobertKock Я думал об UNION-ing, но у меня очень плохой опыт работы с UNION, это действительно единственный способ решить эту проблему? –

ответ

3

IN (SELECT ...) обычно варьируются неэффективна. Избегай это.

Все ответы до сих пор работают сложнее, чем им нужно. Кажется, что JOINs не нужны до послеUNION. См. Далее Примечания ниже.

SELECT Ads.AdId 
    FROM Ads, 
    JOIN (
     (SELECT AdId 
      FROM AdsGeometry 
      WHERE ST_CONTAINS(GeomFromText('Polygon((-4.9783515930176 36.627100703563, 
         -5.0075340270996 36.61222072018, -4.9896812438965 36.57638676015, 
         -4.965991973877 36.579419508882, -4.955005645752 36.617732160006, 
         -4.9783515930176 36.627100703563))'), 
           AdsGeometry.GeomPoint) 
       AND AdId > 1000000) 
     UNION DISTINCT 
     (SELECT ads_AdId AS AdId 
      FROM AdsHierarchy 
      WHERE locations_LocationId = 148022797 
       AND ads_AdId > 1000000) 
     ) AS nt ON Ads.AdId = nt.AdId 
    ORDER BY Ads.ModifiedDate ASC 

Примечания:

  • Оба AdsGeometry и AdsHierarchy имеют Адид (под разными названиями); нет необходимости делать JOIN во внутренних запросах, за исключением, возможно, для проверки того, что он существует в Ads. Это проблема? Во всяком случае, мой запрос позаботится о внешнем SELECT's JOIN.
  • UNION DISTINCT необходимо, потому что два SELECTs могут извлекать одинаковые идентификаторы.
  • Переместите > 1000000 внутрь, чтобы сократить количество значений, полученных UNION.
  • UNION всегда (в старых версиях MySQL), а иногда (в более новых версиях) создает временную таблицу. Вы застряли в этом.
  • IN (SELECT ...) обычно оптимизирован ужасно; избегай это.
  • Я добавил некоторые круглые скобки; В настоящий момент возможно (но не обязательно) добавить ORDER BY и т. Д. В UNION; parens дают понять, к чему это будет относиться.
  • Единственная причина для внешнего запроса - получить ModifiedDate для заказа. Вы можете ускорить процесс, исключив это требование. (The UNION вероятно создает таблицу TMP, это ORDER BY вероятно, создает другой.)
+0

Почему метод 'IN (SELECT ...)' медленнее, чем метод SELECT FROM (SELECT ...) как-то? И будет ли использовать неявный 'JOIN' быстрее, а затем использовать явный? (тестирование соединения в предложении 'WHERE' запроса?) –

+1

Оптимизатор не всегда достаточно умен, чтобы превратить' IN' в неявный 'JOIN'. Вы можете перейти к 'EXISTS (...)', чтобы явно получить «полу-соединение», но его нужно оценивать _every_ time. «Производная таблица» ('FROM (SELECT ...)') оценивается один раз. –

+0

5.6 имеет улучшения в подзапросах, подобных этому; 5.7 имеет больше улучшений. –

2

Оба ваших UNION запросов делает дополнительную работу по поиску результатов уже найденных делает

SELECT Ads.AdId FROM Ads WHERE AdId IN ...
или
SELECT Ads.AdId FROM Ads, (SELECT Ads.AdId ...) AS nt WHERE Ads.AdId = nt.AdId

Также SELECT Ads.AdId FROM Ads, ... GROUP BY Ads wil л, вероятно, будет более эффективным, а также легче понять, если записать в виде SELECT DISTINCT Ads.AdID FROM Ads, ...

Таким образом, это должно дать лучший запрос:

SELECT DISTINCT AdId FROM 
    (SELECT Ads.AdId FROM Ads 
    INNER JOIN AdsGeometry ON AdsGeometry.AdId = Ads.AdId 
    WHERE 
     ST_CONTAINS(GeomFromText('Polygon(( 
     -4.9783515930176 36.627100703563, 
     -5.0075340270996 36.61222072018, 
     -4.9896812438965 36.57638676015, 
     -4.965991973877 36.579419508882, 
     -4.955005645752 36.617732160006, 
     -4.9783515930176 36.627100703563 
    ))'), AdsGeometry.GeomPoint) 
    UNION ALL 
    SELECT Ads.AdId FROM Ads 
    INNER JOIN AdsHierarchy ON Ads.AdId = AdsHierarchy.ads_AdId 
    WHERE AdsHierarchy.locations_LocationId = 148022797) AS sub 
WHERE AdId > 100000 
+0

Да, но что, если мне нужны другие варианты сортировки помимо AdId? Например, что, если я хочу отфильтровать или отсортировать основной запрос на основе 'ModifyDate', который является полем в таблице« Объявления »? Если в последнем запросе я не включаю таблицу «Объявления», моя единственная опция - переносить дополнительные поля из двух запросов, которые формируют 'UNION'.Поскольку таблица «Объявления» содержит около 24 полей, и их можно фильтровать или сортировать почти по каждому из них, я бы нести вокруг еще 23 полей в этих двух запросах, не будут ли эти + поля создавать два объединенных запроса гораздо медленнее? –

+0

@AdamBaranyai Выбор дополнительных полей, которые будут использоваться для фильтрации или сортировки, не должен замедлять запрос так же, как это делают дополнительные объединения. –

1

Не имея набор данных, я не могу быть уверен, но это может работать для вас:

SELECT AdId 
FROM Ads 
WHERE EXISTS (SELECT 1 FROM AdsHierarchy 
       WHERE Ads.AdId = AdsHierarchy.ads_AdId 
       AND locations_LocationId = 148022797) 
    OR EXISTS (SELECT 1 FROM AdsGeometry, AdsHierarchy 
       WHERE Ads.AdId = AdsHierarchy.ads_AdId 
       AND Ads.AdId = AdsGeometry.AdId 
       AND ST_CONTAINS(GeomFromText('Polygon((
        -4.9783515930176 36.627100703563, 
        -5.0075340270996 36.61222072018, 
        -4.9896812438965 36.57638676015, 
        -4.965991973877 36.579419508882, 
        -4.955005645752 36.617732160006, 
        -4.9783515930176 36.627100703563 
       ))'), GeomPoint) 
    ) 
ORDER BY AdId 
+0

Вы попробовали это? Никаких СОЕДИНЕНИЙ или СОЮЗОВ не требуется. –

1

Некоторые базы данных имеют разную производительность для INNER JOIN и LEFT OUTER JOIN. Просто попробуйте следующий запрос, и если он медленный, добавьте EXPLAIN перед SELECT и предоставите результаты.

SELECT DISTINCT Ads.AdId 
FROM Ads 
LEFT OUTER JOIN AdsGeometry ag ON ag.AdId = Ads.AdId 
LEFT OUTER JOIN AdsHierarchy ah ON ah.ads_AdId = Ads.AdId 
WHERE ah.locations_LocationId = 148022797 
    OR (ST_CONTAINS(GeomFromText('Polygon((
     -4.9783515930176 36.627100703563, 
     -5.0075340270996 36.61222072018, 
     -4.9896812438965 36.57638676015, 
     -4.965991973877 36.579419508882, 
     -4.955005645752 36.617732160006, 
     -4.9783515930176 36.627100703563 
    ))'), ag.GeomPoint)) 
+0

Это еще медленнее, чем предыдущие запросы. Добавлен результат объяснения к исходному вопросу. –

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