it-swarm.com.de

Bessere Techniken zum Abschneiden führender Nullen in SQL Server?

Ich benutze this seit einiger Zeit:

SUBSTRING(str_col, PATINDEX('%[^0]%', str_col), LEN(str_col))

In letzter Zeit habe ich jedoch ein Problem mit Spalten mit allen "0" -Zeichen wie "00000000" gefunden, da es nie ein Nicht-"0" -Zeichen zur Übereinstimmung findet.

Eine alternative Technik, die ich gesehen habe, ist TRIM:

REPLACE(LTRIM(REPLACE(str_col, '0', ' ')), ' ', '0')

Dies hat ein Problem, wenn eingebettete Leerzeichen vorhanden sind, da diese in "0" umgewandelt werden, wenn die Leerzeichen wieder in "0" umgewandelt werden.

Ich versuche, eine skalare UDF zu vermeiden. Ich habe in SQL Server 2005 zahlreiche Leistungsprobleme mit UDFs gefunden.

125
Cade Roux
SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col))
221
Arvo

Warum wandeln Sie den Wert nicht einfach in INTEGER und dann wieder in VARCHAR um?

SELECT  CAST(CAST('000000000' AS INTEGER) AS VARCHAR)

--------
       0
26
Quassnoi

Andere Antworten hier nicht berücksichtigt, wenn Sie alle Nullen (oder sogar eine einzelne Null) haben.
Einige setzen einen leeren String standardmäßig auf Null, was falsch ist, wenn er leer bleiben soll.
Lesen Sie die ursprüngliche Frage erneut. Dies beantwortet, was der Fragesteller möchte.

Lösung Nr. 1:

--This example uses both Leading and Trailing zero's.
--Avoid losing those Trailing zero's and converting embedded spaces into more zeros.
--I added a non-whitespace character ("_") to retain trailing zero's after calling Replace().
--Simply remove the RTrim() function call if you want to preserve trailing spaces.
--If you treat zero's and empty-strings as the same thing for your application,
--  then you may skip the Case-Statement entirely and just use CN.CleanNumber .
DECLARE @WackadooNumber VarChar(50) = ' 0 0123ABC D0 '--'000'--
SELECT WN.WackadooNumber, CN.CleanNumber,
       (CASE WHEN WN.WackadooNumber LIKE '%0%' AND CN.CleanNumber = '' THEN '0' ELSE CN.CleanNumber END)[AllowZero]
 FROM (SELECT @WackadooNumber[WackadooNumber]) AS WN
 OUTER APPLY (SELECT RTRIM(RIGHT(WN.WackadooNumber, LEN(LTRIM(REPLACE(WN.WackadooNumber + '_', '0', ' '))) - 1))[CleanNumber]) AS CN
--Result: "123ABC D0"

Lösung Nr. 2 (mit Beispieldaten):

SELECT O.Type, O.Value, Parsed.Value[WrongValue],
       (CASE WHEN CHARINDEX('0', T.Value)  > 0--If there's at least one zero.
              AND LEN(Parsed.Value) = 0--And the trimmed length is zero.
             THEN '0' ELSE Parsed.Value END)[FinalValue],
       (CASE WHEN CHARINDEX('0', T.Value)  > 0--If there's at least one zero.
              AND LEN(Parsed.TrimmedValue) = 0--And the trimmed length is zero.
             THEN '0' ELSE LTRIM(RTRIM(Parsed.TrimmedValue)) END)[FinalTrimmedValue]
  FROM 
  (
    VALUES ('Null', NULL), ('EmptyString', ''),
           ('Zero', '0'), ('Zero', '0000'), ('Zero', '000.000'),
           ('Spaces', '    0   A B C '), ('Number', '000123'),
           ('AlphaNum', '000ABC123'), ('NoZero', 'NoZerosHere')
  ) AS O(Type, Value)--O is for Original.
  CROSS APPLY
  ( --This Step is Optional.  Use if you also want to remove leading spaces.
    SELECT LTRIM(RTRIM(O.Value))[Value]
  ) AS T--T is for Trimmed.
  CROSS APPLY
  ( --From @CadeRoux's Post.
    SELECT SUBSTRING(O.Value, PATINDEX('%[^0]%', O.Value + '.'), LEN(O.Value))[Value],
           SUBSTRING(T.Value, PATINDEX('%[^0]%', T.Value + '.'), LEN(T.Value))[TrimmedValue]
  ) AS Parsed

Ergebnisse:

MikeTeeVee_SQL_Server_Remove_Leading_Zeros

Zusammenfassung:

Sie könnten das, was ich oben habe, für eine einmalige Entfernung von führenden Nullen verwenden.
Wenn Sie eine häufige Wiederverwendung planen, platzieren Sie sie in einer Inline-Table-Valued-Function (ITVF).
.__ Ihre Bedenken in Bezug auf Leistungsprobleme bei UDFs sind verständlich.
Dieses Problem gilt jedoch nur für All-Scalar-Funktionen und Multi-Statement-Table-Funktionen.
Die Verwendung von ITVFs ist vollkommen in Ordnung.

