selecione todas as linhas com um valor mínimo

9

No Sqlite 3, estou tentando descobrir como selecionar linhas com base em um valor mínimo. Eu acho que estou limitado por não conhecer o suficiente da terminologia relacionada para pesquisar efetivamente no Google.

A tabela se parece com:

num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           a           3         
1           b           4         

Eu quero obter as linhas onde num2está 1, 2, e 4. Quero fazer a seleção com base no valor mínimo de num para cada valor exclusivo da coluna de texto.

Então, para text = 'a', o valor mínimo de numé 0, então eu quero as linhas 1 e 2. Para text = 'b', o valor mínimo de numé 1, então eu quero a linha 4.

Usando várias combinações de agrupar por, eu sou capaz de obter linhas 1e / 2ou linhas 1e 4. Sinto que estou sentindo falta de um componente SQL que faria o que eu quero, mas não consegui descobrir o que poderia ser.

Qual é a maneira correta de fazer esse tipo de consulta?

Solução possível

Eu encontrei uma maneira de fazer isso. Não tenho reputação suficiente para responder à minha própria pergunta, por isso estou fazendo a atualização aqui. Não tenho certeza se está sempre correto ou como é a eficiência. Quaisquer comentários são bem-vindos.

Eu usei uma instrução de seleção composta, em que uma consulta encontra o valor mínimo de num para cada valor exclusivo do texto:

sqlite> select num, text from t group by text having num = min( num );
num         text      
----------  ----------
0           a         
1           b         

Então juntei isso à tabela completa para obter todas as linhas correspondentes a essas duas colunas.

sqlite> with u as
      ( select num, text from t group by text having num = min( num ) )
        select t.* from t join u on t.num = u.num and t.text = u.text;
num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           b           4         
user35292
fonte

Respostas:

10

Como você viu, um simples GROUP BY não funcionará porque retornaria apenas um registro por grupo.

Sua associação funciona bem. Para uma tabela grande, ela será eficiente apenas se houver um índice nas colunas de junção ( nume text).

Como alternativa, você pode usar uma subconsulta correlacionada:

SELECT *
FROM t
WHERE num = (SELECT MIN(num)
             FROM t AS t2
             WHERE t2.text = t.text);

SQLFiddle

Ao ser executada, essa consulta não requer uma tabela temporária (sua consulta exige o resultado de u), mas executará a subconsulta para cada registro t, portanto, textdeve ser indexada. (Ou use um índice nos dois texte numobtenha um índice de cobertura .)

CL.
fonte
ele não possui nenhuma tabela temporária em sua consulta, apenas uma CTE, que é bem diferente.
precisa saber é o seguinte
Quando executada, o resultado da uconsulta é armazenado em uma tabela temporária, independentemente de ser gravada como uma CTE, uma exibição ou embutida como uma subconsulta.
CL.
Obrigado, esta versão é muito mais fácil de escrever do que a que encontrei. Conhecer a terminologia correta também é útil para eu analisar mais isso.
user35292
@CL É assim que o SQLite executa consultas com CTEs? Você tem uma referência para isso? Porque outros DBMS não usam necessariamente tabelas temporárias para ctes.
precisa saber é o seguinte
As CTEs, visualizações e subconsultas do @ypercube são niveladas ou implementadas como corotinas, se possível. Mas um GROUP BY em uma coluna não indexada deve poder coletar os dados para todos os grupos em paralelo, portanto, requer alguma forma de tabela temporária (em todos os bancos de dados).
CL.
1

Eu costumo fazer esse tipo de coisa com uma junção externa:

SELECT
    M1.Num,
    M1.Text,
    M1.Num2
FROM
    MyDb M1
LEFT OUTER JOIN
    MyDB M2
ON
    M1.text = M2.text
AND
    M1.num > m2.num
WHERE
    M2.num is null

Isso é basicamente dizer; me dê todos os registros que não têm um valor maior, isto é, nulo.

BEIJO
fonte
1

Então, como você pode encontrar a resposta para sua pergunta da próxima vez? Na minha opinião, é decompondo e seguindo a lógica. E você acertou:

Eu quero fazer a seleção com base no valor mínimo de num para cada valor exclusivo da coluna de texto

Isso se traduz em:

select text, min(num) from t group by text;

(Isso deve ser equivalente à sua havingconsulta. Pode ser interessante dar uma olhada nas linhas onde numé igual a NULL. O mais preciso: veja qual é o efeito das linhas com nulos, que você pode filtrar primeiro com a where num is not null)

A partir daqui, você pode alcançar o resultado desejado:

select * from t where (num, text) in ( *insert query above* )

Ou usando uma junção:

select t1.* from t t1,
    (select text, min(num) as n from t group by text) t2
where t1.num = t2.n and t1.text = t2.text.

E quando o desempenho não for suficiente para suas tabelas, comece a analisar declarações mais complexas.

Grimaldi
fonte
-2

Essa consulta não deve ser exatamente o que você precisa?

select min(num), text, num2 group by text, num2
Jens W.
fonte
Isso retornará todos os quatro registros, porque os num2valores são únicos.
CL.