2013-05-01 17 views
8

Mam kolumnę, która zawiera dane takie jak ta. kreski wskazują obsługujący kilka kopii tej samej fakturze, a te muszą być uporządkowane w kolejności rosnącejSortuj ciąg znaków jako numer w serwerze sql

790711 
790109-1 
790109-11 
790109-2 

muszę uporządkować je w kolejności rosnącej od tego numeru, ale ponieważ jest to pole varchar sortuje w kolejności alfabetycznej, jak ten

790109-1 
790109-11 
790109-2 
790711 

w celu ustalenia tego próbowałem wymiany - (myślnik) z pusta, a następnie rzucając go jako liczby, a następnie sortowania na tym

select cast(replace(invoiceid,'-','') as decimal) as invoiceSort...............order by invoiceSort asc 

wHI le to jest lepsze i rodzaju jak ten

  invoiceSort 
790711  (790711) <-----this is wrong now as it should come later than 790109 
790109-1 (7901091) 
790109-2 (7901092) 
790109-11 (79010911) 

ktoś zaproponował mi podzielić na fakturze id - (myślnik) i porządku przez na 2 części podzielonych

jak =====>order by split1 asc,split2 asc (790109,1)

Który działałby myślę, ale jak podzielić kolumnę.

Różne funkcje podziału w Internecie to te, które zwracają tabelę, podczas gdy w tym przypadku wymagałoby to funkcji skalarnej.

Czy istnieją inne podejścia, które można zastosować? Dane są wyświetlane w widoku siatki, a widok siatki nie obsługuje domyślnie sortowania na 2 kolumnach (mogę to zaimplementować :)), więc jeśli są jakieś prostsze podejścia, byłoby bardzo miło.

EDYTOWANIE: dzięki za wszystkie odpowiedzi. Chociaż każda odpowiedź jest poprawna, wybrałem odpowiedź, która pozwoliła mi na włączenie tych kolumn do sortowania GridView przy minimalnym ponownym uwzględnianiu zapytań sql.

+1

Myślę, że to pokazuje dobitnie dlaczego przechowywanych wartości powinna zmienić .... –

+0

Dlaczego chcesz 790109-11 przyjść przed 790109-2? –

Odpowiedz

3

legalne wykorzystywanie REVERSE, CHARINDEX i SUBSTRING, może nas to, co chcemy. Użyłem miejmy nadzieję objaśniających nazw kolumn w poniższym kodzie, aby zilustrować, co się dzieje.

Konfigurowanie przykładowe dane: dane

DECLARE @Invoice TABLE (
    InvoiceNumber nvarchar(10) 
); 

INSERT @Invoice VALUES 
('790711') 
,('790709-1') 
,('790709-11') 
,('790709-21') 
,('790709-212') 
,('790709-2') 

SELECT * FROM @Invoice 

Próbka:

InvoiceNumber 
------------- 
790711 
790709-1 
790709-11 
790709-21 
790709-212 
790709-2 

A oto kod. Mam dokuczliwe uczucie, że końcowe wyrażenia można uprościć.

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) 

wyjściowa:

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 

Pamiętaj, że wszystko, co rzeczywiście potrzebne jest klauzula ORDER BY, reszta jest po prostu pokazać moją pracę, która wygląda tak:

  • Odwrotny ciąg , znajdź łącznik, pobierz ciąg po łączu, odwróć tę część: Jest to liczba bez żadnego atrybutu.
  • Długość (nu mber bez żadnego atrybutu) mówi nam, ile znaków należy upuścić od samego początku, aby uzyskać łącznik z łącznikiem. Upuść dodatkowy znak, aby uzyskać tylko część liczbową, i przekonwertuj ją na int. Na szczęście otrzymujemy przerwę od SQL Server, ponieważ ta konwersja daje zero dla pustego łańcucha.
  • Wreszcie, po tych dwóch elementach, mamy prosty ORDER BY (numer bez żadnego afiksu), a następnie przez (wartość liczbowa atrybutu). To jest ostateczne zamówienie, którego szukamy.

Kod byłoby bardziej zwięzły jeśli SQL Server pozwoliło nam powiedzieć SUBSTRING(value, start) aby uzyskać ciąg rozpoczynający w tym punkcie, ale to nie jest, więc musimy powiedzieć SUBSTRING(value, start, LEN(value)) dużo.

+0

Wydaje się niepotrzebnie skomplikowane do mnie. –

