Сортировать строку как число в sql server
у меня есть столбец, содержащий такие данные. тире указывают несколько копий одного и того же счета, и они должны быть отсортированы в порядке возрастания
790711
790109-1
790109-11
790109-2
Я должен сортировать его в порядке возрастания на это число, но так как это поле varchar, оно сортируется в алфавитном порядке, как это
790109-1
790109-11
790109-2
790711
чтобы исправить это, я попытался заменить- (тире) пустым, а затем бросить его как число, а затем отсортировать по этому
select cast(replace(invoiceid,'-','') as decimal) as invoiceSort...............order by invoiceSort asc
пока это лучше и вроде этого
invoiceSort
790711 (790711) <-----this is wrong now as it should come later than 790109
790109-1 (7901091)
790109-2 (7901092)
790109-11 (79010911)
кто - то предложил мне разделить идентификатор счета-фактуры на - (тире ) и заказать по 2 разделенным частям
как=====> order by split1 asc,split2 asc (790109,1)
который будет работать, я думаю, но как бы я разделил столбец.
различные функции разделения в интернете-это те, которые возвращают таблицу, в то время как в этом случае мне потребуется скалярная функция.
есть ли другие подходы, которые можно использовать? Данные показанный в представлении сетки и представлении сетки не поддерживает сортировку по 2 столбцам по умолчанию (я могу реализовать его, Хотя:)), поэтому, если есть какие-либо более простые подходы, мне было бы очень приятно.
редактировать : спасибо за все ответы. Хотя каждый ответ правильный, я выбрал ответ, который позволил мне включить эти столбцы в сортировку GridView с минимальным повторным факторингом sql-запросов.
9 ответов
рациональное использование REVERSE
, CHARINDEX
и SUBSTRING
, может получить нам то, что мы хотим. Я использовал имена столбцов в моем коде ниже, чтобы проиллюстрировать, что происходит.
настройка выборочных данных:
DECLARE @Invoice TABLE (
InvoiceNumber nvarchar(10)
);
INSERT @Invoice VALUES
('790711')
,('790709-1')
,('790709-11')
,('790709-21')
,('790709-212')
,('790709-2')
SELECT * FROM @Invoice
пример данных:
InvoiceNumber
-------------
790711
790709-1
790709-11
790709-21
790709-212
790709-2
и вот код. У меня есть ноющее чувство, что последние выражения могут быть упрощены.
SELECT
InvoiceNumber
,REVERSE(InvoiceNumber)
AS Reversed
,CHARINDEX('-',REVERSE(InvoiceNumber))
AS HyphenIndexWithinReversed
,SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))
AS ReversedWithoutAffix
,SUBSTRING(InvoiceNumber,1+LEN(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))),LEN(InvoiceNumber))
AS AffixIncludingHyphen
,SUBSTRING(InvoiceNumber,2+LEN(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))),LEN(InvoiceNumber))
AS AffixExcludingHyphen
,CAST(
SUBSTRING(InvoiceNumber,2+LEN(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))),LEN(InvoiceNumber))
AS int)
AS AffixAsInt
,REVERSE(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber)))
AS WithoutAffix
FROM @Invoice
ORDER BY
-- WithoutAffix
REVERSE(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber)))
-- AffixAsInt
,CAST(
SUBSTRING(InvoiceNumber,2+LEN(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))),LEN(InvoiceNumber))
AS int)
выход:
InvoiceNumber Reversed HyphenIndexWithinReversed ReversedWithoutAffix AffixIncludingHyphen AffixExcludingHyphen AffixAsInt WithoutAffix
------------- ---------- ------------------------- -------------------- -------------------- -------------------- ----------- ------------
790709-1 1-907097 2 907097 -1 1 1 790709
790709-2 2-907097 2 907097 -2 2 2 790709
790709-11 11-907097 3 907097 -11 11 11 790709
790709-21 12-907097 3 907097 -21 21 21 790709
790709-212 212-907097 4 907097 -212 212 212 790709
790711 117097 0 117097 0 790711
обратите внимание, что все, что вам действительно нужно, это ORDER BY
предложение, остальное просто показать мою работу, которая идет следующим образом:
- переверните строку, найдите дефис, получите подстроку после дефиса, переверните эту часть: это число без какого-либо аффикса
- длина (число без какого-либо аффикса) говорит нам, сколько символов нужно отбросить с самого начала, чтобы получить аффикс, включая дефис. Отбросьте дополнительный символ, чтобы получить только числовую часть, и преобразуйте это в
int
. К счастью мы получаем перерыв от SQL Server в том, что это преобразование дает ноль для пустой строки. - наконец, получив эти две части, мы просто
ORDER BY
(число без какого-либо аффикса), а затем (числовое значение аффикса). Это последний приказ, который мы ищем.
код был бы более кратким, если бы SQL Server позволил нам сказать SUBSTRING(value, start)
чтобы получить строку, начиная с этого момента, но это не так, поэтому мы должны сказать SUBSTRING(value, start, LEN(value))
много.
попробуйте это -
запрос:
DECLARE @Invoice TABLE (InvoiceNumber VARCHAR(10))
INSERT @Invoice
VALUES
('790711')
, ('790709-1')
, ('790709-21')
, ('790709-11')
, ('790709-211')
, ('790709-2')
;WITH cte AS
(
SELECT
InvoiceNumber
, lenght = LEN(InvoiceNumber)
, delimeter = CHARINDEX('-', InvoiceNumber)
FROM @Invoice
)
SELECT InvoiceNumber
FROM cte
CROSS JOIN (
SELECT repl = MAX(lenght - delimeter)
FROM cte
WHERE delimeter != 0
) mx
ORDER BY
SUBSTRING(InvoiceNumber, 1, ISNULL(NULLIF(delimeter - 1, -1), lenght))
, RIGHT(REPLICATE('0', repl) + SUBSTRING(InvoiceNumber, delimeter + 1, lenght), repl)
выход:
InvoiceNumber
-------------
790709-1
790709-2
790709-11
790709-21
790709-211
790711
попробуй такое
SELECT invoiceid FROM Invoice
ORDER BY
CASE WHEN PatIndex('%[-]%',invoiceid) > 0
THEN LEFT(invoiceid,PatIndex('%[-]%',invoiceid)-1)
ELSE invoiceid END * 1
,CASE WHEN PatIndex('%[-]%',REVERSE(invoiceid)) > 0
THEN RIGHT(invoiceid,PatIndex('%[-]%',REVERSE(invoiceid))-1)
ELSE NULL END * 1
выше запрос использует два оператора case
- сортирует первую часть Invoiceid 790109-1 (например: 790709)
- сортирует вторую часть Invoiceid после разделения на ' - ' 790109-1 (например: 1)
для детального понимания проверьте ниже SQLfiddle
или использовать 'Функция charindex'
SELECT invoiceid FROM Invoice
ORDER BY
CASE WHEN CHARINDEX('-', invoiceid) > 0
THEN LEFT(invoiceid, CHARINDEX('-', invoiceid)-1)
ELSE invoiceid END * 1
,CASE WHEN CHARINDEX('-', REVERSE(invoiceid)) > 0
THEN RIGHT(invoiceid, CHARINDEX('-', REVERSE(invoiceid))-1)
ELSE NULL END * 1
заказ по каждой части отдельно-это самый простой и надежный путь, зачем искать другие подходы? Взгляните на этот простой вопрос.
select *
from Invoice
order by Convert(int, SUBSTRING(invoiceid, 0, CHARINDEX('-',invoiceid+'-'))) asc,
Convert(int, SUBSTRING(invoiceid, CHARINDEX('-',invoiceid)+1, LEN(invoiceid)-CHARINDEX('-',invoiceid))) asc
много хороших ответов здесь, но я думаю, что это может быть самый компактный порядок по предложению, которое эффективно:
SELECT *
FROM Invoice
ORDER BY LEFT(InvoiceId,CHARINDEX('-',InvoiceId+'-'))
,CAST(RIGHT(InvoiceId,CHARINDEX('-',REVERSE(InvoiceId)+'-'))AS INT)DESC
демо: - SQL Fiddle
Примечание. я добавил версию "790709" в свой тест, так как некоторые из перечисленных здесь методов не обрабатывают версию без суффикса как меньшую, чем версии с суффиксом.
Если ваш invoiceID изменяется по длине, перед "-" , То есть, вам нужно:
SELECT *
FROM Invoice
ORDER BY CAST(LEFT(list,CHARINDEX('-',list+'-')-1)AS INT)
,CAST(RIGHT(InvoiceId,CHARINDEX('-',REVERSE(InvoiceId)+'-'))AS INT)DESC
демо с различные длины перед тире:SQL Fiddle
мой вариант:
declare @Len int
select @Len = (select max (len (invoiceid) - charindex ( '-', invoiceid))-1 from MyTable)
select
invoiceid ,
cast (SUBSTRING (invoiceid ,1,charindex ( '-', invoiceid )-1) as int) * POWER (10,@Len) +
cast (right(invoiceid, len (invoiceid) - charindex ( '-', invoiceid) ) as int )
from MyTable
вы можете реализовать это как новый столбец в своей таблице:
ALTER TABLE MyTable ADD COLUMN invoice_numeric_id int null
GO
declare @Len int
select @Len = (select max (len (invoiceid) - charindex ( '-', invoiceid))-1 from MyTable)
UPDATE TABLE MyTable
SET invoice_numeric_id = cast (SUBSTRING (invoiceid ,1,charindex ( '-', invoiceid )-1) as int) * POWER (10,@Len) +
cast (right(invoiceid, len (invoiceid) - charindex ( '-', invoiceid) ) as int )
один из способов-разделить InvoiceId
в его части, а затем отсортировать на части. Здесь я использую производную таблицу, но это можно сделать с помощью CTE или временной таблицы.
select InvoiceId, InvoiceId1, InvoiceId2
from
(
select
InvoiceId,
substring(InvoiceId, 0, charindex('-', InvoiceId, 0)) as InvoiceId1,
substring(InvoiceId, charindex('-', InvoiceId, 0)+1, len(InvoiceId)) as InvoiceId2
FROM Invoice
) tmp
order by
cast((case when len(InvoiceId1) > 0 then InvoiceId1 else InvoiceId2 end) as int),
cast((case when len(InvoiceId1) > 0 then InvoiceId2 else '0' end) as int)
выше InvoiceId1
и InvoiceId2
- являются составными частями InvoiceId
. The внешний select
включает в себя детали, но только для демонстрационных целей - вам не нужно делать это в вашем выборе.
производная таблица (внутренняя select
) схватил InvoiceId
также как составные части. Как это работает:
- когда есть тире в
InvoiceId
,InvoiceId1
будет содержать первую часть числа иInvoiceId2
будет содержать второго. - когда нет тире,
InvoiceId1
будет пустым иInvoiceId2
будет содержать все число.
втором случае выше (без тире) не является оптимальным, потому что в идеале InvoiceId1
будет содержать число и InvoiceId2
будет пустой. Чтобы сделать внутреннюю работу select оптимально уменьшит читаемость select. Я выбрал неоптимальный, более читаемый подход, поскольку он достаточно хорош для сортировки.
вот почему ORDER BY
тесты предложения для длины-он должен обрабатывать два случая выше.
демо на SQL Fiddle
разбейте сортировку на две части:
Настройка схемы MS SQL Server 2008:
CREATE TABLE TestData
(
data varchar(20)
)
INSERT TestData
SELECT '790711' as data
UNION
SELECT '790109-1'
UNION
SELECT '790109-11'
UNION
SELECT '790109-2'
запрос 1:
SELECT *
FROM TestData
ORDER BY
FLOOR(CAST(REPLACE(data, '-', '.') AS FLOAT)),
CASE WHEN CHARINDEX('-', data) > 0
THEN CAST(RIGHT(data, len(data) - CHARINDEX('-', data)) AS INT)
ELSE 0
END
| DATA |
-------------
| 790109-1 |
| 790109-2 |
| 790109-11 |
| 790711 |