Selecionando várias colunas através de uma subconsulta

18

Estou tentando selecionar 2 colunas da subconsulta na consulta a seguir, mas não é possível fazer isso. Tentei criar a tabela de alias, mas ainda não consegui obtê-los.

SELECT
  DISTINCT petid,
  userid,
  (SELECT MAX(comDate) FROM comments WHERE petid=pet.id) AS lastComDate,
  (SELECT userid FROM comments WHERE petid=pet.id ORDER BY id DESC LIMIT 1) AS lastPosterID
FROM 
  pet LEFT JOIN comments ON pet.id = comments.petid
WHERE 
  userid='ABC'      AND 
  deviceID!='ABC'   AND 
  comDate>=DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 2 MONTH);

Basicamente, estou tentando obter o lastComDate& lastPosterIDda mesma linha - a linha que é a mais recente nos comentários para o animal de estimação específico. Por favor, sugira como posso obtê-los de maneira eficiente.

A consulta acima funciona, mas parece exagerada, pois a mesma linha é buscada duas vezes. Além disso, a ORDER BYcláusula é significativamente mais lenta que a função agregada - como eu encontrei ao criar uma consulta de perfil. Portanto, uma solução para evitar a classificação seria apreciada.

BufferStack
fonte
11
Se você tivesse um índice (petid, id) na tabela de comentários, a ordem de provavelmente não seria lenta, mas primeiro o primeiro: parece que sua consulta está solicitando todos os animais de estimação nos quais o userid 'ABC' comentou nos últimos dois meses, em que deviceID não é 'ABC' (embora não esteja claro em qual tabela o deviceID está em uma coluna, possivelmente animais de estimação e possivelmente comentários) e quem foi o último comentarista e a data do último comentário. Isso está certo?
Michael - sqlbot
@ Michael-sqlbot - Sim, é exatamente isso que estou tentando reunir. O deviceIDé da petstabela - o que significa que simplesmente não me dão os animais de estimação enviados pelo próprio 'ABC'.
BufferStack 14/02

Respostas:

13
SELECT DISTINCT petid, userid, lastComDate, lastPosterId
FROM 
    pet 
    LEFT JOIN comments ON pet.id = comments.petid 
    LEFT JOIN (
        SELECT MAX(comDate), userid, petid FROM comments GROUP BY userid
    ) a ON a.petid = pet.id
WHERE 
    userid='ABC' 
    AND deviceID!='ABC' 
    AND comDate>=DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 2 MONTH)
;

Você também pode extrair sua subconsulta para uma tabela temporária se o desempenho for impactado em algum lugar no futuro.

Valquíria
fonte
Eu tinha tentado isso mais cedo também ... isso retorna NULLpara ambos lastComDatee lastPosterIdpara todos os registros.
BufferStack
Você tem alguma amostra de dados disponível?
Valkyrie
Como forneço os dados da amostra?
BufferStack
Tente as dicas nesta publicação: meta.stackexchange.com/questions/156729/…
Valkyrie
11
Isso é bom, mas o SQLFiddle é melhor;). Veja um exemplo aqui . É muito melhor ver apenas o código e os dados iniciais, e não alguma tabela que você precisará formatar.
Marian
6

Dado que suas tabelas ficam assim:

create table pet (id int, userid int, deviceid int);
create table comments (id int, petid int, comdate date);

Esta consulta deve fazer o truque:

SELECT 
        p.id, 
        p.userid,
        (SELECT MAX(comDate)
         FROM comments
         WHERE petid = p.id
         AND comDate >= DATE_SUB(
                 CURRENT_TIMESTAMP, INTERVAL 2 MONTH)
               ) AS lastComDate,
        (SELECT userid
         FROM comments
         WHERE petid = p.id
         AND comDate >= DATE_SUB(
              CURRENT_TIMESTAMP, INTERVAL 2 MONTH
         ) ORDER BY id DESC LIMIT 1) AS lastPosterID
    FROM 
        pet p

    WHERE 
        p.userid=1
        AND p.deviceID!=1
druzin
fonte