2017-09-01 2 views
0

J'ai une question à propos de SQL Server: J'ai une colonne de base de données avec un motif qui ressemble à ceci:SQL Server modèle d'extrait d'expression régulière de DB colomn

  1. jusqu'à 10 chiffres
  2. puis une virgule
  3. jusqu'à 10 chiffres
  4. puis un point-virgule

par exemple

100000161, 100000031; 100000243, 100000021; 
100000161, 100000031; 100000243, 100000021; 

et je veux extraire dans le modèle les premiers chiffres (jusqu'à 10) (1.), puis un point-virgule (4.)

(ou, autrement dit, tout enlever de la point-virgule au point-virgule suivant)

100000161; 100000243; 100000161; 100000243; 

Pouvez-vous me conseiller comment établir cela dans SQL Server? Je ne suis pas très familier avec regex et n'ai donc aucune idée de comment résoudre ce problème.

Merci,

Alex

+1

SQL Server est connu parmi les bases de données d'entreprise pour avoir un support de remplacement de regex assez moche, ce qui est probablement ce que vous souhaitez utiliser pour ce problème.Y a-t-il une chance que vous puissiez nettoyer ces données ailleurs? –

+0

@TimBiegeleisen Peu importe à quel point le support regex est moche, quelque chose de * simple * ne sera jamais un problème dans un moteur regex. Regex est également quelque chose que vous ne voudriez certainement pas utiliser pour cette tâche. – Tomalak

+0

@Tomalak 'SUBSTRING_INDEX' n'est pas une fonction SQL Server, c'est une fonction _MySQL_, et oui, regex est le genre de chose que vous voudriez utiliser ici. –

Répondre

1

Essayez cette

Declare @Sql Table (SqlCol nvarchar(max)) 
INSERT INTO @Sql 
SELECT'100000161,100000031;100000243,100000021;100000161,100000031;100000243,100000021;' 
    ;WITH cte 
    AS (SELECT Row_number() 
        OVER( 
        ORDER BY (SELECT NULL))   AS Rno, 
       split.a.value('.', 'VARCHAR(1000)') AS Data 
     FROM (SELECT Cast('<S>' 
          + Replace(Replace(sqlcol, ';', ','), ',', 
          '</S><S>') 
          + '</S>'AS XML) AS Data 
       FROM @Sql)AS A 
       CROSS apply data.nodes('/S') AS Split(a)) 
SELECT Stuff((SELECT '; ' + data 
       FROM cte 
       WHERE rno%2 <> 0 
        AND data <> '' 
       FOR xml path ('')), 1, 2, '') AS ExpectedData 

ExpectedData 
------------- 
100000161; 100000243; 100000161; 100000243 
+0

Vous n'avez pas besoin de tout cela pour extraire la première valeur. Utilisez différentes étiquettes internes et externes au lieu d'un seul '' et sélectionnez celui que vous voulez –

+0

Cela semble bon. Le seul problème que je réalise ici quand j'ai vérifié sur les données réelles était que la sortie de la colonne select de la table est écrite dans une ligne tandis que les données de source sont dans différentes rangées? – user3898488

+0

@ user3898488 Que * voulez-vous? Tous les résultats dans une seule ligne? Une paire par ligne d'entrée? –

1

Je crois que ce que vous obtiendrez ce que vous êtes après aussi longtemps que ce modèle tient vraiment. Sinon, il est assez facile de s'assurer qu'il ne se conforme à ce modèle et puis appliquer cette

Select Substring(TargetCol, 1, 10) + ';' From TargetTable 
+0

OP a un peu changé la spécification, ce serait donc 'SELECT LEFT (TargetCol, CHARINDEX (',', TargetCol) - 1) + ';' O WH CHARINDEX («,», TargetCol) ENTRE 1 ET 11; –

+0

Cela semble plutôt bien, mais où dois-je ajouter le de targettable? J'ai vérifié votre première commande et il fonctionne bien, mais im échouer dans la fusion de vos deux commandes – user3898488

+0

@ user3898488 Oups! J'ai testé avec une variable et changé le nom sans ajouter dans le FROM, donc ... 'SELECT LEFT (TargetCol, CHARINDEX (',', TargetCol) - 1) + ';' FROM SomeTable O WH CHARINDEX (',', TargetCol) ENTRE 1 ET 11; '. Mais cela n'aidera pas si vous avez plus d'une paire de données dans une rangée. –

0

Vous pouvez profiter du support XML de SQL Server pour convertir la chaîne d'entrée en une valeur XML et d'interroger avec XQuery et expressions XPath .

Par exemple, la requête suivante remplacera chaque ; avec </b><a> et chaque ,-</a><b> pour transformer chaque chaîne en <a>100000161</a><a>100000243</a><a />. Après cela, vous pouvez sélectionner <a> individuels noeuds avec /a[1], /a[2]:

declare @table table (it nvarchar(200)) 

insert into @table values 
('100000161, 100000031; 100000243, 100000021;'), 
('100000161, 100000031; 100000243, 100000021;') 

select 
    xCol.value('/a[1]','nvarchar(200)'), 
    xCol.value('/a[2]','nvarchar(200)') 
from (
    select convert(xml, '<a>' 
         + replace(replace(replace(it,';','</b><a>'),',','</a><b>'),' ','') 
         + '</a>') 
        .query('a') as xCol 
    from @table) as tmp 

------------------------- 
A1   A2 
100000161 100000243 
100000161 100000243 

value extrait une valeur unique à partir d'un champ XML. nodes renvoie une table de nœuds correspondant à l'expression XPath. La requête suivante renverra toutes les « clés »:

select 
    a.value('.','nvarchar(200)') 
from (
    select convert(xml, '<a>' 
         + replace(replace(replace(it,';','</b><a>'),',','</a><b>'),' ','') 
         + '</a>') 
        .query('a') as xCol 
    from @table) as tmp 
    cross apply xCol.nodes('a') as y(a) 
where a.value('.','nvarchar(200)')<>'' 

------------ 
100000161 
100000243 
100000161 
100000243 

Avec 200K lignes de données mais, je sérieusement envisager de transformer les données lors de son chargement et son stockage dans les colonnes Indivisual, indexables, ou ajouter une séparée , table connexe. L'application de fonctions de manipulation de chaînes sur une colonne signifie que le serveur ne peut utiliser aucun index de couverture pour accélérer les requêtes.

Si ce n'est pas possible (pourquoi?) Je considérerais au moins l'ajout d'une colonne distincte de type XML qui contiendrait les mêmes données sous forme XML, pour permettre la création d'un index XML.