0
votes

Comment identifier et éditer toutes les instances d'un motif correspondant dans T-SQL

J'ai une obligation d'exécuter une fonction sur certains champs pour identifier et rediriger tous les nombres à 5 chiffres ou plus longtemps, que tous les 4 derniers chiffres sont remplacés par *

par exemple: "Quelqu'un de texte avec 12345 et 1234 et 12345678 "deviendrait" du texte avec * 2345 et 1234 et **** 5678 " P>

J'ai utilisé Patindex pour identifier le caractère de départ du motif: P>

PATINDEX('%[0-9][0-9][0-9][0-9][0-9]%', TEST_TEXT)


3 commentaires

. . Bien que vous puissiez écrire un UDF pour le faire, SQL Server n'est pas vraiment le bon outil de ce problème.


@Gordonlinoff Merci, malheureusement, cela fait partie d'une livraison techniquement restreinte. Cette pièce tire des données d'une source, mais je dois éditer ces données au niveau de la requête et utiliser SQL. Donc, il doit être T-SQL, le client a mentionné, je peux faire une fonction sur le serveur SQL si j'ai vraiment besoin de, mais à part ça, ils m'ont limité à T-SQL


Même dans les langues de programmation qui soutiennent la regex, votre exigence n'est toujours pas staïtilleuse. Vous auriez besoin d'un remplacement de regex avec une fonction de rappel le plus probable.


3 Réponses :


1
votes

Une solution sale avec CTE récursive

DECLARE 
  @tags nvarchar(max) = N'Some text with 12345 and 1234 and 12345678',
  @c nchar(1) = N' ';
;
WITH Process (s, i)
as
(
SELECT @tags, PATINDEX('%[0-9][0-9][0-9][0-9][0-9]%', @tags)
UNION ALL 
SELECT value,  PATINDEX('%[0-9][0-9][0-9][0-9][0-9]%', value)
FROM
(SELECT SUBSTRING(s,0,i)+'*'+SUBSTRING(s,i+4,len(s)) value
FROM Process
WHERE i >0) calc
  -- we surround the value and the string with leading/trailing ,
  -- so that cloth isn't a false positive for clothing
) 
SELECT * FROM Process
WHERE i=0


3 commentaires

Ce n'est pas tout à fait les résultats qu'ils recherchent. Vraiment fermer cependant.


Merci Tohm, point de départ vraiment utile. Cependant, il doit remplacer chaque caractère de la rédaction avec A *, pas le motif entier (12345678 = ***** 5678, pas * 5678 comme le code actuellement). Je vais jouer avec ça et voir ce que je peux faire. Merci :-)


Essayez simplement avec Sélectionnez SUBSTRING (S, 0, I) + '****' + SUBSTRING (S, I + 4, LEN (S)) Valeur



1
votes

Voici une option utilisant le délimitedsplit8k_lead qui peut être trouvé ici. https: // www.sqlservercentral.com/articles/reaving-the-benefits-fof-the-window-fonctions-in-t-sql-2 Il s'agit d'une extension du séparateur de Jeff Moden qui est même un peu plus rapide que la original. Le grand avantage que ce séparateur a sur la plupart des autres, c'est qu'il renvoie la position ordinale de chaque élément. Une mise en garde à ceci est que j'utilise un espace pour scinder sur la base de vos données d'échantillon. Si vous aviez des chiffres gravés au milieu d'autres personnages, cela les ignorera. Cela peut être bon ou mauvais en fonction de vos besoins spécifiques.

declare @Something varchar(100) = 'Some text with 12345 and 1234 and 12345678';

with MyCTE as
(
    select x.ItemNumber 
        , Result = isnull(case when TRY_CONVERT(bigint, x.Item) is not null then isnull(replicate('*', len(convert(varchar(20), TRY_CONVERT(bigint, x.Item))) - 4), '') + right(convert(varchar(20), TRY_CONVERT(bigint, x.Item)), 4) end, x.Item)
    from dbo.DelimitedSplit8K_LEAD(@Something, ' ') x
)
select Output = stuff((select ' ' + Result 
                        from MyCTE 
                        order by ItemNumber
                        FOR XML PATH('')), 1, 1, '')


2 commentaires

Cela a l'air très prometteur, bien que d'être honnête, je ne comprends pas comment cela fonctionne d'un coup d'œil curseur - je vais lire plus en détail le lien et le code et que vous sachiez s'il traite de la question. Merci beaucoup :-)


La fonction Delimitedsplit8K est l'endroit où la magie se produit. Le CTE ici isole simplement si l'élément est un nombre ou non. Et s'il s'agit d'un nombre, il sort des chiffres pour * sauf pour les 4 derniers.



2
votes

