THEN statement in a SQL query - mysql

I'm trying to alter a Wordpress search query so that it returns broader results.
The scenario:
There is a post entry titles "Carousel Watch" in the database. When I search either (or both) of these words, I get the post returned, which is what I'd expect.
If, however, I search for "Carousel Watch Gift", I do not get any results.
The SQL query for this search is the following:
SELECT SQL_CALC_FOUND_ROWS sosen_posts.ID FROM sosen_posts WHERE 1=1
AND (((sosen_posts.post_title LIKE '%carousel%') OR (sosen_posts.post_content LIKE '%carousel%'))
AND ((sosen_posts.post_title LIKE '%watch%') OR (sosen_posts.post_content LIKE '%watch%'))
AND ((sosen_posts.post_title LIKE '%gift%') OR (sosen_posts.post_content LIKE '%gift%')))
AND (sosen_posts.post_password = '') AND sosen_posts.post_type = 'wp_aff_products' AND (sosen_posts.post_status = 'publish')
ORDER BY
(CASE WHEN sosen_posts.post_title LIKE '%carousel watch gift%'
THEN 1
WHEN sosen_posts.post_title LIKE '%carousel%' AND sosen_posts.post_title LIKE '%watch%' AND sosen_posts.post_title LIKE '%gift%'
THEN 2
WHEN sosen_posts.post_title LIKE '%carousel%' OR sosen_posts.post_title LIKE '%watch%' OR sosen_posts.post_title LIKE '%gift%'
THEN 3
WHEN sosen_posts.post_content LIKE '%carousel watch gift%'
THEN 4 ELSE 5 END), sosen_posts.post_date DESC LIMIT 0, 3
My questions are:
What part of the query prevents returning results ("Carosuel Watch")?
How I should alter it to get the result returned?
And finally, what does the THEN statement do in this query? Does it set the value to compare in the WHERE statement?

