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 / 153458
Accepted
Andy
Andy
Asked: 2016-10-28 00:23:07 +0800 CST2016-10-28 00:23:07 +0800 CST 2016-10-28 00:23:07 +0800 CST

Função PostgreSQL não executada quando chamada de dentro do CTE

  • 772

Apenas esperando para confirmar minha observação e obter uma explicação sobre por que isso está acontecendo.

Eu tenho uma função definida como:

CREATE OR REPLACE FUNCTION "public"."__post_users_id_coin" ("coins" integer, "userid" integer) RETURNS TABLE (id integer) AS '
UPDATE
users
SET
coin = coin + coins
WHERE
userid = users.id
RETURNING
users.id' LANGUAGE "sql" COST 100 ROWS 1000
VOLATILE
RETURNS NULL ON NULL INPUT
SECURITY INVOKER

Quando eu chamo essa função de uma CTE, ela executa o comando SQL mas não aciona a função, por exemplo:

WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))

SELECT
1 -- Select 1 but update not performed

Por outro lado, se eu chamar a função de um CTE e depois selecionar o resultado do CTE (ou chamar a função diretamente sem CTE) ele executa o comando SQL e aciona a função, por exemplo:

WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))

SELECT
*
FROM
test -- Select result and update performed

ou

SELECT * FROM __post_users_id_coin(10,1)

Como não me importo muito com o resultado da função (só preciso dela para realizar a atualização), existe alguma maneira de fazer isso funcionar sem selecionar o resultado do CTE?

postgresql cte
  • 2 2 respostas
  • 2983 Views

2 respostas

  • Voted
  1. Best Answer
    ypercubeᵀᴹ
    2016-10-28T00:46:41+08:002016-10-28T00:46:41+08:00

    Esse é o tipo de comportamento esperado. CTEs são materializados, mas há uma exceção.

    Se um CTE não for referenciado na consulta pai, ele não será materializado. Você pode tentar isso, por exemplo, e funcionará bem:

    WITH not_executed AS (SELECT 1/0),
         executed AS (SELECT 1)
    SELECT * FROM executed ;
    

    Código copiado de um comentário na postagem do blog de Craig Ringer:
    CTEs do PostgreSQL são cercas de otimização .


    Antes de tentar esta e várias consultas semelhantes, pensei que a exceção era: "quando um CTE não é referenciado na consulta pai ou em outro CTE e não se referencia a outro CTE". Então, se você queria que o CTE fosse executado, mas os resultados não aparecem no resultado da consulta, pensei que isso seria uma solução alternativa (referenciando-o em outro CTE).

    Mas, infelizmente, não funciona como eu esperava:

    WITH test AS
        (SELECT * FROM __post_users_id_coin(10, 1)),
      execute_test AS 
        (TABLE test)
    SELECT 1 ;     -- no, it doesn't do the update
    

    e, portanto, minha "regra de exceção" não está correta. Quando uma CTE é referenciada por outra CTE e nenhuma delas é referenciada pela consulta pai, a situação fica mais complicada e não sei exatamente o que acontece e quando as CTEs são materializadas. Também não consigo encontrar nenhuma referência para esses casos na documentação.


    Não vejo solução melhor do que usar o que você já sugeriu:

    SELECT * FROM __post_users_id_coin(10, 1) ;
    

    ou:

    WITH test AS
        (SELECT * FROM __post_users_id_coin(10, 1))
    SELECT *
    FROM test ;
    

    Se a função atualizar várias linhas e você obtiver muitas linhas (com 1) no resultado, poderá agregar para obter uma única linha:

    SELECT MAX(1) AS result FROM __post_users_id_coin(10, 1) ;
    

    mas eu preferiria ter o resultado da função que faz uma atualização retornado, SELECT *como seu exemplo, então o que quer que chame esta consulta saiba se houve atualizações e quais foram as alterações na tabela.

    • 12
  2. Erwin Brandstetter
    2016-10-28T19:12:49+08:002016-10-28T19:12:49+08:00

    Esse é um comportamento esperado e documentado.

    Tom Lane explica aqui.

    Documentado no manual aqui:

    As instruções de modificação de dadosWITH são executadas exatamente uma vez e sempre até a conclusão , independentemente de a consulta primária ler todas (ou mesmo algumas) de suas saídas. Observe que isso é diferente da regra para SELECTin WITH: conforme declarado na seção anterior, a execução de a SELECTé realizada apenas até onde a consulta primária exigir sua saída .

    Ênfase em negrito minha. "Modificação de dados" são INSERT, UPDATEe DELETEconsultas. (Em oposição a SELECT.). O manual mais uma vez:

    Você pode usar instruções de modificação de dados ( INSERT, UPDATEou DELETE) em WITH.

    função adequada

    CREATE OR REPLACE FUNCTION public.__post_users_id_coin (_coins integer, _userid integer)
      RETURNS TABLE (id integer) AS
    $func$
    UPDATE users u
    SET    coin = u.coin + _coins  -- see below
    WHERE  u.id = _userid
    RETURNING u.id
    $func$ LANGUAGE sql COST 100 ROWS 1000 STRICT;
    

    Abandonei as cláusulas padrão (ruído) e STRICTé o sinônimo curto paraRETURNS NULL ON NULL INPUT .

    Certifique-se de alguma forma que os nomes dos parâmetros não entrem em conflito com os nomes das colunas. Prefixei com _, mas essa é apenas minha preferência pessoal.

    Se coinpuder NULL, sugiro:

    SET    coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
    

    Se users.idfor a chave primária, então RETURNS TABLEnem ROWs 1000faz sentido. Apenas uma única linha pode ser atualizada/retornada. Mas isso é tudo fora do ponto principal.

    chamada adequada

    Não faz sentido usar a RETURNINGcláusula e retornar valores de sua função se você for ignorar os valores retornados na chamada de qualquer maneira. Também não faz sentido decompor as linhas retornadas SELECT * FROM ...se você as ignorar de qualquer maneira.

    Basta retornar uma constante escalar ( RETURNING 1), definir a função como RETURNS int(ou descartar RETURNINGcompletamente e torná-la RETURNS void) e chamá-la comSELECT my_function(...)

    Solução

    Desde que você ...

    realmente não me importo com o resultado

    .. apenas SELECTuma constante do CTE. É garantido que será executado desde que seja referenciado no exterior SELECT(direta ou indiretamente).

    WITH test AS (SELECT __post_users_id_coin(10, 1))
    SELECT 1 FROM test;
    

    Se você realmente tem uma função de retorno definido e ainda não se importa com a saída:

    WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
    SELECT 1 FROM test LIMIT 1;
    

    Não há necessidade de retornar mais de 1 linha. A função ainda é chamada.

    Finalmente, não está claro por que você precisa do CTE para começar. Provavelmente apenas uma prova de conceito.

    Intimamente relacionado:

    • PostgreSQL parece ignorar RAISE EXCEPTION em um CTE

    Resposta relacionada no SO:

    • PostgreSQL: usando chaves estrangeiras, exclua o pai se não for referenciado por nenhum outro filho

    E considere:

    • Vários CTE em uma única consulta
    • 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