Esiste un confronto per ordinare le seguenti stringhe nel seguente ordine 1,2,3,6,10,10A, 10B, 11?


12

Ho un database con una colonna VARCHAR che contiene numeri interi di lunghezza variabile. Voglio ordinarli in modo che 10 arrivi dopo 9, non 1 e 70A dopo 70. Sono stato in grado di farlo con le istruzioni PATINDEX () , un CTE e CASE nella clausola WHERE.

Tuttavia, mi chiedevo se ci fosse una collazione in cui ciò non sarebbe necessario.


Ecco il nuovo link per quel suggerimento ora che Microsoft è migrata da Connect a UserVoice ma non ha inoltrato gli URI: Supporta "ordinamento naturale" / DIGITSASNUMBERS come opzione di confronto
Solomon Rutzky,

2
Microsoft ha affermato che implementerà questa funzionalità come funzionalità integrata in SQL Server se ottengono voti sufficienti. Quindi vai qui e fai clic sul pulsante di voto .
Peter Aylett,

Risposte:


8

No. Le regole di confronto riguardano l'ordinamento alfabetico, a seconda della tabella codici, dell'accento, del caso, della larghezza, del kana. I caratteri numerici (0-9) non hanno alcuna proprietà lì.

Quindi 9è sempre dopo 10Bin qualsiasi tipo.

Devi dividerlo come hai notato o ordinare in questo modo:

ORDER BY
    RIGHT('                              ' + MyColumn, 30)

La lunghezza a destra determina quanti spazi hai.

Potresti ovviamente:

  • hanno 2 colonne per renderlo superfluo (e molto più veloce) e una colonna calcolata per combinarle
  • insistere sul fare zeri iniziali
  • giustificare a destra in un carattere (una versione memorizzata del mio DESTRA sopra)

