简体   繁体   中英

Search by spliting string in multiple columns of SQL table?

I have a Books table:

Title                     Board    Class
----------------------------------------
Interactive English       CBSE       9
Interactive  Math         ICSE      10
Hindi                     CBSE       9

I have a search textbox in asp.net website. If user types in "9 CBSE" in textbox, my select query should return

Title                      Board    Class
-----------------------------------------
Interactive English        CBSE        9
Hindi                      CBSE        9

And if user types in "9 English" it should return

Title                      Board    Class
------------------------------------------
Interactive English        CBSE        9

So what should my select query be to match textbox value to all these three columns ?

I can not verify this because I have no access to a Sqlserver right now but this should work:

select
 *
from
 books
where
  patindex('%' + left(_textbox_contents_, charindex(' ') - 1) + '%',  Title + Board + Class) > 0 
  and  patindex('%' + substring(_textbox_contents_, charindex(' ') + 1) + '%',  Title + Board + Class) > 0 

If you can break up the string in c# before you send the query and create variables holding the parts eg:

string1 = "9" string2 = "English"

Then you need to appened a '%' to each side giving you:

string1 = "%9%" string2 = %English%"

Then pass the query as:

Select *
  from books
 where (title like string1
     or title like string2)
    or (board like string1
     or board like string2)
    or (class like string1
     or class like string2);

I figure splitting the search string up before sending the query is easier than trying to do it with SQL where you'll need to do a bunch of instrings and substrings.

In order to solve this issue, we should plan how to attach the problem in hand.

  1. We should extract from the search input the Title or Board values and Number value if it available.
  2. We should construct our SQL Query in such order that it will match our extract value from Title or Board columns.
  3. We should try to find a Number match in our SQL Query only if Number is indeed extracted (If it extracted than it has a value different from null or Whitespace ).

SearchInput: Entity representing the search input values

public class SearchInput
{
    public string TitleOrBoard { get; set; }
    public string Number { get; set; }
}

CreateSearchInput: Transform the input string into the above entity.

Search if the first value in the string is a number. If the first value is a number than assign it to Number and remove the number from the input string.

public static SearchInput CreateSearchInput(string input)
{
    var inputSplitted = input.Trim().Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
    int number;

    string searchText = "";
    if (int.TryParse(inputSplitted.First(), out number))
    {
        searchText = String.Join(" ", inputSplitted.Skip(1));
    }

    var searchInput = new SearchInput()
    {
        Number = number.ToString(),
        TitleOrBoard = searchText
    };

    return searchInput;
}

ConstructSearchQuery: Building the SQLQuery based on the SearchInput entity values.

public static string ConstructSearchQuery(SearchInput searchInput)
{
    StringBuilder sb = new StringBuilder("SELECT * FROM Books WHERE");
    sb.AppendFormat(" ([Title] LIKE '%{0}%' OR [Board] LIKE '%{0}%')", searchInput.TitleOrBoard);
    if (!String.IsNullOrWhiteSpace(searchInput.Number))
    {
        sb.AppendFormat(" OR Class = '%{0}%'", searchInput.Number);
    }
    return sb.ToString();
}

Main function

public static void Main()
{
    string input = "9 English";
    var searchInput = CreateSearchInput(input);
    string sqlQuery = ConstructSearchQuery(searchInput);
    Console.WriteLine("Search: Number[{0}] TitleOrBoard[{1}]", searchInput.Number, searchInput.TitleOrBoard);
    Console.WriteLine("SQL Query: {0}", sqlQuery);
}

Output:

Search: Number[9] TitleOrBoard[English]
SQL Query: SELECT * FROM Books WHERE ([Title] LIKE '%English%' OR [Board] LIKE '%English%') OR Class = '%9%'

.NET Fiddle

try this,

        declare @t table (Title  varchar(100), Board varchar(10),   Class int)
        declare @chk varchar(10)='9 English'
        insert into @t (Title, Board,   Class) values
        ('Interactive English','CBSE',9),
        ('Interactive  Math','ICSE',10),
        ('Hindi','CBSE',9)



select * from @t where  cast(Class as varchar(2)) +' '+Board  like '%' + @chk +'%' 
or Board +' '+cast(Class as varchar(2)) like '%' + @chk +'%' or

cast(Class as varchar(2)) +' '+case when charindex(' ', title)=0 then  
title else REverse(substring(reverse(title),1,charindex(' ', REVERSE(title))-1)) end 
like '%' +@chk +'%' or

