combine two CONTAINS calls within where clause - sql

I'm trying to create the following SQL query (as in, this is an example of the final query) :-
DECLARE #SearchQuery AS NVARCHAR(100) = 'yellow bird'
SELECT Id, Name
FROM dbo.FooBars
WHERE CONTAINS(Name, N'FORMSOF(Thesaurus, yellow)')
AND CONTAINS(Name, N'FORMSOF(Thesaurus, bird)')
Notice how I've got two CONTAINS lines? This is because the search query has two words in it. (a space is the delimiter). This query could be from 1 to n words.
How can I generate this SQL code based upon the number of words in the search query?

You can put the "AND" in the contains itself, so it coud be
select *
from dbo.FooBars
where contains(Name, 'FORMSOF(Thesaurus, yellow) AND FORMSOF(Thesaurus, bird)')
with the string 'FORMSOF(Thesaurus, yellow) AND FORMSOF(Thesaurus, bird)'
built up into a variable like
declare #searchCriteria varchar(200)
set #searchCriteria = 'Some string you built up'
select *
from dbo.FooBars
where contains(Name, #searchCriteria)

Related

Finding Where Clause Criteria in SQL Queries

I have a Table which has 3 different Select queries.
e.g.
Staff
------------------------------------------------------------
ID Code Name Phone DOB Email Addr1 Addr2 Addr3
Query1
Select ID, Code, Phone From Staff Where Code = 'ABC'
Query2
Select ID, Code, Phone From Staff Where Name = 'ABCXYZ' And Code = 'B'
Query3
Select ID, Code, Phone From Staff Where Phone= '1234' And Email = 'a#b'
These 3 queries are there in 3 different stored procedure.
I want to find the names of Attributes which i have used in all my Where clauses. But want to Automate this as I have more than 100 tables to look for
Something like this
exec fxGetWhereColList ('Staff');
Result:
Code
Name
Phone
Email
I agree with all posts before, it's a complex case.
I post you a small idea to help you in your work.
If you can retrieve each execution plan, you can parse XML result and get the impacted columns.
For example: On the node <Predicate>
Like Lamak says, doing this right is nearly impossible, at least in SQL.
To do it properly you should use a SQL Parser for the SQL variant you are using. A SQL Parser will identify the objects referenced in the statement, and the elements of the where clauses.
But if you know that the queries are simple and look like the one you are listing, you can use some simple queries to pick apart the statements. I have made an example for a single statement:
declare
#foo nvarchar(max)=N'Select ID, Code, Phone From Staff Where Phone= ''1234'' And Email = ''a#b'' '
, #From nvarchar(max)=' From '
, #Table nvarchar(max)
declare #a int
-- find FROM
set #a= PATINDEX('%'+#From+'%',#foo)
-- Find statement to the right of from
set #foo=ltrim(RIGHT(#foo,len(#foo)-(#a)))
-- Find first space
set #a=CHARINDEX(' ',#foo)
-- find first word, we assume it is the table name
set #Table=ltrim(rtrim(LEFT(#foo,#a)))
-- Find WHERE statement
set #foo=ltrim(rtrim(replace(right(#foo,len(#foo)-#a),'Where','')))
-- Now find matching columns in table, I am using SQL Server so I look up column names in information_schema.columns
select #Table,column_name
from INFORMATION_SCHEMA.COLUMNS
--from (values ('ID'),('Code'),('Name'),('Phone'),('DOB'),('Email'),('Addr1'),('Addr2'),('Addr3'))cols(column_name)
where TABLE_NAME=#table
and #foo like '%'+column_name+'%'
This solution will only work for some simple statements, it assumes a lot of stuff.

How do i match text excluding difference between brackets in SQL server?

I've an audit log table that has a series of strings like
"Some Text[details]more text"
the pattern before and after the [details] indicates what the audit trail entry type is. The text in the bracket indicates what it is for. I want to create a query to only find the audit entries i'm after. I thought to use the following like "Some Text[%]more text" but it does not seem to work
When I run the below query it retrieves the expected results + more
select top 1000 *
from Table
where NAME like 'Some Text%'
When I try
select top 1000 *
from Table
where NAME like 'Some Text[%'
Nothing comes back is the
Brackets have a special syntactic meaning in regular expressions. So you need to escape the bracket if you want to use it in your query:
select top 1000 *
from Table
where NAME like 'Some Text[[]%'
Special characters can be escaped by placing them inside brackets. In this case, the opening bracket itself needs to be placed inside brackets, i.e. [[]
try the t-sql code below:
create table dbo.tblTest (ID int IDENTITY(1, 1), strings varchar(200))
insert dbo.tblTest
select 'i have to find this text excluding [these strings inside the brackets]'
union all select '[don''t include these texts inside the brackets]. but include these!'
union all select 'why can''t i search for these, but [not these]? nothing seems to work when brackets are involved. :('
select *
from dbo.tblTest
DECLARE #stringToSearchFor VARCHAR(200) = 'nothing seems'
SELECT t.*
FROM dbo.tblTest t
JOIN
(SELECT nobrackets.*
FROM
(SELECT cleanString = REPLACE(t.strings, SUBSTRING(t.strings, CHARINDEX('[', t.strings), CHARINDEX(']', t.strings) - CHARINDEX('[', t.strings) + 1), '')
, t.ID
FROM dbo.tblTest t) noBrackets
WHERE noBrackets.cleanString LIKE CONCAT('%', #stringToSearchFor, '%')) tNoBracket ON tNoBracket.ID = t.ID
If you will take sometime here in stackoverflow, a lot of post will answer your question.. Please see below.
You need to use [ ] bracket to surround the text with special character..
The query now look something like:
select top 1000 *
from Table
where NAME like '[Some Text[]%'
SQL LIKE CONDITION
SQL Server LIKE containing bracket characters
select top 1000 *
from Table
where NAME like 'Some Text[[%] more text'
or
select top 1000 *
from Table
where NAME like 'Some Text![%] more text' ESCAPE '!'
How can I escape square brackets in a LIKE clause?

SQL inner join list split for an SSRS report

I have a field that lists all language descriptions that a product has and the field can contain data like:
EN;FR;DE
It will always be a two letter language code followed by a semi colon.
I then have a stored procedure that looks for all products with a particular language code. Simply done by:
WHERE
ext.languages LIKE '%' + #language + '%'
The #language variable might just represent the letters EN for example. Now when I want to find a product that has both French and English languages on I need to pass in 'FR, EN' for the language variable. Now I have a custom function in SQL that splits the language variable into rows so I effectively have
Row 1-EN
Row 2-FR
Now I need to check my ext.language field to see if both those values exist.
I have attempted to do:
INNER JOIN MyFunctionsDatabase.dbo.listSplit(#language) as var1
ON ext.language LIKE '%'+var1.StringLiteral+'%'
This only brings back products where it contains either french or english I need it to bring back values where it contains both English and French.
Any advice would be greatly appreciated:
Try with below script, this i write for 3 language but can be done generic
Declare #Product AS Table(ProductID INT, [Language] Varchar(500))
Insert Into #Product Values(1,'EN;FR;DE'),(2,'EN'),(3,'EN;DE'),(4,'EN;FR')
SELECT * FROM
(
Select P.ProductID,L.Value
From #Product P
CROSS APPLY dbo.[udfSplit]([Language],';') L
) Product
PIVOT
(
Count(Value)
For Value in (EN,FR,DE)
)
AS PV
Where EN=1 AND FR=1
I'd be inclined to use a function that accepts a delimited string containing the language codes to check for and the string to check. It checks that each language code is in the string and returns false as soon as one of the desired languages isn't found. If everything is found it returns true.
Your sql would look like
select *
from mytable
where CheckHasAllLanguages(language, #languagesToCheck) = 1
I would make your parameter a multi-select and have each individual language be a selection. You could even feed the parameter with values from the database so it would automatically update if there is a new language. I'm going to call this parameter #LangMultiSelect
Since you only want items that items that match all of the selections you need to pass in a second parameter with the number of items that have been selected. In the properties of your dataset you can add another parameter that is set by an expression. Name it #LangCount and use the expression:
=Parameters!LangMultiSelect.Count
Then use a SQL query similar to this:
SELECT Name
FROM (
SELECT Name,
COUNT(*) OVER(PARTITION BY pt.id) AS lCount
FROM ProductTable AS pt
INNER JOIN MyFunctionsDatabase.dbo.listSplit(#language) AS var1 ON var1.id=pt.id
WHERE pt.language IN (#LangMultiSelect)
) AS t
WHERE t.lCount = #LangCount
That query uses the COUNT() aggregate as a window function to determine the number of matches the item has and then only returns results that match all of the selections in the multi-select parameter.
It works because I am splitting the count by a field that is the same for all of the item names that are the same item but in a different language. If you don't have a field like that this won't work.

Sql Server Full Text Search Single Result Column Searched Across Multiple Columns

I am trying to implement an AutoComplete search box(like google) using SQL Server 2008 and Full Text Search.
Say I have 3 columns that I want to search across and have created the proper indexes and what not.
The columns are ProductName, ProductNumber, and Color...
For the user input I want to search for possible matches across all three columns and suggest the proper search term.
So say the user starts typing "Bl"
id like to return a single column containtng results like "Black" "Blue" which come from the Color column and also any matches from the other two columns(like ProductNumber: BL2300)
So basically I need to search across multiple columns and return a single column as the result. Is there a way to do this?
UPDATED follwoing comment of op If you created a FULLTEXT INDEX on different columns, then you can simple use CONTAINS or FREETEXT to look on one of them, all of them, or some of them. Like this:
SELECT *
FROM YourTable
WHERE CONTAINS(*, #SearchTerm);
If you want to look on all the columns that are included in the FULLTEXT INDEX. or:
SELECT *
FROM YourTable
WHERE CONTAINS((ProductName, ProductNumber, Color), #SearchTerm);
If you want to specify the columns that you want to search.
If you need the results in one column, you are gonna have to do a UNION and do a search for every column you want to be searched.
SELECT *
FROM YourTable
WHERE CONTAINS(ProductName, #SearchTerm)
UNION
SELECT *
FROM YourTable
WHERE CONTAINS(ProductNumber, #SearchTerm)
UNION
SELECT *
FROM YourTable
WHERE CONTAINS(Color, #SearchTerm)
If you do not need to associate the single columns, something like
SELECT * FROM Table WHERE ProductName LIKE #SearchTerm + '%'
UNION
SELECT * FROM Table WHERE ProductNumber LIKE #SearchTerm + '%'
UNION
SELECT * FROM Table WHERE Color LIKE #SearchTerm + '%'
is a good point to start from.

SP to find keywords like a list or strings

In my mssql database I have a table containing articles(id, name, content) a table containing keywords(id, name) and a link table between articles and keywords ArticleKeywords(articleId, keywordID, count). Count is the number of occurrences of that keyword in the article.
How can I write a SP that gets a list of comma separated strings and gives me the articles that have this keywords ordered by the number of occurrences of the keywords in the article?
If an article contains more keywords I want to sum the occurrences of each keyword.
Thanks, Radu
Although it isn't completely clear to me what the source of your comma-separated string is, I think what you want is an SP that takes a string as input and produces the desired result:
CREATE PROC KeywordArticleSearch(#KeywordString NVARCHAR(MAX)) AS BEGIN...
The first step is to verticalize the comma-separated string into a table with the values in rows. This is a problem that has been extensively treated in this question and another question, so just look there and choose one of the options. Whichever way you choose, store the results in a table variable or temp table.
DECLARE #KeywordTable TABLE (Keyword NVARCHAR(128))
-- or alternatively...
CREATE TABLE #KeywordTable (Keyword NVARCHAR(128))
For lookup speed, it is even better to store the KeywordID instead so your query only has to find matching ID's:
DECLARE #KeywordIDTable TABLE (KeywordID INT)
INSERT INTO #KeywordTable
SELECT K.KeywordID FROM SplitFunctionResult S
-- INNER JOIN: keywords that are nonexistent are omitted
INNER JOIN Keywords K ON S.Keyword = K.Keyword
Next, you can go about writing your query. This would be something like:
SELECT articleId, SUM(count)
FROM ArticleKeywords AK
WHERE K.KeywordID IN (SELECT KeywordID FROM #KeywordIDTable)
GROUP BY articleID
Or instead of the WHERE you could use an INNER JOIN. I don't think the query plan would be much different.
For the sake or argument lets say you want to look-up all articles containg the keywords Foo, Bar and Shazam.
ALTER PROCEDURE spArticlesFromKeywordList
#KeyWords varchar(1000) = 'Foo,Bar,Shazam'
AS
SET NOCOUNT ON
DECLARE #KeyWordInClause varchar(1000)
SET #KeyWordInClause = REPLACE (#KeyWords ,',',''',''')
EXEC(
'
SELECT
t1.Name as ArticleName,
t2.Name as KeyWordName,
t3.Count as [COUNT]
FROM ArticleKeywords t3
INNER JOIN Articles t1 on t3.ArticleId = t1.Id
INNER JOIN Keywords t2 on t3.KeywordId = t2.Id
WHERE t2.KeyWord in ( ''' + #KeyWordInClause + ''')
ORDER BY
3 descending, 1
'
)
SET NOCOUNT OFF
I think I understand what you are after so here goes ,(not sure what lang you are using but) in PHP (from your description) I would query ArticleKeywords using a ORDER BY count DESC statement (i.e. the highest comes first) - Obviously you can "select by keywordID or articleid. In very simple terms (cos that's me - simple & there may be much better people than me) you can return the array but create a string from it a bit like this:
$arraytostring .= $row->keywordID.',';
If you left join the tables you could create something like this:
$arraytostring .= $row->keywordID.'-'.$row->name.' '.$row->content.',';
Or you could catch the array as
$array[] = $row->keywordID;
and create your string outside the loop.
Note: you have 2 fields called "name" one in articles and one in keywords it would be easier to rename one of them to avoid any conflicts (that is assuming they are not the same content) i.e. articles name = title and keywords name= keyword