我在PostgreSQL中有2个表a
& b
。
CREATE TABLE a
(
id serial PRIMARY,
name character varying(50) NOT NULL,
);
CREATE TABLE b
(
id serial PRIMARY,
name character varying(50) NOT NULL,
a_id integer,
CONSTRAINT a_id_fk FOREIGN KEY (a_id) REFERENCES a (id)
);
此外,我在b
上有这两个部分索引,以确保b.name
在a
为空时是唯一的,否则(a.name, b.name)
对是唯一的。
CREATE UNIQUE INDEX idx1 ON b (name, a_id) WHERE a_id IS NOT NULL;
CREATE UNIQUE INDEX idx2 ON b (name) WHERE a_id IS NULL;
如果我想查询格式为a.name + ' ' + b.name
的字符串,这是最有效的方法吗?反正我是否可以创建一个索引来确保(a.name, b.name)
的唯一性,并使用它来有效地查询它?
SELECT * FROM b
INNER JOIN a on b.a_id = a.id
WHERE CONCAT(a.name, ' ' , b.name) = 'some string';
我需要精确查找,不需要LIKE
/ CONTAINS
。
答案 0 :(得分:1)
首先,您需要额外的UNIQUE
约束来满足您的要求:
否则
(a.name, b.name)
对是唯一的。
CREATE TABLE a (
, id serial PRIMARY KEY
, name text UNIQUE NOT NULL
);
MATERIALIZED VIEW
这可以在任何情况下快速运行:带有连接字符串的MATERIALIZED VIEW
。由于a
和b
已关联,因此我们只会获得b
中的行数,而不是笛卡尔积。
CREATE MATERIALIZED VIEW ab AS
SELECT b.a_id, b.id, concat_ws(' ', a.name, b.name) AS abname
FROM b
LEFT JOIN a ON a.id = b.a_id;
由于你只使用了相等,现在就有了一个简单的b树索引:
CREATE INDEX ab_abname_idx ON ab (abname);
查询:
SELECT *
FROM ab
-- optionally (left) join to a and b ...
WHERE abname = 'some string';
LEFT JOIN
对于b
与a_id IS NULL
的行包含至关重要。
concat_ws()
仅插入a.name
不是NULL
的空格。
根据您的访问模式刷新实体化视图。如果你有并发写访问权,这可能是棘手的部分。
MATERIALIZED VIEW
'some string' LIKE (a.name || '%')
不是sargable。索引支持是不可能的。你必须扭转表达式:
a.name = left('some string', length(a.name))
仍然不是可以接受的。你必须一步一步地做到这一点:
a.name = left('some string', 1) OR
a.name = left('some string', 2) OR
a.name = left('some string', 3) OR
...
这可以通过索引来支持。关于dba.SE的相关答案:
我会使用递归CTE来查找所有匹配...