SELECT REPLACE('<strong>100</strong><b>.00 GB', '%^(^-?\d*\.{0,1}\d+$)%', '');
Ich möchte ein Markup zwischen zwei Teilen der Zahl durch den obigen regulären Ausdruck ersetzen, aber es scheint nicht zu funktionieren. Ich bin mir nicht sicher, ob es sich um eine falsche Regex-Syntax handelt, da ich eine einfachere versucht habe, z. B. '%[^0-9]%'
nur zum Testen, aber es hat auch nicht funktioniert. Weiß jemand, wie ich das erreichen kann?
sql-server
regex
JanT
quelle
quelle
100.00
oder100.00 GB
? Und gibt es andere Beispiele für formatierte Zahlen, die nicht zum Muster des Markups passen und sich nur um den Teil links von der Dezimalstelle befinden? Kann Markup um die gesamte Zahl wie sein100<i>.00</i> GB
? Gibt es rechts immer einen 2-stelligen Währungscode?Antworten:
Mit PATINDEX können Sie den ersten Index des Auftretens von Mustern (Zeichenfolgen) ermitteln. Verwenden Sie dann STUFF, um eine weitere Zeichenfolge in das übereinstimmende Muster (Zeichenfolge) einzufügen .
Schleife durch jede Reihe. Ersetzen Sie alle unzulässigen Zeichen durch das, was Sie wollen. In Ihrem Fall ersetzen Sie nicht numerische durch Leerzeichen. Die innere Schleife ist, wenn Sie mehr als ein unzulässiges Zeichen in einer aktuellen Zelle haben, das der Schleife.
DECLARE @counter int SET @counter = 0 WHILE(@counter < (SELECT MAX(ID_COLUMN) FROM Table)) BEGIN WHILE 1 = 1 BEGIN DECLARE @RetVal varchar(50) SET @RetVal = (SELECT Column = STUFF(Column, PATINDEX('%[^0-9.]%', Column),1, '') FROM Table WHERE ID_COLUMN = @counter) IF(@RetVal IS NOT NULL) UPDATE Table SET Column = @RetVal WHERE ID_COLUMN = @counter ELSE break END SET @counter = @counter + 1 END
Achtung: Dies ist jedoch langsam! Eine Varchar-Säule kann sich auswirken. Die Verwendung von LTRIM RTRIM kann also etwas hilfreich sein. Egal, es ist langsam.
Kredit geht an dieser Stackoverflow Antwort.
EDIT Credit geht auch an @srutzky
Bearbeiten (von @Tmdean) Anstatt jeweils eine Zeile zu erstellen, kann diese Antwort an eine satzbasiertere Lösung angepasst werden. Es wird immer noch das Maximum der Anzahl nicht numerischer Zeichen in einer einzelnen Zeile wiederholt, daher ist es nicht ideal, aber ich denke, es sollte in den meisten Situationen akzeptabel sein.
WHILE 1 = 1 BEGIN WITH q AS (SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n FROM Table) UPDATE Table SET Column = STUFF(Column, q.n, 1, '') FROM q WHERE Table.ID_Column = q.ID_Column AND q.n != 0; IF @@ROWCOUNT = 0 BREAK; END;
Sie können die Effizienz auch erheblich verbessern, wenn Sie eine Bitspalte in der Tabelle beibehalten, die angibt, ob das Feld noch bereinigt wurde. (NULL steht in meinem Beispiel für "Unbekannt" und sollte der Spaltenstandard sein.)
DECLARE @done bit = 0; WHILE @done = 0 BEGIN WITH q AS (SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n FROM Table WHERE COALESCE(Scrubbed_Column, 0) = 0) UPDATE Table SET Column = STUFF(Column, q.n, 1, ''), Scrubbed_Column = 0 FROM q WHERE Table.ID_Column = q.ID_Column AND q.n != 0; IF @@ROWCOUNT = 0 SET @done = 1; -- if Scrubbed_Column is still NULL, then the PATINDEX -- must have given 0 UPDATE table SET Scrubbed_Column = CASE WHEN Scrubbed_Column IS NULL THEN 1 ELSE NULLIF(Scrubbed_Column, 0) END; END;
Wenn Sie Ihr Schema nicht ändern möchten, können Sie es einfach anpassen, um Zwischenergebnisse in einer Variablen mit Tabellenwert zu speichern, die am Ende auf die tatsächliche Tabelle angewendet wird.
quelle
[^0-9.]
. Wenn nicht , dann Streifen Sie das Dezimalsystem und drehen , was sein sollte100.00
in10000
.Im Allgemeinen unterstützt SQL Server keine regulären Ausdrücke und Sie können sie nicht im nativen T-SQL-Code verwenden.
Sie können dazu eine CLR-Funktion schreiben. Siehe hier zum Beispiel.
quelle
Anstatt den gefundenen Charakter durch seine einzige Position zu entfernen,
Replace(Column, BadFoundCharacter, '')
könnte die Verwendung wesentlich schneller sein. Anstatt nur das eine schlechte Zeichen zu ersetzen, das als nächstes in jeder Spalte gefunden wird, werden alle gefundenen Zeichen ersetzt.WHILE 1 = 1 BEGIN UPDATE dbo.YourTable SET Column = Replace(Column, Substring(Column, PatIndex('%[^0-9.-]%', Column), 1), '') WHERE Column LIKE '%[^0-9.-]%' If @@RowCount = 0 BREAK; END;
Ich bin überzeugt, dass dies besser funktionieren wird als die akzeptierte Antwort, schon allein deshalb, weil es weniger Operationen ausführt. Es gibt andere Möglichkeiten, die möglicherweise auch schneller sind, aber ich habe momentan keine Zeit, diese zu erkunden.
quelle
Replace(Column, Substring(Column, PatIndex('%[^0-9.-]%', Column), 1), '')
Bit für eine ausgewählte Abfrage verwendet. So danke!Ich bin über diesen Beitrag gestolpert und habe nach etwas anderem gesucht, dachte aber, ich würde eine Lösung erwähnen, die weitaus effizienter ist - und die eigentlich die Standardimplementierung jeder Funktion sein sollte, wenn sie mit einer satzbasierten Abfrage verwendet wird -, nämlich die Verwendung eines angewendeten Kreuzes Tabellenfunktion. Das Thema scheint noch aktiv zu sein, also ist dies hoffentlich für jemanden nützlich.
Die Beispiellaufzeit für einige der bisherigen Antworten, die auf der Ausführung rekursiver satzbasierter Abfragen oder der Skalarfunktion basieren, basierend auf einem 1-m-Zeilentestsatz, bei dem die Zeichen aus einer zufälligen neuen ID entfernt werden, reicht von 34 s bis 2 m 05 s für die WHILE-Schleifenbeispiele und von 1 m 3 s bis { für immer} für die Funktionsbeispiele.
Die Verwendung einer Tabellenfunktion mit Kreuzanwendung erreicht das gleiche Ziel in 10 Sekunden . Möglicherweise müssen Sie es an Ihre Bedürfnisse anpassen, z. B. an die maximale Länge, die es handhabt.
Funktion:
CREATE FUNCTION [dbo].[RemoveChars](@InputUnit VARCHAR(40)) RETURNS TABLE AS RETURN ( WITH Numbers_prep(Number) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ) ,Numbers(Number) AS ( SELECT TOP (ISNULL(LEN(@InputUnit),0)) row_number() OVER (ORDER BY (SELECT NULL)) FROM Numbers_prep a CROSS JOIN Numbers_prep b ) SELECT OutputUnit FROM ( SELECT substring(@InputUnit,Number,1) FROM Numbers WHERE substring(@InputUnit,Number,1) like '%[0-9]%' ORDER BY Number FOR XML PATH('') ) Sub(OutputUnit) )
Verwendung:
UPDATE t SET column = o.OutputUnit FROM ##t t CROSS APPLY [dbo].[RemoveChars](t.column) o
quelle
Hier ist eine Funktion, die ich geschrieben habe, um dies basierend auf den vorherigen Antworten zu erreichen.
CREATE FUNCTION dbo.RepetitiveReplace ( @P_String VARCHAR(MAX), @P_Pattern VARCHAR(MAX), @P_ReplaceString VARCHAR(MAX), @P_ReplaceLength INT = 1 ) RETURNS VARCHAR(MAX) BEGIN DECLARE @Index INT; -- Get starting point of pattern SET @Index = PATINDEX(@P_Pattern, @P_String); while @Index > 0 begin --replace matching charactger at index SET @P_String = STUFF(@P_String, PATINDEX(@P_Pattern, @P_String), @P_ReplaceLength, @P_ReplaceString); SET @Index = PATINDEX(@P_Pattern, @P_String); end RETURN @P_String; END;
Kern
Bearbeiten:
Ursprünglich hatte ich hier eine rekursive Funktion, die mit SQL Server nicht gut funktioniert, da sie ein Limit von 32 Verschachtelungsstufen hat, was zu einem Fehler wie dem folgenden führen würde, wenn Sie versuchen, mehr als 32 Ersetzungen mit der Funktion vorzunehmen. Anstatt zu versuchen, eine Änderung auf Serverebene vorzunehmen, um mehr Verschachtelung zu ermöglichen (was gefährlich sein kann, wie das Zulassen von nie endenden Schleifen), ist das Wechseln zu einer while-Schleife viel sinnvoller.
Die maximale Verschachtelungsstufe für gespeicherte Prozeduren, Funktionen, Trigger oder Ansichten wurde überschritten (Limit 32).
quelle
Das Umschließen der Lösung in eine SQL-Funktion kann hilfreich sein, wenn Sie sie wiederverwenden möchten. Ich mache es sogar auf Zellebene, deshalb setze ich dies als eine andere Antwort:
CREATE FUNCTION [dbo].[fnReplaceInvalidChars] (@string VARCHAR(300)) RETURNS VARCHAR(300) BEGIN DECLARE @str VARCHAR(300) = @string; DECLARE @Pattern VARCHAR (20) = '%[^a-zA-Z0-9]%'; DECLARE @Len INT; SELECT @Len = LEN(@String); WHILE @Len > 0 BEGIN SET @Len = @Len - 1; IF (PATINDEX(@Pattern,@str) > 0) BEGIN SELECT @str = STUFF(@str, PATINDEX(@Pattern,@str),1,''); END ELSE BEGIN BREAK; END END RETURN @str END
quelle
Ich habe diese Funktion erstellt, um eine Zeichenfolge zu bereinigen, die nicht numerische Zeichen in einem Zeitfeld enthält. Die Zeit enthielt Fragezeichen, als sie die Minuten nicht hinzufügten, so etwas wie diese 20: ??. Die Funktion durchläuft jedes Zeichen und ersetzt das? mit einer 0:
CREATE FUNCTION [dbo].[CleanTime] ( -- Add the parameters for the function here @intime nvarchar(10) ) RETURNS nvarchar(5) AS BEGIN -- Declare the return variable here DECLARE @ResultVar nvarchar(5) DECLARE @char char(1) -- Add the T-SQL statements to compute the return value here DECLARE @i int = 1 WHILE @i <= LEN(@intime) BEGIN SELECT @char = CASE WHEN substring(@intime,@i,1) like '%[0-9:]%' THEN substring(@intime,@i,1) ELSE '0' END SELECT @ResultVar = concat(@ResultVar,@char) set @i = @i + 1 END; -- Return the result of the function RETURN @ResultVar END
quelle
Wenn Sie dies nur für einen Parameter tun, der in eine gespeicherte Prozedur eingeht, können Sie Folgendes verwenden:
declare @badIndex int set @badIndex = PatIndex('%[^0-9]%', @Param) while @badIndex > 0 set @Param = Replace(@Param, Substring(@Param, @badIndex, 1), '') set @badIndex = PatIndex('%[^0-9]%', @Param)
quelle
Ich denke, ein einfacherer und schnellerer Ansatz wird von jedem Zeichen des Alphabets wiederholt:
DECLARE @i int SET @i = 0 WHILE(@i < 256) BEGIN IF char(@i) NOT IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.') UPDATE Table SET Column = replace(Column, char(@i), '') SET @i = @i + 1 END
quelle