Pergunta

Eu tenho esta tabela releases em um banco de dados SQLite3, listando cada versão lançada de um aplicativo:

|release_id|release_date|app_id|
|==========|============|======|
|      1001| 2009-01-01 |     1|
|      1003| 2009-01-01 |     1|
|      1004| 2009-02-02 |     2|
|      1005| 2009-01-15 |     1|

Assim, para cada app_id, haverá várias linhas. Eu tenho outra tabela, apps:

|app_id|name    |
|======|========|
|     1|Everest |
|     2|Fuji    |

Eu quero mostrar o nome da aplicação e a versão mais recente, onde os meios "mais novos" (a) mais novo release_date, e se houver duplicatas, (b) maior release_id.

Eu posso fazer isso para uma aplicação individual:

SELECT apps.name,releases.release_id,releases.release_date 
  FROM apps 
  INNER JOIN releases 
    ON apps.app_id = releases.app_id
  WHERE releases.release_id = 1003
  ORDER BY releases.release_date,releases.release_id
  LIMIT 1

mas é claro que ORDER BY se aplica a toda a consulta SELECT, e se eu deixar de fora a cláusula WHERE, ele ainda retorna apenas uma linha.

É uma consulta one-shot em um pequeno banco de dados, consultas tão lento, tabelas temporárias, etc. são muito bem -. Eu simplesmente não pode obter o meu cérebro em torno da maneira SQL para fazer isso

Foi útil?

Solução

Isso é fácil de fazer com o ROW_NUMBER função analítica (), que eu acho que sqlite3 não suporta. Mas você pode fazê-lo de uma forma que é um pouco mais flexível do que o que é dado nas respostas anteriores:

SELECT
  apps.name,
  releases.release_id,
  releases.release_date 
FROM apps INNER JOIN releases 
ON apps.app_id = releases.app_id
WHERE NOT EXISTS (
-- // where there doesn't exist a more recent release for the same app
  SELECT * FROM releases AS R
  WHERE R.app_id = apps.app_id
  AND R.release_data > releases.release_data
)

Por exemplo, se você tiver várias colunas de ordenação que definem "mais recente," MAX não iria funcionar para você, mas você pode modificar a subconsulta EXISTS para capturar o mais complicado significado de "mais recente".

Outras dicas

Este é o "maior N por grupo" problema. Vem-se várias vezes por semana em StackOverflow.

Eu costumo usar uma solução como a do @ Steve Kass' , mas fazê-lo sem subconsultas (eu entrei dos anos hábito atrás com o MySQL 4.0, que não apoiaram subconsultas):

SELECT a.name, r1.release_id, r1.release_date
FROM apps a
INNER JOIN releases r1
LEFT OUTER JOIN releases r2 ON (r1.app_id = r2.app_id 
  AND (r1.release_date < r2.release_date
    OR r1.release_date = r2.release_date AND r1.release_id < r2.release_id))
WHERE r2.release_id IS NULL;

Internamente, isso provavelmente otimiza idêntica à sintaxe NOT EXISTS. Você pode analisar a consulta com EXPLAIN para certificar-se.


Re seu comentário, você poderia simplesmente ignorar o teste para release_date porque release_id é tão útil para estabelecer a ordem cronológica de lançamentos, e eu suponho que está garantido para ser original, assim que isso simplifica a consulta:

SELECT a.name, r1.release_id, r1.release_date
FROM apps a
INNER JOIN releases r1
LEFT OUTER JOIN releases r2 ON (r1.app_id = r2.app_id 
  AND r1.release_id < r2.release_id)
WHERE r2.release_id IS NULL;

É feio, mas eu acho que ele vai trabalhar

select apps.name, (select releases.release_id from releases where releases.app_id=apps.app_id order by releases.release_date, releases.release_id), (select releases.release_date from releases where releases.app_id=apps.app_id order by releases.release_date, releases.release_id) from apps order by apps.app_id

Espero que haja alguma maneira de obter essas duas colunas em um incorporado select, mas eu não conhecê-lo.

Tente:

SELECT a.name,
       t.max_release_id,
       t.max_date
  FROM APPS a
  JOIN (SELECT t.app_id,
               MAX(t.release_id) 'max_release_id',
               t.max_date
          FROM (SELECT r.app_id,
                       r.release_id,
                       MAX(r.release_date) 'max_date'
                  FROM RELEASES r
              GROUP BY r.app_id, r.release_id)
      GROUP BY t.app_id, t.max_date) t

Err segunda tentativa. Assumindo que IDs são monótona crescente e estouro não é uma ocorrência provável, você pode ignorar a data e apenas fazer:

SELECT apps.name, releases.release_id, releases.release_date 
FROM apps INNER JOIN releases on apps.app_id = releases.app_id
WHERE releases.release_id IN 
(SELECT Max(release_id) FROM releases
GROUP BY app_id);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top