SELECT DISTINCT: контролировать приоритет при наличии акцентов - PullRequest
3 голосов
/ 29 февраля 2012

Пример:

> SELECT name, culture FROM city_i18n WHERE ID = 2745;
+-------+---------+
| name  | culture |
+-------+---------+
| Paris | en_GB   |
| París | es_ES   |
| Paris | pt_BR   |
| Paris | pt_PT   |
+-------+---------+
4 rows in set (0.00 sec)

> SELECT DISTINCT name FROM city_i18n WHERE ID = 2745 AND culture IN ('es_ES', 'en_GB');
+-------+
| name  |
+-------+
| Paris |
+-------+
1 row in set (0.00 sec)

> SELECT DISTINCT name FROM city_i18n WHERE ID = 2745 AND culture IN ('es_ES', 'pt_PT');
+-------+
| name  |
+-------+
| París |
+-------+
1 row in set (0.00 sec)

Обратите внимание на различный вывод двух SELECT DISTINCT: Париж, Пари (с акцентом)

Теперь, что является простым методом для всегда дать es_ES приоритет (т.е. всегда получить пари с акцентом)?

Поскольку возникли вопросы, некоторые уточнения : названия городов, которые отличаются толькопо акцентам (согласно сличению) должен появляться только один раз.В этом случае имя, которое появляется, должно иметь акценты культуры es_ES.

Ответы [ 3 ]

3 голосов
/ 29 февраля 2012

Проблема заключается в последовательности сортировки, используемой в вашей базе данных. Существует огромное количество информации о последовательностях сортировки в зависимости от вашей RDBMS. Вы должны быть очень осторожны при изменении последовательности сортировки, поскольку это может привести к непредвиденным последствиям.

Последовательность сортировки используется для проверки равенства или неравенства столбцов. В зависимости от используемой сортировки следующие операторы могут быть оценены как true или false:

'TexT' = 'text'
'TEXT' = 'text'
'áéíóú' = 'aeiou'

В вашей ситуации вам нужно будет выбрать последовательность сортировки, в которой буквы с акцентом и обычные буквы будут рассматриваться как разные значения. В настоящее время ваша база данных определяет, что они равны. Как только вы это сделаете, вы сможете разработать свою логику для выбора подходящего значения на основе предпочитаемого вами culture

Просто добавьте, что ваша база данных в настоящее время определяет, что Paris = París, и я подозреваю, что нет гарантии, что будет выбрано отдельное значение, аналогично тому факту, что нет определенного порядка набора результатов, если только Используется предложение ORDER BY.

Я немного подумал об этом. Я думаю, что лучше всего было бы создать CultureRank

CREATE TABLE CultureRank
(
    Rank INTEGER,
    Culture VARCHAR(5)
);

INSERT INTO CultureRank VALUES (1, 'es_ES');
INSERT INTO CultureRank VALUES (2, 'en_GB');
INSERT INTO CultureRank VALUES (3, 'pt_BR');
INSERT INTO CultureRank VALUES (4, 'pt_PT');

SELECT
    Name
FROM
    City_i18n
    LEFT JOIN CultureRank ON City_i18n.Culture = CultureRank.Culture
WHERE
    ID = 2745
AND City_i18n.Culture IN ('es_ES', 'pt_PT') -- If Required
ORDER BY
    IF(ISNULL(CultureRank.Culture),1,0), 
    CultureRank.Rank
LIMIT 1;

ORDER BY ISNULL требуется, если в city_i18n будут присутствовать культуры, которых нет в CultureRank. Функция IsNull здесь гарантирует, что культурам в таблице CultureRank будет присвоен высший приоритет, а затем, если ни один из них не связан с городом, механизм выберет случайную культуру из таблицы city_i18n для этого города. Если вы предпочитаете, чтобы культуры, которых нет в CultureRank, были приоритетными, поменяйте местами параметры 1 и 0 в функции ISNULL.

EDIT:

SELECT
    Name
FROM
    City_i18n
    LEFT JOIN CultureRank ON City_i18n.Culture = CultureRank.Culture
WHERE
    ID = 2745
AND City_i18n.Culture IN ('es_ES', 'pt_PT') -- If Required
AND NOT EXISTS (
    SELECT
        NULL
    FROM
        City_i18n Cities
        LEFT JOIN CultureRank CitiesRank ON Cities.Culture = CitiesRank.Culture
    WHERE
        City_i18n.Name = Cities.Name
    AND CultureRank.Rank < CitiesRank.Rank)

Это должно дать вам каждое имя из таблицы City_i18n для данного идентификатора, если только нет другой записи с таким же именем, основанной на последовательности сортировки, которая имеет более высокий ранг в таблице CultureRank.

0 голосов
/ 29 февраля 2012

Похоже, я нашел решение:

 SELECT name FROM city_i18n
 WHERE (name != (SELECT name FROM city_i18n WHERE ID = 2745 AND culture = 'es_ES') 
        OR culture = 'es_ES')
 AND ID = 2745 AND culture IN ('es_ES', 'en_GB');

Если есть лучшее решение, я рад услышать об этом!

0 голосов
/ 29 февраля 2012

Unicode в PHP / MySQL - это боль. Возьмите следующие две строки кода:

setlocale(LC_ALL, 'en_US.utf8');
mysql_query("SET NAMES 'utf8'");

и разбросайте их по всему коду, как святую воду.

...