it-swarm.com.de

LOWER LIKE vs iLIKE

Wie wird die Leistung der folgenden beiden Abfragekomponenten verglichen?

NIEDRIGER WIE

... LOWER(description) LIKE '%abcde%' ...

iLIKE

... description iLIKE '%abcde%' ...
29
user664833

Die Antwort hängt von vielen Faktoren ab, insbesondere von der Postgres-Version, der Codierung und dem Gebietsschema - LC_COLLATE .

Der bloße Ausdruck lower(description) LIKE '%abc%' ist in der Regel etwas schneller als description ILIKE '%abc%' Und entweder etwas schneller als der entsprechende reguläre Ausdruck: description ~* 'abc'. Dies ist wichtig für sequentielle Scans, bei denen der Ausdruck für jede getestete Zeile ausgewertet werden muss.

Aber für große Tabellen, wie Sie in Ihrer Antwort zeigen, würde man sicherlich einen Index verwenden. Für beliebige Muster (nicht nur links verankert) empfehle ich einen Trigrammindex mit dem Zusatzmodul pg_trgm . Dann sprechen wir von Millisekunden anstatt von Sekunden, und der Unterschied zwischen den obigen Ausdrücken wird aufgehoben.

GIN- und Gist-Indizes (unter Verwendung der Operatorklassen gin_trgm_ops Oder Gist_trgm_ops) Unterstützen LIKE (~~), ILIKE (~~*), ~, ~* (Und einige weitere Varianten). Bei einem Trigramm-GIN-Index für description (normalerweise größer als Gist, aber schneller für Lesevorgänge) würde Ihre Abfrage description ILIKE 'case_insensitive_pattern' Verwenden.

Verbunden:

Grundlagen zum Pattern Matching in Postgres:

Wenn Sie mit diesem Trigramm-Index arbeiten, ist es in der Regel praktischer, mit Folgendem zu arbeiten:

description ILIKE '%abc%'

Oder mit dem Operator für reguläre Ausdrücke ohne Berücksichtigung der Groß- und Kleinschreibung (ohne Platzhalter %):

description ~* 'abc'

Ein Index für (description) Unterstützt keine Abfragen für lower(description) wie:

lower(description) LIKE '%abc%'

Und umgekehrt.

Bei Prädikaten für lower(description) exclusive ist der Ausdrucksindex die etwas bessere Option.

In allen anderen Fällen ist ein Index für (description) Vorzuziehen, da er beide Prädikate unterstützt, bei denen zwischen Groß- und Kleinschreibung unterschieden wird.

20

Nach meinen Tests ( zehn von jeder Abfrage) handelt es sich bei LOWERLIKE um 17% schneller als iLIKE.

Erklärung

Ich habe eine Million Zeilen erstellt, die zufällige gemischte Textdaten enthalten:

require 'securerandom'
inserts = []
1000000.times do |i|
        inserts << "(1, 'fake', '#{SecureRandom.urlsafe_base64(64)}')"
end
sql = "insert into books (user_id, title, description) values #{inserts.join(', ')}"
ActiveRecord::Base.connection.execute(sql)

Überprüfen Sie die Anzahl der Zeilen:

my_test_db=# select count(id) from books ;
  count  
---------
 1000009

(Ja, ich habe neun zusätzliche Zeilen aus anderen Tests - kein Problem.)

Beispielabfrage und Ergebnisse:

my_test_db=# SELECT "books".* FROM "books" WHERE "books"."published" = 'f'
my_test_db=# and (LOWER(description) LIKE '%abcde%') ;
   id    | user_id | title |                                      description                                       | published 
---------+---------+-------+----------------------------------------------------------------------------------------+------
 1232322 |       1 | fake  | 5WRGr7oCKABcdehqPKsUqV8ji61rsNGS1TX6pW5LJKrspOI_ttLNbaSyRz1BwTGQxp3OaxW7Xl6fzVpCu9y3fA | f
 1487103 |       1 | fake  | J6q0VkZ8-UlxIMZ_MFU_wsz_8MP3ZBQvkUo8-2INiDIp7yCZYoXqRyp1Lg7JyOwfsIVdpPIKNt1uLeaBCdelPQ | f
 1817819 |       1 | fake  | YubxlSkJOvmQo1hkk5pA1q2mMK6T7cOdcU3ADUKZO8s3otEAbCdEcmm72IOxiBdaXSrw20Nq2Lb383lq230wYg | f

