3

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

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


因复制铸造线时出现错误而进行了编辑。

4

2 回答 2

1

这是我在主应用程序中解决此问题的方法。

我有一个我希望用户能够搜索的主要实体。调用它customercontact此实体在 1:n (电话、电子邮件等)表中具有关联的详细记录。

我定义了一个视图,customer_quicksearch它计算一个快速搜索键 - 一个包含客户记录text串联的字段以及直接的一些字段。contactcustomer

我已经在customercontact customer_summary表中添加了触发器。customer触发器在插入行时添加记录,在customer_summary删除customer记录时删除行customer。它们customer_summary通过SELECT从 `customer_quicksearch 获取更新的快速搜索键来更新。我可以为此使用 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来处理客户insert,适当updatedelete维护该customer_summary客户的记录。

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%'的情况下进行搜索 - 没有任何好处。

于 2012-10-16T00:12:38.737 回答
0

无操作(转换为 JOIN 语法)(不是答案!):

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))对我来说毫无意义。双引号?范围?

于 2012-10-15T23:50:49.703 回答