2009-10-19 6 views
0

J'ai un champ varchar (100) qui contient à la fois des lettres et des chiffres. Les valeurs sont généralement sous la forme de voiture 1, voiture 10, voiture 100, voiture 20. Mais les valeurs peuvent avoir n'importe quel mot précédant le nombre. Y at-il un moyen de trier ces valeurs numériquement de sorte que la voiture 2 viendra avant la voiture 10? Merci.Comment trier une colonne VARCHAR dans un serveur SQL contenant des mots et des nombres?

+1

Il ya des questions similaires à ce sujet - recherche de "natural number sorting sql" - vous obtiendrez des entrées comme celle-ci: http://stackoverflow.com/questions/153633/natural-sort-in-mysql – schnaader

+0

Plate-forme sql utilisez-vous? – avguchenko

Répondre

0

Ecrivez une fonction qui reformate les chaînes (par exemple, la voiture 10 devient la voiture 010), puis l'utilise pour transformer les données de colonne dans votre requête SQL. Vous devrez vérifier les performances de cette solution. Ou ... vous pouvez transformer les données existantes de la manière décrite ci-dessus, afin de permettre l'ordre alphabétique en SQL sans appliquer de fonctions lors de l'interrogation.

1

Vous aurez à comprendre le dans vos données et diviser le car 2 en un varchar car et et int 2.

Le modèle pourrait être aussi simple que WORD SPACE NUMBER et vous pouvez le diviser en fonction de SPACE en utilisant PATINDEX ou CHARINDEX conjointement avec SUBSTRING.

Ensuite, vous pouvez trier par les deux colonnes.

Voici un exemple de travail

SET NOCOUNT ON 

Declare @Table table 
(
    Id INT Identity (1, 1), 
    StringValue VarChar (30) 
) 

INSERT INTO @Table (StringValue) VALUES ('CAR 10') 
INSERT INTO @Table (StringValue) VALUES ('CAR 20') 
INSERT INTO @Table (StringValue) VALUES ('CAR 2') 
INSERT INTO @Table (StringValue) VALUES ('CAR 3') 
INSERT INTO @Table (StringValue) VALUES ('CAR 4') 

INSERT INTO @Table (StringValue) VALUES ('SHIP 32') 
INSERT INTO @Table (StringValue) VALUES ('SHIP 310') 
INSERT INTO @Table (StringValue) VALUES ('SHIP 320') 
INSERT INTO @Table (StringValue) VALUES ('SHIP 33') 
INSERT INTO @Table (StringValue) VALUES ('SHIP 34') 


SELECT Id, 
    SubString (StringValue, 1, CharIndex (' ', StringValue)) ObjectName, 
    CONVERT (INT, SubString (StringValue, CharIndex (' ', StringValue), LEN (StringValue))) ObjectId 
FROM @Table 
ORDER BY 2, 3 

SELECT Id, StringValue 
FROM @Table 
ORDER BY 
    SubString (StringValue, 1, CharIndex (' ', StringValue)), 
    CONVERT (INT, SubString (StringValue, CharIndex (' ', StringValue), LEN (StringValue))) 
0
WITH TABLE_NAME(NAME) AS 
(
SELECT 'car 20' 
UNION ALL 
SELECT 'car 2' 
UNION ALL 
SELECT 'car 10' 
) 
SELECT 
    * 
FROM TABLE_NAME 
ORDER BY 
SUBSTRING(NAME,1,CHARINDEX(' ',NAME,1)),CAST(SUBSTRING(NAME,CHARINDEX(' ',NAME,1)+1,100) AS INT) 

SUBSTRING (NOM, 1, CHARINDEX (' ' NOM, 1)) - texte avant l'espace CAST (SUBSTRING (NOM, CHARINDEX ('' , NAME, 1) +1,100) AS INT) - texte après espace avec cast en int pour la commande correcte

+0

La syntaxe WITH est uniquement prise en charge sur SQL Server 2005+ et l'OP n'a pas fourni la version utilisée. –

+0

@rexem, le _WITH_ est seulement pour générer des données d'échantillon pour _ORDER BY_, il ne fait pas partie de la solution réelle ici –

1

