我正在尝试编写一些SQL,它将接受一组字母并返回它可以生成的所有可能的单词。我的第一个想法是创建一个基本的三表数据库,如下所示:
Words -- contains 200k words in real life
------
1 | act
2 | cat
Letters -- contains the whole alphabet in real life
--------
1 | a
3 | c
20 | t
WordLetters --First column is the WordId and the second column is the LetterId
------------
1 | 1
1 | 3
1 | 20
2 | 3
2 | 1
2 | 20
但我有点担心如何编写一个查询,返回在传入的每个字母的WordLetters中都有条目的单词。它还需要考虑具有两个相同字母的单词。我从这个查询开始,但它显然不起作用:
SELECT DISTINCT w.Word
FROM Words w
INNER JOIN WordLetters wl
ON wl.LetterId = 20 AND wl.LetterId = 3 AND wl.LetterId = 1
我如何编写查询以仅返回包含所有传入的字母并且会计入重复字母的单词?
其他信息:
我的Word表包含近200,000个单词,这就是我尝试在数据库端而不是代码中执行此操作的原因。如果有人关心,我正在使用enable1 word list。
答案 0 :(得分:5)
暂时忽略问题的SQL部分,我使用的算法非常简单:先从字典中取出每个单词,然后按照排序顺序生成一个字母的版本,以及指向该单词原始版本的指针。
这将为表格提供如下条目:
sorted_text word_id
act 123 /* we'll assume `act` was word number 123 in the original list */
act 321 /* we'll assume 'cat' was word number 321 in the original list */
然后,当我们收到一个输入(比如“tac”)时,我们对它们的字母进行排序,在我们的表格中查找它们,这些字母表与原始单词的表格相连,这给了我们一个单词列表。从那个输入创建。
如果 I 正在执行此操作,我会在SQL数据库中拥有相应的表格,但可能会使用其他内容将单词列表预处理为已排序的表单。同样,我可能会将用户输入的字母排序为我用于创建前端的任何内容,因此SQL可以做其擅长的事情:关系数据库管理。
答案 1 :(得分:0)
如果您使用您提供的解决方案,则需要向WordLetters表添加订单列。如果没有它,则无法保证您检索的行与您插入的行的顺序相同。
但是,我认为我有更好的解决方案。根据您的问题,您似乎想要查找具有相同组件字母的所有单词,与顺序或出现次数无关。这意味着您的可能性有限。如果将字母表中的每个字母翻译成不同的2的幂,则可以为每个字母组合(也称为位掩码)创建唯一值。然后,您可以简单地将单词中找到的每个字母的值相加。这将使单词匹配变得微不足道,因为具有相同字母的所有单词将映射到相同的值。这是一个例子:
WITH letters
AS (SELECT Cast('a' AS VARCHAR) AS Letter,
1 AS LetterValue,
1 AS LetterNumber
UNION ALL
SELECT Cast(Char(97 + LetterNumber) AS VARCHAR),
Power(2, LetterNumber),
LetterNumber + 1
FROM letters
WHERE LetterNumber < 26),
words
AS (SELECT 1 AS wordid, 'act' AS word
UNION ALL SELECT 2, 'cat'
UNION ALL SELECT 3, 'tom'
UNION ALL SELECT 4, 'moot'
UNION ALL SELECT 5, 'mote')
SELECT wordid,
word,
Sum(distinct LetterValue) as WordValue
FROM letters
JOIN words
ON word LIKE '%' + letter + '%'
GROUP BY wordid, word
正如您将看到的那样,如果您运行此查询,“act”和“cat”具有相同的WordValue,“tom”和“moot”也是如此,尽管字符数不同。
是什么让这比你的解决方案更好?你不需要构建很多非单词来清除它们。这将大大节省执行任务所需的存储和处理。
答案 2 :(得分:0)
SQL中有一个解决方案。它涉及使用技巧来计算每个字母出现在单词中的次数。以下表达式计算'a'出现的次数:
select len(word) - len(replace(word, 'a', ''))
这个想法是计算单词中所有字母的总和,看看它是否与整体长度相符:
select w.word, (LEN(w.word) - SUM(LettersInWord))
from
(
select w.word, (LEN(w.word) - LEN(replace(w.word, wl.letter))) as LettersInWord
from word w
cross join wordletters wl
) wls
having (LEN(w.word) = SUM(LettersInWord))
此特定解决方案允许多次出现字母。我不确定原始问题是否需要这个。如果我们想要达到一定数量的事件,那么我们可能会执行以下操作:
select w.word, (LEN(w.word) - SUM(LettersInWord))
from
(
select w.word,
(case when (LEN(w.word) - LEN(replace(w.word, wl.letter))) <= maxcount
then (LEN(w.word) - LEN(replace(w.word, wl.letter)))
else maxcount end) as LettersInWord
from word w
cross join
(
select letter, count(*) as maxcount
from wordletters wl
group by letter
) wl
) wls
having (LEN(w.word) = SUM(LettersInWord))
如果您希望与字母完全匹配,则case语句应使用" = maxcount"
而不是" <= maxcount"
。
根据我的经验,我实际上看到了小交叉连接的不错表现。这实际上可能在服务器端工作。在服务器上执行此工作有两大优势。首先,它利用了盒子上的并行性。其次,需要通过网络传输更小的数据集。