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.
Title
or Board
columns. 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%'
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.