使用Postgres在多个字段中搜索一个术语

时间:2012-10-15 23:18:57

标签: postgresql

我正在尝试将相同的文本搜索到我的数据库中的字段以获取实时搜索框。

SELECT DISTINCT u.id, u.username FROM
users AS u, user_invoice AS ui, user_roles AS ur, roles AS r WHERE
u.id = ur.user_id AND
ur.role_id = r.id AND
r.name = 'teacher' AND
(
    ui.user_id = u.id AND
    CAST(ui.invoice AS TEXT) = 'searchterm'
)

此查询搜索发票表并正确且非常快速地返回结果。

SELECT DISTINCT u.id, u.username FROM
users AS u, user_invoice AS ui, user_roles AS ur, roles AS r WHERE
u.id = ur.user_id AND
ur.role_id = r.id AND
r.name = 'teacher' AND
(u.username like '%searchterm%')

此查询会搜索匹配的用户名,并且也会非常快速地返回。

但是当我把这两个结合起来的时候:

SELECT DISTINCT u.id, u.username FROM
users AS u, user_invoice AS ui, user_roles AS ur, roles AS r WHERE
u.id = ur.user_id AND
ur.role_id = r.id AND
r.name = 'teacher' AND
(
    u.username like '%searchterm%' OR
    (
        ui.user_id = u.id AND
        CAST(ui.invoice AS TEXT) = 'searchterm'
    )
)

它会返回正确的结果,但需要几分钟才能完成。我做错了什么?

编辑:解释我的疑问:

第一: http://explain.depesz.com/s/PvS

第二: http://explain.depesz.com/s/D5c

组合: http://explain.depesz.com/s/Dhf


在复制演员阵容时编辑错误。

2 个答案:

答案 0 :(得分:1)

以下是我在主应用中解决此问题的方法。

我有一个主要实体,我希望用户能够搜索。称之为customer。此实体在1:n contact(用于电话,电子邮件等)表中包含关联的详细记录。

我定义了一个用于计算quicksearch密钥的视图customer_quicksearch - 一个text字段,其中包含客户contact记录的串联以及customer的一些记录直接领域。

我已向customercontact customer_summary表添加了触发器。当customer中插入行时,customer_summary触发器会将记录添加到customer,并在删除customer记录时删除该行。他们通过customer_summary来自`customer_quicksearch的更新的quicksearch键更新SELECT。我可以使用SQL函数代替视图,但发现视图更有用,更快。通过视图,可以更快地计算所有客户的快速搜索键,例如,批量插入或更新后。

CREATE VIEW customer_quicksearch AS
SELECT
        customer.id AS customer_id, array_to_string(ARRAY[
                customer.code,
                customer.name,
                string_agg(array_to_string(ARRAY[
                        contact.email::text,contact.altemail::text, contact.mobile_phone, contact.work_phone, contact.home_phone, contact.fax
                ],'|'),'|')
        ], '|') AS quicksearch_key
FROM customer
LEFT OUTER JOIN contact ON (customer.id = contact.customer_id)
GROUP BY customer.id;

和其中一个触发器:

CREATE OR REPLACE FUNCTION customer_summary_update_for_contact() RETURNS trigger AS $$
DECLARE
    _customer_id integer;
BEGIN
    -- When a contact is added/removed/changed we have to regenerate the customer search key
    IF tg_op = 'INSERT' OR tg_op = 'UPDATE' THEN
      _customer_id = NEW.customer_id;
    ELSE
      _customer_id = OLD.customer_id;
    END IF;
    UPDATE customer_summary
    SET quicksearch_key = (SELECT quicksearch_key FROM customer_quicksearch WHERE customer_id = _customer_id)
    WHERE customer_id = _customer_id;
    RETURN NULL;
END;
$$
LANGUAGE 'plpgsql'
SET search_path = 'public';

CREATE TRIGGER customer_summary_update_for_contact_trg AFTER INSERT OR UPDATE OR DELETE ON contact
FOR EACH ROW EXECUTE PROCEDURE customer_summary_update_for_contact();

您还需要customer上的触发器来处理客户的insertupdatedelete,并妥善维护该客户的customer_summary记录。< / p>

customer_summary表包含的记录包含quicksearch_key字段的管道连接,如:

'1800MA|1800 MAKE IT BUILDERS|info@1800makeit.example.com|1234 5678|0499 999 999'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^
[from customer record]        [from 1st contact record]             [from another contact record]

使用简单的LIKE模式搜索。如果我正在进行前缀搜索,我可以在其上添加text_pattern_ops索引以提高性能,但由于我主要进行没有左侧或右侧锚点的搜索 - LIKE '%search%' - 这没有任何好处。

答案 1 :(得分:0)

No-op(转换为JOIN语法)(不是anwer!):

SELECT DISTINCT u.id, u.username 
FROM
users AS u 
JOIN user_invoice AS ui ON u.username like '%searchterm%'
                        OR ( ui.user_id = u.id AND ui.invoice = CAST('searchterm' AS INTEGER))
JOIN user_roles AS ur ON u.id = ur.user_id
JOIN roles AS r ON ur.role_id = r.id
WHERE r.name = 'teacher'
   ;

CAST('searchterm' AS INTEGER))对我毫无意义。双引号?参数β