Les données entrées de manière incorrecte et incohérente sont difficiles à corriger par programme. Cependant, vous devez corriger ces données, pas dans votre SELECT, donc votre ORDER BY fonctionne, mais dans les données, vous n'avez donc plus à vous en soucier.

Vous devriez envisager de créer des colonnes séparées pour les parties «mot» et «nombre» des données en question. Vous pouvez ensuite exécuter un script qui essaie de placer les données dans les colonnes appropriées, puis effectuer tout suivi manuel nécessaire. Vous aurez la possibilité de modifier la logique de l'application et éventuellement le frontal pour que les données entrant dans la base de données restent valides. Tout ce qui manque à cela se traduira par un tri inefficace des données.

+2

Je suis avec vous, s'il y a un besoin fréquent de trier de cette façon, la seule solution raisonnable d'un perspective de performance est de stocker les données correctement. Cela peut être fait avec un déclencheur ou des champs calibrés (selon la complexité de la façon de diviser les données). – HLGEM

0

Que diriez-vous quelque chose comme:

SELECT 
    col1, col1_ltrim 
, col1_sort 
    = CONVERT(INT 
     , SUBSTRING(col1_ltrim,1 
      , ISNULL(
       NULLIF(PATINDEX('%[^0-9]%',col1_ltrim),0)-1 
      , LEN(col1_ltrim) 
      ) 
     ) 
     ) 
FROM ( 
    SELECT col1 
    , col1_ltrim = STUFF(col1,1,PATINDEX('%[0-9]%',col1)-1,'') 
    FROM (
    SELECT 'car 505' UNION ALL 
    SELECT 'car 95' UNION ALL 
    SELECT 'car 8776 blue' UNION ALL 
    SELECT 'car' 
    ) a (col1) 
) a 
ORDER BY col1_sort 

Vous pouvez envelopper dans une UDF pour la santé mentale:

CREATE FUNCTION dbo.StringToInt (@string VARCHAR(128)) 
RETURNS INT AS 
BEGIN 

    SELECT @string = STUFF(@string,1,PATINDEX('%[0-9]%',@string)-1,'') 
    RETURN CONVERT(INT 
      , SUBSTRING(@string,1 
      , ISNULL(
       NULLIF(PATINDEX('%[^0-9]%',@string),0)-1 
       , LEN(@string) 
      ) 
      ) 
     ) 

END 

GO 

SELECT col1, col1_sort = dbo.StringToInt(col1) 
FROM (
    SELECT 'car 505' UNION ALL 
    SELECT 'car 95' UNION ALL 
    SELECT 'car 8776 blue' UNION ALL 
    SELECT 'car' 
) a (col1) 
ORDER BY col1_sort 
0

Vous pouvez profiter du fait que

patindex('%...', _col_) 

correspond à la le dernier caractère et

patindex('%...%', _col_) 

correspond à n'importe quel nombre de caractères.Aussi longtemps que les chiffres sont à la fin de la valeur de la colonne, vous pouvez facilement extraire la valeur numérique de la colonne (StackOverflow ne me laisse pas mettre le mot UNION dans un UUU post-remplacement avec UNION):

select Rec, 
case 
    when patindex('%[0-9]', Rec) > 0 
    then left(Rec, patindex('%[0-9]%', Rec)-1) 
    else Rec 
end, 
case 
    when patindex('%[0-9]', Rec) > 0 
    then cast(right(Rec, len(Rec)-patindex('%[0-9]%', Rec)+1) as int) 
end 
from (select 'SHIP34' Rec UUU select 'SHIP 33' UUU select 'SHIP 320' UUU select 'SHIP310' UUU select 'SHIP32' UUU select 'CAR 4X' UUU select 'CAR 4' UUU select 'CAR3' UUU select 'CAR 2' UUU select 'CAR20' UUU select 'CAR 10') TestData 
order by 
    case 
     when patindex('%[0-9]', Rec) > 0 
     then left(Rec, patindex('%[0-9]%', Rec)-1) 
     else Rec 
    end, 
    case 
     when patindex('%[0-9]', Rec) > 0 
     then cast(right(Rec, len(Rec)-patindex('%[0-9]%', Rec)+1) as int) 
    end 
Questions connexes