Betere technieken voor het trimmen van toonaangevende nullen in SQL Server?

Ik gebruik dit Al geruime tijd:

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

Eén onlangs heb ik een probleem met kolommen gevonden met alle “0” -karakters zoals ‘00000000’ omdat het nooit een niet-“0” -karakter vindt om te matchen.

Een alternatieve techniek die ik heb gezien, is het gebruik TRIM:

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

Dit heeft een probleem als er embedded spaties zijn, omdat ze worden omgezet in “0” s wanneer de spaties terug worden ingeschakeld in “0”.

Ik probeer een scalaire UDF te vermijden. Ik heb veel prestatieproblemen gevonden met UDFS in SQL Server 2005.


Antwoord 1, Autoriteit 100%

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

Antwoord 2, Autoriteit 16%

Waarom spookt u niet zojuist de waarde op INTEGERen vervolgens terug naar VARCHAR?

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

Antwoord 3, Autoriteit 4%

Andere antwoorden hier om niet in aanmerking te nemen als u alle nul’s (of zelfs een enkele nul) hebt.
Sommigen standaard een lege reeks naar nul, die verkeerd is wanneer het zou moeten blijven.
Lees de oorspronkelijke vraag opnieuw. Dit beantwoordt wat de vraagsteller wil.

Oplossing # 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"

Oplossing # 2 (met voorbeeldgegevens):

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

Resultaten:

Samenvatting:

Je zou kunnen gebruiken wat ik hierboven heb voor een eenmalige verwijdering van voorloopnullen.
Als je van plan bent om het veel te hergebruiken, plaats het dan in een Inline-Table-Valued-Function (ITVF).
Uw zorgen over prestatieproblemen met UDF’s zijn begrijpelijk.
Dit probleem is echter alleen van toepassing op All-Scalar-Functions en Multi-Statement-Table-Functions.
Het gebruik van ITVF’s is prima.

Ik heb hetzelfde probleem met onze database van derden.
Met alfanumerieke velden worden er veel ingevoerd zonder de voorloopspaties, verdorie mensen!
Dit maakt samenvoegen onmogelijk zonder de ontbrekende voorloopnullen op te ruimen.

Conclusie:

In plaats van de voorloopnullen te verwijderen, kunt u overwegen om uw getrimde waarden op te vullen met voorloopnullen wanneer u uw joins uitvoert.
Beter nog, ruim uw gegevens in de tabel op door voorloopnullen toe te voegen en vervolgens uw indexen opnieuw op te bouwen.
Ik denk dat dit VEEL sneller en minder complex zou zijn.

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

Antwoord 4, autoriteit 2%

In plaats van een ruimte vervangen de 0’s met een ‘zeldzaam’ witruimtekarakter dat normaal niet in de tekst van de kolom zou moeten zijn. Een lijnvoeding is waarschijnlijk goed genoeg voor een kolom als deze. Dan kun je Normaal ltrim en het speciale teken vervangen door 0’s weer.


Antwoord 5

Het volgende zal ‘0’ terugkeren als de string volledig bestaat uit Nullen:

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

Antwoord 6

Dit maakt een mooie functie ….

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 = '%[^'+@stripChar+']%'
    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

Antwoord 7

Gegoten (waarde als int) werkt altijd als een cijfer een cijfer

is


Antwoord 8

Mijn versie hiervan is een aanpassing van het werk van Arvo, met een beetje meer toegevoegd om twee andere gevallen te garanderen.

1) Als we alle 0s hebben, moeten we het cijfer retourneren 0.

2) Als we een blanco hebben, moeten we nog steeds een leeg teken retourneren.

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

Antwoord 9

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

De suggestie van Thomas G werkte voor onze behoeften.

Het veld in ons geval was al een string en alleen de voorloopnullen moesten worden bijgesneden. Meestal is het allemaal numeriek, maar soms zijn er letters zodat de vorige INT-conversie crashte.


Antwoord 10

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

Dit heeft een limiet voor de lengte van de string die kan worden geconverteerd naar een INT


Antwoord 11

Als u Snowflake SQL gebruikt, kunt u dit gebruiken:

ltrim(str_col,'0')

De ltrim-functie verwijdert alle instanties van de aangewezen set tekens aan de linkerkant.

Dus ltrim(str_col,’0′) op ‘00000008A’ zou ‘8A’ retourneren

En rtrim(str_col,’0.’) op ‘$125.00’ zou ‘$125’ opleveren


Antwoord 12

Probeer dit:

replace(ltrim(replace(@str, '0', ' ')), ' ', '0')

Antwoord 13

Als je niet wilt converteren naar int, geef ik de voorkeur aan onderstaande logica omdat het nulls aankan
IFNULL(veld;LTRIM(veld;’0′))


Antwoord 14

 SUBSTRING(str_col, IIF(LEN(str_col) > 0, PATINDEX('%[^0]%', LEFT(str_col, LEN(str_col) - 1) + '.'), 0), LEN(str_col))

Werkt prima, zelfs met ‘0’, ’00’ enzovoort.


Antwoord 15

Voor het converteren van getallen als varcharnaar intkunt u ook eenvoudig

gebruiken

(column + 0)

Antwoord 16

Dit kan helpen

SELECT ABS(column_name) FROM [db].[schema].[table]

Antwoord 17

In MySQL kunt u dit doen…

Trim(Leading '0' from your_column)

Other episodes