case when charindex(' ', title)=0 then  
title else REverse(substring(reverse(title),1,charindex(' ', REVERSE(title))-1)) end +' '+cast(Class as varchar(2)) 
like '%' +@chk +'%' 

If you're willing to find all results where any of search keywords match at least one value this should work for you:

private string BuildQueryString(List<string> keywords)
{
    List<string> numbers = keywords.Where(keyword => keyword.All(Char.IsDigit))
                                   .ToList();

    List<string> words = keywords.Except(numbers)
                                 .Select(keyword => string.Format("('{0}')", keyword))
                                 .ToList();

    var queryBuilder = new StringBuilder();

    if (words.Count > 0)
    {
        queryBuilder.AppendFormat(@"
            CREATE TABLE #Keywords
            (
                Keyword NVARCHAR(MAX)
            )

            INSERT INTO #Keywords (Keyword)
            VALUES {0} ",

            string.Join(", ", words));
    }

    queryBuilder.AppendFormat(@"
        SELECT Title, Board, Class
        FROM Books ");

    string numbersFilter = string.Format("Class IN ({0})", string.Join(", ", numbers));
    string wordsFilter = @"EXISTS (SELECT TOP 1 1
                                   FROM #Keywords
                                   WHERE Title LIKE '%' + Keyword + '%'
                                      OR Board LIKE '%' + Keyword + '%')";

    if (numbers.Count > 0 && words.Count > 0)
    {
        queryBuilder.AppendFormat(@"
            WHERE {0} AND {1}", numbersFilter, wordsFilter);
    }
    else if (numbers.Count > 0)
    {
        queryBuilder.AppendFormat(@"
            WHERE {0}", numbersFilter);
    }
    else if (words.Count > 0)
    {
        queryBuilder.AppendFormat(@"
            WHERE {0}", wordsFilter);
    }

    return queryBuilder.ToString();
}

Use case example:

txtSearch.Text = "CBSE 9 MEH 23 Hey 90 N0 Word1";

List<string> keywords = txtSearch.Text
    .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
        .ToList();

string queryString = BuildQueryString(keywords);

Your query string will return next query for this Search Text :

询问

And for the table content you gave in question description, result of this query will yield the next result:

表结果

Both of these records match on 9 in Class column and CBSE in Board column. You can change ANDs and ORs inside query builder if you want different behavior, but generally, this is what you asked for. Hope it helps.

Using a full text query you can give the end user a lot of flexibility with how they construct their search. Also you won't need to perform as much parsing of the search string, and the search is typically faster than using LIKE.

1. Create the full text catalog and index.

CREATE FULLTEXT CATALOG [ftcat_Books] WITH ACCENT_SENSITIVITY = OFF
GO

CREATE FULLTEXT INDEX ON [Books] ([Title] LANGUAGE 'English', [Board] LANGUAGE 'English', [Class] LANGUAGE 'English')
KEY INDEX [PK_Books] ON ([ftcat_Books], FILEGROUP [PRIMARY])
WITH (CHANGE_TRACKING = AUTO, STOPLIST = OFF)

Your Books.Class column has to be varchar or nvarchar for this to work.

I recommend using no stoplists ( STOPLIST = OFF ) so that single digit numbers, like 9 in your example, are not ignored.

More about accent sensitivity (This example uses no accent sensitivity)

More about change tracking (This example uses change tracking)

2. Convert user queries to boolean syntax.

When a user types 9 CBSE it will have to be converted to "9" AND "CBSE" . Each term must be surrounded in double quotes and separated by "AND". (You can also use OR, NOT, NEAR, and wildcards but I am not sure you need them in your case.)

Here's a simple way to convert to the boolean syntax you need using .NET regex (basically, replace each series of 1+ spaces with " AND " and surround the result with more double quotes):

// assume searchString = "9 CBSE"
string searchStringBoolean = "\"" + Regex.Replace(searchString, @"\s+", "\" AND \"") + "\"";

Keep in mind this is a very simple approach. You will need to consider what to do if the user already typed double quotes or AND in their search string.

3. Run the full text query.

-- @SearchStringBoolean is the boolean string created in step 2
SELECT TOP 100 Books.Title, Books.Board, Books.Class
FROM ContainsTable(Books, *, @SearchStringBoolean) as FullTextResults
join Books on FullTextResults.KEY = Books.Id
ORDER BY FullTextResults.RANK desc

This returns the top 100 matches with the best matches sorted first.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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