Ergebnisse für LOWER LIKE

my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (LOWER(description) LIKE '%abcde%') ;
                                                   QUERY PLAN                                                   
----------------------------------------------------------------------------------------------------------------
 Seq Scan on books  (cost=0.00..32420.14 rows=1600 width=117) (actual time=938.627..4114.038 rows=3 loops=1)
   Filter: ((NOT published) AND (lower(description) ~~ '%abcde%'::text))
   Rows Removed by Filter: 1000006
 Total runtime: 4114.098 ms

Ergebnisse für iLIKE

my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (description iLIKE '%abcde%') ;
                                                   QUERY PLAN                                                   
----------------------------------------------------------------------------------------------------------------
 Seq Scan on books  (cost=0.00..29920.11 rows=100 width=117) (actual time=1147.612..4986.771 rows=3 loops=1)
   Filter: ((NOT published) AND (description ~~* '%abcde%'::text))
   Rows Removed by Filter: 1000006
 Total runtime: 4986.831 ms

Offenlegung von Datenbankinformationen

Postgres-Version:

my_test_db=# select version();
                                                                                 version
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.2.4 on x86_64-Apple-darwin12.4.0, compiled by i686-Apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00), 64-bit

Sortierungseinstellung:

my_test_db=# select datcollate from pg_database where datname = 'my_test_db';
 datcollate  
-------------
 en_CA.UTF-8

Tabellendefinition:

my_test_db=# \d books 
                                      Table "public.books"
   Column    |            Type             |                       Modifiers
-------------+-----------------------------+-------------------------------------------------------
 id          | integer                     | not null default nextval('books_id_seq'::regclass)
 user_id     | integer                     | not null
 title       | character varying(255)      | not null
 description | text                        | not null default ''::text
 published   | boolean                     | not null default false
Indexes:
    "books_pkey" PRIMARY KEY, btree (id)
25
user664833

In meinem Rails Project. ILIKE ist fast 10x schneller als LOWER LIKE, Ich füge einen GIN Index auf entities.name Spalte

> Entity.where("LOWER(name) LIKE ?", name.strip.downcase).limit(1).first
Entity Load (2443.9ms)  SELECT  "entities".* FROM "entities" WHERE (lower(name) like 'baidu') ORDER BY "entities"."id" ASC LIMIT $1  [["LIMIT", 1]]
> Entity.where("name ILIKE ?", name.strip).limit(1).first
Entity Load (285.0ms)  SELECT  "entities".* FROM "entities" WHERE (name ilike 'Baidu') ORDER BY "entities"."id" ASC LIMIT $1  [["LIMIT", 1]]
# explain analyze SELECT  "entities".* FROM "entities" WHERE (name ilike 'Baidu') ORDER BY "entities"."id" ASC LIMIT 1;
                                                                   QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=3186.03..3186.04 rows=1 width=1588) (actual time=7.812..7.812 rows=1 loops=1)
   ->  Sort  (cost=3186.03..3187.07 rows=414 width=1588) (actual time=7.811..7.811 rows=1 loops=1)
         Sort Key: id
         Sort Method: quicksort  Memory: 26kB
         ->  Bitmap Heap Scan on entities  (cost=1543.21..3183.96 rows=414 width=1588) (actual time=7.797..7.805 rows=1 loops=1)
               Recheck Cond: ((name)::text ~~* 'Baidu'::text)
               Rows Removed by Index Recheck: 6
               Heap Blocks: exact=7
               ->  Bitmap Index Scan on index_entities_on_name  (cost=0.00..1543.11 rows=414 width=0) (actual time=7.787..7.787 rows=7 loops=1)
                     Index Cond: ((name)::text ~~* 'Baidu'::text)
 Planning Time: 6.375 ms
 Execution Time: 7.874 ms
(12 rows)

Der GIN-Index ist sehr hilfreich, um die Leistung von ILIKE zu verbessern

0
lfx_cool