Нужна помощь с запросом SQL, включающим несколько таблиц - присоединение не вариант - PullRequest
0 голосов
/ 04 февраля 2009
SELECT i.*, i.id IN (
  SELECT id
  FROM w 
  WHERE w.status='active') AS wish 
FROM i
INNER JOIN r ON i.id=r.id
WHERE r.member_id=1 && r.status='active' 
ORDER BY wish DESC 
LIMIT 0,50

Это запрос, который я пытаюсь выполнить. Это плохо масштабируется, и мне интересно, может ли кто-нибудь здесь сказать мне, где я могу что-то улучшить. Я не присоединяю w к r и i, потому что мне нужно показать строки из i, которые не представлены в w. Я попробовал левое соединение, но это не очень хорошо. Это лучше, но еще не идеально. Все три стола очень большие. Все три индексации указаны в полях, к которым я присоединяюсь и которые я выбираю.

Любые комментарии, указатели или конструктивная критика будут с благодарностью.

РЕДАКТИРОВАТЬ Дополнение:

Я должен был поставить это в свой первоначальный вопрос. Это ОБЪЯСНЕНИЕ как возвращение из SQLYog.

id|select_type       |table|type          |possible_keys|key      |key_len|ref  |rows|Extra|  
1 |PRIMARY           |r    |ref           |member_id,id |member_id|3      |const|3120|Using where; Using temporary; Using filesort  
1 |PRIMARY           |i    |eq_ref        |id           |id       |8      |r.id |1   |  
2 |DEPENDENT SUBQUERY|w    |index_subquery|id,status    |id       |8      |func |8   |Using where


РЕДАКТИРОВАТЬ Le Dorfier - больше комментариев ...

Я должен упомянуть, что ключ для w - это (member_id, id). Таким образом, каждый идентификатор может существовать несколько раз в w, и я только хочу знать, существует ли он.

Ответы [ 5 ]

3 голосов
/ 04 февраля 2009

WHERE x IN () идентичен от INNER JOIN до SELECT DISTINCT подзапроса, и, как правило, соединение с подзапросом будет работать лучше, если оптимизатор не превратит IN в JOIN - который он должен:

SELECT i.*
FROM i
INNER JOIN (
    SELECT DISTINCT id
    FROM w 
    WHERE w.status = 'active'
) AS wish 
    ON i.id = wish.id
INNER JOIN r
    ON i.id = r.id
WHERE r.member_id = 1 && r.status = 'active' 
ORDER BY wish.id DESC 
LIMIT 0,50

Что, вероятно, будет эквивалентно этому, если вам не нужно DISTINCT:

SELECT i.*
FROM i
INNER JOIN w 
    ON w.status = 'active'
    AND i.id = wish.id
INNER JOIN r
    ON i.id = r.id
    AND r.member_id = 1 && r.status = 'active' 
ORDER BY i.id DESC 
LIMIT 0,50

Пожалуйста, опубликуйте свою схему.

Если вы используете желание как флаг существования, попробуйте:

SELECT i.*, CASE WHEN w.id IS NOT NULL THEN 1 ELSE 0 END AS wish
FROM i
INNER JOIN r
    ON i.id = r.id
    AND r.member_id = 1 && r.status = 'active' 
LEFT JOIN w 
    ON w.status = 'active'
    AND i.id = w.id
ORDER BY wish DESC 
LIMIT 0,50

Вы можете использовать ту же технику с подзапросом от LEFT JOIN до SELECT DISTINCT. Я предполагаю, что вы не указываете w.member_id, потому что хотите знать, есть ли у кого-либо из них? В этом случае обязательно используйте SELECT DISTINCT. Вы должны иметь индекс с id в качестве первого столбца на w, чтобы это можно было выполнить:

SELECT i.*, CASE WHEN w.id IS NOT NULL THEN 1 ELSE 0 END AS wish
FROM i
INNER JOIN r
    ON i.id = r.id
    AND r.member_id = 1 && r.status = 'active' 
LEFT JOIN (
    SELECT DISTINCT w.id
    FROM w 
    WHERE w.status = 'active'
) AS w
    ON i.id = w.id
