简体   繁体   English

跨多个表的SQL简单搜索功能

[英]SQL simple search functionality across multiple tables

I'm using SQL Server 2012. I need to implement a search functionality using a single text field. 我正在使用SQL Server2012。我需要使用单个文本字段实现搜索功能。

Let's say I have the following table: 假设我有下表:

--------------------------------------------------------------------------------
FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR    
--------------------------------------------------------------------------------
John        Doe         Boston      2005        Mc Donald   
Marc        Forestier   Bruxelle    2010        Private bank    
Céline      Durand      Paris       1999        Food SA     
Simon       Forestier   Toulouse    2001        Forestier SARL  
John        Smith       New York    1992        Events Org. 
Sonia       Grappe      Toulon      2010        Forestier SARL  
--------------------------------------------------------------------------------

Behavior is the following: 行为如下:

  • All words (space separated) have to be searched over all the column. 必须在所有列中搜索所有单词(以空格分隔)。
  • A LIKE should be applied for each word 每个单词都应使用LIKE
  • If only one word is searched, return all records that contains that word 如果仅搜索一个单词,则返回包含该单词的所有记录
  • If several words are searched, return only records that contains the largest number of different words (see "forestier" example below) 如果搜索到多个单词,则仅返回包含最大数量的不同单词的记录(请参见下面的“ forestier”示例)
  • I need a single query , no TSQL 我只需要一个查询 ,不需要TSQL

I tried a lot of things, but it is not as simple as it seems. 我尝试了很多事情,但这并不像看起来那么简单。

Some examples: 一些例子:

"John": “约翰”:

-------------------------------------------------------------------------------
FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR
-------------------------------------------------------------------------------
John        Doe         Boston      2005        Mc Donald
John        Smith       New York    1992        Events Org.
-------------------------------------------------------------------------------

"John Doe": “约翰·多伊”:

-------------------------------------------------------------------------------
FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR
-------------------------------------------------------------------------------
John        Doe         Boston      2005        Mc Donald
-------------------------------------------------------------------------------

"forestier": “弗赖斯”:

-------------------------------------------------------------------------------
FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR
-------------------------------------------------------------------------------
Marc        Forestier   Bruxelle    2010        Private bank
Simon       Forestier   Toulouse    2001        Forestier SARL
Sonia       Grappe      Toulon      2010        Forestier SARL
-------------------------------------------------------------------------------

"for 2010 xelle": “ 2010年xelle”:

FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR
--------------------------------------------------------------------------------
Marc        Forestier   Bruxelle    2010        Private bank
--------------------------------------------------------------------------------

This example uses a single table; 这个例子使用一个表。 in reality my 5 columns are from 4 different tables, so it is a little more complex to implement full text search! 实际上,我的5列来自4个不同的表,因此实现全文搜索要稍微复杂一点!

How about adding another field eg a text field containing all the information from the other fields. 如何添加另一个字段,例如包含其他字段中所有信息的文本字段。

FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR   SEARCHFIELD
John        Doe         Boston      2005        Mc Donald  John Doe Boston 2005 Mc Donald

And make the search on this field. 并在此字段上进行搜索。 It's not elegant but it could work. 它不是很优雅,但是可以工作。

Adding below: 在下面添加:

I don't think that SQL syntax supports all your need but you can make another workaround. 我认为SQL语法不能满足您的所有需求,但您可以采取其他解决方法。 Create a table which includes all the words you want to search: 创建一个包含所有要搜索的单词的表:

create table searchtable
(
rowid int, --key to the id for the row in your table
mothertableName varchar(), -- name of the table if necessary
motherfieldName varchar(), -- name of field
word varchar() -- the actual word to be searchable
)

The search for the words and where they have the largest occurrence: 搜索单词及其出现次数最多的地方:

SELECT * FROM myTable WHERE id IN(
   SELECT rid as id, MAX(c) FROM (
      SELECT rowid as rid, COUNT(rowid) as c FROM Searchtable WHERE word IN ('john','doe')
   )
)

The above SQL will surely not work, but I hope you get the idea of what I am proposing. 上面的SQL肯定不会起作用,但是希望您对我的建议有所了解。 You should get one row with the largest numbers of you search words. 您应该获得最多搜索词的一行。 But the 'IN' operator in SQL demands that you make some dynamically SQL. 但是SQL中的“ IN”运算符要求您动态创建一些SQL。