Questi ultimi 2 suggerimenti sono come il mio DIRITTO sopra e leggermente diversi. Ordinamento più rapido (non è necessaria l'elaborazione della colukmn) ma è necessaria una maggiore quantità di memoria


non vedo come funziona. Si rompe per 2, 2a, 3, ecc ...
Mladen Prajdic,

@Mladen Prajdic: hai ragione, oops. Dimenticato gli alfabeti finali
gbn,

Per quanto riguarda " Così 9è sempre dopo 10Bin qualsiasi ordinamento ": è solo così in SQL Server perché l'opzione di ordinamento sottostante per gestire "DigitsAsNumbers" non è stata esposta come opzione di confronto. Eppure ;-). Questo è diventato disponibile per le app basate su Windows a partire da Windows 7, in particolare in Esplora file. E un giorno può essere esposto a SQL Server, se un numero sufficiente di persone supporta l'idea. Ho provato a far rotolare la palla presentando il seguente suggerimento Connect: Supporta "ordinamento naturale" / DIGITSASNUMBERS come opzione di confronto .
Solomon Rutzky,

8

Vorrei impostare una colonna calcolata e quindi ordinare in base a quello. Qualcosa di simile a

CAST( 
     CASE WHEN IS_NUMERIC(left(OtherColumn, 2) = 1) then 
         left(OtherColumn,2) 
     else 
         left(otherColumn, 1)  
AS INT)

Quindi utilizzare questa colonna per ordinare in quanto è ora possibile indicizzare la colonna.


È molto utile sapere per problemi simili. Tuttavia, in questo caso non posso cambiare lo schema.
Justin Dearing il

Puoi aggiungere allo schema? Escludendo una colonna calcolata, è sempre possibile creare una vista, sebbene ciò non sia realmente ottimizzabile come una colonna calcolata.
Aaron Bertrand

Se esegui una vista indicizzata e hai l'edizione Enterprise, la tua query utilizzerà automaticamente la vista indicizzata se riesce a capire cosa stai cercando di fare. Se l'edizione standard è necessario utilizzare WITH (NOEXPAND) per attivare la vista indicizzata da utilizzare. A quel punto dovresti avere la dichiarazione del caso nel tuo ordine ma dovrebbe funzionare, credo.
mrdenny,

Non è necessario creare una colonna calcolata. Puoi usare quell'espressione direttamente nella clausola ORDER BY
a_horse_with_no_name

Se vuoi garantire un indice o una scansione della tabella, puoi farlo. Se si desidera poter indicizzare il valore, è necessaria una colonna calcolata o una vista indicizzata.
mrdenny,

5

Se vuoi un modo doloroso per dimostrare ciò che @gbn sta dicendo (essenzialmente che non puoi dire una collation per ordinare le sottostringhe in modo diverso), puoi creare una rapida tabella #temp che ha un coefficiente per l'ordine che ti aspetti, e vedere se l'ordinazione per qualsiasi confronto restituisce lo stesso ordine:

CREATE TABLE #foo(id INT, n NVARCHAR(10));

CREATE TABLE #bar(collation SYSNAME);

SET NOCOUNT ON;

INSERT #foo SELECT 1,'1'
UNION SELECT 2,'2'
UNION SELECT 3,'3'
UNION SELECT 4,'6'
UNION SELECT 5,'10'
UNION SELECT 6,'10A'
UNION SELECT 7,'10B'
UNION SELECT 8,'11';

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += N'
    WITH x AS 
    (
        SELECT n, rn = ROW_NUMBER() OVER 
        (ORDER BY n COLLATE ' + name + ') FROM #foo
    ) 
    INSERT #bar 
    SELECT TOP (1) ''' + name + ''' FROM x
    WHERE NOT EXISTS
    (
        SELECT COUNT(*) FROM #foo AS f
        WHERE f.id = x.rn
        AND f.n <> x.n
    );' FROM sys.fn_helpcollations();

EXEC sp_executesql @sql;

SELECT collation FROM #bar;

GO
DROP TABLE #foo, #bar;

Questo funziona per me in circa 10 secondi e produce 0 righe - il che significa che nessuna confronto disponibile per SQL Server (almeno 2008 R2, non ho provato Denali) si ordinerà nel modo previsto. È necessario un modo diverso per definire l'ordinamento.


2

Desideri un mezzo ragionevole ed efficiente per ordinare i numeri nelle stringhe come numeri reali? Prendi in considerazione la possibilità di votare il mio suggerimento per Microsoft Connect: supporta "ordinamento naturale" / DIGITSASNUMBERS come opzione di confronto


Mentre questa domanda è specifica per SQL Server e questa risposta non lo è, ho sentito che avrei dovuto pubblicare queste informazioni semplicemente per sensibilizzarle e non essere in contrasto con nessuna delle altre risposte.

Detto questo, al di fuori di SQL Server, in alcuni ambienti è possibile eseguire questo tipo di ordinamento. È qualcosa che è almeno specificato nella documentazione Unicode. Nella LINGUA DI MARCHIO DEI DATI DI UNICODE LOCALE (LDML) PARTE 5: standard / report di COLLEZIONE , esiste un grafico per le impostazioni di confronto che descrive varie opzioni per personalizzare il comportamento di ordinamento. Una delle opzioni è -kn-trueo [numericOrdering on]:

Se impostato su on , qualsiasi sequenza di cifre decimali (General_Category = Nd in [ UAX44 ]) viene ordinata a un livello primario con il suo valore numerico. Ad esempio, "A-21" <"A-123". I pesi primari calcolati sono tutti all'inizio del gruppo di riordino delle cifre . Pertanto, con una tabella UCA non personalizzata, "a $" <"a0" <"a2" <"a12" <"a⓪" <"aa".

Tuttavia, questo documento è uno "standard tecnico" e non fa parte delle specifiche Unicode di base. Una nota nella parte superiore del documento afferma:

Uno standard tecnico Unicode (UTS) è una specifica indipendente. La conformità allo standard Unicode non implica la conformità a nessun UTS.

Pertanto, questo comportamento particolare non è disponibile in SQL Server o anche in .NET (almeno non in modo nativo), sebbene entrambi siano conformi alle specifiche Unicode di base.

Il progetto ICU (International Components for Unicode) è un insieme di librerie C / C ++ e Java che implementa questa funzionalità e ne esiste persino una demo online. E in "progetti correlati" esiste un collegamento a un progetto .NET che sembra essere un wrapper di oggetti COM per la libreria ICU che consentirebbe a questa funzionalità di essere esposta al codice gestito. Ma non è chiaro se quel progetto .NET sia ancora attivo.

Ma per vedere questo comportamento in azione, vai alla Demo Collation ICU .

Incolla quanto segue nell'area di testo di input sul lato sinistro:

1
2
10B
6
11
10A
3
10

Impostare tutte le opzioni su "predefinito". Seleziona l'opzione "immetti numeri di riga" a destra del sortpulsante e assicurati che l'opzione "punti di forza diff" sia deselezionata.

Fai clic sul sortpulsante e dovresti recuperare quanto segue:

[1] 1
[8] 10
[6] 10A
[3] 10B
[5] 11
[2] 2
[7] 3
[4] 6

Questo è ciò che dovrebbe essere previsto quando si esegue un ordinamento di stringhe tipico e ciò che viene visualizzato in SQL Server.

Ora, nella serie di pulsanti di opzione appena sopra il sortpulsante, la seconda riga è etichettata "numerica". Seleziona il pulsante di opzione "on".

Fai di sortnuovo clic sul pulsante e dovresti recuperare quanto segue:

[1] 1
[2] 2
[7] 3
[4] 6
[8] 10
[6] 10A
[3] 10B
[5] 11

In dubbio se questo funziona quando la parte numerica si trova al centro della stringa? Ok, incolla quanto segue nell'area di testo di input sul lato sinistro (sostituendo l'elenco precedente):

