DROP TABLE IF EXISTS A;
CREATE TABLE a (id int);
CREATE or replace FUNCTION insert_and_return(int)
RETURNS int AS $$
BEGIN
INSERT INTO a VALUES ($1);
RETURN $1;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM insert_and_return(10),A AS y;
O código acima tem efeitos colaterais de inserção de dados no SELECT por meio de uma função.
Esperado
inserir_e_retornar | eu ia |
---|---|
10 | 10 |
Eu esperava insert_and_return
inserir uma nova linha quando FROM insert_and_return(10),A
ocorre uma junção cruzada, antes que o SELECT a veja e a retorne no resultado.
Real :
Conjunto de resultados em branco
Pergunta :
Por que não vejo os dados? Tenho que SELECIONAR novamente para vê-los.
Acho que os SELECTs estão sempre vendo 1 linha a menos do que há.
Espero entender a ordem dos eventos nos bastidores, se os dados já estão inseridos, mas de alguma forma ocultos do SELECT, ou se não foram inseridos no momento em que o SELECT é executado.
Perguntas relacionadas
Essa pergunta tem algo a ver com níveis de isolamento ou é irrelevante porque os níveis de isolamento só se aplicam a múltiplas transações?
Por que os dados podem ser gerados dinamicamente com CASE ou padrões semelhantes,
SELECT price, price * 0.9
mas isso não funciona aqui?
Tentei criar separação com CTE, mas vejo o mesmo comportamento em que só vejo dados ao executar a consulta inteira abaixo uma 2ª vez. Isso pode ser explicado pelos mesmos motivos acima, ou o CTE tem suas próprias ressalvas?
WITH inserted_data AS (
INSERT INTO a (id) VALUES (10)
RETURNING id
)
SELECT * FROM inserted_data,A;
Por que outra ajuda falhou
Todas as fontes que encontro online falam sobre leitura/gravação simultânea de múltiplas transações, o que acredito ser irrelevante porque se trata de uma única consulta em uma única transação implícita.
Origem da 1ª consulta
Eu o ajustei deste artigo que foca no balanceamento de carga em vez da minha pergunta: https://www.cybertec-postgresql.com/en/why-select-from-table-is-not-a-read , por favor, indique se minha pergunta é muito extrema para ser relevante na prática
No PostgreSQL, o comportamento que você está observando é devido à sequência em que as instruções são executadas na consulta. Quando você executa:
Execução da função : A função insert_and_return(10) é chamada primeiro, inserindo o valor 10 na tabela A.
Varredura da tabela A : Após executar a função, o PostgreSQL tenta recuperar registros de A (com o alias de y).
No entanto, o isolamento de transação do PostgreSQL impede que a ação de inserção da função seja imediatamente visível para a consulta. Isso ocorre porque o PostgreSQL efetivamente cria o plano de execução para ambas as partes da consulta antes que quaisquer linhas reais sejam inseridas. A linha inserida não está disponível para a mesma consulta devido às regras de visibilidade no PostgreSQL, que separam as modificações de dados das leituras de dados em uma única instrução.
A função insere a linha e também a retorna para sua consulta. Ela é então unida cruzadamente com um conjunto vazio que a consulta vê na tabela
a
, o que anula tudo.EXPLAIN ANALYZE VERBOSE
pode mostrar que a consulta intermediária produzrows=1
e é apenas a final que acaba vazia porque aquela linha foi unida a zero linhas:demo em db<>fiddle
Saída: insert_and_return.insert_and_return, y.id
-> Função Scan em public.insert_and_return (custo=0,25..0,26 linhas=1 largura=4) (tempo real=0,187..0,187 linhas=1 loops=1)
Saída: insert_and_return.insert_and_return
Chamada de função: insert_and_return(10)
-> Seq Scan em public.ay (custo=0,00..35,50 linhas=2550 largura=4) (tempo real=0,006..0,006 linhas=0 loops=1)
Saída: y.id
Não tenho certeza do que e por que você está tentando fazer aqui, mas se você está tentando ver todas as linhas que inseriu, não apenas o valor fornecido para uma coluna, parece que você só deseja
Se você estiver tentando ver o estado final de toda a tabela logo após a inserção, você está procurando por
union
:O
table
comando é apenas uma forma abreviada de aselect*from
no PostgreSQL. É a única forma razoável de envolver a tabelaa
incondicionalmente - caso contrário, se houver algo naquela tabela, uma,
junção cruzada de vírgula atingiria você com quantas linhas você acabou de inserir, vezes quantas você tinha antes, tudo pareado e listado com todo o resto, esse número de vezes.Se você estiver tentando comparar a carga útil recebida com o que efetivamente acaba sendo escrito na tabela após disparar todos
trigger
os s (erule
s , com algumas ressalvas), é exatamente isso quereturning
mostra:Isso significa que se os valores forem processados de alguma forma,
returning
não mostrará o que entrou inicialmente, apenas o que acabou na tabela, entãoreturning *
perde de vista a entrada bruta. Para vê-la, você pode repeti-la como uma constante:Exemplo na demonstração :
Não há realmente nenhuma sequência determinística dessas operações. A varredura de função e a varredura de tabela podem ou não ser executadas em alguma ordem ou outra, ou em paralelo ambas ao mesmo tempo - o planejador/otimizador é livre para reorganizá-las como achar melhor.
Você pode usar subqueries e
lateral
CTEs para definir algum tipo de fluxo dentro de uma única consulta multi-instrução, mas esse fluxo não pode envolver o estado da tabela, ele não pode escrever nela, depois dela, e depois novamente nela. Por causa do MVCC , todas as instruções na sua consulta veem exatamente o mesmo instantâneo dessa tabela durante todo o tempo de vida da consulta.Isso é exatamente correto. O isolamento de transação não tem nada a ver com declarações de uma única consulta.
Funciona aqui, você gerou dados na hora: você especificou uma constante
10
e ela foi escrita na tabela e retornada dela. Só que você decidiu fazercross join
isso com um conjunto vazio, então você obteve um conjunto vazio. Outra ilustração:cross join
Não importa se o conjunto vazio com o qual você está relacionado está relacionado ou não.Não importa. O
cross join A
resultado da antiga sintaxe implícita de junção por vírgula,A
apaga seus resultados deinserted_data
porque você está unindo cruzadamente com um conjunto vazio: durante todo o tempo de vida desta consulta, o instantâneo deA
não reflete nenhuma alteração que a consulta pretende aplicar a ele como consequência.Se você realmente quer tudo lado a lado com tudo, troque isso por uma junção qualificada :
Isso lhe dá tudo o que você acabou de inserir à esquerda, pareado com nulos à direita se for completamente novo ou com qualquer
id
s correspondente que você já tinha na tabelaa
anterior, então lista todo o restoa
da direita. Diferentemente docross join
, ele só multiplica os recém-chegados que encontram mais de umaid
correspondência na tabela de destino.Você também pode listar tudo junto, uma vez, usando o
union
mencionado anteriormente: