Query to Search All possible words - mysql

I need to write a Delphi 7 and mysql database query which would return the records including ALL words in the submitted name. So query will return records which has all those name words but can have different order.
For example, if search string is John Michael Smith, query should be able to return records with names such as John Smith Michael, Michael Smith John, Smith John Michael or other combination with all those words there.
As can be seen return only records which still has all words in name field but can have different order.
I can't figure out how to write a query for such requirement that I have. Please help.
procedure Tfrm_Query.Button1Click(Sender: TObject);
var
mask : string;
begin
mask:='''%'+StringReplace(Edit1.text,' ','%',[rfReplaceAll, rfIgnoreCase])+'%''';
if Edit1.Text > '' then
begin
Adosorgulama.Close;
Adosorgulama.SQL.Clear;
Adosorgulama.SQL.Add('SELECT * FROM stok.product');
Adosorgulama.SQL.ADD('Where (P_Name like '+mask+') limit 50');
Adosorgulama.Open;
end;
end;
as a result;
edit1.text:='Jo Mich'; // Result Ok!
edit1.text:='Smi Jo Mic'; //No result
edit1.text:='Mich Sm'; // No result

Instead of replacing spaces with %, you could replace them with % AND P_Name LIKE %:
mask:='''WHERE (P_Name LIKE %'+StringReplace(Edit1.text,' ','% AND P_Name LIKE %',[rfReplaceAll, rfIgnoreCase])+'%)''';
Apologies if there is some problem with the syntax (I don't know Delphi), but if Edit1.text:= 'John Michael Smith' this should generate the following WHERE clause:
WHERE (P_Name LIKE %John% AND P_Name LIKE %Michael% AND P_Name LIKE %Smith%)
Which should find all records where P_Name contains the strings 'John', 'Michael' and 'Smith'.
Then, of course, instead of
Adosorgulama.SQL.ADD('Where (P_Name like '+mask+') limit 50');
you'd do something like
Adosorgulama.SQL.ADD(mask + ' limit 50');
If the input can contain extraneous spaces, you will need to remove those first, otherwise this won't work.
Forming SQL queries with string concatenation could make your application vulnerable to SQL injection, just so you know. I don't know how to do prepared statements with Delphi, so I can't help you there.

Write a function that can split the name according to space. Use the following code inside a loop that is looping split results.
Declare #sqlq as nvarchar(max);
-- loop start
#sqlq = sqlq + 'Select * from mytable where names';
#sqlq = sqlq + 'like ''%' + loopvalue + '%''';
--loop end
Exec #sqlq

You can build table of words dynamically. To find yours match do query that join both tables in possible match, and by grouping results test it - is name have all of words, try this:
WITH
words AS (SELECT 'John' AS word FROM dual union
SELECT 'Michael' FROM dual union
SELECT 'Smith' FROM dual ) , --build your table of words (this is example on oracle DB engine)
names AS (SELECT 'John Michael Smith' AS name FROM dual UNION
SELECT 'John SmithMichael' FROM dual union
SELECT 'Smith Michael' FROM dual union
SELECT 'Smith Michael John' FROM dual union
SELECT 'John' FROM dual union
SELECT 'John John' FROM dual union
SELECT 'John John John' FROM dual union
SELECT 'xyz abc' FROM dual ) --this is simulation of yours table of names
SELECT name, Count(DISTINCT word)
FROM names, words
WHERE ' ' || name || ' ' LIKE '% ' || word || ' %'
GROUP BY name
HAVING Count(DISTINCT word) = (SELECT Count(1) FROM words)
;

Problem is solved.
Delphi + MySQL connection with word order with the following code, regardless of calls can be made. Thank you for inspiration. Respects.
Database model;
CREATE TABLE IF NOT EXISTS `TableName` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`PNUMBER` varchar(20) DEFAULT NULL,
`PNAME` varchar(255) DEFAULT NULL,
`PBARCODE` varchar(30) DEFAULT NULL,
`PSearch` mediumtext,
PRIMARY KEY (`ID`),
FULLTEXT KEY `PSearch` (`PSearch`)
) ENGINE=MyISAM DEFAULT CHARSET=latin5 ;
Psearch = PNUMBER + PNAME + PBARCODE ...; (Type in all areas PSearch)
Delphi7 Code;
procedure TForm1.Button1Click(Sender: TObject);
var
mask : string;
begin
mask:='+'+StringReplace(Edit1.text,' ','* +',[rfReplaceAll, rfIgnoreCase])+'*';
if Edit1.Text > '' then
begin
Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Add('SELECT MATCH(PSearch) AGAINST("'+mask+'" IN BOOLEAN MODE), tablename.* FROM database.tablename');
Query1.SQL.Add('WHERE MATCH(PSearch) AGAINST("'+mask+'" IN BOOLEAN MODE) limit 300;');
Query1.Open;
end; end;

Related

Is it possible that I could find a row contains a string? Assume that I do not know which columns contain a string

I know that there are several ways to find which row's column contains a string, like using [column name] regexp ' ' or [column name] like ' '
while currently what I need some help is I have a table with several columns, all of there are varchar or text and I am not sure which column contains a certain string. Just say that I want to search a "xxx from a table. Several different columns could contain this string or not. Is there a way that I could find which column contains this string?
I have a thinking and the solution could be
select * from [table name] where [column1] regexp 'xxx' or
[column2] regexp 'xxx' or ...... [column39] regexp 'xxx' or .....
[colum60] regexp 'xxx' or ... or [column 80] regexp 'xxx';
I do not want the query like this. Is there another effective way?
To give a better example, say that we are searching for a table that belongs to a blog.
We have title, URL, content, key words, tag, comment and so on. Now we just say, if any blog article is related to "database-normalization", this word may appear in the title, URL or content or anywhere, and I do not want to write it one by one like
where title regexp 'database-normalization' or content regexp 'database-normalization' or url regexp 'database-normalization'......
as when there are hundreds columns, I need to write a hundred, or in this case is there an effective way instead of write hundred or statement? Like using if-else or collections or some others to build the query.
If you want a pure dynamic way, you can try this. I've tried it long back on sql-server and hope it may help you.
#TMP_TABLE -- a temporary table
- PK, IDENTITY
- TABLE_NAME
- COLUMN_NAME
- IS_EXIST
INSERT INTO #TMP_TABLE (TABLE_NAME,COLUMN_NAME)
SELECT C.TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.TABLE_NAME = <your-table> AND C.DATA_TYPE = 'varchar'; -- you can modify it to handle multiple table at once.
-- boundaries
SET #MINID = (SELECT ISNULL(MIN(<PK>),0) FROM #TMP_TABLE );
SET #MAXID = (SELECT ISNULL(MAX(<PK>),0) FROM #TMP_TABLE );
WHILE ((#MINID<=#MAXID) AND (#MINID<>0))
BEGIN
SELECT #TABLE_NAME = TABLE_NAME,#COLUMN_NAME = COLUMN_NAME
FROM #TMP_TABLE
WHERE <PK> = #MINID;
SET #sqlString = ' UPDATE #TMP_TABLE
SET IS_EXIST = 1
WHERE EXIST (SELECT 1 FROM '+ #TABLE_NAME+' WHERE '+ #COLUMN_NAME +' = ''demo.webstater.com'') AND <PK> = '+ #MINID;
EXEC(#sql) ;
SET #MINID = (SELECT MIN(<PK>) FROM #TMP_TABLE WHERE <PK> > #MINID );
END
SELECT * FROM #TMP_TABLE WHERE IS_EXIST = 1 ; -- will give you matched results.
If you know the columns in advance, what you proposed is probably the most effective way (if a little verbose).
Otherwise, you could get the column names from INFORMATION_SCHEMA.COLUMNS and construct dynamic SQL based on that.
His question is not to query specific columns with like clause. He has been asking to apply same pattern across columns dynamically.
Example: Table having 3 columns - FirstName, LastName, Address and pattern matching is "starts with A" then resulting query should be:
Select * From Customer where FirstName like 'A%" or LastName like 'A%" or Address like 'A%'
If you want to build query in business layer, this could easily be done with reflection along with EF.
If you are motivated to do in database then you can achieve by building query dynamically and then execute through sp_executesql.
Try this (Just pass tablename and the string to be find)-:
create proc usp_findString
#tablename varchar(500),
#string varchar(max)
as
Begin
Declare #sql2 varchar(max),#sql nvarchar(max)
SELECT #sql2=
STUFF((SELECT ', case when '+QUOTENAME(NAME)+'='''+#string+''' then 1 else 0 end as '+NAME
FROM (select a.name from sys.columns a join sys.tables b on a.[object_id]=b.[object_id] where b.name=#tablename) T1
--WHERE T1.ID=T2.ID
FOR XML PATH('')),1,1,'')
--print #string
set #sql='select '+#sql2+' from '+#tablename
print #sql
EXEC sp_executesql #sql
End
SQL Server 2014
One way is to use CASE to check the substring existence with LOCATE in mysql and return the column but all you have to check in every column of the table as below:
CREATE TABLE test(col1 VARCHAR(1000), col2 VARCHAR(1000), col3 VARCHAR(1000))
INSERT INTO test VALUES
('while currently what I need some help is I have a table with 10 columns',
'contains a certain string. Just say that I want to search a table',
'contains a certain string demo.webstater.com')
SELECT (CASE WHEN LOCATE('demo.webstater.com', col1, 1) > 0 THEN 'col1'
WHEN LOCATE('demo.webstater.com', col2, 1) > 0 THEN 'col2'
WHEN LOCATE('demo.webstater.com', col3, 1) > 0 THEN 'col3'
END) whichColumn
FROM test
OUTPUT:
whichColumn
col3
There are many ways in which you can do your analysis. You can use "LIKE A%%" if it starts from A in SQL, "REGEX" LibrarY for multiple checks.

SQL joining 1 to 1 and 1 to many table [duplicate]

This question already has answers here:
How to concatenate text from multiple rows into a single text string in SQL Server
(47 answers)
Closed 7 years ago.
If I issue SELECT username FROM Users I get this result:
username
--------
Paul
John
Mary
but what I really need is one row with all the values separated by comma, like this:
Paul, John, Mary
How do I do this?
select
distinct
stuff((
select ',' + u.username
from users u
where u.username = username
order by u.username
for xml path('')
),1,1,'') as userlist
from users
group by username
had a typo before, the above works
This should work for you. Tested all the way back to SQL 2000.
create table #user (username varchar(25))
insert into #user (username) values ('Paul')
insert into #user (username) values ('John')
insert into #user (username) values ('Mary')
declare #tmp varchar(250)
SET #tmp = ''
select #tmp = #tmp + username + ', ' from #user
select SUBSTRING(#tmp, 0, LEN(#tmp))
good review of several approaches:
http://blogs.msmvps.com/robfarley/2007/04/07/coalesce-is-not-the-answer-to-string-concatentation-in-t-sql/
Article copy -
Coalesce is not the answer to string concatentation in T-SQL I've seen many posts over the years about using the COALESCE function to get string concatenation working in T-SQL. This is one of the examples here (borrowed from Readifarian Marc Ridey).
DECLARE #categories varchar(200)
SET #categories = NULL
SELECT #categories = COALESCE(#categories + ',','') + Name
FROM Production.ProductCategory
SELECT #categories
This query can be quite effective, but care needs to be taken, and the use of COALESCE should be properly understood. COALESCE is the version of ISNULL which can take more than two parameters. It returns the first thing in the list of parameters which is not null. So really it has nothing to do with concatenation, and the following piece of code is exactly the same - without using COALESCE:
DECLARE #categories varchar(200)
SET #categories = ''
SELECT #categories = #categories + ',' + Name
FROM Production.ProductCategory
SELECT #categories
But the unordered nature of databases makes this unreliable. The whole reason why T-SQL doesn't (yet) have a concatenate function is that this is an aggregate for which the order of elements is important. Using this variable-assignment method of string concatenation, you may actually find that the answer that gets returned doesn't have all the values in it, particularly if you want the substrings put in a particular order. Consider the following, which on my machine only returns ',Accessories', when I wanted it to return ',Bikes,Clothing,Components,Accessories':
DECLARE #categories varchar(200)
SET #categories = NULL
SELECT #categories = COALESCE(#categories + ',','') + Name
FROM Production.ProductCategory
ORDER BY LEN(Name)
SELECT #categories
Far better is to use a method which does take order into consideration, and which has been included in SQL2005 specifically for the purpose of string concatenation - FOR XML PATH('')
SELECT ',' + Name
FROM Production.ProductCategory
ORDER BY LEN(Name)
FOR XML PATH('')
In the post I made recently comparing GROUP BY and DISTINCT when using subqueries, I demonstrated the use of FOR XML PATH(''). Have a look at this and you'll see how it works in a subquery. The 'STUFF' function is only there to remove the leading comma.
USE tempdb;
GO
CREATE TABLE t1 (id INT, NAME VARCHAR(MAX));
INSERT t1 values (1,'Jamie');
INSERT t1 values (1,'Joe');
INSERT t1 values (1,'John');
INSERT t1 values (2,'Sai');
INSERT t1 values (2,'Sam');
GO
select
id,
stuff((
select ',' + t.[name]
from t1 t
where t.id = t1.id
order by t.[name]
for xml path('')
),1,1,'') as name_csv
from t1
group by id
;
FOR XML PATH is one of the only situations in which you can use ORDER BY in a subquery. The other is TOP. And when you use an unnamed column and FOR XML PATH(''), you will get a straight concatenation, with no XML tags. This does mean that the strings will be HTML Encoded, so if you're concatenating strings which may have the < character (etc), then you should maybe fix that up afterwards, but either way, this is still the best way of concatenating strings in SQL Server 2005.
building on mwigdahls answer. if you also need to do grouping here is how to get it to look like
group, csv
'group1', 'paul, john'
'group2', 'mary'
--drop table #user
create table #user (groupName varchar(25), username varchar(25))
insert into #user (groupname, username) values ('apostles', 'Paul')
insert into #user (groupname, username) values ('apostles', 'John')
insert into #user (groupname, username) values ('family','Mary')
select
g1.groupname
, stuff((
select ', ' + g.username
from #user g
where g.groupName = g1.groupname
order by g.username
for xml path('')
),1,2,'') as name_csv
from #user g1
group by g1.groupname
You can use this query to do the above task:
DECLARE #test NVARCHAR(max)
SELECT #test = COALESCE(#test + ',', '') + field2 FROM #test
SELECT field2 = #test
For detail and step by step explanation visit the following link
http://oops-solution.blogspot.com/2011/11/sql-server-convert-table-column-data.html
DECLARE #EmployeeList varchar(100)
SELECT #EmployeeList = COALESCE(#EmployeeList + ', ', '') +
CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1
SELECT #EmployeeList
source:
http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string
In SQLite this is simpler. I think there are similar implementations for MySQL, MSSql and Orable
CREATE TABLE Beatles (id integer, name string );
INSERT INTO Beatles VALUES (1, "Paul");
INSERT INTO Beatles VALUES (2, "John");
INSERT INTO Beatles VALUES (3, "Ringo");
INSERT INTO Beatles VALUES (4, "George");
SELECT GROUP_CONCAT(name, ',') FROM Beatles;
you can use stuff() to convert rows as comma separated values
select
EmployeeID,
stuff((
SELECT ',' + FPProjectMaster.GroupName
FROM FPProjectInfo AS t INNER JOIN
FPProjectMaster ON t.ProjectID = FPProjectMaster.ProjectID
WHERE (t.EmployeeID = FPProjectInfo.EmployeeID)
And t.STatusID = 1
ORDER BY t.ProjectID
for xml path('')
),1,1,'') as name_csv
from FPProjectInfo
group by EmployeeID;
Thanks #AlexKuznetsov for the reference to get this answer.
A clean and flexible solution in MS SQL Server 2005/2008 is to create a CLR Agregate function.
You'll find quite a few articles (with code) on google.
It looks like this article walks you through the whole process using C#.
If you're executing this through PHP, what about this?
$hQuery = mysql_query("SELECT * FROM users");
while($hRow = mysql_fetch_array($hQuery)) {
$hOut .= $hRow['username'] . ", ";
}
$hOut = substr($hOut, 0, strlen($hOut) - 1);
echo $hOut;

mysql concatenated string with row value as column name for every row in select query (only by query)

Below is my table TABLE
id colname1 colname2 colname3
1 Alex John Mary
2 Alyssa Eben Stephen
3 Sandra Tina William
I try to use below query
SELECT * FROM TABLE WHERE CONCAT('colname',id) = 'Eben'
I expected the result would be from 2nd row 2nd column. But I get nothing. I referred many solutions which guides to use GROUP_CONCAT but I get nothing worked.
Is this possible to do this with mysql?
You can try dynamic sql. Populate a string variable with your select such as:
DECLARE #str varchar(50) = 'SELECT * FROM TABLE WHERE colname#id = ''' + 'Eben' + ''''
Then replace the placeholder string ("#id") with the column number you want such as:
select REPLACE(#str, '#id', 2)
Last to actually execute the statement simply use "EXEC " + statement. ie:
EXEC (#str)
I had to create another answer because this forum won't allow me to use the #(ampersand) in comments.
DECLARE #str varchar(50) = 'SELECT * FROM TABLE WHERE colname#id = ''' +
'EBEN' + ''''
select #str = REPLACE(#str, '#id', 2)
select #str '#str' --This is just to examine the code, get the results of
--this and run it to test
--you should get this: SELECT * FROM TABLE WHERE colname2 = 'EBEN'
EXEC (#str) --this will actually execute your code

SQL find same value on multiple filelds with like operator

I have this records from my users table:
user_id first_name last_name gender email
******* ********** ********* ****** *****
229 Natalie Fern F natalie.fern#hotmail.com
and I want to search same First Name & Last Name from first_name OR last_name.
I have created sql query but not getting record.
SELECT *
FROM user_detail
WHERE first_name LIKE '%Natalie Fern%'
OR last_name LIKE '%Natalie Fern%'
LIMIT 0 , 30
You notice that I am passing first & last name from both fields.
Any Idea why I am not getting records?
Thanks.
You are using the LIKE operator for first_name and last_name as follows:
LIKE '%Natalie Fern%'
This will only match strings which contain anything followed by 'Natalie Fern' followed by anything. But since the first and last name columns only contain (surprise) the first and last names, your query isn't matching any records. You can use CONCAT to try to match the combination of first and last names:
SELECT *
FROM user_detail
WHERE CONCAT(first_name, ' ', last_name)
LIKE '%Natalie Fern%'
LIMIT 0, 30
If you want to check for people whose first or last names could be 'Natalie' or 'Fern', then you could use this query:
SELECT *
FROM user_detail
WHERE first_name LIKE '%Natalie%'
OR first_name LIKE '%Fern%'
OR last_name LIKE '%Natalie%'
OR last_name LIKE '%Fern%'
LIMIT 0, 30
You get no records because no column contains somethink like "Natalie Fern".
You have to concat both columns and you should get the correct result.
But concatination of columns is not a standard. So you have to look in the documantation of your DBMS.
Here some examples:
MySQL: CONCAT( )
Oracle: CONCAT( ), ||
SQL Server: +
Try this
Sql Fiddle
Function
CREATE FUNCTION [dbo].[fn_Split](#text varchar(8000), #delimiter varchar(20))
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value varchar(8000)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index > 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
ELSE
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
RETURN
END
Query
SELECT * FROM user_detail inner join
(select value from fn_split('Natalie Fern',' ')) as split_table
on (user_detail.first_name like '%'+split_table.value+'%'
or user_detail.last_name like '%'+split_table.value+'%' ;
instead of LIKE you can use LOCATE to see if the first name is in a string
SELECT *
FROM user_detail
WHERE LOCATE(first_name,'Natalie Fern') > 0
OR LOCATE(last_name ,'Natalie Fern') > 0
LIMIT 0 , 30
If you need to perform this search with one search input field:
SELECT *
FROM user_detail
WHERE last_name LIKE '%###VALUE###%'
OR first_name LIKE '%###VALUE###%'
OR concat(last_name, ' ', first_name) LIKE '%###VALUE###%'
OR concat(first_name, ' ', last_name) LIKE '%###VALUE###%';

How to split string in sql server and put it in a table

My input is like
(abcd#123, xyz#324, def#567)
I want an output
col1 Col2
abcd 123
xyz 324
def 567
and so on
Column 1 should have abcd and xyz, and column 2 should have 123 and 324 both in different rows and so on. and the String can be of any size.
Thank you
Try this
SELECT LEFT('abcd#123', CHARINDEX('#', 'abcd#123')-1),
RIGHT('abcd#123', CHARINDEX('#', 'abcd#123')-1)
You will need to use CHARINDEX() and SUBSTRING() functions to split your input values.
your actual problem is turning a complex String into a table i gues.
That for i found help with a procedure about 1 year ago, that does exactly that:
CREATE FUNCTION [dbo].[fnSplitString] (
#myString varchar(500),
#deliminator varchar(10))
RETURNS
#ReturnTable TABLE (
[id] [int] IDENTITY(1,1) NOT NULL,
[part] [varchar](50) NULL
)
AS
BEGIN
Declare #iSpaces int
Declare #part varchar(50)
Select #iSpaces = charindex(#deliminator,#myString,0)
While #iSpaces > 0
BEGIN
Select #part = substring(#myString,0,charindex(#deliminator,#myString,0))
Insert Into #ReturnTable(part)
Select #part
Select #myString = substring(#mystring,charindex(#deliminator,#myString,0)+ len(#deliminator),len(#myString) - charindex(' ',#myString,0))
Select #iSpaces = charindex(#deliminator,#myString,0)
END
If len(#myString) > 0
Insert Into #ReturnTable
Select #myString
RETURN
END
Create this procedure and you can do:
DECLARE #TestString varchar(50)
SET #TestString = 'abcd#123,xyz#324,def#567'
Select * from dbo.fnSplitString(#TestString, ',')
Result:
id| part
1 | abcd#123
2 | xyz#324
3 | def#567
this part you can combine with Leonardos answer:
SELECT
LEFT(part, CHARINDEX('#', part)-1) As Col1,
RIGHT(part, LEN(part) - CHARINDEX('#', part)) As Col2
from dbo.fnSplitString(#TestString, ',')
to get your problem solved.
(little note: the function has little issues with whitespaces, so please try to avoid them there)