Script - 1.sql
Script - 2.sql
Script - 10B.sql
Script - 6.sql
Script - 11.sql
Script - 10A.sql
Script - 3.sql
Script - 10.sql

Assicurarsi che l' impostazione numerica sia ancora impostata su "on". Fai di sortnuovo clic sul pulsante e dovresti recuperare quanto segue:

[1] Script - 1.sql
[2] Script - 2.sql
[7] Script - 3.sql
[4] Script - 6.sql
[8] Script - 10.sql
[6] Script - 10A.sql
[3] Script - 10B.sql
[5] Script - 11.sql

Vuoi vederlo in un altro posto? Crea una cartella sul tuo hard disk, qualcosa come C: \ temp \ sorting \ , e crea file vuoti con gli stessi nomi "Script -...". Esegui un DIRcomando in una finestra di comando e vedrai l'ordinamento standard. Ma quando guardi la lista dei file in Windows Explorer vedrai la lista ordinata usando l'opzione "numerica" ​​:-).


Cordiali saluti, Postgres 10 ottiene il supporto per le regole di confronto ICU. Vedi questo post sul blog di Peter Eisentraut.
Basil Bourque,

@BasilBourque Grazie per averlo menzionato su PG10. Quel post sul blog, alla fine, afferma che "ICU offre molte funzionalità in quest'area che non stiamo ancora esponendo con PostgreSQL. Esistono opzioni per l'ordinamento senza distinzione tra maiuscole e minuscole, l'ordinamento senza accento e la personalizzazione totale di una collazione. per quelli nelle future versioni di PostgreSQL. " Quindi nella sua prima / attuale implementazione, non cambia nessuna delle informazioni nella mia risposta. Se un'offerta futura consente l'ordinamento numerico, lo menzionerò nella mia risposta, ma come nota a piè di pagina poiché questa domanda è specifica di SQL Server.
Solomon Rutzky,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.