2016-08-06 3 views
0

У меня есть база данных со строками, как следующее:Плохо UNION ALL производительность в MySQL

+------------+---------+------------+-------+ 
| continent | country | city  | value | 
+------------+---------+------------+-------+ 
| Asia  | China | Beijing | 3  | 
| ...  | ...  | ...  | ... | 
| N. America | USA  | D.C  | 7  | 
| ....  | .... | ....  | .... | 

Для того, чтобы произвести визуализацию TreeMap, мне нужно работать это в таблицу со следующей формой:

+-----+------------+-------+ 
| uid | parent-uid | value | 
+-----+------------+-------+ 

В этом случае Asia является «родительским» для China, который является «родительским» для Beijing. Так что для этих трех вы бы что-то вроде:

+---------+--------+-----+ 
| Beijing | China | 3 | 
| China | Asia | ... | 
| Asia | global | ... | 
+---------+--------+-----+ 

«ценность» для China должна быть совокупность всех значений ребенка. Аналогично, значение Asia должно быть совокупностью всех дочерних значений.

Для этого чисто в SQL я создал следующие три запроса и совместили их с UNION ALL:

# City-level: 
SELECT 
    CONCAT(continent, "-", country, "-", city) as uid, 
    CONCAT(continent, "-", country) as parentuid, 
    value 
FROM 
    table 

UNION ALL 

# Country-level 
SELECT 
    CONCAT(continent, "-", country) as uid, 
    continent as parentuid, 
    SUM(value) as value 
FROM 
    table 
GROUP BY 
    country 

UNION ALL 

# Continent-level 
SELECT 
    continent as uid, 
    "global" as parentuid, 
    SUM(value) as value 
FROM 
    table 
GROUP BY 
    continent 

Каждый из индивидуальных запросов завершается в миллисекундах. Результаты всех возвратов на уровне города, уровня страны и континента на уровне < 0,01 секунд

Когда я объединяю их всех вместе, для получения результатов требуется 8 секунд!

Я попытался погуглить вопросы, но все просто говорит: «Использование UNION ALL вместо UNION» (я уже)

Я считал, что она не может иметь достаточно оперативной памяти для создания таблицы временных результатов, так что это диск громя , но я не знаю, как увеличить предел памяти. Я попытался натыкаясь innodb_buffer_pool_size до 1 ГБ (1073741824), но это не помогло

+0

Вы уверены, что это запрос, который замедляет работу, а не прохождение или рендеринг результирующего набора? –

+0

Сколько строк в таблице? Сколько строк на выходе? –

+0

Сколько у вас RAM? Какую версию mysql вы используете? (Может возникнуть проблема с неявной таблицей temp, участвующей здесь.) –

ответ

1

Первый select, выбирает все строки в таблице, то получение первой строки очень быстро, но выборка всех строк займет очень много времени (MySQL Workbench append limit 1000 до конца запроса по умолчанию).

Чтобы проверить, что выборки все строки займет больше времени, попробуйте следующий запрос и сказать нам время потребляет:

select * from (
SELECT 
    CONCAT(continent, "-", country, "-", city) as uid, 
    CONCAT(continent, "-", country) as parentuid, 
    value 
FROM 
    table 
) t1; 

Если это занимает почти 8 секунд, то ваш союз не имеет никаких проблем. И для повышения производительности вы должны ограничить строки, используя предложение where.

Надеюсь, это поможет.

+0

Конечно, это объясняет производительность. Я использовал запрос 'select * from (...)', который вы рекомендовали, и он изменился с 0.0027 секунд до 9.3 секунд. Guess MySQL workbench меня просто сбивает с толку. – stevendesu

+0

@stevendesu Рад, я мог бы помочь –

1

Я думаю, мой вопрос: что случилось с WITH ROLLUP?

SELECT 
    CONCAT_WS('-',continent,country,city) as uid, 
    CONCAT_WS('-',continent,COALESCE(country,'global')) as parentuid, 
    value 
FROM (
    SELECT continent, country, city, SUM(value) as value 
    FROM table 
    GROUP BY continent, country, city WITH ROLLUP 
) t1 
WHERE t1.continent IS NOT NULL; 

не может иметь CONCAT_WS() вызовы правильно, особенно если у вас есть города или страны, названные '', но я должен думать, что это будет быстрее. Предложение WHERE находится здесь, чтобы удалить общее резюме.

Вот пример WITH ROLLUP из док MySQL, чтобы помочь объяснить, что он делает:

mysql> SELECT year, country, product, SUM(profit) 
    -> FROM sales 
    -> GROUP BY year, country, product WITH ROLLUP; 
+------+---------+------------+-------------+ 
| year | country | product | SUM(profit) | 
+------+---------+------------+-------------+ 
| 2000 | Finland | Computer |  1500 | 
| 2000 | Finland | Phone  |   100 | 
| 2000 | Finland | NULL  |  1600 | 
| 2000 | India | Calculator |   150 | 
| 2000 | India | Computer |  1200 | 
| 2000 | India | NULL  |  1350 | 
| 2000 | USA  | Calculator |   75 | 
| 2000 | USA  | Computer |  1500 | 
| 2000 | USA  | NULL  |  1575 | 
| 2000 | NULL | NULL  |  4525 | 
| 2001 | Finland | Phone  |   10 | 
| 2001 | Finland | NULL  |   10 | 
| 2001 | USA  | Calculator |   50 | 
| 2001 | USA  | Computer |  2700 | 
| 2001 | USA  | TV   |   250 | 
| 2001 | USA  | NULL  |  3000 | 
| 2001 | NULL | NULL  |  3010 | 
| NULL | NULL | NULL  |  7535 | 
+------+---------+------------+-------------+ 
+0

'WITH ROLLUP' может делать именно то, что мне нужно (я не знал, что это даже вещь). Я попробую в понедельник и посмотрю, что получится. – stevendesu

+0

Просто протестировал это, и на самом деле это потребовало немного ** дольше **, используя 'WITH ROLLUP', чем мое решение UNION ALL (~ 11 секунд вместо ~ 8 секунд). Я подозреваю, что это потому, что на самом низком уровне (когда я не использовал группу) я выбирал значение непосредственно вместо выбора совокупности. Как ни странно, «WITH ROLLUP» вернул 275000 строк, тогда как мое решение «UNION ALL» вернуло 250000 строк. Я не знаю, что это за новые строки. Может быть, какая-то причуда с Австралией (континент/страна такая же)? – stevendesu