使用子查询更新postgres中的表行

sta*_*ver 268 sql postgresql subquery sql-update

使用postgres 8.4,我的目标是更新现有表:

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
);
Run Code Online (Sandbox Code Playgroud)

最初我使用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
Run Code Online (Sandbox Code Playgroud)

是新手我没有转换为更新语句即,用select语句返回的值更新现有行.任何帮助都非常感谢.

And*_*rus 612

Postgres允许:

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;
Run Code Online (Sandbox Code Playgroud)

此语法不是标准SQL,但对于此类查询比标准SQL更方便.我相信Oracle(至少)接受类似的东西.

  • `dummy`必须替换为您要更新的表的名称.在申请之前请先理解问答. (59认同)
  • FWIW,Oracle确实接受了这种基本结构,但随着表变大,更新的性能会严重降低.虽然Oracle也支持MERGE语句,但这没关系. (3认同)
  • 这完全不适用于postgresql 9.5,我得到`ERROR:42P01:关系"虚拟"不存在 (2认同)
  • 是的,对此感到抱歉。我的错。没有注意到原来的问题是使用名为“dummy”的表 (2认同)
  • 可能值得一提的是,在查询的开头不需要指定左侧列的路径,只在末尾指定,否则db会报错 ERROR: column reference "address_id" is ambiguous (2认同)

Bri*_*ter 106

你是在UPDATE FROM语法之后.

UPDATE 
  table T1  
SET 
  column1 = T2.column1 
FROM 
  table T2 
  INNER JOIN table T3 USING (column2) 
WHERE 
  T1.column2 = T2.column2;
Run Code Online (Sandbox Code Playgroud)

参考


ste*_*vee 42

如果使用连接没有性能提升,那么我更喜欢通用表表达式(CTE)以提高可读性:

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;
Run Code Online (Sandbox Code Playgroud)

恕我直言更现代.

  • 不想从我在 Stack Exchange 上评价最高的答案中拿走任何东西,这种语法(在我编写答案时还不存在)更容易阅读。 (5认同)
  • 执行超过 30k 行的复杂更新,WITH 语法花了 32 秒,FROM (SELECT ... ) 花了 1 分 5 秒 (4认同)
  • 该语法与 v9.1 之前的旧版本 Postgres 不兼容(请参阅 https://www.postgresql.org/docs/9.1/static/sql-update.html 和以前的版本)我使用的是 v8 .2,因此您必须将整个 CTE/With 语句放在 FROM 关键字后面的括号内,它才会起作用。 (3认同)
  • 这就是我到处寻找的。谢谢! (2认同)

May*_*yur 29

有很多方法可以更新行。

当涉及UPDATE使用子查询的行时,您可以使用这些方法中的任何一种。

  1. 方法 1 [使用直接表引用]
UPDATE
  <table1>
SET
  customer=<table2>.customer,
  address=<table2>.address,
  partn=<table2>.partn
FROM
  <table2>
WHERE
  <table1>.address_id=<table2>.address_i;
Run Code Online (Sandbox Code Playgroud)

说明:table1是我们要更新table2 的表,是我们将要从中获取要替换/更新的值的表。我们正在使用FROM子句来获取table2的数据。WHERE 子句将有助于设置正确的数据映射。

  1. 方法 2 [使用子查询]
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;
Run Code Online (Sandbox Code Playgroud)

说明:这里我们在FROM子句中使用子查询,并给它一个别名。这样它就会像桌子一样工作。

  1. 方法 3 [使用多个连接表]
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;
Run Code Online (Sandbox Code Playgroud)

解释:有时我们会遇到这样的情况,表连接对于获取正确的更新数据非常重要。为此,Postgres 允许我们在FROM子句中加入多个表。

  1. 方法 4 [使用 WITH 语句]

    • 4.1 【使用简单查询】
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;
Run Code Online (Sandbox Code Playgroud)
  • 4.2 【使用复杂JOIN查询】
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;
Run Code Online (Sandbox Code Playgroud)

说明:从 Postgres 9.1 开始,WITH引入了this( ) 的概念。使用它我们可以进行任何复杂的查询并生成所需的结果。这里我们使用这种方法来更新表。

我希望,这会有所帮助。

  • 很好地表达了@Mayur,感谢您花时间将所有答案汇总在一起。我个人选择第 4 个,因为对我来说这是最易读的一个(通过分离两个查询)并且最灵活,因为你可以将你需要的任何内容放入WITH中 (2认同)

小智 10

@Mayur "4.2 [Using query with complex JOIN]"Common Table Expressions (CTEs)对我有用。

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;
Run Code Online (Sandbox Code Playgroud)

希望这会有所帮助... :D