Изм енить сортировку в MySQL

Кошакевич

Новичок
Изм енить сортировку в MySQL

Стандартная функция сортировки выдает сначала цифры, потом английский по алфавиту, затем русский по алфавиту.
Требуется чтобы сначала выдавался русский, потом английский, потом цифры.

Можно ли это сделать одним запросом без привлечения доп средств? Как сделать несколько запросов или сортировку уже в php я представляю, но система должна работать под большой нагрузкой, потому я считаю, что тратить системные ресурсы на эту бадягу излишне.

В базе примерно 500-600 записей. Из них с цифрами 10, англ 20-30. Вывод постраничный по 10 на стр.
PHP5. Версия сервера MySQL: 4.1.22
 

Кошакевич

Новичок
Подскажите, пожалуйста, как именно можно воспользоваться CASE?
*покурил мануалы*
Что-то в голову вообще не лезет...
 

zerkms

TDD infected
Команда форума
hint1:

ORDER BY
CASE
WHEN первая буква английская THEN 1
WHEN первый знак - цифра THEN 2
ELSE 0
END,
`field`

:)
 

JD

Новичок
Re: Изм енить сортировку в MySQL

В базе примерно 500-600 записей. Из них с цифрами 10, англ 20-30.
Если в дальнейшем записи будут распределяться примерно таким же образом, записей будет много и сервер дейсвительно работает под большой нагрузкой, то, возможно, стоит пересмотреть порядок выражений, выполняемых в условии
ORDER BY CASE WHEN первая буква английская THEN 1 WHEN первый знак - цифра THEN 2 ELSE 0 END, `field`
и изменить его в соответсвии с количесвом записей, которые под них попадают. т.е. в данном случае:
CASE
WHEN первая буква русская THEN 0
WHEN первая буква англ THEN 1
ELSE 2
END

т.к. в таком случае вероятность выполнения второго выражение меньше

Так же, я бы ещё провел эсперимент с тремя запросами в юнионе... хоть внутренний голос мне говорит, что оно врядли будет быстрее, но он может и ошибаться :)
 

Gas

может по одной?
с тремя запросами в юнионе
при таких исходных данных, теоритически он должен быть медленне чем order by case

Если б меня беспокоила скорость, сделал бы отдельное поле с "порядком для сортировки"+нужный индекс. Но на 500 записей, наверное, этот того не стоит.
 

HraKK

Мудак
Команда форума
а гугл молчит? В MySQL можно настраивать самому порядки сортировки.
 

Gas

может по одной?
есть такие collations, где цифры идут после "букв"?
писать свой файл с правилами сортировки - имхо, дело энтузиастов, у которых есть лишнее время.
или речь не о collations?

[deleted]
 

fixxxer

К.О.
Партнер клуба
заведи дополнительное поле (скажем order_type), которое
0 если первая буква - русская
1 - латинская
2 - цифра
и делай order by ( order_type, твоя строка ) :)

а если уж надо не только по первому символу это правило соблюдать, тогда ой, делай свой collation
 

zerkms

TDD infected
Команда форума
Если в дальнейшем записи будут распределяться примерно таким же образом, записей будет много и сервер дейсвительно работает под большой нагрузкой, то, возможно, стоит пересмотреть порядок выражений, выполняемых в условии
кхе-кхе... :) чот слабо верится. аргументация?
 

JD

Новичок
Автор оригинала: zerkms
кхе-кхе... :) чот слабо верится. аргументация?
аргумент: если записей с русскими буквами >90% (как указал топикстартер), то в таком же количестве слуаев будет выполняться только выражение из первого CASE'а с проверкой на русскую букву. выражение во вротом CASE'е будет выполнятся только в том случае, если первый не вернет true. Если надо, могу привести пример.
 

zerkms

TDD infected
Команда форума
JD
супер. а теперь продемонстрируй алгоритм определения "что буква русская" в конструкции WHEN первая буква русская THEN 0
м?
 

JD

Новичок
Автор оригинала: zerkms
супер. а теперь продемонстрируй алгоритм определения "что буква русская" в конструкции WHEN первая буква русская THEN 0
м?
первое что пришло в голову: `column` REGEXP '^[а-я]'
на MySQL: 5.0.51a работает:
[sql]SELECT 'фыв' REGEXP '^[а-я]' AS rus, 'asd' REGEXP '^[а-я]' AS eng, '123' REGEXP '^[а-я]' AS num[/sql]
Код:
+-----+-----+-----+
| rus | eng | num |
+-----+-----+-----+
|   1 |   0 |   0 |
+-----+-----+-----+
возможно, есть и другие способы...
 

zerkms

TDD infected
Команда форума
на MySQL: 5.0.51a работает:
1. учёл ли ты юникод? а букву "ё" :)
2. для английского быстрее будет ORD(`field`) BETWEEN ... делать

а теперь отправь это в BENCHMARK() и сравни производительность
 

JD

Новичок
1. учёл ли ты юникод? а букву "ё" :)
с юникодом проблем быть не должно. а буква "ё" прохоидт нормально
2. для английского быстрее будет ORD(`field`) BETWEEN ... делать
а теперь отправь это в BENCHMARK() и сравни производительность
это ты уже перешел на оптимизацию самих выражений ;-)
я не отрицал наличие других вариантов выражений. бесспорно, ORD() BETWEEEN будет работать быстрее, чем REGEXP.

