1

My query shows like that:

SELECT advert_id
FROM oop_adverts
WHERE cat_down = :id 
  AND province = :province
  AND MATCH (location) AGAINST (:location);

in practise:

SELECT advert_id 
FROM oop_adverts 
WHERE cat_down = 3 
  AND province = 5 
  AND MATCH (location) AGAINST ('Krakow');

And if I try this query, mysql finally get 0 results. The problem is polish phrases in query. When I replaced this query for:

SELECT advert_id 
FROM oop_adverts 
WHERE cat_down = 3
    AND province = 5 
    AND MATCH (location) AGAINST ('Krakow') COLLATE utf8_unicode_ci;`

I got:

Syntax error or access violation: 1253 COLLATION 'utf8_unicode_ci' is not valid for CHARACTER SET 'binary''

I don't know what collate I should use in script php and in mysql. Can you help me?

PS. Sorry for my "best english" bro.

Gadoma
  • 6,475
  • 1
  • 31
  • 34
user3022527
  • 2,107
  • 2
  • 14
  • 11

2 Answers2

2

What's the table schema?

I guess 'location' is defined as a binary string and cannot be compared using a collation like utf8_unicode_ci. Give a read here for more info.

http://dev.mysql.com/doc/refman/5.0/en/charset-binary-collations.html

Edit:

Tell us your schema for further help. However try something like this:

SELECT advert_id 
FROM oop_adverts 
WHERE cat_down = 3 
  AND province = 5 
  AND MATCH (CONVERT(BINARY(location) USING utf8)) AGAINST ('Krakow');
Damiano Barbati
  • 3,356
  • 8
  • 39
  • 51
  • Here is schema of this rows: advert_id int(11) / advert_location varchar(100) utf8_unicode_ci / cat_down int(8). – user3022527 Nov 23 '13 at 10:48
  • And what about the column "location"?? – Damiano Barbati Nov 23 '13 at 10:52
  • In location I have names of the cities, for example: Kraków, Warszawa etc. – user3022527 Nov 23 '13 at 10:55
  • Ok but in the example you posted you're calling the column "location", whether in the schema you posted is called "advert_location" instead. Aren't you confusing two different columns within the same table? Can you please edit your question putting the output of "DESCRIBE oop_adverts"? – Damiano Barbati Nov 23 '13 at 11:00
  • OMG, sorry. I just changed name of this column for example. In database this column's name is advert_location and in script I use this name too. – user3022527 Nov 23 '13 at 11:04
  • Alright, just a side question @user3022527. Do you need MATCH() explicitly or you just want to find Kraków in query, no matter what the user inputs (kraków, krakow, Kraków, Krakow) ? – Gadoma Nov 23 '13 at 11:09
  • Also be sure to initialize the PDO driver with the utf8 names set: new PDO("mysql:host=$host;dbname=$database;charset=utf8", $username, $password); – Damiano Barbati Nov 23 '13 at 11:13
  • @Gadoma Yes, exactly! Can you help me? – user3022527 Nov 23 '13 at 11:14
  • @user3022527 ok just give me a moment to write you a solution as new post – Gadoma Nov 23 '13 at 11:23
1

EDITED as per discussion in comments:

As your table scheme looks fine (in terms of utf8) and the first code example you gave in OP is correct (the one without collate), assuming you've got the proper DB collation and the connection itself as well - most probably you have Kraków for advert_location in more than 50% of rows and that is why you get 0 rows of result.

If you want to user Full Text Search, you have to always remember that if a table's fulltext index contains a keyword that appears in 50% of the data rows, that keyword will be ignored by the match query.

So instead you can use Full Text Serach in boolean mode to bypass the 50% threshold. Check the docs here MySQL Boolean Full-Text Searches

So for example, if you have 3 rows in table with Kraków, Krakow and Warszawa as advert_location, the below query will give you 0 rows result:

SELECT advert_id 
FROM oop_adverts 
WHERE MATCH(`advert_location`) AGAINST ('Kraków')

But if you use the boolean mode, you will get 2 rows result:

SELECT advert_id 
FROM oop_adverts 
WHERE MATCH(`advert_location`) AGAINST ('Kraków' IN BOOLEAN MODE)

If you wish to match multiple words, you can use the "+" operator (refer to the docs linked above for details).

SELECT advert_id 
FROM oop_adverts 
WHERE MATCH(`advert_location`,`advert_title`) AGAINST ('+Kraków' '+Search phrase' IN BOOLEAN MODE)

One remark, remember to construct the bound parameter already with the "+" operator included, for example if you are using PHP you can do like this:

$query= "SELECT advert_id 
        FROM oop_adverts 
        WHERE MATCH(`advert_location`,`advert_title`) AGAINST (:location :title IN BOOLEAN MODE)";

$SQL=$db->prepare($query);                          
$SQL->bindValue(':location', '+'.$searched_location, PDO::PARAM_STR);
$SQL->bindValue(':title', '+'.$searched_title, PDO::PARAM_STR);
Gadoma
  • 6,475
  • 1
  • 31
  • 34
  • Where and how I should add this function? – user3022527 Nov 23 '13 at 12:09
  • Are you using any SQL editor or something similar? (MySQLWorkbench, Navicat, EMS MySQL Manager, HeidiSQL or at least phpMyAdmin). If so, just paste the "create procedure..." code from above into the SQL tab or New Query or similar and execute it - this will store the procedure in you database and it will be available for use. – Gadoma Nov 23 '13 at 12:15
  • hmm, I don't know if it's good idea because my query can shows like that: `SELECT advert_id FROM oop_adverts WHERE cat_down = 3 AND province = 5 AND MATCH (location) AGAINST ('Krakow');` when I search province, location (city) and category. But my query can also shows like that: `SELECT advert_id FROM oop_adverts WHERE cat_down = 3 AND province = 5 AND MATCH (location, title) AGAINST ('Krakow', 'PHP coder');` when I search province, location (city), category and **Phrase (search by title of advert)**. And then when I use your code - it will be very slowly because you use LIKE. – user3022527 Nov 23 '13 at 12:41
  • I'm thinkig about this code from answer 1: `SELECT advert_id FROM oop_adverts WHERE cat_down = 3 AND province = 5 AND MATCH (CONVERT(BINARY(location) USING utf8)) AGAINST ('Krakow');` but mysql result is: `#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CONVERT(BINARY(`advert_location`) USING utf8)) AGAINST ('Krakow') LIMIT 0, 30' at line 5` – user3022527 Nov 23 '13 at 12:43
  • It's not that slow as my example (for location) does not use wildcards (%:location%), but ok, in most cases MATCH might be better. Anyway, please edit your answer to include the data scheme (best in form of a create table statement) and also what version of MySQL are you using? – Gadoma Nov 23 '13 at 12:49
  • yes, please add the schema to the question just as outputted by "describe yourtable". – Damiano Barbati Nov 23 '13 at 12:58
  • @user3022527 you're missing a backtip around advert_location (advert_location`) – Damiano Barbati Nov 23 '13 at 12:59
  • No i don't. Here if you use backtip - it's just a information for a script, you're paste a code. I think, you understand what I mean. Here you are: http://s6.ifotos.pl/img/testpng_naqepew.png 5.1.66-cll - MySQL Community Server (GPL) – user3022527 Nov 23 '13 at 13:06
  • can you paste show create table ; result as requested? More info there. Do you have database as well as table collation also set to utf8_unicode_ci ?
    – Gadoma Nov 23 '13 at 13:25
  • Here is output: http://wklej.to/j4Ruj (I don't pase here because the code is too long). – user3022527 Nov 23 '13 at 14:35
  • `ALTER TABLE `oop_adverts` CONVERT TO CHARACTER SET utf8 COLLATE utf8_polish_ci; ALTER DATABASE `majkelo_home` CONVERT TO CHARACTER SET utf8 COLLATE utf8_polish_ci;` maybe this query will be able to help me? – user3022527 Nov 23 '13 at 14:48
  • @user3022527 check my edited answer, it should solve your problem. – Gadoma Nov 23 '13 at 16:22
  • OMG, I knew about that but... I forgot about that. Thank you very much BRO. I have one more question. What I should to do when I would like to search by city and phrase? This will be ok: `MATCH (advert_location, advert_title) AGAINST (:phrase, :location IN BOOLEAN MODE)` ??? – user3022527 Nov 23 '13 at 16:43
  • @user3022527 to match against multiple phrases you can use the "+" operator, telling mysql you want both of the words present in the tuple. So this should work as you expect: MATCH (advert_location, advert_title) AGAINST ('+:location' '+:title' IN BOOLEAN MODE). If my post solves your issue, accept as answer and +1 - so others facing your problem can find the proper solution. – Gadoma Nov 23 '13 at 17:02
  • hmm... If I search by only phrase or by only city - everything is OK. But when I search by phrase and city, SQL get 0 results... Why? When I search by city/phrase, my code shows like that: `MATCH (`advert_location`) AGAINST (:location IN BOOLEAN MODE)` But when I search by city and phrase, code shows like that: `MATCH (`advert_location`, `advert_title`) AGAINST (:location :phrase IN BOOLEAN MODE)` and bindvalue: `bindValue(':location', '+'.$city, PDO::PARAM_STR)` – user3022527 Nov 23 '13 at 18:33
  • + means that the tuple must have all phrases matched, please check out the docs that are linked. – Gadoma Nov 23 '13 at 18:37