ORDER BY wish DESC 
LIMIT 0,50
1 голос
/ 04 февраля 2009
...
ORDER BY wish DESC 
LIMIT 0,50

Это кажется большим расходом. Вы сортируете по вычисляемому столбцу "wish", который не может извлечь выгоду из индекса. Это вынуждает его использовать вывод filesort (как указано EXPLAIN), что означает, что он записывает весь набор результатов на диск и сортирует его с помощью дискового ввода-вывода, который очень медленный.

Когда вы публикуете подобные вопросы, вы не должны ожидать, что люди догадаются, как вы определили ваши таблицы и индексы. Получить полные определения очень просто:

mysql> SHOW CREATE TABLE w;
mysql> SHOW CREATE TABLE i;
mysql> SHOW CREATE TABLE r;

Затем вставьте вывод в ваш вопрос.

Непонятно, какова ваша цель для столбца "wish". Предикат "IN" является логическим выражением, поэтому оно всегда приводит к 0 или 1. Но я предполагаю, что вы пытаетесь использовать "IN" в надежде выполнить соединение без объединения. Было бы полезно, если бы вы описали, чего пытаетесь достичь.

Попробуйте это:

SELECT i.*
FROM i
 INNER JOIN r ON i.id=r.id
 LEFT OUTER JOIN w ON i.id=w.id AND w.status='active'
WHERE r.member_id=1 AND r.status='active'
 AND w.id IS NULL
LIMIT 0,50;

Он использует дополнительное внешнее объединение, но не выполняет сортировку файлов в соответствии с моим тестом с EXPLAIN.

1 голос
/ 04 февраля 2009

Я должен был поставить это в свой оригинальный вопрос. Это ОБЪЯСНЕНИЕ как возвращение из SQLYog.
идентификатор | SELECT_TYPE | таблица | тип | possible_keys | ключ | key_len | реф | строки | Extra |
1 | PRIMARY | r | ref | member_id, id | member_id | 3 | const | 3120 | Использование где; Используя временные; Использование файловой сортировки
1 | ОСНОВНОЙ | я | eq_ref | идентификатор | идентификатор | 8 | r.id | 1 |
2 | ЗАВИСИМЫЙ ПОДПИСЬ | w | index_subquery | id, status | id | 8 | func | 8 | Использование где

1 голос
/ 04 февраля 2009

Пожалуйста, разместите объявление EXPLAIN. И объясните, что означают таблицы и столбцы.

желание, похоже, логическое - и вы по нему ЗАКАЗЫВАЕТЕ?


РЕДАКТИРОВАТЬ: Ну, похоже, он делает то, что ему поручено делать. Кажется, Кейд много думает о том, что все это могло бы означать (он, вероятно, заслуживает голоса только за усилия). Но я бы действительно предпочел, чтобы вы сказали нам.

Дикие догадки просто смущают всех (включая вас, я уверен.)


ОК, основываясь на новой информации, вот мое (немного менее дикое) предположение.

SELECT i.*,  
    CASE WHEN EXISTS (SELECT 1 FROM w WHERE id = i.id AND w.status = 'active' THEN 1 ELSE 0 END) AS wish  
FROM i  
INNER JOIN r ON i.id = r.id AND r.status = 'active'  
WHERE r.member_id = 1

Хотите ли вы строки для каждого совпадения в w? Или просто узнать для i.id, есть ли активная w запись? Я предположил второй ответ, так что вам не нужно ORDER BY - это все равно только для одного идентификатора. А так как вы возвращаете только столбцы из i, если в r есть несколько строк, вы просто получите повторяющиеся строки.

Как насчет публикации того, что вы ожидаете получить за правильный ответ?

0 голосов
/ 04 февраля 2009

Вы пробовали это?

SELECT i.*, w.id as wish FROM i
LEFT OUTER JOIN w ON i.id = w.id
  AND w.status = 'active'
WHERE i.id in (SELECT id FROM r WHERE r.member_id = 1 AND r.status = 'active')
ORDER BY wish DESC
LIMIT 0,50
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...