Por que meu MySQL Query Usando um Subselect Pendure?
Pergunta
Os trava seguinte consulta: (embora subqueries perfomed separadamente são muito bem)
Eu não sei como fazer o ok tabela de procura explicar. Se alguém me diz, eu vou limpá-lo.
select
sum(grades.points)) as p,
from assignments
left join grades using (assignmentID)
where gradeID IN
(select grades.gradeID
from assignments
left join grades using (assignmentID)
where ... grades.date <= '1255503600' AND grades.date >= '984902400'
group by assignmentID order by grades.date DESC);
Eu acho que o problema é com a primeira tabela de classes ... o tipo ALL com que muitas linhas parece ser a causa .. Tudo é indexado.
I carregado a tabela como uma imagem. Não foi possível obter o direito formatação: http://imgur.com/AjX34.png
Um comentarista queria a cláusula completa, onde:
explain extended select count(assignments.assignmentID) as asscount, sum(TRIM(TRAILING '-' FROM grades.points)) as p, sum(assignments.points) as t
from assignments left join grades using (assignmentID)
where gradeID IN
(select grades.gradeID from assignments left join grades using (assignmentID) left join as_types on as_types.ID = assignments.type
where assignments.classID = '7815'
and (assignments.type = 30170 )
and grades.contactID = 7141
and grades.points REGEXP '^[-]?[0-9]+[-]?'
and grades.points != '-'
and grades.points != ''
and (grades.pointsposs IS NULL or grades.pointsposs = '')
and grades.date <= '1255503600'
AND grades.date >= '984902400'
group by assignmentID
order by grades.date DESC);
Solução
Suponha que você use um banco de dados real (ou seja, qualquer banco de dados, exceto MySQL, mas eu vou usar Postgres como um exemplo) para fazer esta consulta:
SELECT * FROM ta WHERE aid IN (SELECT subquery)
um banco de dados real iria olhar para a subconsulta e estimar o seu número de linhas:
- Se o número de linhas é pequeno (digamos, menos de alguns milhões)
Ele iria correr a subconsulta, em seguida, construir um hash na memória de IDs, o que também os torna únicos, o que é uma característica da IN ().
Então, se o número de linhas extraídos de ta é uma pequena parte da ta, seria usar um índice adequado para puxar as linhas. Ou, se uma parte importante da tabela é selecionada, seria apenas digitalizá-lo inteiramente, e pesquisar cada id no hash, que é muito rápido.
- Se, contudo, o número de linhas subconsulta é bastante grande
O banco de dados provavelmente reescrevê-la como uma junção por mesclagem, adicionando um Sort + Exclusivo para a subconsulta.
No entanto, você está usando MySQL. Neste caso, ele não vai fazer nada disso (ele vai re-executar a subconsulta para cada linha da tabela) por isso vai levar 1000 anos. Desculpe.
Outras dicas
Consulte "A lentidão insuportável de IN": http://www.artfulsoftware.com/infotree/queries.php#568
Super confuso, mas: (obrigado pela ajuda de todos)
SELECT *
FROM grades
LEFT JOIN assignments ON grades.assignmentID = assignments.assignmentID
RIGHT JOIN (
SELECT g.gradeID
FROM assignments a
LEFT JOIN grades g
USING ( assignmentID )
WHERE a.classID = '7815'
AND (
a.type =30170
)
AND g.contactID =7141
g.points
REGEXP '^[-]?[0-9]+[-]?'
AND g.points != '-'
AND g.points != ''
AND (
g.pointsposs IS NULL
OR g.pointsposs = ''
)
AND g.date <= '1255503600'
AND g.date >= '984902400'
GROUP BY assignmentID
ORDER BY g.date DESC
) AS t1 ON t1.gradeID = grades.gradeID
Se o seu executa subconsulta bem quando ele é executado separadamente, em seguida, tente usar um JOIN em vez de, como este:
select count(assignments.assignmentID) as asscount, sum(TRIM(TRAILING '-' FROM grades.points)) as p, sum(assignments.points) as t
from assignments left join grades using (assignmentID)
join
(select grades.gradeID from assignments left join grades using (assignmentID) left join as_types on as_types.ID = assignments.type
where assignments.classID = '7815'
and (assignments.type = 30170 )
and grades.contactID = 7141
and grades.points REGEXP '^[-]?[0-9]+[-]?'
and grades.points != '-'
and grades.points != ''
and (grades.pointsposs IS NULL or grades.pointsposs = '')
and grades.date <= '1255503600'
AND grades.date >= '984902400'
group by assignmentID
order by grades.date DESC) using (gradeID);
Há realmente não é informação suficiente para responder a sua pergunta, e você colocar um ... no meio da cláusula onde o que é estranho. Quão grande são as tabelas envolvidas e quais são os índices?
Dito isto, se há muitos termos em uma em cláusula, você pode ver o desempenho seriamente degradada. Substituir o uso de dentro com um certo juntar-se .
Para começar, a tabela as_types na na cláusula não é usado. Esquerda juntar não serve a nenhum propósito para se livrar dele.
que deixa o em cláusula de ter apenas a tabela de tarefas e notas da consulta externa. Claramente os wheres as atribuições Modificar pertencem na cláusula WHERE para a consulta externa. Você deve mover todos os que notas = whatever na cláusula ON da esquerda juntar-se aos graus.
A consulta é um pouco difícil de seguir, mas eu suspeito que a subconsulta não é necessário em tudo. Parece que sua consulta é basicamente assim:
SELECT FOO()
FROM assignments LEFT JOIN grades USING (assignmentID)
WHERE gradeID IN
(
SELECT grades.gradeID
FROM assignments LEFT JOIN grades USING (assignmentID)
WHERE your_conditions = TRUE
);
Mas, você não está fazendo nada muito chique na cláusula WHERE na subconsulta. Eu suspeito algo mais parecido
SELECT FOO()
FROM assignments LEFT JOIN grades USING (assignmentID)
GROUP BY groupings
WHERE your_conditions_with_some_tweaks = TRUE;
iria funcionar tão bem.
Se eu estou faltando alguma lógica chave aqui por favor comentário de volta e eu vou editar / apagar esta mensagem.