atualizando linhas da tabela no postgres usando subconsulta

304

Usando o postgres 8.4, meu objetivo é atualizar a tabela existente:

CREATE TABLE public.dummy
(
  address_id SERIAL,
  addr1 character(40),
  addr2 character(40),
  city character(25),
  state character(2),
  zip character(5),
  customer boolean,
  supplier boolean,
  partner boolean

)
WITH (
  OIDS=FALSE
);

Inicialmente, testei minha consulta usando a instrução insert:

insert into address customer,supplier,partner
SELECT  
    case when cust.addr1 is not null then TRUE else FALSE end customer, 
    case when suppl.addr1 is not null then TRUE else FALSE end supplier,
    case when partn.addr1 is not null then TRUE else FALSE end partner
from (
    SELECT *
        from address) pa
    left outer join cust_original cust
        on (pa.addr1=cust.addr1 and pa.addr2=cust.addr2 and pa.city=cust.city 
            and pa.state=cust.state and substring(cust.zip,1,5) = pa.zip  )
    left outer join supp_original suppl 
        on (pa.addr1=suppl.addr1 and pa.addr2=suppl.addr2 and pa.city=suppl.city 
                and pa.state=suppl.state and pa.zip = substring(suppl.zip,1,5))
    left outer join partner_original partn
        on (pa.addr1=partn.addr1 and pa.addr2=partn.addr2 and pa.city=partn.city
                  and pa.state=partn.state and pa.zip = substring(partn.zip,1,5) )
where pa.address_id = address_id

sendo novato, estou falhando em converter para atualizar a instrução, atualizando as linhas existentes com valores retornados pela instrução select. Qualquer ajuda é muito apreciada.

empilhamento
fonte
você tem algum tipo de identificação na tabela de endereços que possa ser usada para determinar se a linha existe?
Andrey Adamovich
sim eu faço, mas seu sistema gerado.
stackover

Respostas:

683

O Postgres permite:

UPDATE dummy
SET customer=subquery.customer,
    address=subquery.address,
    partn=subquery.partn
FROM (SELECT address_id, customer, address, partn
      FROM  /* big hairy SQL */ ...) AS subquery
WHERE dummy.address_id=subquery.address_id;

Essa sintaxe não é o SQL padrão, mas é muito mais conveniente para esse tipo de consulta do que o SQL padrão. Acredito que a Oracle (pelo menos) aceite algo semelhante.

Andrew Lazarus
fonte
parece que estou tentando algo diferente, por exemplo. se houver três colunas booleanas c1, c2, c3, todas definidas como false inicialmente. mas com base na subconsulta são definidos como true. atualize o conjunto c1 = TRUE em que id (subconsulta1), defina c2 = TRUE em que id (subconsulta2), defina c3 = True em que id (subconsulta3). Fui bem-sucedido quando divido isso como 3 atualizações, mas não sei como obter o resultado com uma única atualização. espero que isso faça sentido.
stackover
3
FWIW, a Oracle aceita essa construção básica; no entanto, o desempenho da atualização tende a se deteriorar bastante à medida que as tabelas aumentam. Tudo bem, pois o Oracle também suporta a instrução MERGE.
gsiems
3
Isso totalmente não funciona no postgresql 9.5, eu receboERROR: 42P01: relation "dummy" does not exist
user9645 14/16
73
dummydeve ser substituído pelo nome da tabela que você está tentando atualizar. Por favor, entenda a pergunta e a resposta antes de tentar aplicar.
Andrew Lazarus
1
Vale ressaltar que, no início da consulta, não é necessário especificar o caminho para a coluna do lado esquerdo, apenas no final, caso contrário, o banco de dados se queixará de ERRO: a referência da coluna "address_id" é ambígua
OJVM 19/04
126

Você está atrás da UPDATE FROMsintaxe.

UPDATE 
  table T1  
SET 
  column1 = T2.column1 
FROM 
  table T2 
  INNER JOIN table T3 USING (column2) 
WHERE 
  T1.column2 = T2.column2;

Referências

Brian Webster
fonte
2
Deve ser a resposta escolhida
Joshua Kifer
51

Se não houver ganhos de desempenho usando uma associação, prefiro CTEs (Common Table Expressions) para facilitar a leitura:

WITH subquery AS (
    SELECT address_id, customer, address, partn
    FROM  /* big hairy SQL */ ...
)
UPDATE dummy
SET customer = subquery.customer,
    address  = subquery.address,
    partn    = subquery.partn
