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 / dba / Perguntas / 291632
Accepted
CervEd
CervEd
Asked: 2021-05-16 13:47:46 +0800 CST2021-05-16 13:47:46 +0800 CST 2021-05-16 13:47:46 +0800 CST

Junção de atualização do PostgreSQL vs junção de atualização do SQL Server

  • 772

Recentemente, comecei a converter um projeto pessoal do Microsoft SQL Server para o PostgreSQL e fiquei surpreso com o desempenho abismal que encontrei fazendo um UPDATE JOINentre duas tabelas.

Suponha que eles se pareçam com:

CREATE TABLE foo (
  id INTEGER NOT NULL PRIMARY KEY,
  bar INTEGER NULL
);

CREATE TABLE foo2 (
  id INTEGER NOT NULL PRIMARY KEY,
  bar INTEGER NULL
);

No T-SQL eu faria uma atualização usando um join usando algo assim:

UPDATE foo
SET bar = t2.bar
FROM foo t1
JOIN foo2 t2
ON t1.id = t2.id;

Mas rodando no Postgres, a consulta é glacialmente lenta.

Se eu mudar para:

UPDATE foo
SET bar = t2.bar
FROM foo2 t2
WHERE foo.id = t2.id;

Isso não é um problema.

Eu entendo que a sintaxe é diferente, mas eu esperava que o otimizador de consulta resolvesse algo no mesmo estádio. Em vez disso, as coisas enlouquecem. Além das diferenças sintáticas, há uma diferença sutil entre as duas consultas que não consigo ver?

Explique os planos

Update on foo  (cost=85852.43..6211995294.24 rows=338326628280 width=1027)
  ->  Nested Loop  (cost=85852.43..6211995294.24 rows=338326628280 width=1027)
        ->  Seq Scan on foo  (cost=0.00..145721.10 rows=582410 width=1010)
        ->  Materialize  (cost=85852.43..247935.91 rows=580908 width=17)
              ->  Hash Join  (cost=85852.43..241627.37 rows=580908 width=17)
                    Hash Cond: (t1.id = t2.id)
                    ->  Seq Scan on foo t1  (cost=0.00..145721.10 rows=582410 width=10)
                    ->  Hash  (cost=75754.08..75754.08 rows=580908 width=15)
                          ->  Seq Scan on foo2 t2  (cost=0.00..75754.08 rows=580908 width=15)
Update on foo (cost=87575.47..535974.25 rows=581621 width=1022)
  ->  Hash Join  (cost=87575.47..535974.25 rows=581621 width=1022)
        Hash Cond: (foo.id = t2.id)
        ->  Seq Scan on foo (cost=0.00..151301.17 rows=1140417 width=1011)
        ->  Hash  (cost=75761.21..75761.21 rows=581621 width=36)
              ->  Seq Scan on foo2 t2  (cost=0.00..75761.21 rows=581621 width=36)
sql-server postgresql
  • 2 2 respostas
  • 4183 Views