0

Spróbuj:

select invoiceid ... order by Convert(decimal(18, 2), REPLACE(invoiceid, '-', '.')) 
+3

Przepraszam, ale to nie działa, ponieważ konwertuje zarówno -1 i -10 jak 790109.10 –

1

Moja wersja:

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 

można zaimplementować to jako nową kolumnę do tabeli:

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) 
1

Jednym ze sposobów jest podzielona InvoiceId na części, a następnie posortować na części. Tutaj używam wyprowadzonej tabeli, ale można to zrobić również za pomocą CTE lub tabeli tymczasowej.

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) 

W powyższym InvoiceId1 i InvoiceId2 są części składowe InvoiceId. Zewnętrzna zawiera części, ale tylko w celach demonstracyjnych - nie musisz tego robić w swoim wyborze.

Wyprowadzona tabela (wewnętrzna select) pobiera InvoiceId, a także części składowe. Jak to działa to w ten sposób:

  • Gdy jest myślnik w InvoiceId, InvoiceId1 zawiera pierwszą część numeru i InvoiceId2 zawierać będzie drugi.
  • Gdy nie ma myślnika, InvoiceId1 będzie puste, a InvoiceId2 będzie zawierać cały numer.

Drugi przypadek powyżej (bez kreski) nie jest optymalny, ponieważ idealnie InvoiceId1 zawierałby numer, a InvoiceId2 byłby pusty. Aby optymalizacja wewnętrzna działała optymalnie, zmniejszyłaby czytelność zaznaczenia. Wybrałem podejście nieoptymalne, bardziej czytelne, ponieważ jest wystarczająco dobre, aby umożliwić sortowanie.

Dlatego właśnie należy testować klauzulę ORDER BY dla długości - musi ona obsługiwać dwa powyższe przypadki.

Demo na SQL Fiddle

4

Spróbuj jeden -

Zapytanie:

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) 

wyjściowa:

InvoiceNumber 
------------- 
790709-1 
790709-2 
790709-11 
790709-21 
790709-211 
790711 
3

Spróbuj

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 

SQLFiddle Demo

powyżej zapytania wykorzystuje dwie instrukcje przypadków

  1. sortuje pierwszą część Invoiceid 790109-1 (np 790709)
  2. sortuje druga część Invoiceid po rozdzieleniu z "-" 790109-1 (np .: 1)

Dla dokładnego zrozumienia sprawdź poniżej SQLfiddle

SQLFiddle Detailed Demo

lub użyj „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 
2

zamówienia przez każdą część osobno to najprostszy i niezawodny sposób, aby przejść, dlaczego szukać innych rozwiązań ? Spójrz na to proste zapytanie.

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 
2

Mnóstwo dobrych odpowiedzi tutaj, ale myślę, że ten może być najbardziej kompaktowy zamówienie przez klauzuli, która jest skuteczna:

SELECT * 
FROM Invoice 
ORDER BY LEFT(InvoiceId,CHARINDEX('-',InvoiceId+'-')) 
     ,CAST(RIGHT(InvoiceId,CHARINDEX('-',REVERSE(InvoiceId)+'-'))AS INT)DESC 

Demo: - SQL Fiddle

Uwaga, I dodaje Wersja "790709" do mojego testu, ponieważ niektóre z wymienionych tutaj metod nie traktują wersji bez sufiksu jako mniejszej niż wersje z sufiksem.

Jeśli invoiceID zmienia długości, przed „-”, że jest, to należałoby:

SELECT * 
FROM Invoice 
ORDER BY CAST(LEFT(list,CHARINDEX('-',list+'-')-1)AS INT) 
     ,CAST(RIGHT(InvoiceId,CHARINDEX('-',REVERSE(InvoiceId)+'-'))AS INT)DESC 

Demo z różnych długościach przed myślnikiem: SQL Fiddle

1

Przerwa sortowania na dwie sekcje:

SQL Fiddle

MS SQL Server 2008 Konfiguracja Schema:

CREATE TABLE TestData 
(
    data varchar(20) 
) 

INSERT TestData 
SELECT '790711' as data 
UNION 
    SELECT '790109-1' 
UNION 
    SELECT '790109-11' 
UNION 
    SELECT '790109-2' 

Query 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 

Results:

|  DATA | 
------------- 
| 790109-1 | 
| 790109-2 | 
| 790109-11 | 
| 790711 |