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'
)

このクエリは、invoice テーブルを検索し、適切かつ非常に迅速に結果を返します。

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%')

このクエリは、一致するユーザー名を検索し、非常に迅速に返されます。

しかし、この2つを組み合わせると、次のようになります。

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'
    )
)

適切な結果が返されますが、そうするのにほぼ 1 分かかります。私は何を間違っていますか?

編集:私のクエリの説明:

最初: http://explain.depesz.com/s/PvS

2番目: http://explain.depesz.com/s/D5c

組み合わせ: http://explain.depesz.com/s/Dhf


キャストラインのコピーミスを修正。

4

2 に答える 2

1

メインアプリでこの問題を解決する方法は次のとおりです。

ユーザーが検索できるメイン エンティティがあります。それを呼び出しますcustomer。このエンティティには、1:n contact(電話、電子メールなど) テーブルの詳細レコードが関連付けられています。

customer_quicksearchクイックサーチ キーを計算するビュー を定義します。これは、顧客のレコードtextの連結といくつかのフィールドを直接含むフィールドです。contactcustomer

テーブルにトリガーを追加しましたcustomer。トリガーは、行が挿入されたときにレコードを追加しcontact customer_summary、レコードが削除されたときに行を削除します。それらは、`customer_quicksearch. ビューの代わりに SQL 関数を使用することもできましたが、ビューの方が便利で高速であることがわかりました。ビューを使用すると、たとえば、一括挿入または一括更新の後、すべての顧客のクイック検索キーを計算する方が速くなります。customercustomer_summarycustomercustomercustomer_summarySELECT

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;

およびトリガーの 1 つ:

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を処理するためのトリガーも必要です。insertupdatedeletecustomer_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

No-op (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 に答える