Как отсортировать столбец VARCHAR на сервере SQL, содержащий числа?

У меня есть столбец VARCHAR в базе данных SQL Server 2000, который может содержать буквы или цифры. Это зависит от того, как приложение настроено на клиентской стороне.

Когда он содержит числа, я хочу, чтобы он был отсортирован численно, например как «1», «2», «10» вместо «1», «10», «2». Поля, содержащие только буквы или буквы и цифры (например, «A1»), могут быть отсортированы в алфавитном порядке как обычно. Например, это был бы приемлемый порядок сортировки.

1
2
10
A
B
B1

Как лучше всего этого добиться?

Столкнулся с этой проблемой, зашел сюда. Увидел ваш вопрос, заметил неверное предположение. Вы сказали: «Поля, содержащие только буквы или буквы и цифры (например, 'A1'), могут быть отсортированы в алфавитном порядке как обычно». Попробуйте заказать A1, A2 и A10. Вы получите A1, A10, A2. Теперь, когда я закончил с вопросами, я буду искать ответ ниже. Надеюсь найду :)

anar khalilov 08.01.2014 15:59

также обратите внимание, что IsNumeric возвращает 1 для значений 24e4 и 12d34

Mladen 24.09.2008 02:20

@anarkhalilov не так ли?

ahsteele 18.07.2017 19:42

@ahsteele, честно говоря, мне сложно вспомнить это спустя три с половиной года.

anar khalilov 19.07.2017 14:56

@anarkhalilov Я предполагал, что ты не будешь, но решил, что спросить стоит. Спасибо за ответ.

ahsteele 19.07.2017 16:31
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
54
5
119 617
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Есть несколько способов сделать это.

Один был бы

SELECT
 ...
ORDER BY
  CASE 
    WHEN ISNUMERIC(value) = 1 THEN CONVERT(INT, value) 
    ELSE 9999999 -- or something huge
  END,
  value

первая часть ORDER BY преобразует все в int (с огромным значением для нечисловых чисел, для сортировки последней), затем последняя часть заботится об алфавите.

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

Таблицы, как правило, являются простыми «справочными» таблицами и не имеют большого количества строк (максимум несколько сотен, поэтому производительность не будет большой проблемой).

Tim C 23.09.2008 12:16

Переходя к статье об обмене экспертами, я ссылаюсь здесь: stackoverflow.com/questions/119730/… Я думаю, вам нужно преобразовать в MONEY, затем INT, чтобы избежать чтения '$' как числового

Matt Mitchell 23.09.2008 12:28

SELECT FIELD FROM TABLE
ORDER BY 
  isnumeric(FIELD) desc, 
  CASE ISNUMERIC(test) 
    WHEN 1 THEN CAST(CAST(test AS MONEY) AS INT)
    ELSE NULL 
  END,
  FIELD

Согласно эта ссылка вам нужно преобразовать в MONEY, а затем INT, чтобы не упорядочивать $ как число.

select
  Field1, Field2...
from
  Table1
order by
  isnumeric(Field1) desc,
  case when isnumeric(Field1) = 1 then cast(Field1 as int) else null end,
  Field1

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

Производительность не будет слишком высокой со всем этим происходящим преобразованием, поэтому другой подход заключается в добавлении еще одного столбца в таблицу, в которой вы храните целочисленную копию данных, а затем сортировку сначала по этому, а затем по рассматриваемому столбцу. Это, очевидно, потребует некоторых изменений в логике, которая вставляет или обновляет данные в таблице, чтобы заполнить оба столбца. Либо так, либо поместите в таблицу триггер для заполнения второго столбца всякий раз, когда данные вставляются или обновляются.

шлепает по голове So it does! Well, did... have patched it now. Did my classic mistake of fiddling with a working solution after testing! Well spotted, thanks.
Luke Bennett 23.09.2008 17:48

Кажется, это работает:

select your_column  
from your_table  
order by   
case when isnumeric(your_column) = 1 then your_column else 999999999 end,  
your_column   
Ответ принят как подходящий

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

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

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

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

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

Tim C 23.09.2008 12:46

Пришлось изменить char (0) на '0', чтобы заставить его работать ... в противном случае это твердо. Другие решения, приведенные здесь, не работали для сортировки по убыванию, если в таблице есть null

Samuel 20.05.2012 18:06

char (0) имеет значение null, что приводит к нулевому результату, поэтому вместо этого следует использовать '0'

yoel halb 12.09.2012 00:29

Большое вам спасибо, пока вы заменяете char (0) на 0 в приведенном выше примере, это отлично работает!

Coops 31.10.2012 18:59

Прекрасно работает! И вы можете добавить «desc» после конца, чтобы отсортировать в другом направлении. В этом случае сначала будут показаны строки. Мне нравится, как это решение фактически не дополняет числа в выводе результатов.

