Postgres:查询映射 table 中的 ID 列表,如果不存在则创建

Postgres: Query for list of ids in a mapping table and create If they don't exist

假设我们有以下 table,其目的是为不同的(名称、位置)元组自动生成数字 ID:

CREATE TABLE mapping
(
  id bigserial PRIMARY KEY,
  name text NOT NULL,
  location text NOT NULL,
);
CREATE UNIQUE INDEX idx_name_loc on mapping(name location)

查询一组(名称、位置)元组的最有效方法是什么自动创建任何尚不存在的映射,所有映射(包括我们创建的)返回给用户。

我天真的实现是这样的:

SELECT id, name, location 
FROM mappings 
WHERE (name, location) IN ((name_1, location_1)...(name_n, location_n))

用可选择的编程语言对结果进行一些处理,以确定缺少哪些结果。

INSERT 
INTO mappings (name, location) 
VALUES (missing_name_1, missing_loc_1), ... (missing_name_2, missing_loc_2) 
ON CONFLICT DO NOTHING

这完成了工作,但我觉得可能有一些事情可以 a) 以纯粹的方式完成 sql 并且 b) 更有效率。

您可以使用 DISTINCT 获取两列的所有可能值,并使用 CROSS JOIN 获取它们的 Carthesian 乘积。

LEFT JOIN用原来的table得到实际记录(如果有的话):


CREATE TABLE mapping
  ( id bigserial PRIMARY KEY
  , name text NOT NULL
  , location text NOT NULL
 , UNIQUE (name, location)
);

INSERT INTO mapping(name, location) VALUES ('Alice', 'kitchen'), ('Bob', 'bedroom' );

SELECT * FROM mapping;

SELECT n.name, l.location, m.id
FROM (SELECT DISTINCT name from mapping) n
CROSS JOIN (SELECT DISTINCT location from mapping) l
LEFT JOIN mapping m ON m.name = n.name AND m.location = l.location
        ;

结果:


DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 2
 id | name  | location 
----+-------+----------
  1 | Alice | kitchen
  2 | Bob   | bedroom
(2 rows)

 name  | location | id 
-------+----------+----
 Alice | kitchen  |  1
 Alice | bedroom  |   
 Bob   | kitchen  |   
 Bob   | bedroom  |  2
(4 rows)

如果您想实际插入缺失的组合:


INSERT INTO mapping(name, location)
SELECT n.name, l.location
FROM (SELECT DISTINCT name from mapping) n
CROSS JOIN (SELECT DISTINCT location from mapping) l
WHERE NOT EXISTS(
        SELECT *
        FROM mapping m
        WHERE m.name = n.name AND m.location = l.location
        )
        ;

SELECT * FROM mapping;

INSERT 0 2
 id | name  | location 
----+-------+----------
  1 | Alice | kitchen
  2 | Bob   | bedroom
  3 | Alice | bedroom
  4 | Bob   | kitchen
(4 rows)