postgresql中拼字游戏的最佳查询

时间:2017-10-08 19:57:59

标签: python postgresql

我有一张包含500万行的表格。 想要编写有助于拼抢玩家的查询。 有8个字母,其中2个可以是通配符。

可能的组合数量为8!,在最坏的情况下为ex*ampl*。 假设这个单词是SELECT * from words where word like 'exampl_' or name like 'ex_ample'... 然后我写了查询8!

但是有{{1}}或子句,查询太慢了。 有更快的方法吗?

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;

可根据需要扩展以适应更短的单词或更少的通配符。