LINUX.ORG.RU

Сортировка текстовых строк в SQL

 , ,


0

2

Есть список файлов в таблице в PostgreSQL или SQLite. Команда select file,status from files order by file; выводит в таком порядке:

   file       | status
--------------+--------
 scan/79.txt  |      0
 scan/8.txt   |      0
 scan/80.txt  |      0
 scan/88.txt  |      0
 scan/89.txt  |      0
 scan/9.txt   |      0
 scan/90.txt  |      0
 scan/91.txt  |      0

А мне надо сортировать числа в середине строк по возрастанию: 8-9-79-80-… Можно ли это сделать средствами SQL? Как такая сортировка называется?

Ответ: «natural sort order» – «естественная». Для Postgre нашлось готовое решение:

CREATE COLLATION numeric (provider = icu, locale = 'en-u-kn-true');
SELECT * FROM files ORDER BY file COLLATE numeric;

Для sqlite3 пока нашёл только советы, как писать расширение на Си.

Или можно вычленить числа, преобразовать в числовой формат и сортировать по ним:
SELECT * FROM files ORDER BY CAST(REPLACE(SUBSTR(file, 6), '.txt', '') AS int);

★★★

Последнее исправление: olegd (всего исправлений: 3)
Ответ на: комментарий от olegd

Этот бинарник исключительно для вспомогательных/отладочных целей, ну или можешь считать его демкой как использовать библиотеку. Основной функционал sqlite - именно библиотека.

Ну и в исходник бинарника тоже можно дописать определение сортировки.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 3)
Ответ на: комментарий от ya-betmen

отрезать scan/ если они одинаковы для всех.

Этого оказалось недостаточно. Нужно обрезать весь текст и преобразовать в числа, иначе всё равно сортирует 79-8-80.

Но оказалось, в sqlite есть CAST.

Рабочий вариант:SELECT * FROM files ORDER BY CAST(REPLACE(SUBSTR(file, 6), '.txt', '') AS int);

Спасибо.

Тогда заодно связанный вопрос: почему в обеих базах работает
SELECT *, CAST(REPLACE(SUBSTR(file, 6), '.txt', '') AS int) AS b FROM files ORDER BY b;
но
SELECT *, REPLACE(SUBSTR(file, 6), '.txt', '') AS b FROM files ORDER BY CAST(b as int);
работает в SQLite, но в PostgreSQL выдаёт ошибку «column «b» does not exist»?

olegd ★★★
() автор топика
Последнее исправление: olegd (всего исправлений: 1)
Ответ на: комментарий от ya-betmen

Можешь попробовать упрощать запоос для потгресапока не станет ясно в чем проблема.

ORDER BY b работает, любая операция с b ругается, что нет такого столбца. b можно выбирать из столбцов files без преобразований или с преобразованиями. То есть:
SELECT file AS b FROM files ORDER BY b; – работает.
SELECT file, status AS b FROM files ORDER BY b; – работает.
SELECT file AS b FROM files ORDER BY SUBSTR(b, 6); – ошибка.

olegd ★★★
() автор топика
Ответ на: комментарий от arkhnchul

емнип в постгресе order by работает только конкретно со столбцами, которые выбраны select-ом, выражения не прокатят.

Из-за чего?

И выражения работают. Но в качестве аргумента в них принимаются только столбцы. Алайасы нельзя.

olegd ★★★
() автор топика
Последнее исправление: olegd (всего исправлений: 1)
Ответ на: комментарий от olegd

Потому что в order by допустимы только select выражения.

классика, можно так

select a,b,c from table order by a + b
но так нельзя
select a + b as sum, c from table order by sum + c
потому что ты не можешь написать
select a + b as sum, c, sum + c from table
Стандартная процедура оборот внешним select-ом
select * from (select a + b as sum, c from table) order by sum + c

vtVitus ★★★★★
()
Последнее исправление: vtVitus (всего исправлений: 2)

Для sqlite3 пока нашёл только советы, как писать расширение на Си.

Заведи отдельный столбец с ключами, приведенными в пригодный для сортировки вид. Тупо добить выравнивающими нулями:

scan/00000000008.txt
scan/00000000009.txt
scan/00000000079.txt
scan/00000000080.txt
scan/00000000088.txt
scan/00000000089.txt
scan/00000000090.txt
scan/00000000091.txt
wandrien ★★★
()

REPLACE(SUBSTR(file, 6), ‘.txt’, '')

.txt необязательно удалять:

select * from files order by substr(file, 6)+0;

Через шелл и sort, например:

sqlite3 files.sqlite 'select * from files' -csv | sort -V
anonymous
()

Можно вручную расковырять строку на символы и слепить обратно в строку только цифры, привести её к целому и по ней сортировать.

WITH RECURSIVE split_by_sym AS (
	SELECT 1 AS x, substr(file,1,1) AS s, f.file, f.status FROM files f
	UNION ALL
	SELECT sp.x+1, substr(sp.file, sp.x+1, 1) AS ss, sp.file, sp.status
	FROM split_by_sym sp
	WHERE ss != ''
)
SELECT
	file, status
	,CAST(group_concat(s,'') FILTER(WHERE s BETWEEN '0' AND '9') AS int) AS numbers_name
	,CAST(group_concat(s,'') AS text) AS real_name
FROM
	split_by_sym
GROUP BY
	file, status
ORDER BY
	numbers_name
;
В SQLite (оказывается) RECURSIVE тоже работает, проверил. Это на случай, если нет простого шаблона для вычленения цифр из названия.

Toxo2 ★★★★
()