Sql query for multiple names in a single search engine - sql-server

Can anyone help me to find out the SQL query for following scenario.
I have a search box, which I want to search multiple names separated by spaces.
for example : "David Jones" which gives me the result of David's details and Jones details.
select
emp.cid as empcid,
emp.name,
emp.employeeno,
info.employeeUniqueId,
info.agentId,
info.empBankCode,
info.accountNumber,
info.ibanAccNo
from tblemployee emp,
fk_tblUserEmployeeList f,
empinfo info
where
info.employee = emp.cid
and emp.cid = f.employeeid
and f.userId = 1
and
(
name like '%david%'
or emp.employeeno like '%david%'
or info.employeeUniqueId like '%david%'
or info.agentId like '%david%'
or info.empBankCode like '%david%'
or info.accountNumber like '%david%'
)
I want include Jones inside search box also, then how will the like condition changes>

This seems like a case for full-text search. After setting up full-text indices on your tblemployee, fk_tblUserEmployeeList, and empinfo tables, your query would look something like this:
SELECT
emp.cid AS empcid,
emp.name,
emp.employeeno,
info.employeeUniqueID,
info.agentID,
info.empBankCode,
info.accountNumber,
info.ibanAccNo
FROM dbo.tblemployee emp
INNER JOIN dbo.fk_tblUserEmployeeList f ON
f.employeeid = emp.cid
INNER JOIN dbo.empinfo info ON
info.employee = emp.cid
WHERE
f.userID = 1
AND
( FREETEXT(Emp.*, 'david jones')
OR FREETEXT(info.*, 'david jones')
)
gives you this data:
+--------+-------+------------+------------------+---------+-------------+---------------+-----------+
| empcid | name | employeeno | employeeUniqueID | agentID | empBankCode | accountNumber | ibanAccNo |
+--------+-------+------------+------------------+---------+-------------+---------------+-----------+
| 1 | David | NULL | david | david | david | david | david |
| 2 | Jones | NULL | jones | jones | jones | jones | jones |
+--------+-------+------------+------------------+---------+-------------+---------------+-----------+
Note that I changed your query to use the modern industry-standard join style.
Keep in mind that, to create a full-text index on a table, the table must have a single-column unique index. If one of your tables has a multi-column primary key, you'll have to add a column (see this question for more information).
A couple of notes about your naming conventions:
There's no need to preface table names with tbl (especially since you're not doing so consistently). There are loads of people telling you not to do this: See this answer as an example.
fk_tblUserEmployeeList is a bad table name: The prefixes fk and tbl don't add any information. What kind of information is stored in this table? I would suggest a more descriptive name (with no prefixes).
Now, if you don't want to go the route of using a full-text index, you can parse the input client-side before sending to SQL Server. You can split the search input on a space, and then construct the SQL accordingly.