As you write that you have tried nearly everything, I think that SQL can't do it alone. 当您写到您已经尝试了几乎所有内容时,我认为SQL不能独自完成。

This seems the job that Sql Full Text Indexing was designed for. 这似乎是Sql全文索引设计的工作。

AFAIK Full text indexing doesn't work with numeric types, so you may need to add computed columns for any date or numeric types, eg if PromoYear is numeric: AFAIK全文索引不适用于数字类型,因此您可能需要为任何日期或数字类型添加计算列,例如,如果PromoYear为数字:

ALTER TABLE MyTable
   ADD TextPromoYear AS CAST(PromoYear AS NVARCHAR(4))
   PERSISTED;

You'll need to setup a fulltext catalog in the database: 您需要在数据库中设置全文目录:

CREATE FULLTEXT CATALOG CAT_MyCat AS DEFAULT;

Assuming you have a primary key called PK_MyTable , create the full text index: 假设您有一个名为PK_MyTable的主键,请创建全文索引:

CREATE FULLTEXT INDEX ON MyTable(FirstName, LastName, City, TextPromoYear, 
                                 Employer) 
KEY INDEX PK_MyTable;
With some luck, you'll then be able to execute queries with [`CONTAINS`](http://technet.microsoft.com/en-us/library/ms187787.aspx) or `FREETEXT`, such as: SELECT * FROM mytable WHERE CONTAINS((FirstName, LastName, City, TextPromoYear, Employer), ' for OR 2010 OR xelle') The query syntax isn't exactly what you want, but you may be able to adapt the 'space separated' query you want into ` and ` as required by `CONTAINS`. 幸运的话,您将可以使用[`CONTAINS`](http://technet.microsoft.com/zh-cn/library/ms187787.aspx)或`FREETEXT`执行查询,例如:SELECT * FROM mytable WHERE CONTAINS((FirstName,LastName,City,TextPromoYear,Employer),'for OR 2010 OR xelle')查询语法不是您想要的语法,但是您可以使用'空格分隔的'查询要根据`CONTAINS`进入`和`。

Edit 编辑

This isn't quite as easy as this. 这不是那么容易。 The wildcard * can only be used as a suffix, and you'll also need to aggregate the columns to search across all of them. 通配符*只能用作后缀,并且您还需要汇总列以在所有列中进行搜索。

For complete terms: 完整条款:

SELECT *
FROM mytable
WHERE CONTAINS(*, 'John and Doe');

For partial searches you may need to resort to a hybrid using a vanilla LIKE for testing unknown prefixes (*xelle) : 对于部分搜索,您可能需要求助于使用香草的LIKE来测试未知前缀(*xelle)

SELECT *
FROM mytable
WHERE CONTAINS(*, '"for*"  and 2010') AND SearchableComputedColumn like '%elle%';

Updated SqlFiddle here 在这里更新了SqlFiddle

Here is a solution. 这是一个解决方案。

I have limited the search to 6 words. 我已将搜索范围限制为6个字。 For each word, I check if it exists in the concatenated columns. 对于每个单词,我检查它是否存在于串联的列中。 I get a "score" for each record by adding +1 each time a word is found in it. 每当在其中找到一个单词时,我都会在+1上加上一个“分数”。 I return the records that have the best score. 我返回得分最高的记录。

The function : 功能 :

CREATE FUNCTION [dbo].[SEARCH_SINGLE] (
    @langId INT = 4,
    @searchString VARCHAR(MAX) = NULL
)
RETURNS TABLE
AS
RETURN
WITH words AS (
    SELECT Name as Val, ROW_NUMBER() OVER(ORDER BY Name) as Num FROM [dbo].splitstring(@searchString, ' ')
),
results AS (
    SELECT DISTINCT
        ...
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 1 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 2 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 3 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 4 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 5 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 6 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END as Nb
    FROM
        ...
    WHERE
        ...
)
SELECT 
    ...
FROM
    results
WHERE
    Nb = (SELECT MAX(Nb) FROM results)
    AND Nb <> 0

Comments? 评论?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM