Why do CONTAINS and LIKE return different results? - sql

I have the following query. There are two possible columns that may hold the value I'm looking for, let's call them FieldA and FieldB.
If I execute this:
SELECT COUNT(1)
FROM Table
WHERE CONTAINS(Table.*, 'string')
I get back "0".
However, if I execute this:
SELECT COUNT(1)
FROM TABLE
WHERE FieldA LIKE '%string%' OR FieldB LIKE '%string%'
I get back something like 9000. I then checked and there are rows that have the word string in either FieldA.
Why does this happen? I recall that CONTAINS uses a full-text index, but I also recall that LIKE does the same, so if the problem was that the indexes are outdated, then it should fail for both of them, right?
Thanks

I believe that CONTAINS and full text searching will only yield whole word results, so you won't match the same as LIKE '%string%'. If you want to right wildcard your CONTAINS, you must write it like:
SELECT COUNT(1) FROM Table WHERE CONTAINS(Table.*, '"string*"')
However, if you want to left wildcard, you can't! You have to store a copy of your database reversed and then do:
SELECT COUNT(1) FROM Table WHERE CONTAINS(Table.*, '"gnirts*"')
https://learn.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ms552152(v=office.14)
How do you get leading wildcard full-text searches to work in SQL Server?
So in the example in the question, doing a CONTAINS(Table.*, 'string') is not the same as doing LIKE '%string%' and would not have the same results.

Related

Is there a way to express AND in SIMILAR TO ignoring order of matches?

