2010-02-05 7 views
5

J'ai une colonne Adresse dans une table que je dois diviser en plusieurs colonnes dans une vue dans SQL Server 2005. Je dois diviser la colonne sur le caractère de saut de ligne, chr (10), et il pourrait y avoir de 1 à 4 lignes (0 à 3 lignes) dans la colonne. Voici quelques exemples de ce que je dois faire. Quel est le moyen le plus simple pour y arriver?Colonne d'adresse divisée en colonnes séparées dans la vue SQL

Examples: 

Address     Address1  Address2  Address3   Address4 
------------  = ----------- ----------- ----------------- --------- 
My Company    My Company  123 Main St. Somewhere,NY 12345 
123 Main St.   
Somewhere,NY 12345 

Address     Address1  Address2  Address3  Address4 
------------  = ------------ ---------- ----------- --------- 
123 Main St.   123 Main St. 
+0

Est-il possible que les données ne soient pas * stockées * dans une seule colonne? –

+0

Toutes les lignes d'adresse sont stockées dans une seule colonne. – Jamie

Répondre

3

ce fractionne l'adresse en utilisant la fonction de ParseName et combinant cela avec COALESCE pour saisir les informations correctes dans la bonne colonne

si vous avez plus de 4 lignes cette méthode ne fonctionnera pas

modifier: ajouté le code pour inverser l'ordre

create table #test (address varchar(1000)) 

    --test data 
    insert #test values('My Company 
    123 Main St.   
    Somewhere,NY 12345') 

    insert #test values('My Company2 
    666 Main St. 
    Bla Bla  
    Somewhere,NY 12345') 

    insert #test values('My Company2') 

    --split happens here 
          select 
replace(parsename(address,ParseLen +1),'^','') as Address1, 
replace(parsename(address,ParseLen),'^','') as Address2, 
replace(parsename(address,ParseLen -1),'^','') as Address3, 
replace(parsename(address,ParseLen -2),'^','') as Address4 
from(
select case ascii(right(address,1)) when 10 then 
replace(replace(left(address,(len(address)-1)),'.','^'),char(10),'.') 
else 
replace(replace(address,'.','^'),char(10),'.') end as address, 
case ascii(right(address,1)) when 10 then 
len(replace(replace(address,'.','^'),char(10),'.')) - 
len(replace(replace(address,'.','^'),char(10),'')) -1 
else 
len(replace(replace(address,'.','^'),char(10),'.')) - 
len(replace(replace(address,'.','^'),char(10),'')) end as ParseLen 
from #test) x 
+0

Cela fait un bon travail d'analyse des pièces, mais la fonction parsename remplit son tableau dans l'ordre inverse. Donc, si vous avez quelque chose comme 123.456.789, il renvoie 1 = 789, 2 = 456 et 3 = 123. Et si vous avez 123.456, il renvoie 1 = 456 et 2 = 123. Dans ces deux scenarious, j'ai besoin de 1 = 123, 2 = 456 et dans le premier exemple 3 = 789. Vous ne savez pas si c'est clair. Je pense que je devrais être capable de le faire en utilisant votre méthode coalesce et d'aller dans l'ordre inverse ou quelque chose, mais je ne peux pas sembler faire les choses correctement. – Jamie

+0

a ajouté le code pour inverser la commande – SQLMenace

+0

Ok, il est presque là. Le seul problème que je vois maintenant est que renvoie NULL pour les quatre champs s'il y a un saut de ligne à la fin du champ source. En d'autres termes, il y a une dernière ligne vide. Existe-t-il un moyen de nettoyer les sauts de ligne et/ou les espaces à la fin qui risquent de le faire disparaître? Merci pour votre aide SQLMenace! – Jamie

0

L'analyse de texte dans SQL n'est pas amusante. Si je devais faire quelque chose comme ça, j'exporterais la colonne dans un fichier texte csv et l'analyserais dans un langage de script tel que Perl/PHP/Python. De cette façon, je peux tirer parti des fonctions de chaîne intégrées et de l'expression régulière du langage de script.

1

Ceci est terriblement méchant ... Je recommande fortement que si vous voulez traiter chaque ligne d'adresse séparément, que vous stockez correctement en premier lieu. Au lieu de continuer à faire ce que vous faites, ajoutez les colonnes supplémentaires, corrigez les données existantes une fois (au lieu de "réparer" à chaque fois que vous exécutez une requête), puis ajustez la procédure stockée qui fait l'insertion/mise à jour afin qu'il sache utiliser les autres colonnes.

DECLARE @Address TABLE(id INT IDENTITY(1,1), ad VARCHAR(MAX)); 

INSERT @Address(ad) SELECT 'line 1 
line 2 
line 3 
line 4' 
UNION ALL SELECT 'row 1 
row 2 
row 3' 
UNION ALL SELECT 'address 1 
address 2' 
UNION ALL SELECT 'only 1 entry here' 
UNION ALL SELECT 'let us try 5 lines 
line 2 
line 3 
line 4 
line 5'; 

SELECT 
    id, 
    Line1 = REPLACE(REPLACE(COALESCE(Line1, ''), CHAR(10), ''), CHAR(13), ''), 
    Line2 = REPLACE(REPLACE(COALESCE(Line2, ''), CHAR(10), ''), CHAR(13), ''), 
    Line3 = REPLACE(REPLACE(COALESCE(SUBSTRING(Rest, 1, COALESCE(NULLIF(CHARINDEX(CHAR(10), Rest), 0), LEN(Rest))), ''), CHAR(10), ''), CHAR(13), ''), 
    Line4 = REPLACE(REPLACE(COALESCE(SUBSTRING(Rest, NULLIF(CHARINDEX(CHAR(10), Rest) + 1, 1), LEN(Rest)), ''), CHAR(10), ''), CHAR(13), '') 
FROM 

(
    SELECT 
     id, 
     ad, 
     Line1, 
     Line2 = SUBSTRING(Rest, 1, COALESCE(NULLIF(CHARINDEX(CHAR(10), Rest), 0), LEN(Rest))), 
     Rest = SUBSTRING(Rest, NULLIF(CHARINDEX(CHAR(10), Rest) + 1, 1), LEN(Rest)) 
    FROM 
    (
     SELECT 
      id, 
      ad, 
      Line1 = SUBSTRING(ad, 1, COALESCE(NULLIF(CHARINDEX(CHAR(10), ad), 0), LEN(ad))), 
      Rest = SUBSTRING(ad, NULLIF(CHARINDEX(CHAR(10), ad) + 1, 1), LEN(ad)) 
     FROM 
      @address 
    ) AS x 
) AS y 
ORDER BY id; 

Denis ParseName() truc est beaucoup plus net bien sûr, mais vous devez être extrêmement prudent d'utiliser un caractère de remplacement qui est vraiment impossible d'apparaître dans les données naturellement. Le carat (^) est probablement un bon pari, mais comme je l'ai dit, vous devez faire attention.

Il existe également des progiciels qui sont très efficaces pour scruter l'adresse et d'autres données démographiques. Mais le nettoyage de l'entrée de données est la chose la plus importante ici que je vais continuer à souligner ... si chaque ligne d'adresse doit être traitée séparément, puis les stocker de cette façon.

+0

Je suis absolument d'accord à 100%, mais dans ce cas particulier, je n'ai aucun contrôle sur la structure des données. C'est frustrant, mais c'est ce que c'est. – Jamie

+1

Eh bien, aussi longtemps que les utilisateurs sont prêts à attendre le point de vue d'effectuer cette division chaque fois que vous exécutez une requête ... alors je suppose que vous avez raison, il est ce qu'il est (conception merdique). –