【问题标题】:Query on concatenated fields across tables跨表查询连接字段
【发布时间】:2014-12-13 05:10:37
【问题描述】:

我在 PostgreSQL ab 中有 2 个表。

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.namea 为空时是唯一的,否则(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

【问题讨论】:

  • 您的系统中是否需要 (a.name, b.name) 的组合应该是唯一的,或者只是为了提高性能?
  • 你不能把“some string”分成“some”和“string”做两个比较吗?
  • 我能够创建功能索引CREATE UNIQUE INDEX idxa ON a (length(name), name) ;(b 相同)。如果 (length(a.name) = 4 AND length(b.name) = 4) 等被添加到 where 子句中,优化器会使用它,但它无法解开 (length(a.name) + length(b.name) = 9) 另一种方法是将长度存储在一个额外的列中并使用触发函数来维护它。
  • @Srikanth 唯一性是必需的。 @GordonLinoff 不,复合字符串是外部输入。 @joop 我的理解是功能索引只能在同一张表上工作。
  • 这是一个棘手的问题。你能告诉我们更多关于弦的性质吗? a.nameb.name 可以包含空格字符(' ')吗?搜索模式可以包含多个空格字符吗? External input 没有说明字符串的外观。 a.nameb.name 的最小长度是多少?它们中的任何一个都可以是空字符串('')吗?在这种情况下,您还想要b.name 前面的空间吗?为什么你使用varchar(50) 而不仅仅是varchartext

标签: sql postgresql indexing pattern-matching


【解决方案1】:

首先,您需要一个额外的UNIQUE 约束来满足您的要求:

否则(a.name, b.name) 对是唯一的。

CREATE TABLE a (
 , id   serial PRIMARY KEY
 , name text UNIQUE NOT NULL
);

MATERIALIZED VIEW

这在 任何 情况下都可以快速工作:MATERIALIZED VIEW 与串联字符串。由于ab 是链接的,所以我们只能得到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-tree 索引就可以解决问题:

CREATE INDEX ab_abname_idx ON ab (abname);

还有查询:

SELECT *
FROM   ab
-- optionally (left) join to a and b ...
WHERE  abname = 'some string';
  • LEFT JOIN 必须包含来自ba_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 来查找所有匹配项...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    • 2021-11-15
    • 2010-12-25
    • 1970-01-01
    • 1970-01-01
    • 2022-11-01
    相关资源
    最近更新 更多