mikekidder 16.07.2014 20:19

SELECT *, CONVERT(int, your_column) AS your_column_int
FROM your_table
ORDER BY your_column_int

ИЛИ ЖЕ

SELECT *, CAST(your_column AS int) AS your_column_int
FROM your_table
ORDER BY your_column_int

Я думаю, оба довольно портативны.

+1 за простоту и лаконичность. В отличие от других, на это очень легко взглянуть и понять. Мой единственный вопрос: будет ли он по-прежнему сортировать, если бы были случаи, когда данные не были числовыми?

dallin 13.10.2012 23:39

Как указано в OP, your_column также включает буквы. Предлагаемое вами решение не будет работать для значений, включая буквы.

anar khalilov 08.01.2014 16:08

Я решил это очень просто, написав это в части "порядок"

ORDER BY (
sr.codice +0
)
ASC

Кажется, это работает очень хорошо, на самом деле у меня была следующая сортировка:

16079   Customer X 
016082  Customer Y
16413   Customer Z

Так что 0 перед 16082 считается правильным.

У меня были только числовые значения в поле varchar, которые нужно было отсортировать. Это сработало для меня!

Brainfeeder 01.08.2018 12:25

вы всегда можете преобразовать свой столбец varchar в bigint, поскольку целое число может быть слишком коротким ...

select cast([yourvarchar] as BIGINT)

но вы всегда должны заботиться об альфа-символах

where ISNUMERIC([yourvarchar] +'e0') = 1

+ 'e0' происходит от http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/isnumeric-isint-isnumber

это привело бы к вашему заявлению

SELECT
  *
FROM
  Table
ORDER BY
   ISNUMERIC([yourvarchar] +'e0') DESC
 , LEN([yourvarchar]) ASC

первый столбец сортировки поместит число вверху. второй сортируется по длине, поэтому 10 будет предшествовать 0001 (что глупо ?!)

это приводит ко второй версии:

SELECT
      *
    FROM
      Table
    ORDER BY
       ISNUMERIC([yourvarchar] +'e0') DESC
     , RIGHT('00000000000000000000'+[yourvarchar], 20) ASC

второй столбец теперь правильно дополняется '0', поэтому естественная сортировка помещает целые числа с ведущими нулями (0,01,10,0100 ...) в правильном порядке (правильно!) - но все альфа-значения будут улучшены с помощью '0' -chars (исполнение)

так третья версия:

 SELECT
          *
        FROM
          Table
        ORDER BY
           ISNUMERIC([yourvarchar] +'e0') DESC
         , CASE WHEN ISNUMERIC([yourvarchar] +'e0') = 1
                THEN RIGHT('00000000000000000000' + [yourvarchar], 20) ASC
                ELSE LTRIM(RTRIM([yourvarchar]))
           END ASC

теперь числа сначала дополняются символами '0' (конечно, длина 20 может быть увеличена) - что позволяет правильно сортировать числа - и только альфа-символы обрезаются

 SELECT *,
       ROW_NUMBER()OVER(ORDER BY CASE WHEN ISNUMERIC (ID)=1 THEN CONVERT(NUMERIC(20,2),SUBSTRING(Id, PATINDEX('%[0-9]%', Id), LEN(Id)))END DESC)Rn ---- numerical
        FROM
            (

        SELECT '1'Id UNION ALL
        SELECT '25.20' Id UNION ALL

    SELECT 'A115' Id UNION ALL
    SELECT '2541' Id UNION ALL
    SELECT '571.50' Id UNION ALL
    SELECT '67' Id UNION ALL
    SELECT 'B48' Id UNION ALL
    SELECT '500' Id UNION ALL
    SELECT '147.54' Id UNION ALL
    SELECT 'A-100' Id
    )A

    ORDER BY 
    CASE WHEN ISNUMERIC (ID)=0                                /* alphabetical sort */ 
         THEN CASE WHEN PATINDEX('%[0-9]%', Id)=0
                   THEN LEFT(Id,PATINDEX('%[0-9]%',Id))
                   ELSE LEFT(Id,PATINDEX('%[0-9]%',Id)-1)
              END
    END DESC

Этот запрос полезен для вас. В этом запросе столбец имеет тип данных varchar, упорядоченный в хорошем порядке. Например, в этом столбце данные: - G1, G34, G10, G3. Итак, после выполнения этого запроса вы увидите результаты: - G1, G10, G3, G34.

SELECT *,
       (CASE WHEN ISNUMERIC(column_name) = 1 THEN 0 ELSE 1 END) IsNum
FROM table_name 
ORDER BY IsNum, LEN(column_name), column_name;

Это может вам помочь, я пробовал это, когда у меня возникла такая же проблема.

SELECT * FROM tab ORDER BY IIF(TRY_CAST(val AS INT) IS NULL, 1, 0),TRY_CAST(val AS INT);

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