【问题标题】:How do you get leading wildcard full-text searches to work in SQL Server?如何让领先的通配符全文搜索在 SQL Server 中工作?
【发布时间】:2010-09-05 10:15:48
【问题描述】:

注意:使用 SQL 的全文搜索功能、CONTAINS 子句和所有 - * 是全文中的通配符,% 仅用于 LIKE 子句.

现在我在几个地方读到 MS SQL 不支持“前导通配符”搜索(例如,使用“*overflow”匹配“stackoverflow”)。我正在考虑使用CLR function to add regex matching,但我很想知道人们可能有什么其他解决方案。

更多信息You can add the asterisk only at the end of the word or phrase. - 以及我的经验:匹配“myvalue”时,“my*”有效,但“(asterisk)value”在执行查询时返回不匹配很简单:

SELECT * FROM TABLENAME WHERE CONTAINS(TextColumn, '"*searchterm"');

因此,我需要一种解决方法。我只在我的网站上的实际搜索页面上使用搜索 - 所以它的工作方式必须与 Google 的工作方式基本相同(在 Joe Sixpack 类型的用户看来)。没有那么复杂,但这种匹配确实不应该失败。

【问题讨论】:

    标签: sql-server full-text-search


    【解决方案1】:

    注意:这是我在修订版 #2 中引入 CONTAINS 关键字之前为问题的原始版本 #1 提交的答案。事实上,它仍然是准确的。

    SQL Server 中的通配符是% 符号,它可以正常工作,无论是前导、尾随还是其他。

    也就是说,如果您要进行任何形式的严肃全文搜索,那么我会考虑使用全文索引功能。使用%_ 通配符会严重影响数据库的性能。

    【讨论】:

    • 鉴于他使用了 CONTAINS 关键字,他似乎已经在使用全文索引。
    • @PRMan - 在提到 CONTAINS 关键字之前,我回答了问题的修订 #1。这是很久以前的事了。
    【解决方案2】:

    % 匹配任意数量的字符 _ 匹配单个字符

    我从未使用过全文索引,但您只需使用内置的 T-SQL 字符串函数即可完成相当复杂和快速的搜索查询。

    【讨论】:

      【解决方案3】:

      值得牢记的一点是,与其他通配符用法相比,领先的通配符查询具有显着的性能优势。

      【讨论】:

        【解决方案4】:

        前导通配符的问题:它们无法被索引,因此您正在执行全表扫描。

        【讨论】:

          【解决方案5】:

          来自 SQL Server 联机丛书:

          编写全文查询 Microsoft SQL Server 2005,您必须 学习如何使用 CONTAINS 和 FREETEXT Transact-SQL 谓词,以及 CONTAINSTABLE 和 FREETEXTTABLE 行集值函数。

          这意味着上面使用 % 和 _ 编写的所有查询都不是有效的全文查询。

          下面是调用 CONTAINSTABLE 函数时查询的样例。

          SELECT RANK , * FROM TableName , CONTAINSTABLE (TableName, *, ' "*WildCard" ') 搜索表 WHERE [KEY] = TableName.pk 排序方式 searchTable.RANK DESC

          为了让 CONTAINSTABLE 函数知道我正在使用通配符搜索,我必须将它用双引号括起来。我可以在开头或结尾使用通配符 *。在为 CONTAINSTABLE 函数构建搜索字符串时,您还可以做很多其他事情。您可以搜索靠近另一个词的词、搜索屈折词(drive = drive、driven、driving 和driven),以及搜索另一个词的同义词(metal 可以有同义词,如铝和钢)。

          我刚刚创建了一个表格,在表格上放置了一个全文索引并进行了几次测试搜索,没有出现问题,因此通配符搜索按预期工作。

          [更新]

          我看到您已经更新了您的问题,并且知道您需要使用其中一项功能。

          您仍然可以在开头使用通配符进行搜索,但如果该单词不是通配符后的完整单词,则必须在末尾添加另一个通配符。

          Example:  "*ildcar" will look for a single word as long as it ends with "ildcar".
          
          Example:  "*ildcar*" will look for a single word with "ildcar" in the middle, which means it will match "wildcard".  [Just noticed that Markdown removed the wildcard characters from the beginning and ending of my quoted string here.]
          

          [更新#2]

          Dave Ward - 将通配符与其中一个功能一起使用应该不会造成巨大的性能损失。如果我只用“*”创建了一个搜索字符串,它不会返回所有行,在我的测试用例中,它返回了 0 条记录。

          【讨论】:

          • 我无法在 SQL 2005 中重现这一点。如图所示,在搜索字符串的前面使用 * 会导致不返回任何行。
          • 不确定为什么将其标记为答案,因为它并不完全准确。 前导通配符不适用于全文搜索。 在 SQL Server 2008 中使用 containstable 函数对全文索引进行了验证。请参阅 Michael Stum 的回答/帖子了解原因。
          • @Jagd - 提供一个更好的答案。
          • 同意@Jagd,这应该被否决并取消标记为答案。 OP 显然是在谈论 FT 索引(因此 * 作为通配符),并且说 * 在 FT 索引操作中用作前缀是完全不正确的。很高兴被证明是错误的,但不要认为我是。
          • 前导通配符在sqlserver中不起作用,所以从原始问题的角度来看,这个答案是不正确的。
          【解决方案6】:

          在全文搜索方面,对于我来说,没有什么比 Lucene 更好的了。有一个.Net port available 与使用 Java 版本创建的索引兼容。

          创建/维护索引涉及一些工作,但搜索速度非常快,您可以创建各种有趣的查询。甚至索引速度也相当不错——我们每天只需完全重建一次索引,不用担心更新它们。

          例如,this search functionality 由 Lucene.Net 提供支持。

          【讨论】:

            【解决方案7】:

            仅供参考,Google 不会进行任何子字符串搜索或截断,无论是向右还是向左。他们有一个通配符 * 来查找短语中的未知单词,但不是单词。

            Google 与大多数全文搜索引擎一样,会根据单词的字母顺序设置倒排索引,并提供指向其源文档的链接。二进制搜索速度很快,即使对于巨大的索引也是如此。但是在这种情况下进行左截断真的很难,因为它失去了索引的优势。

            【讨论】:

              【解决方案8】:

              仅针对前导通配符的解决方法:

              • 将反转的文本存储在不同的字段中(或物化视图中)
              • 在此列上创建全文索引
              • 查找带 * 的反转文本

                SELECT * 
                FROM TABLENAME 
                WHERE CONTAINS(TextColumnREV, '"mrethcraes*"');
                

              当然有很多缺点,只是为了快速解决...

              更不用说 CONTAINSTABLE...

              【讨论】:

                【解决方案9】:

                可以在单词或短语的末尾使用通配符“*”(前缀搜索)。

                例如,此查询将查找所有“datab”、“database”、“databases”...

                SELECT * FROM SomeTable WHERE CONTAINS(ColumnName, '"datab*"')
                

                但是,很遗憾,无法使用前导通配符进行搜索。

                例如,此查询将找不到“数据库”

                SELECT * FROM SomeTable WHERE CONTAINS(ColumnName, '"*abase"')
                

                【讨论】:

                • 我为这个东西做了很多搜索,可悲的是,大多数人都错了,认为他们可以做一个领先的通配符搜索。前导通配符搜索不起作用。 Franjo 是正确的,通配符必须在搜索词组的末尾。我正在使用 SQL 2008 R2。它根本找不到它(它不会进行表或索引扫描并找到它 - 它根本找不到它)
                【解决方案10】:

                从我对 2008 R2 的测试来看,为了让这个线程更加清晰,Franjo 在上面是正确的。在处理全文搜索时,至少在使用 CONTAINS 短语时,不能使用前导 ,而只能使用尾随。 * 是通配符,而不是全文中的 %。

                有些人建议忽略 *。情况似乎并非如此,我的结果似乎表明尾随 * 功能确实有效。我认为前导 * 会被引擎忽略。

                然而,我的另一个问题是,使用带有通配符的全文的相同查询在 2005 年(20 秒)工作相对较快,并且在将数据库迁移到 2008 R2 后减慢到 12 分钟。似乎至少有一个其他用户有类似的结果,他开始了我添加到的论坛帖子... FREETEXT 仍然运行得很快,但是随着 2008 年处理 CONTAINS 中尾随 * 的方式,“似乎”发生了一些变化。他们在升级顾问中给出了各种各样的警告,他们“改进”了全文,所以你的代码可能会中断,但不幸的是,他们没有给你任何关于某些已弃用代码等的具体警告......只是他们改变了它的免责声明,使用风险自负。

                http://social.msdn.microsoft.com/Forums/ar-SA/sqlsearch/thread/7e45b7e4-2061-4c89-af68-febd668f346c

                也许,这是与这些问题最接近的 MS 命中...http://msdn.microsoft.com/en-us/library/ms143709.aspx

                【讨论】:

                  【解决方案11】:

                  作为存储过程中的参数,您可以将其用作:

                  ALTER procedure [dbo].[uspLkp_DrugProductSelectAllByName]
                  (
                      @PROPRIETARY_NAME varchar(10)
                  )
                  as
                      set nocount on
                      declare @PROPRIETARY_NAME2 varchar(10) = '"' + @PROPRIETARY_NAME + '*"'
                  
                      select ldp.*, lkp.DRUG_PKG_ID
                      from Lkp_DrugProduct ldp
                      left outer join Lkp_DrugPackage lkp on ldp.DRUG_PROD_ID = lkp.DRUG_PROD_ID
                      where contains(ldp.PROPRIETARY_NAME, @PROPRIETARY_NAME2)
                  

                  【讨论】:

                    【解决方案12】:

                    也许下面的链接将提供这种使用通配符的最终答案:Performing FTS Wildcard Searches

                    请注意以下段落:“但是,如果您指定“Chain”或“Chain”,您将不会得到预期的结果。星号将被视为正常的标点符号,而不是通配符。"

                    【讨论】:

                      【解决方案13】:

                      如果您可以访问全文搜索引擎的单词列表,您可以在此列表上进行“喜欢”搜索,并将数据库与找到的单词进行匹配,例如包含以下单词的表格“单词”:

                          pie
                          applepie
                          spies
                          cherrypie
                          dog
                          cat
                      

                      要在 fts 表 'full_text' 上匹配此数据库中包含 'pie' 的所有单词,并带有字段 'text':

                          to-match <- SELECT word FROM words WHERE word LIKE '%pie%'
                          matcher = ""
                          a = ""
                          foreach(m, to-match) {
                            matcher += a
                            matcher += m
                            a = " OR "
                          }
                          SELECT text FROM full_text WHERE text MATCH matcher
                      

                      【讨论】:

                        猜你喜欢
                        • 2011-02-09
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2013-01-29
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多