вот, что у меня получилось:
REGEXP - не особо быстр, зато выглядит красивее и понятнее, чем следующие за ним примеры :)
BENCHMARK(1000000, ('фыв' REGEXP '^[а-я]'))
1 row in set (0.23 sec)

Вот эти выражения будут выполняться всегда, когда будет попадаться русская буква (а по условию это >90% случаем)
проверки на англ. букву в верхнем и нижнем регистре, и на цифру.
BENCHMARK(1000000, ( (ORD('фыв') BETWEEN 65 AND 90) OR (ORD('фыв') BETWEEN 97 AND 122) OR (ORD('фыв') BETWEEN 48 AND 57) ))
1 row in set (0.24 sec)
или
BENCHMARK(1000000, ( (ORD(UPPER('фыв')) BETWEEN 65 AND 90) OR (ORD('фыв') BETWEEN 48 AND 57) ));
1 row in set (0.25 sec)

Вот такие выражения необходими для распознавания русской буквы
Проверка на руск букву в верхнем регистре (тут не забыта любимая буква zerkmsа ;-) - "ё") (больше ничего не забыл?)
BENCHMARK(1000000, ( (ORD(UPPER('фыв')) BETWEEN 53392 AND 53423) OR (ORD(UPPER('фыв')) = 53377) ) );
1 row in set (0.28 sec)

А если учесть, что, как я понял, BENCHMARK() выполняет все выражения, а в CASE...WHEN... выполняется минимально необходимое количество (т.е. в данном случае, если буква русская, но не Ё, то ORD(UPPER('фыв')) = 53377) выполняться не будет) и буква "ё" встречается очень редко, то для теста выражение можно упростить до:
BENCHMARK(1000000, (ORD(UPPER('фыв')) BETWEEN 53392 AND 53423) );
1 row in set (0.12 sec)

т.е. при установке в первом CASE'е условия на проверку русск.буквы, то на 1000000(!) записей получаем
выигрыш примерно в (0.24 - 0.12) = 0,12 sec с вероятностью >0.9 :) отсюда, средний выигрыш (0,12 * 0,9) = 0,108 sec :)

Это конечно мелочь и её можно списать на погрешность, но все же :) Поправьте, если я что-то напутал или неправильно понял идею с ORD() BETWEEN

-~{}~ 02.06.08 19:57:

Ещё можно поиграться с вынесением ORD(UPPER('фыв')) "за скобки". тогда просто получим выигрыш в количестве BETWEEN'ов
 

zerkms

TDD infected
Команда форума
т.е. при установке в первом CASE'е условия на проверку русск.буквы, то на 1000000(!) записей получаем
выигрыш примерно в (0.24 - 0.12) = 0,12 sec с вероятностью >0.9
с какой стати? в предложенном мной варианте - проверяется, что буква английская или цифра. так что в худшем случае необходимо сравнивать время нахождения "буква латинская" + "цифра" vs время нахождения "буква русская"
Проверка на руск букву в верхнем регистре (тут не забыта любимая буква zerkmsа ;-) - "ё") (больше ничего не забыл?)
BENCHMARK(1000000, ( (ORD(UPPER('фыв')) BETWEEN 53392 AND 53423) OR (ORD(UPPER('фыв')) = 53377) ) );
Код:
mysql> SELECT ORD(UPPER('а'));
+-----------------+
| ORD(UPPER('а')) |
+-----------------+
|             160 |
+-----------------+
1 row in set (0.00 sec)
а если так? ещё диапазоны добавь и переизмерь заново :)
+ верни ё
буква "ё" встречается очень редко, то для теста выражение можно упростить до
отлично просто :) сделай тогда сразу WHEN RAND() < 0.9, упрости. давай-давай, все нюансы учитывай, и ё, и ещё один диапазон кириллических знаков в ascii :)
 

JD

Новичок
с какой стати? в предложенном мной варианте - проверяется, что буква английская или цифра. так что в худшем случае необходимо сравнивать время нахождения "буква латинская" + "цифра" vs время нахождения "буква русская"
Именно это я и сравнивал. смотри, 2й и 3й BENCHMARK() VS 5й(упрощенный)

а если так? ещё диапазоны добавь и переизмерь заново :)
По-идее, мы должны знать, в какой кодировке храним данные. Проверять по нескольким диапазонам из разных кодировок кажется неправильным.

+ верни ё

отлично просто :) сделай тогда сразу WHEN RAND() < 0.9, упрости. давай-давай, все нюансы учитывай, и ё, и ещё один диапазон кириллических знаков в ascii :)
Не надо впадать в крайности. Почему я убрал Ё я объяснял. И это не только потому, что она имеет маленькую частоту вхождения, а потому, как BENCHMARK() выполняет выражения.

Вот, Кошакевич уже, наверно, и забыл о своей проблеме, а мы тут с тобой спорим ;)
Предлагаю сделать выводы и закончить :)
Вывод: вариант zerkmsа достаточно прост для понимания, быстр и не зависит от кодировки данных, но если учесть набор входных данных и немного извратиться, то можно получить небольшой вигрыш в скорости. Что важнее - зависит от каждого конкретного случая. В случае топикстартера, заморачиваться не стоит, но иметь ввиду, что есть и другие варианты, это хорошо.
 
Сверху