Pergunta

Eu estou tentando adicionar recursos a um aplicativo preexistente e me deparei com uma visão MySQL algo como isto:

SELECT
     AVG(table_name.col1),
     AVG(table_name.col2),
     AVG(table_name.col3),
     table_name.personID,
     table_name.col4
FROM table_name
GROUP BY table_name.personID;

OK por isso há algumas funções agregadas. Você pode selecionar personId porque você está agrupando por ele. Mas também é a selecção de uma coluna que não está presente em uma função de agregação e não é uma parte do GROUP BY. Como isso é possível??? Será que basta escolher um valor aleatório porque os valores definitivamente não são únicos por grupo?

De onde eu venho (MSSQL Server), isso é um erro. Alguém pode explicar esse comportamento para mim e por isso que é permitido no MySQL?

Foi útil?

Solução

É verdade que este recurso permite que algumas consultas ambíguas, e silenciosamente retorna um conjunto de resultados com um valor arbitrário escolhido a partir dessa coluna. Na prática, ela tende a ser o valor da linha dentro do grupo que estão fisicamente armazenados em primeiro lugar.

Estas consultas não são ambíguas se você só escolher colunas que são funcionalmente dependente da coluna (s) no grupo por critérios. Em outras palavras, se não pode haver apenas um valor distinto da coluna "ambígua" por valor que define o grupo, não há nenhum problema. Essa consulta seria ilegal em Microsoft SQL Server (e ANSI SQL), mesmo que ele não pode logicamente resultar em ambigüidade:

SELECT AVG(table1.col1), table1.personID, persons.col4
FROM table1 JOIN persons ON (table1.personID = persons.id)
GROUP BY table1.personID;

Além disso, MySQL tem um modo SQL para torná-lo comportar acordo com o padrão: ONLY_FULL_GROUP_BY

FWIW, SQLite também permite que estes GROUP ambígua por cláusulas, mas escolhe o valor do última linha do grupo.


Pelo menos na versão que eu testei. O que significa ser arbitrária é que MySQL ou SQLite poderia mudar a sua implementação no futuro, e ter um comportamento diferente. Você não deve, portanto, contar com o comportamento ficando eles jeito que está atualmente em casos ambíguos como este. É melhor para reescrever suas consultas para ser determinista e não ambígua. É por isso que o MySQL 5.7 agora permite que ONLY_FULL_GROUP_BY por padrão.

Outras dicas

Eu deveria ter Googled para apenas um pouco mais ... Parece que eu encontrei minha resposta .

MySQL estende o uso de GROUP BY assim que você pode usar colunas não-agregadas ou cálculos na lista SELECT que não aparecem no GROUP BY cláusula. Você pode usar esse recurso para obter um melhor desempenho, evitando a classificação de coluna e desnecessário agrupamento. Por exemplo, você não precisa o grupo em customer.name no seguinte consulta

No padrão SQL, você teria que adicionar customer.name para a cláusula GROUP BY. No MySQL, o nome é redundante.

Ainda assim, que apenas parece ... errado.

select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)

Vamos dizer que você tem uma consulta como esta:

SELECT g, v 
FROM t
GROUP BY g;

Neste caso, para cada valor possível para g, picaretas mysql um dos valores correspondentes da v.

No entanto, qual é escolhido, depende de algumas circunstâncias.

Eu li em algum lugar que, para cada grupo de g, o primeiro valor de v é mantido, na ordem como os registros foram inseridos no t mesa.

Isso é muito feio, porque os registros em uma tabela deve ser tratado como um set , onde a ordem dos elementos não importa. Isto é assim "mysql-ish" ...

Se você quiser determinar qual valor para v para manter, você precisa aplicar um subselect para t assim:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        ORDER BY g, v DESC
) q
GROUP BY g;

Desta forma, você definir que ordem os registros da subconsulta são processados ??pela consulta externa, assim você pode confiar que o valor de v ele vai pegar para os valores individuais de g.

No entanto, se você precisa de algumas condições em que, em seguida, ter muito cuidado. Se você adicionar a condição WHERE da subconsulta em seguida, ele irá manter o comportamento, ele sempre retornará o valor que você espera:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
GROUP BY g;

Isto é o que você espera, os filtros de subseleção e ordens da mesa. Ele mantém os registros onde g tem o valor dado e os retornos de consulta externos que g eo primeiro valor para v.

No entanto, se você adicionar a mesma condição WHERE da consulta externa, em seguida, obter um resultado não-determinístico:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g;

Surpreendentemente, você pode ter valores diferentes para v ao executar a mesma consulta novamente e novamente que é ... estranho. O comportamento esperado é para obter todos os registros na ordem apropriada da subconsulta, filtrá-los na consulta externa e, em seguida, escolher a mesma que pegou no exemplo anterior. Mas isso não acontece.

Ele escolhe um valor para v aparentemente de forma aleatória. A mesma consulta retornou valores diferentes para v se eu executou mais (~ 20) vezes, mas a distribuição não foi uniforme.

Se em vez de adicionar um exterior onde, você especificar uma condição TENDO assim:

SELECT g, v 
FROM (
    SELECT * 
        FROM t1 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
-- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g
HAVING g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9';

Em seguida, você recebe um comportamento consistente novamente.

CONCLUSÃO: Eu sugeriria não contar com esta técnica em tudo. Se você realmente quer / necessidade de evitar, em seguida, condições WHERE na consulta externa. Usá-lo na consulta interna se você pode ou uma cláusula HAVING na consulta externa.

Eu testei com esses dados:

CREATE TABLE t1 (
    v INT,
    g VARCHAR(36)
);

INSERT INTO t1 VALUES (1, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');
INSERT INTO t1 VALUES (2, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');

no mysql 5.6.41.

Talvez seja apenas um bug que fica / ficou fixo em versões mais recentes, por favor, dar feedback se você tem experiência com as versões mais recentes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top