Vous pouvez le faire en utilisant les fonctions intégrées de SQL Server. Tous utilisés dans cet exemple sont présents dans SQL Server 2008 et supérieur.

DECLARE @String VARCHAR(500) = 'Example Input: 1234567890, 1234, 12345, 123456, 1234567, 123asd456'
DECLARE @StartPos INT = 1, @EndPos INT = 1;
DECLARE @Input VARCHAR(500) = ISNULL(@String, '') + ' '; --Sets input field and adds a control character at the end to make the loop easier.
DECLARE @OutputString VARCHAR(500) = ''; --Initalize an empty string to avoid string null errors

WHILE (@StartPOS <> 0)
BEGIN
    SET @StartPOS = PATINDEX('%[0-9][0-9][0-9][0-9][0-9]%', @Input);
    IF @StartPOS <> 0
    BEGIN
        SET @OutputString += SUBSTRING(@Input, 1, @StartPOS - 1); --Seperate all contents before the first occurance of our filter
        SET @Input = SUBSTRING(@Input, @StartPOS, 500); --Cut the entire string to the end. Last value must be greater than the original string length to simply cut it all.

        SET @EndPos = (PATINDEX('%[0-9][0-9][0-9][0-9][^0-9]%', @Input)); --First occurance of 4 numbers with a not number behind it.
        SET @Input = STUFF(@Input, 1, (@EndPos - 1), REPLICATE('*', (@EndPos - 1))); --@EndPos - 1 gives us the amount of chars we want to replace.
    END
END
SET @OutputString += @Input; --Append the last element

SET @OutputString = LEFT(@OutputString, LEN(@OutputString))
SELECT @OutputString;


7 commentaires

Cela fonctionne aussi mais je fais tout ce que je peux pour éviter les boucles comme la peste.


Cela a l'air jolie - j'ai besoin de partir à la minute, mais je vais tout ce que tout est vérifié demain et mettez à jour la question. Merci beaucoup pour votre contribution!


@Seanlange Je suis d'accord, mais à partir d'une perspective de programmation, j'ai essayé de le garder aussi comme natif et "relativement" que possible et facilement compréhensible. Et je ne suis pas un fan de simplement lancer des fonctions / procédures externes dans des bases de données. Même si j'ai le séparateur de Jeff Moden sur mon serveur SQL 2008.


@Dkramer je ne suis pas en désaccord et je ne faisais pas du tout à claquer votre approche. Mais je pense qu'une fois que nous allons au-delà de la sélection de données et de faire des choses étranges comme celle-ci, vous devez commencer à penser à la boîte. Et ce n'est pas une fonction externe, toute la fonction n'est rien d'autre que pure t-SQL. Je suis beaucoup plus préoccupé par les requêtes performantes que de garder tout ce qui est "propre". Juste mon 2 ¢. Avis dans votre exemple ici que pour le faire à partir d'une table nécessiterait une autre boucle extérieure.


@Seanlange Vous avez raison et, tandis que je testais mon code / exemple et que je ajoute dans des commentaires, il n'y avait pas encore de réponses. Votre solution, dans mes yeux, est également plus adapté à ce problème. Cela n'a tout simplement pas eu source d'esprit pour moi-même et l'OP mentionné, il peut faire une fonction (seulement) si nécessaire, alors j'ai essayé de prendre comme une approche d'os, comme je pouvais.


Pour être juste i à travers une table avec vos échantillons de données et l'échantillon OPS. Puis enveloppé votre logique dans un curseur. J'ai aussi modifié le mien un peu pour ajouter une CTE préliminaire pour le try_convert. Pour augmenter le nombre de lignes, je viens d'insérer des copies des mêmes données. À moins de 50 rangées, il n'y avait pas de comparaison. La boucle était plus lente par des ordres de grandeur (mais bien sûr non perceptibles). Une fois la table étendue à environ 15 à 20 000 rangées, il se rapprochait de la performance. Environ 100 000 lignes, votre processus de boucle a commencé à être un peu plus rapide. Et à un million de lignes, vos boucles imbriquées étaient nettement plus rapides.


C'est absolument parfait - exactement ce dont j'avais besoin. Je l'ai créé en tant que fonction avec des modifications mineures pour accueillir mes données, à savoir la modification de la taille des varcharars vers max à mesure que les données source ont souvent d'énormes quantités de texte de plus de 500 caractères et une vérification rapide au début pour renvoyer NULL où l'entrée est null (comme la colonne que je fonctionne peut également être nulle). Merci beaucoup pour l'aide @dkramer, j'étais vraiment bloqué avec celui-ci et à tous ceux qui ont fourni des conseils et du code! Marquant cela comme la réponse qu'il convient le mieux à mes besoins.