PostgreSQL:使用左连接

时间:2018-01-29 10:59:30

标签: postgresql sql-update left-join self-join

我在PostgreSQL 9.3上。我是唯一一个在数据库上工作的人,我的代码按顺序运行查询以进行单元测试。

大多数情况下,以下UPDATE查询运行没有问题,但有时它会在PostgreSQL服务器上进行锁定。然后查询似乎永远不会结束,而通常只需要3秒。 我必须确切地说,查询在单元测试上下文中运行,即数据完全相同,而锁定是否发生。代码是唯一更新数据的过程。

我知道在对自更新表使用更新查询时,PostgreSQL可能存在锁定问题。当使用LEFT JOIN时,大部分结束。

我也知道LEFT JOIN查询可以用UPDATE的NOT EXISTS查询替换,但在我的情况下,LEFT JOIN要快得多,因为要更新的数据很少,而NOT EXISTS应该访问所有行候选

所以我的问题是:我应该使用什么PostgreSQL命令(如表上的Explicit Locking LOCK或选项(如SELECT FOR UPDATE)来确保运行我的查询没有永无止境的锁定。

查询:

-- for each places of scenario #1 update all owners that
-- are different from scenario #0
UPDATE t_territories AS upt
SET id_owner = diff.id_owner
FROM (
    -- list of owners in the source that are different from target
    SELECT trg.id_place, src.id_owner
    FROM t_territories AS trg
    LEFT JOIN t_territories AS src
       ON  (src.id_scenario = 0)
       AND (src.id_place = trg.id_place)
    WHERE (trg.id_scenario = 1)
    AND (trg.id_owner IS DISTINCT FROM src.id_owner)
    -- FOR UPDATE -- bug SQL : FOR UPDATE cannot be applied to the nullable side of an outer join
) AS diff
WHERE (upt.id_scenario = 1)
AND (upt.id_place = diff.id_place)

表格结构:

CREATE TABLE t_territories
(
  id_scenario integer NOT NULL,
  id_place integer NOT NULL,
  id_owner integer,
  CONSTRAINT t_territories_pk PRIMARY KEY (id_scenario, id_place),
  CONSTRAINT t_territories_fkey_owner FOREIGN KEY (id_owner)
      REFERENCES t_owner (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE RESTRICT
)

1 个答案:

答案 0 :(得分:0)

我认为您的查询已被其他查询锁定。您可以通过

找到此查询
SELECT
  COALESCE(blockingl.relation::regclass::text,blockingl.locktype) as locked_item,
  now() - blockeda.query_start AS waiting_duration, blockeda.pid AS blocked_pid,
  blockeda.query as blocked_query, blockedl.mode as blocked_mode,
  blockinga.pid AS blocking_pid, blockinga.query as blocking_query,
  blockingl.mode as blocking_mode
FROM pg_catalog.pg_locks blockedl
JOIN pg_stat_activity blockeda ON blockedl.pid = blockeda.pid
JOIN pg_catalog.pg_locks blockingl ON(
  ( (blockingl.transactionid=blockedl.transactionid) OR
  (blockingl.relation=blockedl.relation AND blockingl.locktype=blockedl.locktype)
  ) AND blockedl.pid != blockingl.pid)
JOIN pg_stat_activity blockinga ON blockingl.pid = blockinga.pid
  AND blockinga.datid = blockeda.datid
WHERE NOT blockedl.granted
AND blockinga.datname = current_database()

我在此处找到此查询http://big-elephants.com/2013-09/exploring-query-locks-in-postgres/

也可以使用ACCESS EXCLUSIVE LOCK来阻止任何查询读写表t_territories

LOCK t_territories IN ACCESS EXCLUSIVE MODE;

有关此处https://www.postgresql.org/docs/9.1/static/explicit-locking.html

的更多信息