mysql BETWEEN является исключительным правом для символов?

Рассмотрим эту таблицу с именем easy_drinks,

+------------------+
| drink_name       |
+------------------+
| Kiss on the Lips |
| Hot Gold         |
| Lone Tree        |
| Greyhound        |
| Indian Summer    |
| Bull Frog        |
| Soda and It      |
| Blackthorn       |
| Blue Moon        |
| Oh My Gosh       |
| Lime Fizz        |
+------------------+

Запрос как таковой,

 select drink_name from easy_drinks where drink_name BETWEEN 'G' and 'O';

приводит к

+------------------+
| drink_name       |
+------------------+
| Kiss on the Lips |
| Hot Gold         |
| Lone Tree        |
| Greyhound        |
| Indian Summer    |
| Lime Fizz        |
+------------------+

названия напитков, начинающиеся с O, не включаются в результат. Но согласно странице руководства

expr МЕЖДУ мин. и макс.

Если выражение больше или равно min, а выражение меньше или равно max, функция BETWEEN возвращает 1, в противном случае возвращается 0. Если выражение больше или равно min, а выражение меньше или равно max, BETWEEN > возвращает 1, иначе возвращает 0.

Почему запрос дает такие результаты?

Я рассмотрел вопросы, объясняющие поведение Timestamp и Date. В чем причина в данном случае?


person Suvarna Pattayil    schedule 23.05.2013    source источник


Ответы (2)


tl;dr

Не используйте BETWEEN для строк

где drink_name >= 'G' и drink_name ‹ 'P';

Почему?

O эффективно расширяется конечными пробелами, чтобы соответствовать столбцу. Так

'O                  '

до

'Oh My Gosh'

Так что вам нужно

where drink_name BETWEEN 'G' and 'OZ';

Если у вас есть напиток под названием Ozymandias, то это не сработает. Так:

where drink_name BETWEEN 'G' and 'OZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ';

Но можно ли смело предположить, что нет напитка под названием P и много пробелов?
Это непонятно.

select drink_name from easy_drinks
where drink_name BETWEEN 'G' and 'P';

Очевидным выбором может быть сравнение только первых букв с помощью LEFT< /а>

select drink_name from easy_drinks
where LEFT(drink_name, 1) BETWEEN 'G' and 'O';

Но это предотвратит использование любого индекса для drink_name.

person gbn    schedule 23.05.2013
comment
Также where drink_name >= 'G' and drink_name < 'P' - person ypercubeᵀᴹ; 23.05.2013
comment
@ypercube: собирался сказать это, но увлёкся :) - person gbn; 23.05.2013
comment
В чем я не уверен, так это в том, как чувствительность к регистру влияет на эти варианты. - person ypercubeᵀᴹ; 23.05.2013
comment
Это будет проблемой сопоставления. Я работаю без учета регистра почти всегда - person gbn; 23.05.2013

Вот более простой способ определить, что происходит:

select drink_name, drink_name >='G', drink_name <='O'
from easy_drinks
order by 1;

Основные результаты:

  • Greyhound is >= 'G'
  • Oh My Gosh не ‹= 'O'

Это имеет смысл, как только вы думаете об этом. В словаре одна запись О всегда является первой записью на букву О.

Возможно, вам понадобится что-то вроде этого:

select drink_name
from easy_drinks
where SUBSTRING(drink_name, 1, 1) BETWEEN 'G' and 'O';

Изменить:

Я совершенно забыл о LEFT(drink_name, 1), удобном сокращении для SUBSTRING(drink_name, 1, 1).

person Álvaro González    schedule 23.05.2013
comment
LEFT также означает, что не нужно использовать какой-либо индекс. - person gbn; 23.05.2013