Как оптимизировать / изменить производительность MySQL Pivot Table при использовании предложения Where - PullRequest
1 голос
/ 04 октября 2019

У меня есть две простые таблицы MySQL - одна таблица индексов t_id, которая имеет уникальный первичный идентификатор;и сводная таблица t_data, распределяющая эти идентификаторы по различным полям данных:

CREATE TABLE `t_id` (  
  `id` bigint(12) NOT NULL AUTO_INCREMENT,  
  PRIMARY KEY (`id`)  
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;  

CREATE TABLE `t_data` (  
  `id` int(11) NOT NULL,  
  `field` varchar(50) CHARACTER SET cp1251 NOT NULL,  
  `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci 
DEFAULT NULL,  
  UNIQUE KEY `idxfield` (`id`,`field`),  
  KEY `value` (`value`)  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

Вот некоторые примеры данных:

+----+--------------+-------------------+  
| id | field        | value             |  
+----+--------------+-------------------+  
| 1  | organization | Apple Inc.        |  
| 1  | state        | CA                |  
| 2  | organization | Adobe Inc.        |  
| 2  | state        | CA                |  
| 3  | organization | Alphabet Inc.     |  
| 3  | state        | CA                |  
| 4  | organization | Rockwell Collins  |  
| 4  | state        | IA                |  
| 5  | organization | GEICO             |  
| 5  | state        | MD                |  
| 6  | organization | Anheuser-Busch    |  
| 6  | state        | MO                |  
| 7  | organization | Bank of America   |  
| 7  | state        | NC                |  
+----+--------------+-------------------+    

, о которых можно сообщить с помощью стандартного выбора сводной таблицыquery:

select  
  i.id,  
  ifnull (max(case when d.field = 'organization' then d.value end),'') 'organization',  
  ifnull (max(case when d.field = 'state' then d.value end),'') 'state'  
from `t_id` i  
left join `t_data` d  
on i.id = d.id  
group by i.id  
limit 0,10  

В этом простом примере показаны только два "виртуальных" поля (организация и состояние) с 7 уникальными идентификаторами:

+----+------------------+-------+  
| id | organization     | state |  
+----+------------------+-------+  
| 1  | Apple Inc.       |  CA   |  
| 2  | Adobe Inc.       |  CA   |  
| 3  | Alphabet Inc.    |  CA   |  
| 4  | Rockwell Collins |  IA   |  
| 5  | GEICO            |  MD   |  
| 6  | Anheuser-Busch   |  MO   |  
| 7  | Bank of America  |  NC   |  
+----+------------------+-------+  

В нашей реальной производственной ситуации у нас есть десятки«виртуальные» поля (не только 2) и миллионы уникальных идентификаторов (не только 7). База данных довольно хорошо выполняет запросы типа crud для одного идентификатора (менее секунды) и даже перечисляет одну группу ограничений за раз (опять же менее секунды). Проблема возникает, когда делается попытка ограничить выбор предложением where (запросы занимают десятки секунд). Например, чтобы найти все организации в Калифорнии:

select  
 x.id,  
 x.organization,  
 x.state  
from  
(  
select  
  i.id,  
  ifnull (max(case when d.field = 'organization' then d.value end),'') 'organization',  
  ifnull (max(case when d.field = 'state' then d.value end),'') 'state'  
from `t_id` i  
left join `t_data` d  
on i.id = d.id  
group by i.id  
) as x  
where x.state='CA'  
limit 0,10  


+----+---------------+-------+  
| id | organization  | state |  
+----+---------------+-------+  
| 1  | Apple Inc.    |  CA   |  
| 2  | Adobe Inc.    |  CA   |  
| 3  | Alphabet Inc. |  CA   |  
+----+---------------+-------+  

это работает, но это занимает ДЛИННОЕ время (опять же, 10 секунд)! Какова лучшая практика здесь - есть ли лучший способ написать эти типы запросов? Как можно оптимизировать эти запросы сводной таблицы для предложения where?

Ответы [ 3 ]

2 голосов
/ 04 октября 2019

Если вы хотите найти организации, работающие в штате Калифорния, вам на самом деле не нужен подзапрос:

SELECT
    i.id,
    COALESCE(MAX(CASE WHEN field = 'organization' THEN value END), '') AS organization,
    COALESCE(MAX(CASE WHEN field = 'state' THEN value END), '') AS state
FROM t_id i
LEFT JOIN t_data d
    ON i.id = d.id
GROUP BY
    i.id
HAVING
    COUNT(CASE WHEN field = 'state' AND value = 'CA' THEN 1 END) > 0;

Хитрость заключается в том, чтобы утвердить в предложении HAVING, что соответствует id группа должна иметь запись для state в Калифорнии.

1 голос
/ 14 октября 2019

Это EAV, а не Pivot. Таким образом, решение заключается в «самостоятельном соединении».

SELECT  a.id,
        a.value AS organization,
        b.value AS state
    FROM t_data AS a
    JOIN t_data AS b  ON a.id = b.id
    WHERE a.field = 'organization'
      AND b.field = 'state';

Если вам нужно t_id для управления тем, какие идентификаторы, добавьте

    JOIN t_id AS i  ON i.id = a.id

Если вы хотите ограничить CAнажмите на

      AND b.value = 'CA'

и добавьте

INDEX(field, value)

, чтобы не приходилось сканировать столько строк, чтобы найти записи CA.

1 голос
/ 05 октября 2019

Это должно быть намного быстрее с большими наборами данных. Кроме того, его можно легко расширить на любое количество «виртуальных» полей. Вы можете разместить любой критерий поиска между %%.

select  
    i.id,  
    coalesce(max(case when field = 'organization' then value end), '') as organization,  
    coalesce(max(case when field = 'state' then value end), '') as state  
from t_id i  
left join t_data d  
on i.id = d.id  
and i.id like '%%'  
and i.id in (  
  select id  
  from `t_data`  
  where `field` = 'organization'   
  and `value` like '%%'  
  and  id  in (  
    select id  
    from `t_data`  
    where `field` = 'state'  
    and `value` like '%%'  
  )  
)  
group by i.id  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...