Как сортировать и отображать смешанные списки альфа и числа, как ожидают пользователи? - PullRequest
3 голосов
/ 16 февраля 2009

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

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

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

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

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

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

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

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

Ответы [ 5 ]

4 голосов
/ 16 февраля 2009

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

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

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

2 голосов
/ 16 февраля 2009

В 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]+'))

Это будет отсортировано по первому вхождению числа, не учитывая его положение, т.е. e.:

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

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

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

Для принудительного порядка сортировки для случая 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.

1 голос
/ 17 февраля 2009

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

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

UnitCode - это 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

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

1 голос
/ 16 февраля 2009

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

ORDER BY CustomerNumberInt, CustomerNumber

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

0 голосов
/ 20 июня 2014

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

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

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

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

...