declare #SearchString varchar(200)='David Jones', #Word varchar(100)
declare #Words table (Word varchar(100))
-- Parse the SearchString to extract all words
while len(#SearchString) > 0 begin
if charindex(' ', #SearchString)>0 begin
select #Word = rtrim(ltrim(substring(#SearchString,0,charindex(' ', #SearchString)))),
#SearchString = rtrim(ltrim(replace(#SearchString, #Word, '')))
end
else begin
select #Word = #SearchString,
#SearchString = ''
end
if #Word != ''
insert into #Words select #Word
end
-- Return Results
select t.*
from MyTable t
join #Words w on
' ' + t.MyColumn + ' ' like '%[^a-z]' + w.Word + '[^a-z]%'

Related

Extraction queries with variable last part of the search term

I have a problem with an extraction query.
I want to extract all records that begin with exactly one name.
The problem:
Not always the end of the name is this extension;
When they are present, they have no fixed length.
Example:
TabNames
id | Name
1 | Mike
2 | Mike Nell-1
3 | Mike-2
4 | Robert-1
5 | Mike Rio-NN
6 | Mike-Orio-2
.....
Name searched for: 'Mike'
Desired outcome:
Mike, Mike-2
If interested, I use SQL Server.
How can I do?
You can do this with FOR XML. You will have update the Table Names, Columns Names and Where Statement in both select statements. This is a variation of this Concatenation Script.
SELECT DISTINCT
SUBSTRING(
(
SELECT ', '+ T1.Name AS [text()]
FROM dbo.TabNames T1
WHERE T1.Name LIKE 'Mike%'
ORDER BY T1.Name
FOR XML PATH ('')
), 3, 8000) AS Names
FROM
dbo.TabNames T2
WHERE
T2.Name LIKE 'Mike%'

How to build search engine for website using sql server

I need some help with creating a simple search engine for website. Basic idea is that user will enter a string in search bar, which will compare in database key_word and get the results.
Let's say I have the following table in the SQL Server database:
|----|----------|----------------------|
| ID | URL | key_word |
|----|----------|----------------------|
| 1 | url1.com | cat short red NYC |
| 2 | url2.com | tall blue LA |
| 3 | url3.com | skinny NYC green |
| 4 | url4.com | cat black get |
|----|----------|----------------------|
Now in search bar, lets say user want to search the below string "get red cat from NYC". I want to search this in database 'key_word'.
String key = "get red cat from NYC"
What I have tried:
So far I have the following below query to search from database. This is good for if user want to search for only one word. but the string 'key' will not work here and it will return 0 result. I need some idea so I can make this better query.
SELECT *
FROM [SearchTable]
WHERE [key_Word] LIKE % key %;
What I want:
I want to change this sql server query so that it return ID=1,3,4.
So in other words. I want to take this string:
String key = "get red cat from NYC"
and first search in database the word "get". it doesn't show up so go to next word. Next word is "red", this shows up in ID=1. next word is "cat", this shows up in ID=1,4. Next word is "from", this doesn't show up in any rows. Next word is "NYC", this shows up in ID=1,3.
put all id's together and you get ID's=1,1,4,1,3.
than I want to sort it so that ID=1 shows up at top and ID=3,4 can be at button since they are tied.
I was hoping to do this by only one SQL query, because if I keep connecting to database than the speed will go down too. So I was think of some SQL Server functions?
You need a string splitter for this. See this article for some functions:
DECLARE #key VARCHAR(MAX) = 'get red cat from NYC'
SELECT t.ID
FROM tbl t
CROSS APPLY dbo.SplitStrings_XML(t.key_word, ' ') tx
INNER JOIN (
SELECT Item
FROM dbo.SplitStrings_XML(#key, ' ')
)k
ON k.Item = tx.Item
GROUP BY T.ID
ORDER BY COUNT(*) DESC
SQL Fiddle
Here is the SplitStrings_XML function:
CREATE FUNCTION dbo.SplitStrings_XML
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
The above function will not work if your string has illegal XML characters like >, <, and &. You can use other splitter but the idea stays the same.

Join tables by column names, convert string to column name

I have a table which store 1 row per 1 survey.
Each survey got about 70 questions, each column present 1 question
SurveyID Q1, Q2 Q3 .....
1 Yes Good Bad ......
I want to pivot this so it reads
SurveyID Question Answer
1 Q1 Yes
1 Q2 Good
1 Q3 Bad
... ... .....
I use {cross apply} to acheive this
SELECT t.[SurveyID]
, x.question
, x.Answer
FROM tbl t
CROSS APPLY
(
select 1 as QuestionNumber, 'Q1' as Question , t.Q1 As Answer union all
select 2 as QuestionNumber, 'Q2' as Question , t.Q2 As Answer union all
select 3 as QuestionNumber, 'Q3' as Question , t.Q3 As Answer) x
This works but I dont want to do this 70 times so I have this select statement
select ORDINAL_POSITION
, COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = mytable
This gives me the list of column and position of column in the table.
So I hope I can somehow join 2nd statement with the 1st statement where by column name. However I am comparing content within a column and a column header here. Is it doable? Is there other way of achieving this?
Hope you can guide me please?
Thank you
Instead of Cross Apply you should use UNPIVOT for this query....
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE Test_Table(SurveyID INT, Q1 VARCHAR(10)
, Q2 VARCHAR(10), Q3 VARCHAR(10), Q4 VARCHAR(10))
INSERT INTO Test_Table VALUES
(1 , 'Yes', 'Good' , 'Bad', 'Bad')
,(2 , 'Bad', 'Bad' , 'Yes' , 'Good')
Query 1:
SELECT SurveyID
,Questions
,Answers
FROM Test_Table t
UNPIVOT ( Answers FOR Questions IN (Q1,Q2,Q3,Q4))up
Results:
| SurveyID | Questions | Answers |
|----------|-----------|---------|
| 1 | Q1 | Yes |
| 1 | Q2 | Good |
| 1 | Q3 | Bad |
| 1 | Q4 | Bad |
| 2 | Q1 | Bad |
| 2 | Q2 | Bad |
| 2 | Q3 | Yes |
| 2 | Q4 | Good |
If you need to perform this kind of operation to lots of similar tables that have differing numbers of columns, an UNPIVOT approach alone can be tiresome because you have to manually change the list of columns (Q1,Q2,Q3,etc) each time.
The CROSS APPLY based query in the question also suffers from similar drawbacks.
The solution to this, as you've guessed, involves using meta-information maintained by the server to tell you the list of columns you need to operate on. However, rather than requiring some kind of join as you suspect, what is needed is Dynamic SQL, that is, a SQL query that creates another SQL query on-the-fly.
This is done essentially by concatenating string (varchar) information in the SELECT part of the query, including values from columns which are available in your FROM (and join) clauses.
With Dynamic SQL (DSQL) approaches, you often use system metatables as your starting point. INFORMATION_SCHEMA exists in some SQL Server versions, but you're better off using the Object Catalog Views for this.
A prototype DSQL solution to generate the code for your CROSS APPLY approach would look something like this:
-- Create a variable to hold the created SQL code
-- First, add the static code at the start:
declare #SQL varchar(max) =
' SELECT t.[SurveyID]
, x.question
, x.Answer
FROM tbl t
CROSS APPLY
(
'
-- This syntax will add to the variable for every row in the query results; it's a little like looping over all the rows.
select #SQL +=
'select ' + cast(C.column_id as varchar)
+ ' as QuestionNumber, ''' + C.name
+ ''' as Question , t.' + C.name
+ ' As Answer union all
'
from sys.columns C
inner join sys.tables T on C.object_id=T.object_id
where T.name = 'MySurveyTable'
-- Remove final "union all", add closing bracket and alias
set #SQL = left(#SQL,len(#SQL)-10) + ') x'
print #SQL
-- To also execute (run) the dynamically-generated SQL
-- and get your desired row-based output all at the same time,
-- use the EXECUTE keyword (EXEC for short)
exec #SQL
A similar approach could be used to dynamically write SQL for the UNPIVOT approach.

Bulk Replace in SQL-Server

Is it possible to do bulk replace without while loop or what is the best way
Table-1
+-------+--------+
| name | value |
+-------+--------+
| #1# | one |
| #2# | two |
| #3# | three |
+-------+--------+
Table-2 (updated: there is more than one different tokens in table2)
+-----------------------+
| col1 |
+-----------------------+
| string #1# string #2# |
| string #2# string #1# |
| string #3# string #2# |
+-----------------------+
I like to replace all token from Table-2 with Table-1's value column respectively.
Expected Result
+-------------------------+
| col1 |
+-------------------------+
| string one string two |
| string two string one |
| string three string two |
+-------------------------+
Current solution with While loop
declare #table1 table(name nvarchar(50),value nvarchar(50))
insert into #table1 values('#1#','one'),('#2#','two'),('#1#','three')
declare #table2 table(col1 nvarchar(50))
insert into #table2 values('string #1# string #2#'),('string #2# string #1#'),('string #3# string #2#')
WHILE EXISTS (SELECT 1 FROM #table2 t2 INNER JOIN #table1 t1 ON CHARINDEX(t1.name,[col1])>0)
BEGIN
UPDATE #table2
SET col1=REPLACE(col1,name,value)
FROM #table1
WHERE CHARINDEX(name,[col1])>0
END
select * from #table2
Thanks
I suppose you use Sql Server (you've tagged with tsql):
I've run this query on Sql fiddle with 2012 version, but on my PC I've tried with 2008r2 version.
You can procede in this way:
UPDATE table2
SET col1 = REPLACE(col1,
(SELECT name FROM table1 WHERE col1 LIKE '%' + table1.NAME + '%'),
(SELECT value FROM table1 WHERE col1 LIKE '%' + table1.NAME + '%'))
Sql Fiddle
If you want show only the value without UPDATE you can proceed in this way:
SELECT REPLACE(T2.col1, T1.name, T1.value)
FROM table1 T1
JOIN table2 T2
ON T2.col LIKE '%' T1.name + '%'
EDIT
After editing of question / comment my answer is not complete because on the same row can exist more one token. I'm thinking... :)
I thought: :D
IMHO: You must create a loop on your table because the presence of several tokens don't resolve with a single UPDATE statement with subquery, because as you written, the subquery return more than one value.
In Sql Server the REPLACE function change only token, so if you want change in one step two token you must nest your REPLACE function, but we have a number undefined of token in a row so we can't prevent to apply the exact number of REPLACE. An help you can have using a cursor and a dynamic SQL query build on runtime, so you can do a single UPDATE per row. If you want a guide line to use CURSOR and DYNAMIC SQL, please write me. Good night ;)
You can do bulk replacement with this simple piece of code:
update #table2
set col1= left(a.col1,6)+' ' + b.value from #table2 a
join #table1 b on b.name=substring(a.col1,8,3)
select * from #table2
Basically, it updates the column with a new field value.

TSQL: Extract XML Nested Tags Into Columns

Using SQLServer2008R2
I currently have XML tags with data inside the XML tags (not between them), such as:
<zooid="1"><animals key="all" zebras="22" dogs="0" birds="4" /><animals key="all" workers="yes" vacation="occasion" /> ... *(more)*</zooid>
<zooid="2"><animals key="house" zebras="0" dogs="1" birds="2" /><animals key="house" workers="no" vacation="no" /> ... *(more)*</zoodid>
If I query the XML or use the value function against it, it returns blank values because it tries to read between tags - where no value exists. I need it to read inside of the tags, parse out the values before the equal sign as columns and the values between the quotations as values inside those columns (granted, I could create a function that could do this, but this would be quite meticulous, and I'm curious if something like this already exists). What it should look like this in columns:
Key | Zebras | Dogs | Birds | Key | Workers | Vacation | ... *(more)*
... and this in rows of data:
all | 22 | 0 | 4 | all | yes | occasion | ... *(more)*
house | 0 | 1 | 2 | house | no | no | ... *(more)*
So the final output (just using the two XML rows from the beginning for now), would look like the below data in table form:
Key | Zebras | Dogs | Birds | Key | Workers | Vacation | ... *(more)*
================================================================
all | 22 | 0 | 4 | all | yes | occasion | ... *(more)*
house | 0 | 1 | 2 | house | no | no | ... *(more)*
Other than querying against XML, using the .query tool and even trying the .node tool (using CROSS APPLY see this thread), I haven't been able to generate this.
Try this one -
DECLARE #YourXML NVARCHAR(MAX)
SELECT #YourXML = '
<zooid="1">
<animals key="all" zebras="22" dogs="0" birds="4" />
<animals key="all" workers="yes" vacation="occasion" />
</zooid>
<zooid="2">
<animals key="house" zebras="0" dogs="1" birds="2" />
<animals key="house" workers="no" vacation="no" />
</zoodid>'
DECLARE #XML XML
SELECT #XML =
REPLACE(
REPLACE(#YourXML, 'zooid=', 'zooid id=')
, '</zoodid>'
, '</zooid>')
SELECT
d.[Key]
, Dogs = MAX(d.Dogs)
, Zebras = MAX(d.Zebras)
, Birds = MAX(d.Birds)
, Workers = MAX(d.Workers)
, Vacation = MAX(d.Vacation)
FROM (
SELECT
[Key] = t.p.value('./#key', 'NVARCHAR(50)')
, Zebras = t.p.value('./#zebras', 'INT')
, Dogs = t.p.value('./#dogs', 'INT')
, Birds = t.p.value('./#birds', 'INT')
, Workers = t.p.value('./#workers', 'NVARCHAR(20)')
, Vacation = t.p.value('./#vacation', 'NVARCHAR(20)')
FROM #XML.nodes('/zooid/animals') t(p)
) d
GROUP BY d.[Key]
Your xml appears invalid. How are you able to specify an element like this: ? Generally xml structure is <(elementName) (Attribute)="(Value)"/>. Unless I am mistaken if you are casting text to xml the way it is it will fail. Saying that I can show a working example for proper xml in a self extracting example that will run in SQL Managment Studio as is.
declare #text1 varchar(max) = '<zooid="1"><animals="all" zebras="22" dogs="0" birds="4" /><animals="all" workers="yes" vacation="occasion" /></zooid>'
, #text2 varchar(max) = '<a zooid="1"><b animals="all" zebras="22" dogs="0" birds="4" /><b animals="all" workers="yes" vacation="occasion" /></a>'
, #xml xml
;
begin try
set #xml = cast(#text1 as xml)
end try
begin catch
set #xml = '<ElementName Attribute="BadData Elements are not named" />'
end catch
select #xml
begin try
set #xml = cast(#text2 as xml)
end try
begin catch
set #xml = '<ElementName Attribute="BadData" />'
end catch
select
#xml.value('(/a/b/#animals)[1]', 'varchar(20)') as AnimalsValue
, #xml.value('(/a/b/#zebras)[1]', 'int') as ZebrasValue
, #xml.value('(/a/b/#dogs)[1]', 'int') as DogsValue
, #xml.value('(/a/b/#birds)[1]', 'int') as BirdsValue
, #xml.value('(/a/b/#workers)[1]', 'varchar(16)') as Workers
, #xml.value('(/a/b/#vacation)[1]', 'varchar(16)') as Vacation
The '.value' method is a syntax for querying xml in SQL. I am basically finding the elements(I did generics of a that contained b). Then once at the level I want '#animals' stands for 'attribute of name animals'. The [1] is a position since I can only return one thing at a time, so I chose the first position. Then it needs to a datatype to return. Text is varchar and numbers are ints.
XML query methods: http://msdn.microsoft.com/en-us/library/ms190798.aspx

Resources