It is possible to match COALESCE(x,y) from two different tables against a string?
Here is my request (not working...)
SELECT COALESCE(title_translations.title,collection.title)
LEFT JOIN title_translations ON title_translations.ref_collection=collection.id
WHERE MATCH(COALESCE(title_translations.title,collection.title)) AGAINST("string")
The request works properly if i try to only match collection.title, but doesn't with both
https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html says:
MATCH() takes a comma-separated list that names the columns to be searched.
By trying to use COALESCE(), you're passing a string expression to MATCH(), not column identifiers.
That won't work.
Re your comment:
MATCH(title_translations.title,collection.title) wouldn't work anyway because the columns you list must belong to a single fulltext index, and each index belongs to one table. You can't list columns from different tables. Also you must list all the columns defined, if you defined a multi-column fulltext index.
I assume in your case, you have one fulltext index defined for the single column title in each table.
You will need this:
WHERE MATCH(title_translations.title) AGAINST('string')
OR MATCH(collection.title) AGAINST('string')
You must do this in two matching terms, so you must repeat the AGAINST in each term.
But I'm not sure if that does what you intend. It isn't clear from your original question.
Re your clarification:
If the title exists in title_translations, string need to be matched against title_translations.title and only against it. If the title doesn't exist in title_translations, string need to be matched against collection.title
This is what I come up with:
SELECT x.title FROM
(
SELECT IF(t.title IS NOT NULL,
IF(MATCH(t.title) AGAINST('string') > 0, t.title, NULL),
IF(MATCH(c.title) AGAINST('string') > 0, c.title, NULL)
) AS title
FROM collection AS c
LEFT OUTER JOIN title_translations AS t
ON t.ref_collection = c.id
) AS x
WHERE x.title IS NOT NULL
Related
I have a table, one of the columns contains a text values, some of which are comma separated string, like this:
Downtown, Market District, Warehouse District
I need to modify my query to see is a given value matches this column. I decided that using IN() is the best choice.
SELECT *
FROM t1
WHERE myValue IN (t1.nighborhood)
I am getting spotty results - sometimes I return records and sometimes not. If there's a value in t1.nighborhood that matches myValue, I do get data.
I checked and there are no MySQL errors. What am I missing?
You can use FIND_IN_SET() to search a comma-delimited list:
SELECT *
FROM t1
WHERE FIND_IN_SET(myValue, REPLACE(t1.nighborhood, ', ', ','));
The REPLACE() is necessary to remove the extra spaces.
Another solution is to use regex to match your search value surrounded by commas if necessary:
SELECT *
FROM t1
WHERE t1.nighborhood REGEXP CONCAT('(^|, )', myValue, '(, |$)');
In general, it's bad design to store distinct values in a single column. The data should be normalized into a related table with a foreign key.
I' trying to write a MYSQL query which looks for a string in an aggregation of fields.
The following query finds all the concatenations where "io sono" is present:
SELECT chapter, GROUP_CONCAT(text_search) AS aggregated_chapters
FROM bible_it_cei_2008
GROUP BY chapter
HAVING aggregated_chapters LIKE '%io sono%';
However, trying to use MATCH... AGAINST instead of LIKE:
SELECT chapter, GROUP_CONCAT(text_search) AS aggregated_chapters
FROM bible_it_cei_2008
GROUP BY chapter
HAVING MATCH ( aggregated_chapters ) AGAINST ( '+"io sono"' IN BOOLEAN MODE);
returns the error:
#1210 - Incorrect arguments to MATCH
Isn't there any way to use MATCH AGAINST with GROUP_CONCAT?
Isn't there any way to use MATCH AGAINST with GROUP_CONCAT?
No. That's not the way FULLTEXT search works in MySQL.
If your table contains the columns chapter and text_search, and you hope to find the values of chapter matching text search, you want something like this.
SELECT chapter,
MATCH(text_search) AGAINST ('+"io sono"' IN NATURAL LANGUAGE MODE) AS score
FROM bible_it_cei_2008
To get this to work you'll need to create an appropriate FULLTEXT index.
I'm attempting to implement a search function on a two tables with a one-to-many relationship. Think of it as a post with multiple tags. Each tag has its own row in the tag table.
I'd like to retrieve a post if all of the search terms can be found in either a) the post text, b) the post tags or c) both.
Let's say I've created my tables like this:
CREATE TABLE post (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
text VARCHAR(100) NOT NULL
);
CREATE TABLE tag (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name VARCHAR(30) NOT NULL,
post MEDIUMINT NOT NULL
);
And I create indexes like this:
CREATE FULLTEXT INDEX post_idx ON post(text);
CREATE FULLTEXT INDEX tag_idx ON tag(name);
If my search query were "TermA TermB" and wanted to search just in the post text, I'd formulate my SQL query like this:
SELECT * FROM post WHERE MATCH(text) AGAINST('+TermA +TermB' IN BOOLEAN MODE);
Is there a way to add tags into the mix? My previous attempt was this:
SELECT * FROM post
RIGHT JOIN tag ON tag.post = post.id
WHERE MATCH(post.text) AGAINST('TermA TermB' IN BOOLEAN MODE)
OR MATCH(tag.name) AGAINST('TermA TermB' IN BOOLEAN MODE);
The problem is, this is only an any words query and not an all words query. By this I mean, I'd like to retrieve the post if TermA is in the text and TermB is in the tags.
What am I missing here? Is this even possible using a fulltext search? Is there a better way to approach this?
Try this one:
SELECT post.*
FROM post
INNER JOIN (SELECT post, GROUP_CONCAT(name SEPARATOR ' ') tags FROM tag GROUP BY post) tag ON post.id=tag.post
WHERE MATCH(post.text) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
OR MATCH(tags) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
This might work to also get results that match from either content or tags, but it didn't work in the MySQL 5.1:
SELECT post.*, GROUP_CONCAT(tag.name SEPARATOR ' ') tags
FROM post
LEFT JOIN tag ON post.id=tag.post
GROUP BY post.id
HAVING MATCH(post.text,tags) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
so I rewrote it as:
SELECT post.*, tags
FROM post
LEFT JOIN (SELECT post, GROUP_CONCAT(tag.name SEPARATOR ' ') tags FROM tag GROUP BY post) tags ON post.id=tags.post
WHERE MATCH(post.text, tags) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
This is possible, but I'm guessing that in your Tags table, you have one row for each tag per post. So one row containing the tag 'TermA' for post 1 and another record with the tag 'TermB', right?
The all words query (with +) only returns rows where the searched field contains all the specified words. For the tags table, that is never the case.
One possible solution would be to store all tags in a single field in the posts table itself. Then it would be easy to do advanced matching on the tags as well.
Another possibility is to change the condition for tags altogether. That is, use an all query for the text and an any query for the tags. To do that, you'll have to modify the search query yourself, which can fortunately be as easy as removing the plusses from the query.
You can also query for an exact match, like this:
SELECT * FROM post p
WHERE
MATCH(p.text) AGAINST('TermA TermB' IN BOOLEAN MODE)
AND
/* Number of matching tags .. */
(SELECT COUNT(*) FROM tags t
WHERE
t.post = p.id
AND (t.tag in ('TermA', 'TermB')
= /* .. must be .. */
2 /* .. number of searched tags */ )
In this query, I count the number of matching tags. In this case I want it to be exactly 2, meaning that both tags match (provided that tags are unique per post). You could also check for >= 1 to see if any tags match.
But as you can see, this also requires parsing of the search string. You will have to remove the plusses (or even check their existence to understand whether you want 'any' or 'all'). And you will have to split it as well to get the number of searched words, and get the separate words themselves.
All in all, adding all tags to a 'tags' field in post is the easiest way. Not ideal from a normalisation point of view, but that is managable, I think.
You can search on both text and tags.
SELECT *
FROM post
WHERE MATCH(text,tags) AGAINST('+TermA +TermB' IN BOOLEAN MODE)
To get this to work you'll need to make a FULLTEXT index for both columns together.
CREATE FULLTEXT INDEX keywords ON pos(text,tags)
In Boolean search mode this should do what you want.
i have table called image
there is column called category in that table which varchar and stores all the categories of an image sperated with comma .
i one row i have :
category : ,26,25,
this query works fine
SELECT *
FROM `images`
WHERE `confirm` =1
AND `category` LIKE '%,25,%' AND `category` LIKE '%,26,%'
LIMIT 0 , 20
and i get all the rows with ,25,26, as their category
but
why this doesn't work ?
SELECT *
FROM `images`
WHERE `confirm` =1
AND `category` LIKE '%,25,' AND `category` LIKE '%,26,'
LIMIT 0 , 20
LIKE matches the entire string. LIKE '%,26,' matches strings that end in ,26,, not strings that contain it. You need % on both ends if you want to search for a substring anywhere.
LIKE must match the entire value in the table. If there is content in the table after the "25,", then LIKE '%,25,' will not match it.
If you want regular expression matching in mysql, you can use RLIKE:
AND category RLIKE ',25,' AND category RLIKE ',26,'
but if you use LIKE, you have to match the whole thing.
The like is an exact match, not a match for something arbitrarily inside the string. You need wildcards to say that you want a match somewhere inside.
The expressions:
AND `category` LIKE '%,25,' AND `category` LIKE '%,26,'
Are looking for cases when category ends in ',25,' AND ends in ',26,' at the same time. Clearly, this is not possible.
You can also phrase this in MySQL as:
AND find_in_set(25, category) > 0 and find_in_set(26, category) > 0
Also, you should have a separate table that has one row per category. Such queries would be much easier and more efficient with a proper relational data structure.
I have a problem or rather an understanding problem with a hyphenated searchstring which is quoted.
In my Table there is a table with a column 'company'.
One of the entries in that column is: A-Z Electro
The following examples are simplified a lot (though the real query is much more complex) - but the effect is still the same.
When I do the following search, I don't get the row with the above mentioned company:
SELECT i.*
FROM my_table i
WHERE MATCH (i.company) AGAINST ('+\"A-Z\" +Electro*' IN BOOLEAN MODE)
GROUP BY i.uid ORDER BY i.company ASC LIMIT 0, 40;
If I do the following search, get the row with the above mentioned company (notice only changed the - to a + before "A-Z":
SELECT i.*
FROM my_table i
WHERE MATCH (i.company) AGAINST ('-\"A-Z\" +Electro*' IN BOOLEAN MODE)
GROUP BY i.uid ORDER BY i.company ASC LIMIT 0, 40;
I also get the row, if I remove the operator completely:
SELECT i.*
FROM my_table i
WHERE MATCH (i.company) AGAINST ('\"A-Z\" +Electro*' IN BOOLEAN MODE)
GROUP BY i.uid ORDER BY i.company ASC LIMIT 0, 40;
Can anyone explain to me this behaviour? Because I would expect, when searching with a +, I should get the result too...
I just checked the table index with myisam_ftdump.
Two-Character-Words are indexed properly as there are entries like
14f2e8 0.7908264 ab
3a164 0.8613265 dv
There is also an entry:
de340 0.6801047 az
I suppose this should be the entry for A-Z - so the search should find this entry, shouldn't it?
The default value of ft_min_word_len is 4. See this link for information on that. In short, your system isn't indexing words of less than 4 characters.
Why is this important? Well:
A-Z is less than 4 characters long
...therefore it's not in the index
...but your first query +"A-Z" states it must be in the index in order for the match to succeed
The other two (match if it's not in the index, match if either this or that is in the index) work because it's not in the index.
The hyphen is a red herring - the reason is because "A-Z" is three characters long and your FT index ignores it.