Tenho a seguinte tabela:
create table test (
company_id integer not null,
client_id integer not null,
client_status text,
unique (company_id, client_id)
);
insert into test values
(1, 1, 'y'), -- company1
(2, 2, null), -- company2
(3, 3, 'n'), -- company3
(4, 4, 'y'), -- company4
(4, 5, 'n'),
(5, 6, null), -- company5
(5, 7, 'n')
;
Basicamente são 5 empresas diferentes, cada uma tem um ou mais clientes e cada cliente tem status: 'y' ou 'n' (pode ser nulo também).
O que tenho que fazer é selecionar todos os pares (company_id, client_id)
para todas as empresas para as quais há pelo menos um cliente cujo status não seja 'n' ('y' ou nulo). Portanto, para os dados de exemplo acima, a saída deve ser:
company_id;client_id
1;1
2;2
4;4
4;5
5;6
5;7
Eu tentei algo com funções de janela, mas não consigo descobrir como comparar o número de TODOS os clientes com o número de clientes com STATUS = 'n'
.
select company_id,
count(*) over (partition by company_id) as all_clients_count
from test
-- where all_clients_count != ... ?
Eu descobri como fazer isso, mas não tenho certeza se é o caminho certo:
select sub.company_id, unnest(sub.client_ids)
from (
select company_id, array_agg(client_id) as client_ids
from test
group by company_id
having count(*) != count( (case when client_status = 'n' then 1 else null end) )
) sub
Basicamente você está procurando a expressão:
A coluna
client_status
deve ser realmente tipo de dadosboolean
, nãotext
, o que permitiria a expressão mais simples:O manual tem detalhes no capítulo Operadores de Comparação .
Supondo que sua tabela real tenha uma restrição
UNIQUE
ouPK
, chegamos a:Consultas
Todos eles fazem o mesmo (o que você pediu), que é o mais rápido depende da distribuição de dados:
Ou:
Ou:
Valores booleanos sort
FALSE
->TRUE
->NULL
em ordem crescente. EntãoFALSE
vem por último em ordem decrescente. Se houver algum outro valor disponível, esse será escolhido primeiro ...O PK adicionado é implementado com um índice útil para essas consultas. Se você quiser mais rápido, adicione um índice parcial para a consulta 1:
Você também pode usar funções de janela, mas isso seria mais lento. Exemplo com
first_value()
:Para muitas linhas por
company_id
, uma dessas técnicas pode ser mais rápida, ainda:Acho que isso pode ser um pouco simplificado:
Posso ter entendido errado, mas imagino algo como:
vai funcionar. coalesce é usado para mapear null para 'y', mas qualquer coisa diferente de 'n' deve fazer
Usar uma função OLAP pode nos salvar um "join":
Aqui mapeamos null -> 'y' e 'n' -> null. Como count(x) contará linhas onde x não é nulo, contamos linhas onde client_status <> 'n'. Usei uma função OLAP para evitar GROUP BY, o que significa que só precisamos referenciar a tabela uma vez.
Uma consulta SQL padrão abaixo deve funcionar