AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 78842924
Accepted
peti446
peti446
Asked: 2024-08-07 17:39:39 +0800 CST2024-08-07 17:39:39 +0800 CST 2024-08-07 17:39:39 +0800 CST

Verificando duas listas distintas com operação AND na mesma coluna

  • 772

Tenho o seguinte exemplo de estrutura de dados de cliente que pode fazer parte de vários grupos usando uma tabela de junção e dados:

CREATE TABLE customer(id) AS VALUES (0),(1),(2),(3);
CREATE TABLE groups(id)   AS VALUES (1),(3),(5),(6);
CREATE TABLE customers_to_groups(customer_id, group_id) AS
  VALUES (0, 1)--customer 0 is in group (5 OR 6) AND (1 OR 3)
        ,(0, 5)--customer 0 is in group (5 OR 6) AND (1 OR 3)
        ,(1, 1)
        ,(1, 90)
        ,(2, 1)
        ,(3, 3)--customer 3 is in group (5 OR 6) AND (1 OR 3)
        ,(3, 5)--customer 3 is in group (5 OR 6) AND (1 OR 3)
        ,(3, 90);

Preciso obter clientes que tenham grupos específicos dos quais fazem parte e preciso obter uma lista de todos os clientes que fazem parte de pelo menos um grupo em várias listas de grupos. Por exemplo eu quero pegar todos os clientes que estão no grupo (5 OR 6) AND (1 OR 3), então por exemplo um cliente nos grupos 5 e 1 seria retornado, mas alguém no grupo 1 e 90 ou apenas no grupo 1 não. Com os dados de amostra fornecidos, obteríamos o cliente com os IDs 0 e 3 apenas se eles estivessem em conformidade com as regras fornecidas acima.

Apenas fazer WHERE group_id IN (5,6) AND group_id IN (1,3)não parece funcionar, então estou procurando uma alternativa.

Eu tenho isso até agora que funciona:

SELECT DISTINCT c.id
FROM customer c
INNER JOIN customers_to_groups at1 ON c.id = at1.customer_id
INNER JOIN customers_to_groups at2 ON c.id = at2.customer_id
WHERE at1.group_id IN (5, 6)
  AND at2.group_id IN (1, 3);

Resultados esperados:

eu ia
0
3

Existe uma maneira de fazer isso com mais desempenho?

sql
  • 6 6 respostas
  • 121 Views

