Как сортировать и отображать смешанные списки букв и цифр, как ожидают пользователи?

StackOverflow https://stackoverflow.com/questions/552053

Вопрос

Наше приложение имеет CustomerNumber поле.У нас есть сотни разных людей, использующих систему (у каждого есть свой собственный логин и свой собственный список CustomerNumberы).У отдельного пользователя может быть не более 100 000 клиентов.У многих их меньше 100.

Некоторые люди вводят только фактические цифры в поля своих номеров клиентов, в то время как другие используют смесь вещей.Система допускает 20 символов, которые могут быть A-Z, 0-9 или тире, и сохраняет их в VARCHAR2(20).Все, что записано в нижнем регистре, перед сохранением делается заглавным.

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

SELECT CustomerNumber,CustomerName
FROM Customer
WHERE User = ?
ORDER BY CustomerNumber;

Это наивное решение, поскольку люди, которые используют только цифры, не хотят видеть простую алфавитную сортировку (где "10" стоит перед "9").

Я не хочу задавать пользователю никаких ненужных вопросов об их данных.

Я использую Oracle, но я думаю, было бы интересно увидеть некоторые решения для других баз данных.Пожалуйста, укажите, в какой базе данных работает ваш ответ.

Как вы думаете, каков наилучший способ реализовать это?

Это было полезно?

Решение

В Oracle 10g:

SELECT  cust_name
FROM    t_customer c 
ORDER BY
    REGEXP_REPLACE(cust_name, '[0-9]', ''), TO_NUMBER(REGEXP_SUBSTR(cust_name, '[0-9]+'))

Это приведет к сортировке по первому появлению числа, а не по его позиции, i.e.:

  1. customer1 < customer2 < customer10
    • cust1omer ? customer1
    • cust8omer1 ? cust8omer2

, где a ? означает, что порядок не определен.

Этого достаточно для большинства случаев.

Принудительный порядок сортировки по регистру 2, вы можете добавить REGEXP_INSTR(cust_name, '[0-9]', n) Для ORDER BY Список n раз, наводя порядок при первом появлении n-че (2nd, 3rd и т.д.) группа цифр.

Принудительный порядок сортировки по регистру 3, вы можете добавить TO_NUMBER(REGEXP_SUBSTR(cust_name, '[0-9]+', n)) Для ORDER BY Список n раз, форсируя порядок n-й.группа цифр.

На практике запроса, который я написал, достаточно.

Вы можете создать индекс на основе функции на основе этих выражений, но вам нужно будет принудительно использовать подсказку и однопроходный SORT ORDER BY будет выполняться в любом случае, поскольку CBO недостаточно доверяет индексам на основе функций, чтобы позволить ORDER BY на них.

Другие советы

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

Другая возможность заключается в том, чтобы выполнить сортировку после выбора по возвращенным результатам.

Джефф Этвуд создал блог публикация о том, как некоторые люди вычисляют удобные для человека порядки сортировки.

У вас мог бы быть числовой столбец [CustomerNumberInt], который используется только тогда, когда CustomerNumber является чисто числовым (в противном случае NULL[1]), тогда

ORDER BY CustomerNumberInt, CustomerNumber

[1] в зависимости от того, как ваша версия SQL обрабатывает нули по порядку, вы можете установить значение по умолчанию равным нулю (или бесконечности!)

У меня похожая ужасная ситуация, и я разработал подходящую ужасную функцию для ее решения (SQLServer)

В моей ситуации у меня есть таблица "единиц измерения" (это система отслеживания работы студентов, поэтому единица измерения в данном контексте представляет курс, который они проходят).Единицы измерения имеют код, который по большей части является чисто числовым, но по разным причинам его сделали varchar, и они решили добавить к некоторым префиксам до 5 символов.Таким образом, они ожидают, что 53,123,237,356 будут отсортированы нормально, но также T53, T123, T237, T356

Код единицы измерения - это nvarchar(30)

Вот тело функции:

declare @sortkey nvarchar(30)

select @sortkey = 
    case
        when @unitcode like '[^0-9][0-9]%' then left(@unitcode,1) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-1)
        when @unitcode like '[^0-9][^0-9][0-9]%' then left(@unitcode,2) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-2)
        when @unitcode like '[^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,3) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-3)
        when @unitcode like '[^0-9][^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,4) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-4)
        when @unitcode like '[^0-9][^0-9][^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,5) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-5)
        when @unitcode like '%[^0-9]%' then @unitcode
        else left('000000000000000000000000000000',30-len(@unitcode)) + @unitcode
    end 

return @sortkey

Я хотел выстрелить себе в лицо после того, как написал это, однако это работает и, кажется, не убивает сервер при запуске.

Я использовал это в SQL SERVER и отлично работал:Здесь решение состоит в том, чтобы дополнить числовые значения символом спереди, чтобы все они имели одинаковую длину строки.

Вот пример использования этого подхода:

select MyCol
from MyTable
order by 
    case IsNumeric(MyCol) 
        when 1 then Replicate('0', 100 - Len(MyCol)) + MyCol
        else MyCol
    end

100 следует заменить фактической длиной этого столбца.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top