我有一张包含500万行的表格。 想要编写有助于拼抢玩家的查询。 有8个字母,其中2个可以是通配符。
可能的组合数量为8!
,在最坏的情况下为ex*ampl*
。
假设这个单词是SELECT * from words where word like 'exampl_' or name like 'ex_ample'...
然后我写了查询8!
但是有{{1}}或子句,查询太慢了。 有更快的方法吗?
答案 0 :(得分:1)
您要查询单词但不在手中的字符数小于单词数的单词 可用的通配符。
要在候选词中找到额外字符集:
-- characters in the word
SELECT unnest(regexp_split_to_array(word, ''))
-- except those in the hand
EXCEPT ALL
SELECT unnest('{E, X, A, M, P, L}' :: CHAR [])
您可以使用此选项来选择额外字符集小于可用通配符数的字词。在
您的示例中,手中包含字符{E, X, A, M, P, L}
和两个通配符,因此查询将为:
SELECT word
FROM words
WHERE
(
SELECT count(*)
FROM
(
SELECT unnest(regexp_split_to_array(word, ''))
EXCEPT ALL
SELECT unnest('{E, X, A, M, P, L}' :: CHAR [])
) extra
) <= 2
;
这需要进行表格扫描,因此不会很快。加速它的一种方法是去除一点标准化;存储 单词作为字符数组,您将能够利用GIN索引并使用postgres数组运算符 缩小要搜索的单词集。
使用索引chars
列:
-- Add a chars column to the words table
ALTER TABLE words ADD COLUMN chars CHAR [];
-- Populate it
UPDATE words SET chars = regexp_split_to_array(word, '');
ALTER TABLE words ALTER COLUMN chars SET NOT NULL;
-- A GIN index on the chars column
CREATE INDEX ix_word_chars ON words USING GIN (chars);
-- An index on word length
CREATE INDEX ix_word_length ON words (char_length(word));
您可以使用postgres @>
数组操作快速查找单词的字谜:
SELECT word
FROM words
WHERE
chars @> '{E, X, A, M, P, L, E}' :: CHAR []
AND
char_length(word) = 7
AND
(
SELECT count(*)
FROM
(
SELECT unnest(chars)
EXCEPT ALL
SELECT unnest('{E, X, A, M, P, L, E}' :: CHAR [])
) extra
) = 0
;
注意:即使上面的示例中没有通配符,我们仍然需要过滤掉额外的字词
字符。 @>
运算符只检查左边的数组是否包含数组中的所有元素
对;它不会检查基数,所以重复字母的单词会匹配。
扩展此项以查找具有不同输入字符组合的单词需要更多工作。注意到一个字
长度为7的必须匹配手中五个字符的组合(加上两个通配符),我们可以过滤
针对这些组合的候选人(由python&#39; s itertools.combinations('EXAMPL', 5)
帮助提供):
WITH combinations (combination) AS (
VALUES
('{E, X, A, M, P}' :: CHAR []),
('{E, X, A, M, L}' :: CHAR []),
('{E, X, A, P, L}' :: CHAR []),
('{E, X, M, P, L}' :: CHAR []),
('{E, A, M, P, L}' :: CHAR []),
('{X, A, M, P, L}' :: CHAR [])
)
SELECT DISTINCT word
FROM words w
JOIN combinations c ON w.chars @> c.combination
WHERE
char_length(word) = 7
AND
(
SELECT count(*)
FROM
(
SELECT unnest(chars)
EXCEPT ALL
SELECT unnest('{E, X, A, M, P, L}' :: CHAR [])
) extra
) <= 2;
可根据需要扩展以适应更短的单词或更少的通配符。