The THEN keyword is used as part of a CASE clause. A CASE clause looks like this:
CASE WHEN expression THEN value WHEN otherexpression THEN othervalue ... END
The result of the expression is a single value (sometimes you see people try to use a CASE clause to determine what code will execute, and this won't work).
In this query, the value from the CASE claues is used to determine sort order for posts, and has not bearing on which records are included or not included in the results.
To find out which records are included in the results, we need to dissect the WHERE clause. I re-formatted the existing clause for easier reading below:
WHERE 1=1 AND (
(sosen_posts.post_title LIKE '%carousel%' OR sosen_posts.post_content LIKE '%carousel%')
AND (sosen_posts.post_title LIKE '%watch%' OR sosen_posts.post_content LIKE '%watch%')
AND (sosen_posts.post_title LIKE '%gift%' OR sosen_posts.post_content LIKE '%gift%')
) AND sosen_posts.post_password = '' AND sosen_posts.post_type = 'wp_aff_products' AND sosen_posts.post_status = 'publish'
First, the 1=1 part. This is common for autogenerated code. The generator can put a WHERE 1=1 at the beginning of the WHERE clause whether or not there are any conditions, and the query will still be valid. Then, for each condition, it can always use the form AND condition, without worrying about the prior state of the WHERE clause.
Moving into the next section, we see it checks each keyword individually against both the title and text. These checks are connected via AND operators. This means that if your post does not have the word gift in the title or body somewhere, it cannnot appear in the results.
Finally, this code is incredibly inefficient. You never want to see a LIKE operator with a leading wildcard (%), because it pretty much guarantees that you can't use any indexes to satisfy that condition. I'm more of a Sql Server guy, but in Sql Server -land what you want to do instead is create a special kind of index, called a full text index, and write the query using a special CONTAINS() clause. Failing that, you use a 3rd-party search library such as Lucene. You never want to use LIKE queries for searches like this. I'm not sure what the MySql equivalents to these alternatives are, but what you have here is not the way you want to be doing this.

The LIKE clauses require one or more of each of the input words to exist (I suspect this is how the auto-generator works). If your posts do not contain all 3 words it will not therefore get returned.
You can remove the LIKE clauses for "Gift", though I think this will give you the same code as if you just searched for the first two words.
THEN comes before what is to be returned by the CASE function IE WHEN {true} THEN {return_this}
Hope this helps.

giving your query a nicer format (see below) it results that in the where clause you are filtering rows that have carousel in the title or content, watch AND gift.
The case statement only changes the ordering of the result, but if you build your query with those three words it will fetch rows with all those words.
SELECT SQL_CALC_FOUND_ROWS sosen_posts.ID FROM sosen_posts
WHERE 1=1
AND (((sosen_posts.post_title LIKE '%carousel%')
OR (sosen_posts.post_content LIKE '%carousel%')
)
AND ((sosen_posts.post_title LIKE '%watch%')
OR (sosen_posts.post_content LIKE '%watch%')
)
AND ((sosen_posts.post_title LIKE '%gift%')
OR (sosen_posts.post_content LIKE '%gift%')
)
)
AND (sosen_posts.post_password = '')
AND sosen_posts.post_type = 'wp_aff_products'
AND (sosen_posts.post_status = 'publish')
ORDER BY (CASE WHEN sosen_posts.post_title LIKE '%carousel watch gift%' THEN 1
WHEN sosen_posts.post_title LIKE '%carousel%' AND sosen_posts.post_title LIKE '%watch%' AND sosen_posts.post_title LIKE '%gift%' THEN 2
WHEN sosen_posts.post_title LIKE '%carousel%' OR sosen_posts.post_title LIKE '%watch%' OR sosen_posts.post_title LIKE '%gift%' THEN 3
WHEN sosen_posts.post_content LIKE '%carousel watch gift%' THEN 4
ELSE 5
END),
sosen_posts.post_date DESC
LIMIT 0, 3
Also note that LIMIT 0 will return an empty set.

Related

Use Astrisk mark in sql query with like operator

I am having a issue with sql query.
This is the query I used to search the result
SELECT * FROM wp_wp_campaigns
WHERE `campaign_status`='1'
AND `campaign_type`='o'
AND `campaign_name_decoded` LIKE '%Deep%'
OR `campaign_name_decoded` LIKE '%Deep'
OR `campaign_name_decoded` LIKE 'Deep%'
OR `campaign_name_decoded` LIKE 'Deep_'
OR `campaign_name_decoded` LIKE '_Deep'
OR `campaign_name_decoded` LIKE '_Deep_'
ORDER BY id desc
LIMIT 10 OFFSET 0
It works perfect when used with only text.
But does not return any value when input value is like Deep*
Any help in this is highly appriciated.
This does not directly answers your question. But if you want the filtering on campaign_status and campaign_type and then on the campaign_name_decoded, then this is sufficient:
WHERE campaign_status = '1' AND
campaign_type = 'o' AND
campaign_name_decoded` LIKE '%Deep%'
The % wildcard matches zero or more characters, so is should be doing what you want.
That said, if some campaign names are not matching, that is because you have invalid (unexpected?) characters in either the data column or the comparison.

MYSQL REGEX search many words with no order condition

I try to use a regex with mysql that search boundary words in a json array string but I don't want the regex match words order because I don't know them.
So I started firstly to write my regex on regex101 (https://regex101.com/r/wNVyaZ/1) and then try to convert this one for mysql.
WHERE `Wish`.`services` REGEXP '^([^>].*[[:<:]]Hygiène[[:>:]])([^>].*[[:<:]]Radiothérapie[[:>:]]).+';
WHERE `Wish`.`services` REGEXP '^([^>].*[[:<:]]Hygiène[[:>:]])([^>].*[[:<:]]Andrologie[[:>:]]).+';
In the first query I get result, cause "Hygiène" is before "Radiothérapie" but in the second query "Andrologie" is before "Hygiène" and not after like it written in the query. The problem is that the query is generated automatically with a list of services that are choosen with no order importance and I want to match only boundary words if they exists no matter the order they have.
You can search for words in JSON like the following (I tested on MySQL 5.7):
select * from wish
where json_search(services, 'one', 'Hygiène') is not null
and json_search(services, 'one', 'Andrologie') is not null;
+------------------------------------------------------------+
| services |
+------------------------------------------------------------+
| ["Andrologie", "Angiologie", "Hygiène", "Radiothérapie"] |
+------------------------------------------------------------+
See https://dev.mysql.com/doc/refman/5.7/en/json-search-functions.html#function_json-search
If you can, use the JSON search queries (you need a MySQL with JSON support).
If it's advisable, consider changing the database structure and enter the various "words" as a related table. This would allow you much more powerful (and faster) queries.
JOIN has_service AS hh ON (hh.row_id = id)
JOIN services AS ss ON (hh.service_id = ss.id
AND ss.name IN ('Hygiène', 'Angiologie', ...)
Otherwise, in this context, consider that you're not really doing a regexp search, and you're doing a full table scan anyway (unless MySQL 8.0+ or PerconaDB 5.7+ (not sure) and an index on the full extent of the 'services' column), and several LIKE queries will actually cost you less:
WHERE (services LIKE '%"Hygiène"%'
OR services LIKE '%"Angiologie"%'
...)
or
IF(services LIKE '%"Hygiène"%', 1, 0)
+IF(services LIKE '%"Angiologie"%', 1, 0)
+ ... AS score
HAVING score > 0 -- or score=5 if you want only matches on all full five
ORDER BY score DESC;

SQL show results for A column first then show results for B column

I want SQL to show / order the results for the column name first then show results for the description column last.
Current SQL query:
SELECT * FROM products WHERE (name LIKE '%$search_query%' OR description LIKE '%$search_query%')
I tried adding order by name, description [ASC|DESC] on the end but that didn't work.
It's for optimizing the search results. If a certain word is found in description it should go last if a certain word is also found in the name column.
You can use a CASE statement in an ORDER BY to prioritize name. In the example below all results where name is matched will come first because the CASE statement will evaluate to 1 whereas all other results will evaluate to 2.
I'm not sure by your problem description what exactly you want the behavior to be, but you can certainly use this technique to create more refined cases to prioritize your results.
SELECT *
FROM products
WHERE (name LIKE '%$search_query%' OR description LIKE '%$search_query%')
ORDER BY CASE WHEN name LIKE '%$search_query%' THEN 1 ELSE 2 END
If you want the names first, the simplest order by is:
order by (name like '%$search_query%') desc
MySQL treats booleans as numbers in a numeric context, with "1" for true and "0" for false.
While this is undocumented, when results sets combined by a UNION ALL and not sorted afterwards, they stay in the order returned, as UNION ALL just adds new results to the bottom of the result set. This should work for you:
SELECT * FROM products
WHERE name LIKE '%$search_query%'
UNION ALL
SELECT * FROM products
WHERE (description LIKE '%$search_query%' AND name NOT LIKE '%$search_query%')

SQL search result ranking

I have a table call objects which there are the columns:
object_id,
name_english(vchar),
name_japanese(vchar),
name_french(vchar),
object_description
for each object.
When a user perform a search, they may enter either english, japanese or french... and my sql statement is:
SELECT
o.object_id,
o.name_english,
o.name_japanese,
o.name_french,
o.object_description
FROM
objects AS o
WHERE
o.name_english LIKE CONCAT('%',:search,'%') OR
o.name_japanese LIKE CONCAT('%',:search,'%') OR
o.name_french LIKE CONCAT('%',:search,'%')
ORDER BY
o.name_english, o.name_japanese, o.name_french ASC
And some of the entries are like:
Tin spoon,
Tin Foil,
Doctor Martin Shoes,
Martini glass,
Cutting board,
Ting Soda.
So, when the user search the word "Tin" it will return all results of these, but instead I just want to return the results which specific include the term "Tin" or displaying the result and rank them by relevance order. How can I achieve that?
Thanks.
You can use MySQL FULLTEXT indices to do that. This requires the MyISAM table type, an index on (name_english, name_japanese, name_french, object_description) or whatever fields you want to search on, and the appropriate use of the MATCH ... AGAINST operator on exactly that set of columns.
See the manual at http://dev.mysql.com/doc/refman/5.5/en/fulltext-search.html, and the examples on the following page http://dev.mysql.com/doc/refman/5.5/en/fulltext-natural-language.html
After running the query above , you will get all sort of results including ones that you are not interested, but you can then use regular expressions on the above results(returned by mysql server) set to filter out what u need.
This should do the trick - you may have to filter out duplicates, but the basic idea is obvious.
SELECT
`object`.`object_id`,
`object`.`name_english`,
`object`.`name_japanese`,
`object`.`name_french`,
`object`.`object_info`, 1 as ranking
FROM `objects` AS `object`
WHERE `object`.`name_english` LIKE CONCAT(:search,'%') OR `object`.`name_japanese` LIKE CONCAT(:search,'%') OR `object`.`name_french` LIKE CONCAT(:search,'%')
union
SELECT
`object`.`object_id`,
`object`.`name_english`,
`object`.`name_japanese`,
`object`.`name_french`,
`object`.`object_info`, 10 as ranking
FROM `objects` AS `object`
WHERE `object`.`name_english` LIKE CONCAT('%',:search,'%') OR `object`.`name_japanese` LIKE CONCAT('%',:search,'%') OR `object`.`name_french` LIKE CONCAT('%',:search,'%')
ORDER BY ranking, `object`.`name_english`, `object`.`name_japanese`, `object`.`name_french` ASC

SQL Query where a field value does not contain empty spaces

I am currently using the follow query:
SELECT *
FROM `wp_usermeta`
WHERE meta_key='avatar'
AND meta_key NOT LIKE '% '
ORDER BY RAND()
LIMIT 4
In that way, I want to try to get only field values, where no empty spaces re in the file name. Where is the error in my query? It still selects filenames with empty spaces in the filename.
Try
NOT LIKE '% %'
Your current wildcard match only catches trailing spaces.
Also, you're using meta_key twice. Should the column used in your LIKE clause be meta_value (or whatever it is in Wordpress).
This question is probably worth reading if you're concerned about performance - Which is faster — INSTR or LIKE?