Dada uma tabela my_data
como esta:
id | name | surname | age
----+------+---------+------
1 | john | smith | NULL
1 | NULL | smith | 32
1 | NULL | NULL | NULL
1 | john | smith | NULL
1 | john | NULL | 32
CREATE TABLE my_data(id,name,surname,age)
AS ( VALUES
(1::int, 'john', 'smith' ,NULL::int),
(1, NULL, 'smith' ,32),
(1, NULL, NULL ,NULL),
(1, 'john', 'smith' ,NULL),
(1, 'john', NULL ,32)
);
Para o mesmo id
, os valores (se presentes) nas respectivas colunas são sempre os mesmos, então como posso "esmagá-los" para obter:
id | name | surname | age
----+------+---------+------
1 | john | smith | 32
Minha tentativa
A cross join lateral
para cada coluna é minha única ideia até agora, mas duvido que seja boa:
select
distinct column1, c2.value, c3.value, c4.value
from my_data md
cross join lateral (select column2 from my_data where column1 = md.column1 and column2 is not null limit 1) as c2(value)
cross join lateral (select column3 from my_data where column1 = md.column1 and column3 is not null limit 1) as c3(value)
cross join lateral (select column4 from my_data where column1 = md.column1 and column4 is not null limit 1) as c4(value);
Usando
percentile_disc
Eu acho que algo assim seria o mais rápido,
Aqui estamos usando
percentile_disc
um Agregado de Conjunto Ordenado descrito como "percentil discreto: retorna o primeiro valor de entrada cuja posição na ordenação é igual ou superior à fração especificada " . Então,first_value
sobre as linhas que estão sendo agregadas.NULLS LAST
), o primeiro valor não será nulo, e isso é tudo o que importa aqui (porque você disse "para o mesmo id, os valores (se presentes) nas respectivas colunas são sempre os mesmos" )Você também pode fazer isso, eu acho, com
first_value
uma função de janela e, em seguida, extrair disso comDISTINCT ON
.Usando
mode
Se você quer o consenso dos não-valores, podemos fazer isso também. Eu estava assumindo
percentile_disc
que era o que o OP queria. Outra opção é usar uma Função Agregada de Conjunto Ordenadomode()
diferente . É descrito como "retorna o valor de entrada mais frequente (escolhendo arbitrariamente o primeiro se houver vários resultados igualmente frequentes)". Parece,A solução interessante de Evan Carroll recebeu +1 (nunca ouvi falar
percentile_disc
!).No entanto, tenho outra solução possível, que tem a vantagem de ser genérica (para aqueles
RDBMS
s que possuemCTE
s - ou seja, aWITH
cláusula).Ele NÃO requer nenhuma funcionalidade específica especial do PostgreSQL - além do
LIMIT
- que possui palavras-chave correspondentes em outros servidores.(Reescrita completa!)
Eu fiz o seguinte:
Criei minha tabela e dados:
E então executei o seguinte:
com o resultado (desejado) (correto para os dados):
Esta solução fornece a solução correta mesmo com valores discrepantes - (veja edições anteriores deste post para o problema!) - depende da maioria das respostas estar correta. O uso da
UPPER()
função também eliminaria quaisquer problemas de capitalização.Claro, uma solução muito melhor seria fazer a depuração dos dados antes que eles cheguem perto do HDD - colocar
NOT NULL
declarações, por exemplo, seria um bom começo!