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 / 340542
Accepted
Adrian B.
Adrian B.
Asked: 2024-06-27 05:05:25 +0800 CST2024-06-27 05:05:25 +0800 CST 2024-06-27 05:05:25 +0800 CST

Bloqueio para atualização dentro dos CTEs

  • 772

Se eu fizer uma consulta usando CTEs parecida com esta:

WITH cte_a AS (
    SELECT a.id, a.something
    FROM a
    WHERE a.something IS NOT NULL 
),
-- [...] some other CTEs
cte_d AS (
    SELECT 
        cte_a.id,
        cte_a.something
    FROM cte_a
    JOIN 
        -- something
    JOIN
        -- something
    WHERE 
        -- something
    ORDER BY cte_a.id ASC
    FOR UPDATE  -- Here, will the `FOR UPDATE` locks `a` rows?
),
-- rest of the query, which will update `a.something`.

O bloqueio para atualização será aplicado nas linhas da tabela aou o bloqueio será aplicado em uma tabela materializada gerada por cte_a?

Se o bloqueio se aplicar à tabela materializada, o seguinte resolveria o problema?

WITH cte_a AS NOT MATERIALIZED (
-- Rest of the query

Estou usando o PostgreSQL v14.

Se isso mudar alguma coisa, estou mais interessado no comportamento com o nível de isolamento READ COMMITTED padrão.

postgresql
  • 1 1 respostas
  • 104 Views

1 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2024-06-27T11:18:00+08:002024-06-27T11:18:00+08:00

    A base da minha resposta é fornecida no manual:

    Se tabelas específicas forem nomeadas em uma cláusula de bloqueio, somente as linhas provenientes dessas tabelas serão bloqueadas; quaisquer outras tabelas usadas SELECTsão simplesmente lidas normalmente. Uma cláusula de bloqueio sem uma lista de tabelas afeta todas as tabelas usadas na instrução. [...]
    No entanto, essas cláusulas não se aplicam a WITHconsultas referenciadas pela consulta primária. Se desejar que o bloqueio de linha ocorra em uma WITHconsulta, especifique uma cláusula de bloqueio na WITHconsulta.

    Então:

    • Para bloquear apenas linhas de tabelas específicas na FROMcláusula, liste seus nomes na cláusula de bloqueio. Caso contrário, todas as linhas qualificadas de todas as tabelas da FROMlista serão bloqueadas.

      • Mas você só pode listar nomes de tabelas reais, não nomes de CTE, portanto, para começar, não é possível direcionar CTEs (o que não faria sentido de qualquer maneira, porque a cláusula de bloqueio não se propaga para CTEs, conforme discutido abaixo).
    • Sem restrição de tabela na cláusula de bloqueio, todas as linhas selecionadas de todas as tabelas na FROMcláusula serão bloqueadas.

    • Uma cláusula de bloqueio na consulta primária não é propagada para CTEs.

    Isso ainda não esclarece completamente o seu caso complexo :

    1. SELECTda tabela aem cte_a.
    2. Então você cte_dtem cte_aem sua FROMcláusula, além de algumas outras tabelas, além de mais filtros.
      • Lá você adiciona a FOR UPDATEcláusula de bloqueio sem nomear tabelas específicas.
    3. Finalmente, UPDATE ana consulta primária.

    Por experiência e alguns testes superficiais rápidos adicionais:

    • A cláusula de bloqueio não se aplica a WITHconsultas na FROMlista em nenhum caso. Não apenas na consulta primária, mas também em outros CTEs.

    • Além do mais, adicionar uma cláusula de bloqueio evita o "inlining" do mesmo CTE (otimização automática ou explicitamente exigida no NOT MATERIALIZEDPostgres 12+). Portanto, todas as linhas qualificadas neste CTE específico são bloqueadas, filtros adicionais em outros CTEs ("posteriores") ou na consulta primária não restringem o conjunto.

    Conclusão

    Para bloquear linhas de table a, você deve adicionar a cláusula de bloqueio em uma consulta onde essa tabela esteja listada na FROMcláusula. Sua consulta poderia ser assim:

    WITH cte_a AS (
       SELECT a.id, a.something
       FROM   a
       WHERE  a.something IS NOT NULL 
       ORDER  BY a.id  -- possibly useful to lock rows in particular order (?)
       FOR    UPDATE   -- HERE !!!
       )
    --   ... other CTEs
    , cte_d AS (
       SELECT a.id, a.something
       FROM   cte_a a
       JOIN   some_table
    -- WHERE  something
    -- ORDER BY a.id  -- still needed (?)
    -- FOR UPDATE     -- still needed for other tables (?)
       )
    UPDATE a
    SET    something = null
    FROM   cte_d d
    WHERE  a.id = d.id;
    

    Porém , isso pode bloquear mais linhas do que o necessário. Tente aplicar todos os filtros antes da cláusula de bloqueio. (Nas mesmas SELECTcontagens.)
    Eu provavelmente reescreveria toda a consulta.

    Como testar?

    Configuração mínima:

    -- drop table a;
    CREATE TABLE a (id int PRIMARY KEY, something int);
    INSERT INTO a VALUES
      (1, 1)
    , (2, NULL)
    , (3, 3) -- not in table b !
    ;
    
    -- drop table b;
    CREATE TABLE b (id int PRIMARY KEY, something int);
    INSERT INTO b VALUES
      (1, 1)
    , (2, NULL)
    ;
    

    Em seguida, inicie uma transação em uma sessão e deixe-a aberta:

    BEGIN;
    
    -- EXPLAIN  -- also interesting!
    WITH cte_a AS (  -- row 1 & 3
    -- WITH cte_a AS NOT MATERIALIZED (  -- ?
       SELECT *
       FROM   a
       WHERE  something IS NOT NULL
    -- FOR    UPDATE  -- Here?
       )
    -- more CTEs
    , cte_d AS (  -- only row 1
       SELECT a.id, a.something
       FROM   cte_a a
       JOIN   b USING (id)  -- eliminates row 3
    -- ORDER  BY a.id
    -- FOR    UPDATE  -- Here?
       )
    TABLE cte_d;
    

    Em seguida, tente em outra sessão (conexão separada/janela de consulta separada):

    BEGIN;
    -- UPDATE b SET something = 0 WHERE id = 1;
    UPDATE a SET something = 0 WHERE id = 1;  -- 3?
    

    Isso ocorrerá ou será interrompido por um bloqueio de linha (nesse caso, você aborta).

    Em cada sessão, execute ROLLBACK;antes do próximo teste.

    Varie para ver os efeitos de cada constelação.

    • 5

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

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

  • Sequências Biológicas do UniProt no PostgreSQL

  • 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