2 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2021-05-16T14:02:02+08:002021-05-16T14:02:02+08:00

    Mas rodando no Postgres, a consulta é glacialmente lenta.

    UPDATE foo
    SET    bar = t2.bar
    FROM   foo t1
    JOIN   foo2 t2 ON t1.id = t2.id;
    

    Não há condição de junção entre fooe t1, o implícito CROSS JOINforça um produto cartesiano, ou seja O(N²)(!) operações de atualização em vez de apenas O(N). E o resultado é um absurdo não determinista. O efeito também se torna aparente no plano de consulta: rows=338326628280em vez de rows=581621(Além disso: ambos os planos foram produzidos a partir de tabelas ligeiramente diferentes, mas isso parece irrelevante para a pergunta.)

    Pode ser corrigido adicionando uma condição de junção como:

    UPDATE foo
    SET    bar = t2.bar
    FROM   foo t1
    JOIN   foo2 t2 ON t1.id = t2.id
    WHERE  foo.id = t1.id;  -- !
    

    Bem, tecnicamente, uma WHEREcondição, mas tudo a mesma coisa.

    Mas isso é apenas colocar batom em um porco. Enquanto idé a coluna PK de cada tabela, isso é apenas adicionar ruído. Use o comando que você já encontrou:

    UPDATE foo
    SET    bar = t2.bar
    FROM   foo2 t2
    WHERE  foo.id = t2.id;
    

    O manual aconselha para a FROMcláusula de UPDATE:

    Não repita a tabela de destino como a, a from_itemmenos que você pretenda uma autojunção (nesse caso, ela deve aparecer com um alias no arquivo from_item).

    E:

    Quando uma FROMcláusula está presente, o que essencialmente acontece é que a tabela de destino é unida às tabelas mencionadas na from_itemlista e cada linha de saída da junção representa uma operação de atualização para a tabela de destino. Ao usar FROMvocê deve garantir que a junção produza no máximo uma linha de saída para cada linha a ser modificada. Em outras palavras, uma linha de destino não deve se unir a mais de uma linha da(s) outra(s) tabela(s). Se isso acontecer, apenas uma das linhas de junção será usada para atualizar a linha de destino, mas qual delas será usada não é previsível.

    Essa auto-junção faz sentido (ou é mesmo necessária!) se você precisar LEFT [OUTER] JOINde uma(s) tabela(s) adicional(is). Infelizmente, não há nenhuma disposição no SQL para dizer "FROM LEFT"em um arquivo UPDATE. Exemplo:

    • Anular coluna na atualização se a subconsulta retornar vazia
    • 15
  2. user229856
    2021-05-17T01:58:21+08:002021-05-17T01:58:21+08:00

    Quero abordar parte da questão de uma perspectiva do SQL Server.

    No T-SQL eu faria uma atualização usando um join usando algo assim:

    UPDATE foo
    SET bar = t2.bar
    FROM foo t1
    JOIN foo2 t2
    ON t1.id = t2.id;
    

    Talvez algo assim, mas provavelmente não exatamente isso. É muito fácil acabar com uma junção cruzada acidental . A documentação do SQL Server realmente não fala sobre isso, mas deveria , porque isso atrapalha as pessoas o tempo todo.

    A algebrização de UPDATE...FROMpode ser peculiar (geralmente para compatibilidade com versões anteriores), por isso é importante ser explícito e seguir as melhores práticas. Isso significa:

    1. Atribuindo um alias
    2. Atualizando esse alias ; e
    3. Garantir que a atualização seja determinística , ou seja, cada linha de destino é atualizada a partir de no máximo uma linha de origem.

    Extratos da mesma página de documentação:

    Se o objeto que está sendo atualizado for o mesmo que o objeto na cláusula FROM e houver apenas uma referência ao objeto na cláusula FROM, um alias de objeto pode ou não ser especificado. Se o objeto que está sendo atualizado aparecer mais de uma vez na cláusula FROM, uma e apenas uma referência ao objeto não deve especificar um alias de tabela. Todas as outras referências ao objeto na cláusula FROM devem incluir um alias de objeto.

    Tenha cuidado ao especificar a cláusula FROM para fornecer os critérios para a operação de atualização. Os resultados de uma instrução UPDATE são indefinidos se a instrução incluir uma cláusula FROM não especificada de forma que apenas um valor esteja disponível para cada ocorrência de coluna atualizada, ou seja, se a instrução UPDATE não for determinística.

    e, revelando alguns dos problemas de algebrização:

    Quando uma expressão de tabela comum (CTE) é o destino de uma instrução UPDATE, todas as referências ao CTE na instrução devem corresponder. Por exemplo, se um alias for atribuído ao CTE na cláusula FROM, o alias deverá ser usado para todas as outras referências ao CTE. Referências CTE não ambíguas são necessárias porque uma CTE não tem uma ID de objeto, que o SQL Server usa para reconhecer a relação implícita entre um objeto e seu alias. Sem esse relacionamento, o plano de consulta pode produzir um comportamento de junção inesperado e resultados de consulta não intencionais.


    Então o exemplo T-SQL seria:

    CREATE TABLE dbo.foo (
      id INTEGER NOT NULL PRIMARY KEY,
      bar INTEGER NULL
    );
    
    CREATE TABLE dbo.foo2 (
      id INTEGER NOT NULL PRIMARY KEY,
      bar INTEGER NULL
    );
    
    -- Update the alias
    UPDATE F1
    SET F1.bar = F2.bar
    FROM dbo.foo AS F1
    JOIN dbo.foo2 AS F2
        ON F2.id = F1.id -- deterministic;
    

    Uma maneira de verificar se uma atualização é determinística no SQL Server é primeiro escrevê-la como um MERGE, que verifica atualizações não determinísticas em tempo de execução. Você não escreveria rotineiramente uma atualização simples como uma mesclagem por motivos de desempenho (e talvez porque a mesclagem tenha alguns problemas próprios).

    • 3

relate perguntas

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Como determinar se um Índice é necessário ou necessário

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

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

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

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