6 respostas

  • Voted
  1. Rahul jangid
    2024-08-07T18:40:58+08:002024-08-07T18:40:58+08:00

    Você pode obter o resultado desejado com uma consulta mais otimizada usando uma cláusula GROUP BY e HAVING. Essa abordagem evita a necessidade de múltiplas autojunções

    SELECT customer_id
    FROM customers_to_groups
    WHERE group_id IN (5, 6, 1, 3)
    GROUP BY customer_id
    HAVING COUNT(DISTINCT CASE WHEN group_id IN (5, 6) THEN 1 END) > 0
       AND COUNT(DISTINCT CASE WHEN group_id IN (1, 3) THEN 1 END) > 0;
    

    Consideração sobre índice: para melhorar ainda mais o desempenho, certifique-se de ter um índice nas colunas customer_id e group_id na tabela clients_to_groups:

    CREATE INDEX idx_customer_group ON customers_to_groups (customer_id, group_id);
    
    • 2
  2. Best Answer
    Jonas Metzler
    2024-08-07T18:50:00+08:002024-08-07T18:50:00+08:00

    Podemos GROUP BYidentificar o ID do cliente e usar uma HAVINGcláusula. Lá podemos usar CASEou FILTERse o seu RDBMS suportar. Postgres deveria.

    Lá suas condições serão definidas.

    A consulta será:

    SELECT c.id 
    FROM customer c
    INNER JOIN customers_to_groups ctg
      ON c.id = ctg.customer_id
      AND ctg.group_id IN (1,3,5,6)
    GROUP BY c.id
    HAVING 
      COUNT(CASE WHEN ctg.group_id IN (1,3) THEN 1 END) > 0
      AND COUNT(CASE WHEN ctg.group_id IN (5,6) THEN 1 END) > 0;
    

    ou

    SELECT c.id 
    FROM customer c
    INNER JOIN customers_to_groups ctg
      ON c.id = ctg.customer_id
      AND ctg.group_id IN (1,3,5,6)
    GROUP BY c.id
    HAVING 
      COUNT(*) FILTER(WHERE ctg.group_id IN (1,3)) > 0
      AND COUNT(*) FILTER(WHERE ctg.group_id IN (5,6)) > 0;
    

    Nota: As consultas acima pressupõem que você realmente precisa incluir ambas customersas customers_to_groupstabelas e juntá-las. Caso não precise incluir a customerstabela, basta removê-la e selecionar apenas na tabela customers_to_groupspara melhorar o desempenho:

    SELECT customer_id
    FROM customers_to_groups ctg
    WHERE ctg.group_id IN (1,3,5,6)
    GROUP BY customer_id
    HAVING 
      COUNT(*) FILTER(WHERE ctg.group_id IN (1,3)) > 0
      AND COUNT(*) FILTER(WHERE ctg.group_id IN (5,6)) > 0;
    

    Esta demonstração com seus dados de amostra e muitas linhas adicionais mostra as diferenças de desempenho.

    Usar FILTERor CASEé muito mais rápido (pois evita um segundo JOINna tabela clients_to_groups). A diferença exata de desempenho depende dos dados reais em suas tabelas e de quais índices você usa.

    • 2
  3. Charlieface
    2024-08-07T19:14:12+08:002024-08-07T19:14:12+08:00

    Este é um problema de Divisão Relacional com a diferença de que você tem múltiplos divisores. O que você tem não é ruim em termos de eficiência.

    Mas se você quiser fazer isso dinamicamente e passar diferentes números de grupos e blocos de grupos, precisará de uma solução diferente.

    Você pode passá-lo como um parâmetro de array JSONB

    SELECT
      cg.customer_id
    FROM customers_to_groups cg
    JOIN jsonb_array_elements('[[5,6],[1,3]]'::jsonb) sections(value)
        ON cg.group_id IN (SELECT value::int FROM jsonb_array_elements(sections.value))
    GROUP BY
      cg.customer_id
    HAVING COUNT(*) = MIN(jsonb_array_length('[[5,6],[1,3]]'::jsonb));
    

    Alternativamente, se você tiver os dados de entrada em uma tabela, poderá usar a Divisão Relacional completa

    SELECT
      c.id
    FROM (
        SELECT
          c.id,
          COUNT(cg.group_id) AS count_matches
        FROM customer c
        CROSS JOIN input_groups ig
        LEFT JOIN customers_to_groups cg
            ON cg.group_id = ig.group_id
           AND cg.customer_id = c.id
        GROUP BY
          c.id,
          ig.section_id 
    ) c
    GROUP BY
      c.id
    HAVING COUNT(*) FILTER (WHERE count_matches = 0) = 0;   -- all sections have a match
    

    banco de dados<> violino

    • 1
  4. jarlh
    2024-08-07T20:01:50+08:002024-08-07T20:01:50+08:00

    Faça uma cláusula GROUP BY, use HAVINGpara garantir que pelo menos um de (5, 6) e pelo menos um de (1, 3) estejam lá.

    SELECT customer_id
    FROM customers_to_groups
    WHERE group_id IN (5, 6, 1, 3)
    GROUP BY customer_id
    HAVING MAX(group_id) >= 5
       AND MIN(group_id) <= 3;
    

    Você também pode usar INTERSECT:

    SELECT customer_id FROM customers_to_groups WHERE group_id IN (5, 6)
    INTERSECT
    SELECT customer_id FROM customers_to_groups WHERE group_id IN (1, 3)
    
    • 1
  5. Derek Roberts
    2024-08-07T22:15:26+08:002024-08-07T22:15:26+08:00

    se esta tarefa for repetitiva, você pode colocá-la em uma função

    CREATE OR REPLACE FUNCTION get_customers_by_groups(group_set1 int[], group_set2 int[])
    RETURNS TABLE(customer_id int) AS $f$
        SELECT c.id
        FROM customer c
        INNER JOIN customers_to_groups ctg ON c.id = ctg.customer_id
        GROUP BY c.id
        HAVING COUNT(DISTINCT CASE WHEN ctg.group_id = ANY(group_set1) THEN 1 END) > 0
           AND COUNT(DISTINCT CASE WHEN ctg.group_id = ANY(group_set2) THEN 1 END) > 0;
    $f$ LANGUAGE sql;
    
    • 0
  6. Zegarek
    2024-08-08T01:14:21+08:002024-08-08T01:14:21+08:00

    Como customer_idé provável que seja uma chave estrangeira para customer.idsignificar que ambas as tabelas possuem o mesmo valor, não vejo motivo para alcançá-la customer.

    Uma forma bem padrão SQL de fazer isso é usando INTERSECTjá sugerido pelo @jarlh :

    SELECT customer_id FROM customers_to_groups WHERE group_id=any('{1,3}')
    INTERSECT ALL
    SELECT customer_id FROM customers_to_groups WHERE group_id=any('{5,6}');
    

    ALLa palavra-chave ignora a desduplicação, que é o comportamento padrão de INTERSECT( DISTINCT).

    Você também pode executar uma auto-junção :

    SELECT customer_id 
    FROM customers_to_groups a
    JOIN customers_to_groups b
    USING (customer_id)
    WHERE a.group_id=any('{1,3}')
     AND  b.group_id=any('{5,6}');
    

    E se você gosta da ideia de @Rahul Jangid e Jonas Metzlerhaving , a condição que você procura se simplifica para bool_or():

    SELECT customer_id 
    FROM customers_to_groups
    WHERE group_id=any('{1,3,5,6}')
    GROUP BY customer_id
    HAVING bool_or(group_id=any('{1,3}'))
       AND bool_or(group_id=any('{5,6}'));
    

    Ou você pode usar um EXISTS:

    SELECT customer_id 
    FROM customers_to_groups a
    WHERE group_id=any('{1,3}'
      AND EXISTS (SELECT FROM customers_to_groups b
                  WHERE a.customer_id=b.customer_id
                  AND group_id=any('{5,6}') );
    

    Neste teste db<>fiddle com 19 000clientes com 1 900 000atribuições aleatórias a alguns 19 000grupos, a EXISTSvariante ganha o plano mais rápido com Hash Semi Join .

    Todos estes são melhor atendidos com um índice de cobertura que permite varreduras somente de índice :

    CREATE INDEX ON customers_to_groups (group_id)
      INCLUDE(customer_id);
    

    Se você pretende reutilizá-lo dinamicamente, envolva-o em uma instrução preparada com parâmetros :

    PREPARE customers_in_groups(int[],int[]) AS
    SELECT customer_id 
    FROM customers_to_groups a
    WHERE group_id=any($1)
      AND EXISTS (SELECT FROM customers_to_groups b
                  WHERE a.customer_id=b.customer_id
                  AND group_id=any($2) );
    

    Então

    EXECUTE customers_in_groups('{1,3}','{5,6}');
    
    • 0

relate perguntas

  • Atualizando todas as linhas, exceto uma que tenha os mesmos valores em determinadas colunas

  • Existe uma maneira de inverter apenas os números quando eu retornar uma coluna sql? (hebraico)

  • SQL menor/maior comparação entre booleanos produz resultados inesperados

  • Como atualizar valores na tabela Postgres com base em uma correspondência em uma matriz

  • Como somar colunas no sql server

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve