MySQL Возможно ли присоединить таблицу к предоставленному статическому списку данных? - PullRequest
1 голос
/ 12 октября 2011

Рассмотрим эту таблицу:

TABLE names
+-------+-------+-----+-------------+
| id    | f_key |name | sort_metric |
+-------+-------+-----+-------------+
|     1 |     1 |   a |           1 |
|     2 |     1 |   b |           2 |
|     3 |     1 |   c |           0 |
|     4 |     2 |   d |           0 |
|     5 |     2 |   e |           2 |
|     6 |     2 |   f |           1 |
|     7 |     3 |   g |           1 |
|     8 |     3 |   h |           0 |
...
|  9999 |  2500 | zzz |           2 |
| 10000 |  2500 | zzz |           0 |
+-------+-------+-----+-------------+

В этой таблице около 10000 строк.У меня есть запрос, который возвращает правильные результаты, но, кажется, просят для оптимизации.

Запрос возвращает f_key и name из этой таблицы, упорядоченной по (sort_metric, id) для каждого f_key.Этот запрос выполняется довольно часто, поэтому я хотел бы сделать его максимально эффективным.

SELECT
  name_a.f_key, name_a.name 
FROM ( 
    SELECT 
      DISCTINCT f_key 
    FROM 
      names
    WHERE 
      f_key IN ( 254, 257, ..., 273, 279 )
  ) f_keys 
  JOIN names names_a ON names_a.id = ( 
    SELECT 
      names_b.id 
    FROM 
      names names_b
    WHERE 
      names_b.f_key = f_keys.f_key 
    ORDER BY 
      sort_metric ASC, id ASC LIMIT 1
  )

Количество элементов в предложении IN (...) каждый раз равно 24, но элементыне последовательно, а часто меняются.id - это первичный ключ, и у меня есть дополнительные индексы (f_key) и (sort_metric, id).

В частности, производная таблица SELECT DISTINCT f_key FROM names WHERE f_key IN ( 254, 257, ..., 273, 279 ) кажется мне глупой.Разве нет способа более эффективно использовать предоставленный статический список в качестве производной таблицы?Я не могу понять, как это сделать.Кто-нибудь? * * 1023

Ответы [ 4 ]

2 голосов
/ 12 октября 2011

Если я правильно понимаю ваш запрос, вы намереваетесь выбрать запись с самым низким значением sort_metric, и если их больше, то ту, которая имеет самый низкий идентификатор.Ваше двойное самостоятельное соединение вызвано нежеланием дублировать уродливый список ().Еще один способ сделать это - с помощью CTE, требующего только одного самостоятельного соединения:

WITH ext AS (
  SELECT id,f_key,name,sort_metric
  FROM tmp.names
  WHERE  f_key IN ( 1, 3, 254, 257, 301, 273, 279 )
  )
SELECT t1.*
  FROM ext t1
  WHERE NOT EXISTS (
    SELECT *
    FROM ext t2
    WHERE t2.sort_metric <= t1.sort_metric
    AND t2.f_key = t1.f_key
    AND t2.id < t1.id
    )
  ORDER BY t1.id
  LIMIT 1
  ;
1 голос
/ 12 октября 2011

Количество элементов в предложении IN (...) каждый раз равно 24

Тогда использование предложения 'IN - это путь, но предоставленный вами запрос выполняет МНОГО ненужной работы (действительно ли соединение запроса с предикатом' WHERE ', ссылающимся на te jion, действительно работает ????? ) если я не сильно ошибаюсь, это должно привести к тому же результату:

SELECT names.f_key, names.name
FROM names
WHERE names.f_key IN ( 254, 257, ..., 273, 279 );

обновление

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

SELECT n1.f_key, n1.name
FROM names n1
WHERE n1.f_key IN ( 254, 257, ..., 273, 279 )
AND NOT EXISTS (
    SELECT 1 FROM names n2
    WHERE n2.f_key=n1.f_key
    AND ((n2.sort_metric>n1.sort_metric)
      OR (n2.sort_metric=n1.sort_metric
         AND n2.id>n1.id))
)

... или используйте трюк max concat для исключения подвыбора ...

SELECT n.f_key, SUBSTRING(
    MIN(CONCAT(LPAD(sort_metric, 8, '0'),LPAD(id, 8, '0'), name)),
    17) AS name
FROM names n
WHERE n.f_key IN ( 254, 257, ..., 273, 279 )
GROUP BY f_key
1 голос
/ 12 октября 2011

Это слишком сложно!Попробуйте:

SELECT DISTINCT names_a.f_key, names_a.name
  FROM names names_a
  LEFT JOIN names names_b ON names_b.f_key = names_a.f_key
                         AND (names_b.sort_metric < names_a.sort_metric
                              OR (    names_b.sort_metric = names_a.sort_metrict
                                  AND names_b.id < names_a.id
                                 )
                             )
 WHERE names_a.f_key IN ( 254, 257, ..., 273, 279 )
   AND names_b.id IS NULL;
0 голосов
/ 12 октября 2011

Я не уверен, что хорошо понял ваш вопрос (если нет, пожалуйста, сформулируйте более точно, какой вывод вы хотите), но из вашего предложения * "Запрос возвращает f_key и имя из этой таблицы, упорядоченное по (sort_metric, id)для каждого f_key "* кажется, что решение простое, как это:

select f_key, name

from names

where f_key IN ( 254, 257, ..., 273, 279 )

order by sort_metric, id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...