I have a Redshift table column that contains 1 to many hashtags (e.g. #a, #b, etc.). I want to write a query that finds rows where all tags from a given set exist (e.g. #a and #b) while not picking up other rows that have some but not all of the tags (e.g. only #a or only #b).
I can see how to do this with multiple LIKE statements (e.g. LIKE '%#a %' AND LIKE '%#b%') but I would really like to do it with a single statement. I can see how to do this with SIMILAR TO but not in a way that ignores ordering. The following would work but only if I include all possible combinations of ordering.
SELECT * FROM table WHERE field SIMILAR TO '(%#a%)(%#b%)|(%#b%)(%#a%)'
This works but having to list all combinations of the tags I'm looking for would be a royal pain and prone to error. Is there a way to express 'AND' in SIMLAR TO (or another function) in Redshift that ignores order?
Make sure to capture the whole tag in any position and not match on incomplete tags:
SELECT *
FROM table
WHERE (field LIKE '#a#%' OR field LIKE '%#a') AND
(field LIKE '#b#%' OR field LIKE '%#b')
This avoids matching data such as #ac#b
Use AND and LIKE:
SELECT t.*
FROM table t
WHERE field LIKE '%#a%' AND
field LIKE '%#b%';

Fastest way to check if any case of a pattern exist in a column using SQL

I am trying to write code that allows me to check if there are any cases of a particular pattern inside a table.
The way I am currently doing is with something like
select count(*)
from database.table
where column like (some pattern)
and seeing if the count is greater than 0.
I am curious to see if there is any way I can speed up this process as this type of pattern finding happens in a loop in my query and all I need to know is if there is even one such case rather than the total number of cases.
Any suggestions will be appreciated.
EDIT: I am running this inside a Teradata stored procedure for the purpose of data quality validation.
Using EXISTS will be faster if you don't actually need to know how many matches there are. Something like this would work:
IF EXISTS (
SELECT *
FROM bigTbl
WHERE label LIKE '%test%'
)
SELECT 'match'
ELSE
SELECT 'no match'
This is faster because once it finds a single match it can return a result.
If you don't need the actual count, the most efficient way in Teradata will use EXISTS:
select 1
where exists
( select *
from database.table
where column like (some pattern)
)
This will return an empty result set if the pattern doesn't exist.
In terms of performance, a better approach is to:
select the result set based on your pattern;
limit the result set's size to 1.
Check whether a result was returned.
Doing this prevents the database engine from having to do a full table scan, and the query will return as soon as the first matching record is encountered.
The actual query depends on the database you're using. In MySQL, it would look something like:
SELECT id FROM database.table WHERE column LIKE '%some pattern%' LIMIT 1;
In Oracle it would look like this:
SELECT id FROM database.table WHERE column LIKE '%some pattern%' AND ROWNUM = 1;

How does phpmyadmin implement "search" feature?

In phpMyAdmin, there is a search in database feature by which I can input a word and find it in any table(s).
How to implement it by SQL statement? I know the LIKE operation, but it's syntax is:
WHERE column_name LIKE pattern
How to search in all columns? Any how to specify it's a exact keyword or regular express?
SELECT * FROM your_table_name WHERE your_column_name LIKE 'search_box_text';
Where search_box_text is what you enter in the search. It will also say in the result page what kind of query it made. The same query with regular expressions is:
SELECT * FROM your_table_name WHERE your_column_name REGEXP 'search_box_text';
Remember that the wildcard in mysql is %. Eg. "LIKE '%partial_search_text%'
If you want to search in multiple columns, you can check which columns are in table with:
DESCRIBE TABLE your_table_name;
Or if you already know your columns:
SELECT * FROM your_table_name
WHERE your_column_1 LIKE '%search%'
AND your_column_2 LIKE '%search%'
AND your_column_3 LIKE '%search%';

MySQL question about "reverse LIKEs"

Well given I have a value I want to check for potential matches in a database (in one varchar field) so I write something like:
SELECT * FROM table WHERE column LIKE "%value%"
Which will work if the value is something like "test" and the column has a value of "this is a test" however if it is reversed then I will not get a match I have tried things along the lines of:
SELECT * FROM table WHERE CONCAT("%",column,"%") LIKE "value"
but don't know exactly how to phrase this to Google to get a response I need, please help!
You can reverse a like statement. Just using the same syntax as a regular like query:
select
*
from
table
where
'value' like concat('%', column, '%')
Of course, if you felt wild and crazy, you could also use instr:
select * from table where instr('value', column) > 0
I don't know which one is faster, since I don't have a MySQL instance to test against, but it's worth trying both to see which wins.
SELECT *
FROM table
WHERE 'value' LIKE CONCAT('%', column, '%')

How to implement a Keyword Search in MySQL?

I am new to SQL programming.
I have a table job where the fields are id, position, category, location, salary range, description, refno.
I want to implement a keyword search from the front end. The keyword can reside in any of the fields of the above table.
This is the query I have tried but it consist of so many duplicate rows:
SELECT
a.*,
b.catname
FROM
job a,
category b
WHERE
a.catid = b.catid AND
a.jobsalrange = '15001-20000' AND
a.jobloc = 'Berkshire' AND
a.jobpos LIKE '%sales%' OR
a.jobloc LIKE '%sales%' OR
a.jobsal LIKE '%sales%' OR
a.jobref LIKE '%sales%' OR
a.jobemail LIKE '%sales%' OR
a.jobsalrange LIKE '%sales%' OR
b.catname LIKE '%sales%'
For a single keyword on VARCHAR fields you can use LIKE:
SELECT id, category, location
FROM table
WHERE
(
category LIKE '%keyword%'
OR location LIKE '%keyword%'
)
For a description you're usually better adding a full text index and doing a Full-Text Search (MyISAM only):
SELECT id, description
FROM table
WHERE MATCH (description) AGAINST('keyword1 keyword2')
SELECT
*
FROM
yourtable
WHERE
id LIKE '%keyword%'
OR position LIKE '%keyword%'
OR category LIKE '%keyword%'
OR location LIKE '%keyword%'
OR description LIKE '%keyword%'
OR refno LIKE '%keyword%';
Ideally, have a keyword table containing the fields:
Keyword
Id
Count (possibly)
with an index on Keyword. Create an insert/update/delete trigger on the other table so that, when a row is changed, every keyword is extracted and put into (or replaced in) this table.
You'll also need a table of words to not count as keywords (if, and, so, but, ...).
In this way, you'll get the best speed for queries wanting to look for the keywords and you can implement (relatively easily) more complex queries such as "contains Java and RCA1802".
"LIKE" queries will work but they won't scale as well.
Personally, I wouldn't use the LIKE string comparison on the ID field or any other numeric field. It doesn't make sense for a search for ID# "216" to return 16216, 21651, 3216087, 5321668..., and so on and so forth; likewise with salary.
Also, if you want to use prepared statements to prevent SQL injections, you would use a query string like:
SELECT * FROM job WHERE `position` LIKE CONCAT('%', ? ,'%') OR ...
I will explain the method i usally prefer:
First of all you need to take into consideration that for this method you will sacrifice memory with the aim of gaining computation speed.
Second you need to have a the right to edit the table structure.
1) Add a field (i usually call it "digest") where you store all the data from the table.
The field will look like:
"n-n1-n2-n3-n4-n5-n6-n7-n8-n9" etc.. where n is a single word
I achieve this using a regular expression thar replaces " " with "-".
This field is the result of all the table data "digested" in one sigle string.
2) Use the LIKE statement %keyword% on the digest field:
SELECT * FROM table WHERE digest LIKE %keyword%
you can even build a qUery with a little loop so you can search for multiple keywords at the same time looking like:
SELECT * FROM table WHERE
digest LIKE %keyword1% AND
digest LIKE %keyword2% AND
digest LIKE %keyword3% ...
You can find another simpler option in a thread here: Match Against.. with a more detail help in 11.9.2. Boolean Full-Text Searches
This is just in case someone need a more compact option. This will require to create an Index FULLTEXT in the table, which can be accomplish easily.
Information on how to create Indexes (MySQL): MySQL FULLTEXT Indexing and Searching
In the FULLTEXT Index you can have more than one column listed, the result would be an SQL Statement with an index named search:
SELECT *,MATCH (`column`) AGAINST('+keyword1* +keyword2* +keyword3*') as relevance FROM `documents`USE INDEX(search) WHERE MATCH (`column`) AGAINST('+keyword1* +keyword2* +keyword3*' IN BOOLEAN MODE) ORDER BY relevance;
I tried with multiple columns, with no luck. Even though multiple columns are allowed in indexes, you still need an index for each column to use with Match/Against Statement.
Depending in your criterias you can use either options.
I know this is a bit late but what I did to our application is this. Hope this will help someone tho. But it works for me:
SELECT * FROM `landmarks` WHERE `landmark_name` OR `landmark_description` OR `landmark_address` LIKE '%keyword'
OR `landmark_name` OR `landmark_description` OR `landmark_address` LIKE 'keyword%'
OR `landmark_name` OR `landmark_description` OR `landmark_address` LIKE '%keyword%'