Ich habe das gleiche Problem mit unserer Datenbank von Drittanbietern.
Mit alphanumerischen Feldern werden viele ohne die führenden Leerzeichen eingegeben, dang menschen!
Dies macht Joins unmöglich, ohne die fehlenden führenden Nullen zu bereinigen.

Fazit:

Anstatt die führenden Nullen zu entfernen, sollten Sie in Betracht ziehen, Ihre getrimmten Werte nur mit führenden Nullen aufzufüllen, wenn Sie Ihre Joins ausführen.
Besser noch, bereinigen Sie Ihre Daten in der Tabelle, indem Sie führende Nullen hinzufügen und dann die Indizes neu erstellen.
Ich denke, das wäre viel schneller und weniger komplex.

SELECT RIGHT('0000000000' + LTRIM(RTRIM(NULLIF(' 0A10  ', ''))), 10)--0000000A10
SELECT RIGHT('0000000000' + LTRIM(RTRIM(NULLIF('', ''))), 10)--NULL --When Blank.
12
MikeTeeVee

Ersetzen Sie anstelle eines Leerzeichens die 0 durch ein "seltenes" Leerzeichen, das normalerweise nicht im Text der Spalte enthalten sein sollte. Ein Zeilenvorschub ist wahrscheinlich gut genug für eine solche Kolonne. Dann können Sie normal LTrim und das Sonderzeichen wieder durch 0 ersetzen.

5
Joel Coehoorn

Folgendes gibt '0' zurück, wenn die Zeichenfolge vollständig aus Nullen besteht:

CASE WHEN SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col)) = '' THEN '0' ELSE SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col)) END AS str_col
3
Scott

Dies macht eine schöne Funktion ....

DROP FUNCTION [dbo].[FN_StripLeading]
GO
CREATE FUNCTION [dbo].[FN_StripLeading] (@string VarChar(128), @stripChar VarChar(1))
RETURNS VarChar(128)
AS
BEGIN
-- http://stackoverflow.com/questions/662383/better-techniques-for-trimming-leading-zeros-in-sql-server
    DECLARE @retVal VarChar(128),
            @pattern varChar(10)
    SELECT @pattern = '%[^'[email protected]+']%'
    SELECT @retVal = CASE WHEN SUBSTRING(@string, PATINDEX(@pattern, @string+'.'), LEN(@string)) = '' THEN @stripChar ELSE SUBSTRING(@string, PATINDEX(@pattern, @string+'.'), LEN(@string)) END
    RETURN (@retVal)
END
GO
GRANT EXECUTE ON [dbo].[FN_StripLeading] TO PUBLIC
2
user2600313

cast (Wert als int) funktioniert immer, wenn string eine Zahl ist

2
tichra
replace(ltrim(replace(Fieldname.TableName, '0', '')), '', '0')

Der Vorschlag von Thomas G. hat sich für unsere Bedürfnisse eingesetzt. 

In unserem Fall war das Feld bereits String und nur die führenden Nullen mussten abgeschnitten werden. Meistens ist das alles numerisch, aber manchmal gibt es Buchstaben, so dass die vorherige INT-Konvertierung abstürzen würde. 

1
Randy

Wenn Sie Snowflake SQL verwenden, können Sie Folgendes verwenden:

ltrim(str_col,'0')

Die ltrim-Funktion entfernt alle Instanzen des angegebenen Zeichensatzes von der linken Seite. 

Ltrim (str_col, '0') auf '00000008A' würde also '8A' zurückgeben

Und rtrim (str_col, '0.') Für '$ 125.00' würde '$ 125' zurückgeben

0
JJFord3

Meine Version davon ist eine Adaption von Arvos Arbeit, mit etwas mehr, um zwei weitere Fälle zu gewährleisten.

1) Wenn wir alle 0 haben, sollten wir die Ziffer 0 zurückgeben.

2) Wenn wir ein Leerzeichen haben, sollten wir trotzdem ein Leerzeichen zurückgeben.

CASE 
    WHEN PATINDEX('%[^0]%', str_col + '.') > LEN(str_col) THEN RIGHT(str_col, 1) 
    ELSE SUBSTRING(str_col, PATINDEX('%[^0]%', str_col + '.'), LEN(str_col))
 END
0
Brisbe
SELECT CAST(CAST('000000000' AS INTEGER) AS VARCHAR)

Dies hat eine Begrenzung für die Länge der Zeichenfolge, die in ein INT konvertiert werden kann

0
Curt Ehrhart

Wenn Sie nicht in int konvertieren möchten, bevorzuge ich die folgende Logik, da sie mit Nullen umgehen kann. IFNULL (Feld, LTRIM (Feld, '0')) 

0
shockwave