2012-10-15 59 views
3

我试图在我的数据库中的字段中搜索相同的文本以查找livesearch框。使用Postgres在多个字段中搜索一个词条

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


编辑的错误在复制投行。

+0

PostgreSQL版本?请同时显示'解析分析'输出;粘贴到explain.depesz.com并链接到这里。 –

+0

如果您将查询重写为JOIN语法,您可能会发现自己。 – wildplasser

+0

我必须同意wildplasser--使用'FROM INNER JOIN ON(condition)'而不是'FROM a,b WHERE(condition)'来理解发生了什么很容易。另外,三人中的第二个如何产生明智的结果?你有一个来自不受限制的加入'ui'的笛卡尔产品。 –

回答

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))是没有意义的我。双引号?参数?

+0

searchterm可以是字母数字,而字段发票是一个整数。它没有演员就抛出一个错误,所以我把它留在那里。 – Fillmore

+0

我的错误,我复制了错误的代码。我实际上是CAST(ui.invoice AS TEXT)='searchterm' – Fillmore

1

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

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

我定义视图,customer_quicksearch,计算一个快速搜索键 - 包含contact记录的串联与一些直接customer场沿一个客户text场。

我已将触发器添加到customercontactcustomer_summary表中。当一行插入customer时,customer触发器会向customer_summary添加记录,并在删除customer记录时删除该行。他们通过SELECT更新customer_summary来自`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触发处理insertupdate和客户的delete,保持customer_summary记录该客户适当。

customer_summary表包含的记录,其中包括一个quicksearch_key这是字段的管道串联,如:

'1800MA|1800 MAKE IT BUILDERS|[email protected]|1234 5678|0499 999 999' 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ 
[from customer record]  [from 1st contact record]    [from another contact record] 

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

+0

您可以使用新的trigram索引类型来使'like'%search%'使用索引 –

+0

@a_horse_with_no_name哦,好点。它在小数据集规模上足够快,以至于没有必要担心优化的方式,但每一点都有帮助。 –