B 또는 D로 시작하는 사람들의 이름을 찾는 간단한 쿼리를 작성해야했습니다.
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
더 성능이 좋도록 이것을 다시 작성할 수있는 방법이 있는지 궁금합니다. 그래서 피할 수 or
및 / 또는 like
?
name
성능에 관심이있는 경우 색인 이 유용 할 수 있습니다.
B 또는 D로 시작하는 사람들의 이름을 찾는 간단한 쿼리를 작성해야했습니다.
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
더 성능이 좋도록 이것을 다시 작성할 수있는 방법이 있는지 궁금합니다. 그래서 피할 수 or
및 / 또는 like
?
name
성능에 관심이있는 경우 색인 이 유용 할 수 있습니다.
답변:
귀하의 쿼리는 거의 최적입니다. 구문은 훨씬 짧아지지 않고 쿼리는 훨씬 빠르지 않습니다.
SELECT name
FROM spelers
WHERE name LIKE 'B%' OR name LIKE 'D%'
ORDER BY 1;
구문 을 정말로 줄이려면 분기 와 함께 정규 표현식을 사용하십시오 .
...
WHERE name ~ '^(B|D).*'
또는 문자 클래스를 사용하면 약간 더 빠릅니다 .
...
WHERE name ~ '^[BD].*'
색인이없는 빠른 테스트 SIMILAR TO
는 어느 경우 보다 나에게 더 빠른 결과 를 제공합니다.
적절한 B-Tree 지수가 제자리에 LIKE
오면이 레이스 에서 승리합니다.
매뉴얼에서 패턴 일치에 대한 기본 사항을 읽으십시오 .
성능이 염려되면 더 큰 테이블에 대해 다음과 같은 색인을 작성하십시오.
CREATE INDEX spelers_name_special_idx ON spelers (name text_pattern_ops);
이러한 종류의 쿼리를 수십 배 더 빠르게 만듭니다. 로캘 별 정렬 순서에는 특별한 고려 사항이 적용됩니다. 매뉴얼에서 연산자 클래스에 대해 자세히 알아보십시오 . 표준 "C"로케일을 사용하는 경우 (대부분의 사람들은 그렇지 않음) 일반 색인 (기본 연산자 클래스 포함)이 수행합니다.
이러한 색인은 왼쪽 고정 패턴에만 적합합니다 (문자열의 시작 부분과 일치).
SIMILAR TO
또는 기본 왼쪽 고정식이있는 정규식도이 색인을 사용할 수 있습니다. 그러나 분기 또는 문자 클래스는 아닙니다 (적어도 PostgreSQL 9.0에 대한 테스트에서는).(B|D)
[BD]
Trigram 일치 또는 텍스트 검색은 특수 GIN 또는 GiST 인덱스를 사용합니다.
LIKE
( ~~
)은 단순하고 빠르지 만 기능이 제한적입니다.
ILIKE
( ~~*
) 대소 문자를 구분하지 않는 변형입니다.
pg_trgm은 두 가지 모두에 대한 인덱스 지원을 확장합니다.
~
(정규 표현식 일치)는 강력하지만 더 복잡하며 기본 표현식보다 더 느릴 수 있습니다.
SIMILAR TO
그냥 무의미 . 특이한 반종 LIKE
및 정규 표현. 나는 그것을 사용하지 않습니다. 아래를 참조하십시오.
% 는 추가 모듈에서 제공하는 "유사성"연산자pg_trgm
입니다. 아래를 참조하십시오.
@@
텍스트 검색 연산자입니다. 아래를 참조하십시오.
부터 PostgreSQL의 9.1 당신은 확장을 용이하게 할 수 pg_trgm
에 대한 인덱스 지원을 제공하기 위해 모든 LIKE
/ ILIKE
패턴 (그리고 간단한 정규 표현식 패턴 ~
GIN 또는 GIST 인덱스를 사용하여).
세부 사항, 예제 및 링크 :
pg_trgm
또한 다음 연산자를 제공합니다 .
%
- "유사성"연산자 <%
(정류자 : %>
)-Postgres 9.6 이상의 "word_similarity"연산자<<%
(정류자 : %>>
)-Postgres 11 이상의 "strict_word_similarity"연산자별도의 인프라 및 인덱스 유형과 일치하는 특수한 유형의 패턴입니다. 사전과 형태소 분석을 사용하며 특히 자연 언어의 문서에서 단어를 찾을 수있는 훌륭한 도구입니다.
접두사 일치도 지원됩니다.
뿐만 아니라 구문 검색 포스트 그레스 9.6 가입일 :
매뉴얼 의 소개 와 운영자와 기능 의 개요를 고려하십시오 .
추가 모듈 fuzzystrmatch 는 더 많은 옵션을 제공하지만 성능은 일반적으로 위의 모든 것보다 열등합니다.
특히, levenshtein()
기능 의 다양한 구현은 도구적일 수있다.
~
)이 항상 더 빠릅 SIMILAR TO
니까?대답은 간단합니다. SIMILAR TO
표현식은 내부적으로 정규 표현식으로 다시 작성됩니다. 따라서 모든 SIMILAR TO
표현식 에 대해 하나 이상의 빠른 정규 표현식이 있습니다 (표현을 다시 작성하는 오버 헤드를 절약 함). SIMILAR TO
이제까지 사용하면 성능이 향상되지 않습니다 .
어쨌든 LIKE
( ~~
) 으로 수행 할 수있는 간단한 표현 이 더 빠릅니다 LIKE
.
SIMILAR TO
PostgreSQL에서는 SQL 표준 초안으로 작성 되었기 때문에 지원됩니다. 그들은 여전히 그것을 제거하지 못했습니다. 그러나 그것을 제거하고 대신 정규 표현식 일치를 포함시킬 계획이 있습니다.
EXPLAIN ANALYZE
그것을 공개합니다. 어떤 테이블로든 직접 시도하십시오!
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name SIMILAR TO 'B%';
공개 :
...
Seq Scan on spelers (cost= ...
Filter: (name ~ '^(?:B.*)$'::text)
SIMILAR TO
정규 표현식 ( ~
) 으로 다시 작성되었습니다 .
그러나 EXPLAIN ANALYZE
더 많은 것을 보여줍니다. 위에서 언급 한 색인을 사용하여 시도하십시오.
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name ~ '^B.*;
공개 :
...
-> Bitmap Heap Scan on spelers (cost= ...
Filter: (name ~ '^B.*'::text)
-> Bitmap Index Scan on spelers_name_text_pattern_ops_idx (cost= ...
Index Cond: ((prod ~>=~ 'B'::text) AND (prod ~<~ 'C'::text))
내부적으로 로케일 인식되지 지수 (과 text_pattern_ops
또는 사용하여 로케일 C
:) 간단한 왼쪽 고정 표현은 이러한 텍스트 패턴 사업자로 재 작성되어 ~>=~
, ~<=~
, ~>~
, ~<~
. 이것은을위한 경우이다 ~
, ~~
또는 SIMILAR TO
모두.
동일의 인덱스 마찬가지입니다 varchar
가지는 형태 varchar_pattern_ops
나 char
와 bpchar_pattern_ops
.
따라서 원래 질문에 적용하면 가능한 가장 빠른 방법입니다 .
SELECT name
FROM spelers
WHERE name ~>=~ 'B' AND name ~<~ 'C'
OR name ~>=~ 'D' AND name ~<~ 'E'
ORDER BY 1;
물론 인접한 이니셜 을 검색해야하는 경우 다음을 더 단순화 할 수 있습니다.
WHERE name ~>=~ 'B' AND name ~<~ 'D' -- strings starting with B or C
의 일반 사용에 이득 ~
또는 ~~
작은입니다. 성능이 가장 중요한 요구 사항이 아닌 경우 표준 연산자를 고수해야합니다. 질문에 이미있는 내용에 도달해야합니다.
similar
스캔이 관련되어 있음을 알고 있습니까?
EXPLAIN ANALYZE
2 개의 비트 맵 인덱스 스캔 을 보여주는 빠른 테스트 . 여러 비트 맵 인덱스 스캔을보다 빠르게 결합 할 수 있습니다.
OR
으로 UNION ALL
또는 교체 name LIKE 'B%'
와 name >= 'B' AND name <'C'
포스트 그레스에를?
UNION
범위를 하나의 WHERE
절로 결합 하면 쿼리 속도가 빨라집니다. 내 답변에 더 많이 추가했습니다. 물론 로케일을 고려해야합니다. 로케일 인식 검색은 항상 느립니다.
테이블에 열을 추가하는 것은 어떻습니까. 실제 요구 사항에 따라
person_name_start_with_B_or_D (Boolean)
person_name_start_with_char CHAR(1)
person_name_start_with VARCHAR(30)
PostgreSQL은 SQL Server의 기본 테이블에서 계산 열을 지원하지 않지만 트리거를 통해 새 열을 유지 관리 할 수 있습니다. 분명히이 새 열은 색인화됩니다.
또는 표현식 의 인덱스 가 동일하고 저렴합니다. 예 :
CREATE INDEX spelers_name_initial_idx ON spelers (left(name, 1));
조건에서 표현식과 일치하는 쿼리는이 인덱스를 활용할 수 있습니다.
이러한 방식으로, 데이터가 작성되거나 수정 될 때 성능 저하가 발생하므로 활동이 적은 환경 (즉, 읽기보다 쓰기 수가 훨씬 적음)에만 적합 할 수 있습니다.
당신은 시도 할 수 있습니다
SELECT s.name
FROM spelers s
WHERE s.name SIMILAR TO '(B|D)%'
ORDER BY s.name
위의 표현이나 원래 표현이 Postgres에서 Sargable인지 여부는 알 수 없습니다.
제안 된 색인을 작성하면 이것이 다른 옵션과 비교되는 방식에 관심이 있습니다.
SELECT name
FROM spelers
WHERE name >= 'B' AND name < 'C'
UNION ALL
SELECT name
FROM spelers
WHERE name >= 'D' AND name < 'E'
ORDER BY name
아주 오래된 질문이지만이 문제에 대한 또 다른 빠른 해결책을 찾았습니다.
SELECT s.name
FROM spelers s
WHERE ascii(s.name) in (ascii('B'),ascii('D'))
ORDER BY 1
ascii () 함수는 문자열의 첫 문자 만 찾습니다.
(name)
?
이니셜을 확인하기 위해 종종 "char"
큰 따옴표로 캐스팅을 사용 합니다. 휴대용이 아니지만 매우 빠릅니다. 내부적으로는 단순히 텍스트를 분해하고 첫 번째 문자를 반환하며 "char"비교 작업은 유형이 1 바이트 고정 길이이므로 매우 빠릅니다.
SELECT s.name
FROM spelers s
WHERE s.name::"char" =ANY( ARRAY[ "char" 'B', 'D' ] )
ORDER BY 1
캐스트 "char"
는 ascii()
@ Sole021 의 slution 보다 빠르지 만 UTF8 호환 (또는 그 문제에 대한 다른 인코딩)은 아니며 첫 번째 바이트 만 반환하므로 비교가 평범한 이전 7과 비교되는 경우에만 사용해야합니다 비트 ASCII 문자
이러한 경우를 처리하기 위해 아직 언급되지 않은 두 가지 방법이 있습니다.
부분 (또는 분할-전체 범위 수동으로 작성된 경우) 색인-데이터의 서브 세트 만 필요한 경우 (예 : 일부 유지 보수 중 또는 일부보고를 위해 임시) 가장 유용합니다.
CREATE INDEX ON spelers WHERE name LIKE 'B%'
테이블 자체 분할 (첫 번째 문자를 분할 키로 사용)-이 기술은 특히 PostgreSQL 10+ (더 적은 고통스러운 분할) 및 11+ (쿼리 실행 중 분할 정리)에서 고려할 가치가 있습니다.
또한 테이블의 데이터가 정렬되면 BRIN 인덱스 를 사용 하여 첫 번째 문자를 통해 이점을 얻을 수 있습니다 .
단일 문자 비교를 수행하는 것이 아마도 더 빠를 것입니다.
SUBSTR(s.name,1,1)='B' OR SUBSTR(s.name,1,1)='D'
column LIKE 'B%'
열에서 하위 문자열 함수를 사용하는 것보다 효율적입니다.
s.name
색인?