FROM subquery
WHERE dummy.address_id = subquery.address_id;

IMHO um pouco mais moderno.

steevee
fonte
1
A sintaxe não é compatível com versões mais antigas do Postgres, anteriores à v9.1, (consulte postgresql.org/docs/9.1/static/sql-update.html e as versões anteriores) Estou na v8.2, então você tem colocar a instrução CTE / With inteira entre colchetes após a palavra-chave FROM e ela funcionará.
Spcogg o segundo
9

Existem várias maneiras de atualizar as linhas.

Quando se trata de UPDATElinhas usando subconsultas, você pode usar qualquer uma dessas abordagens.

  1. Abordagem-1 [Usando referência direta da tabela]
UPDATE
  <table1>
SET
  customer=<table2>.customer,
  address=<table2>.address,
  partn=<table2>.partn
FROM
  <table2>
WHERE
  <table1>.address_id=<table2>.address_i;

Explicação: table1é a tabela que queremos atualizar, table2 é a tabela da qual obteremos o valor a ser substituído / atualizado. Estamos usando a FROMcláusula, para buscar os table2dados. WHERE A cláusula ajudará a definir o mapeamento de dados adequado.

  1. Abordagem-2 [Usando subconsultas]
UPDATE
  <table1>
SET
  customer=subquery.customer,
  address=subquery.address,
  partn=subquery.partn
FROM
  (
    SELECT
      address_id, customer, address, partn
    FROM  /* big hairy SQL */ ...
  ) AS subquery
WHERE
  dummy.address_id=subquery.address_id;

Explicação: Aqui estamos usando a subconsulta dentro da FROMcláusula e fornecendo um alias para ela. Para que ele atue como a mesa.

  1. Abordagem-3 [Usando várias tabelas unidas]
UPDATE
  <table1>
SET
  customer=<table2>.customer,
  address=<table2>.address,
  partn=<table2>.partn
FROM
  <table2> as t2
  JOIN <table3> as t3
  ON
    t2.id = t3.id
WHERE
  <table1>.address_id=<table2>.address_i;

Explicação: Às vezes, enfrentamos a situação nessa junção de tabela, é tão importante para obter dados adequados para a atualização. Para fazer isso, o Postgres nos permite juntar várias tabelas dentro da FROMcláusula.

  1. Abordagem-4 [Usando a declaração WITH]

    • 4.1 [Usando consulta simples]
WITH subquery AS (
    SELECT
      address_id,
      customer,
      address,
      partn
    FROM
      <table1>;
)
UPDATE <table-X>
SET customer = subquery.customer,
    address  = subquery.address,
    partn    = subquery.partn
FROM subquery
WHERE <table-X>.address_id = subquery.address_id;
  • 4.2 [Usando consulta com JOIN complexo]
WITH subquery AS (
    SELECT address_id, customer, address, partn
    FROM
      <table1> as t1
    JOIN
      <table2> as t2
    ON
      t1.id = t2.id;
    -- You can build as COMPLEX as this query as per your need.
)
UPDATE <table-X>
SET customer = subquery.customer,
    address  = subquery.address,
    partn    = subquery.partn
FROM subquery
WHERE <table-X>.address_id = subquery.address_id;

Explicação: No Postgres 9.1, este WITHconceito ( ) foi introduzido. Usando isso, podemos fazer consultas complexas e gerar resultados desejados. Aqui estamos usando essa abordagem para atualizar a tabela.

Espero que isso seja útil.

Mayur
fonte
1
update json_source_tabcol as d
set isnullable = a.is_Nullable
from information_schema.columns as a 
where a.table_name =d.table_name 
and a.table_schema = d.table_schema 
and a.column_name = d.column_name;
Pugazendhi Asaimuthu
fonte
1

@ Mayur "4.2 [Usando a consulta com JOIN complexo]" com Common Table Expressions (CTEs) fez o truque para mim.

WITH cte AS (
SELECT e.id, e.postcode
FROM employees e
LEFT JOIN locations lc ON lc.postcode=cte.postcode
WHERE e.id=1
)
UPDATE employee_location SET lat=lc.lat, longitude=lc.longi
FROM cte
WHERE employee_location.id=cte.id;

Espero que isso ajude ...: D

Festus Ngor
fonte