TSQL - Convertit la chaîne en entier ou renvoie la valeur par défaut

116

Existe-t-il un moyen dans T-SQL de convertir un nvarchar en int et de renvoyer une valeur par défaut ou NULL si la conversion échoue?

Oliver Hanappi
la source

Réponses:

66

Si vous utilisez SQL Server 2012 (ou plus récent):

Utilisez la fonction TRY_CONVERT .

Si vous utilisez SQL Server 2005, 2008 ou 2008 R2:

Créez une fonction définie par l'utilisateur. Cela évitera les problèmes mentionnés par Fedor Hajdu en ce qui concerne la devise, les nombres fractionnaires, etc.:

CREATE FUNCTION dbo.TryConvertInt(@Value varchar(18))
RETURNS int
AS
BEGIN
    SET @Value = REPLACE(@Value, ',', '')
    IF ISNUMERIC(@Value + 'e0') = 0 RETURN NULL
    IF ( CHARINDEX('.', @Value) > 0 AND CONVERT(bigint, PARSENAME(@Value, 1)) <> 0 ) RETURN NULL
    DECLARE @I bigint =
        CASE
        WHEN CHARINDEX('.', @Value) > 0 THEN CONVERT(bigint, PARSENAME(@Value, 2))
        ELSE CONVERT(bigint, @Value)
        END
    IF ABS(@I) > 2147483647 RETURN NULL
    RETURN @I
END
GO

-- Testing
DECLARE @Test TABLE(Value nvarchar(50))    -- Result
INSERT INTO @Test SELECT '1234'            -- 1234
INSERT INTO @Test SELECT '1,234'           -- 1234
INSERT INTO @Test SELECT '1234.0'          -- 1234
INSERT INTO @Test SELECT '-1234'           -- -1234
INSERT INTO @Test SELECT '$1234'           -- NULL
INSERT INTO @Test SELECT '1234e10'         -- NULL
INSERT INTO @Test SELECT '1234 5678'       -- NULL
INSERT INTO @Test SELECT '123-456'         -- NULL
INSERT INTO @Test SELECT '1234.5'          -- NULL
INSERT INTO @Test SELECT '123456789000000' -- NULL
INSERT INTO @Test SELECT 'N/A'             -- NULL
SELECT Value, dbo.TryConvertInt(Value) FROM @Test

Référence: J'ai beaucoup utilisé cette page lors de la création de ma solution.

Joseph Sturtevant
la source
1
Quelqu'un fait fonctionner ça? J'ai créé une fonction comme suggéré et je trouve qu'elle génère des erreurs de conversion en utilisant la fonction de test proposée. J'imagine qu'il me manque quelque chose ici ...
Keith Hoffman
1
@JosephSturtevant this is beautiful
StelioK
Oui, bon travail ... M'a sauvé beaucoup de travail ... Je ne suis pas en 2012 ou plus à cause du client ... Ne me donnez pas de crédit cependant :) Je n'ai été bon qu'à chercher la réponse qui a fonctionné pour moi ... Bien que je l'ai changé de renvoyer null à renvoyer zéro car le stupide varchar devrait être une colonne int avec une valeur par défaut de zéro :)
CA Martin
134

Oui :). Essaye ça:

DECLARE @text AS NVARCHAR(10)

SET @text = '100'
SELECT CASE WHEN ISNUMERIC(@text) = 1 THEN CAST(@text AS INT) ELSE NULL END
-- returns 100

SET @text = 'XXX'
SELECT CASE WHEN ISNUMERIC(@text) = 1 THEN CAST(@text AS INT) ELSE NULL END
-- returns NULL

ISNUMERIC()a quelques problèmes signalés par Fedor Hajdu .

Il renvoie true pour les chaînes telles que $(is currency), ,or .(les deux sont des séparateurs) +et -.

Grzegorz Gierlik
la source
3
Merci pour votre réponse. J'espérais qu'il y avait quelque chose de plus élégant.
Oliver Hanappi le
4
Qu'en est-il lorsque la valeur de texte est numérique, mais contient un nombre supérieur à ce qu'une colonne int peut stocker?
Tom Mayfield
8
-1: Malheureusement, il y a trop de problèmes avec ISNUMERICpour que cette réponse soit utile sur des données non validées (et vous n'auriez pas besoin de la ISNUMERICvérification en premier lieu pour des données correctement validées). L'auteur reconnaît l'existence de ces problèmes mais ne les aborde pas.
Douglas
7
Comme Joseph le mentionne , les utilisateurs de SQL 2012+ devraient utiliser TRY_CONVERT/ TRY_CAST, qui sont explicitement destinés à résoudre la question du PO. Cette réponse a été écrite avant la publication de SQL 2012.
Brian
3
Je vote contre parce que ISNUMERIC ('1,1,1,1,1') = 1 et cela ne transite jamais en int.
mischka
14

Je préfère créer une fonction comme TryParse ou utiliser le TRY-CATCHbloc T-SQL pour obtenir ce que vous voulez.

ISNUMERIC ne fonctionne pas toujours comme prévu. Le code donné précédemment échouera si vous faites:

SET @text = '$'

$ sign peut être converti en type de données money, donc ISNUMERIC()renvoie true dans ce cas. Il en sera de même pour '-' (moins), ',' (virgule) et '.' personnages.

Fedor Hajdu
la source
3
. :( vrai ISNUMERIC()rendement 1aussi pour ,et ..
Grzegorz Gierlik
3
... sauf que vous ne pouvez pas utiliser TRY-CATCH dans une fonction (du moins pas dans ma base de données SQL 2005) ... lien
Etherman
12

Comme cela a été mentionné, vous pouvez rencontrer plusieurs problèmes si vous utilisez ISNUMERIC:

-- Incorrectly gives 0:
SELECT CASE WHEN ISNUMERIC('-') = 1 THEN CAST('-' AS INT) END   

-- Error (conversion failure):
SELECT CASE WHEN ISNUMERIC('$') = 1 THEN CAST('$' AS INT) END
SELECT CASE WHEN ISNUMERIC('4.4') = 1 THEN CAST('4.4' AS INT) END
SELECT CASE WHEN ISNUMERIC('1,300') = 1 THEN CAST('1,300' AS INT) END

-- Error (overflow):
SELECT CASE WHEN ISNUMERIC('9999999999') = 1 THEN CAST('9999999999' AS INT) END

Si vous voulez une conversion fiable, vous devrez en coder une vous-même.

Mise à jour : Ma nouvelle recommandation serait d'utiliser une conversion de test intermédiaire FLOATpour valider le numéro. Cette approche est basée sur le commentaire d'Adrianm . La logique peut être définie comme une fonction table en ligne:

CREATE FUNCTION TryConvertInt (@text NVARCHAR(MAX)) 
RETURNS TABLE
AS
RETURN
(
    SELECT
        CASE WHEN ISNUMERIC(@text + '.e0') = 1 THEN 
             CASE WHEN CONVERT(FLOAT, @text) BETWEEN -2147483648 AND 2147483647 
                  THEN CONVERT(INT, @text) 
             END 
         END AS [Result]
)

Quelques tests:

SELECT [Conversion].[Result]
FROM ( VALUES
     ( '1234'                     )   -- 1234
   , ( '1,234'                    )   -- NULL
   , ( '1234.0'                   )   -- NULL
   , ( '-1234'                    )   -- -1234
   , ( '$1234'                    )   -- NULL
   , ( '1234e10'                  )   -- NULL
   , ( '1234 5678'                )   -- NULL
   , ( '123-456'                  )   -- NULL
   , ( '1234.5'                   )   -- NULL
   , ( '123456789000000'          )   -- NULL
   , ( 'N/A'                      )   -- NULL
   , ( '-'                        )   -- NULL
   , ( '$'                        )   -- NULL
   , ( '4.4'                      )   -- NULL
   , ( '1,300'                    )   -- NULL
   , ( '9999999999'               )   -- NULL
   , ( '00000000000000001234'     )   -- 1234
   , ( '212110090000000235698741' )   -- NULL
) AS [Source] ([Text])
OUTER APPLY TryConvertInt ([Source].[Text]) AS [Conversion]

Les résultats sont similaires à la réponse de Joseph Sturtevant , avec les principales différences suivantes:

  • Ma logique ne tolère pas les occurrences .ou ,pour imiter le comportement des INTconversions natives . '1,234'et '1234.0'revenir NULL.
  • Puisqu'elle n'utilise pas de variables locales, ma fonction peut être définie comme une fonction table en ligne, permettant une meilleure optimisation des requêtes.
  • La réponse de Joseph peut conduire à des résultats incorrects en raison de troncatures silencieuses de l'argument; '00000000000000001234'évalue à 12. L'augmentation de la longueur du paramètre entraînerait des erreurs sur les nombres qui débordent BIGINT, tels que les BBAN (numéros de compte bancaire de base) comme '212110090000000235698741'.

Retiré : L'approche ci-dessous n'est plus recommandée, car elle est laissée juste pour référence.

L'extrait ci-dessous fonctionne sur des entiers non négatifs. Il vérifie que votre chaîne ne contient aucun caractère non numérique, n'est pas vide et ne déborde pas (en dépassant la valeur maximale du inttype). Cependant, il donne également NULLdes entiers valides dont la longueur dépasse 10 caractères en raison de zéros non significatifs.

SELECT 
    CASE WHEN @text NOT LIKE '%[^0-9]%' THEN
         CASE WHEN LEN(@text) BETWEEN 1 AND 9 
                OR LEN(@text) = 10 AND @text <= '2147483647' 
              THEN CAST (@text AS INT)
         END
    END 

Si vous souhaitez prendre en charge un nombre quelconque de zéros non significatifs, utilisez ce qui suit. Les CASEinstructions imbriquées , bien que peu maniables, sont nécessaires pour promouvoir l'évaluation de court-circuit et réduire la probabilité d'erreurs (résultant, par exemple, du passage d'une longueur négative à LEFT).

SELECT 
    CASE WHEN @text NOT LIKE '%[^0-9]%' THEN
         CASE WHEN LEN(@text) BETWEEN 1 AND 9 THEN CAST (@text AS INT)
              WHEN LEN(@text) >= 10 THEN
              CASE WHEN LEFT(@text, LEN(@text) - 10) NOT LIKE '%[^0]%'
                    AND RIGHT(@text, 10) <= '2147483647'
                   THEN CAST (@text AS INT)
              END
         END
    END

Si vous souhaitez prendre en charge les entiers positifs et négatifs avec un nombre quelconque de zéros non significatifs:

SELECT 
         -- Positive integers (or 0):
    CASE WHEN @text NOT LIKE '%[^0-9]%' THEN
         CASE WHEN LEN(@text) BETWEEN 1 AND 9 THEN CAST (@text AS INT)
              WHEN LEN(@text) >= 10 THEN
              CASE WHEN LEFT(@text, LEN(@text) - 10) NOT LIKE '%[^0]%'
                    AND RIGHT(@text, 10) <= '2147483647'
                   THEN CAST (@text AS INT)
              END
         END
         -- Negative integers:
         WHEN LEFT(@text, 1) = '-' THEN
         CASE WHEN RIGHT(@text, LEN(@text) - 1) NOT LIKE '%[^0-9]%' THEN
              CASE WHEN LEN(@text) BETWEEN 2 AND 10 THEN CAST (@text AS INT)
                   WHEN LEN(@text) >= 11 THEN
                   CASE WHEN SUBSTRING(@text, 2, LEN(@text) - 11) NOT LIKE '%[^0]%'
                         AND RIGHT(@text, 10) <= '2147483648'
                        THEN CAST (@text AS INT)
                   END
              END
         END
    END
Douglas
la source
3
Pour souligner ce point, le court-circuit n'est pas garanti , même avec l'instruction CASE.
Michael Green
1
@MichaelGreen: j'avais lu cet article; la dernière mise à jour déclare: "Les propriétaires de ce code [?] ont marqué ce bogue comme corrigé. D'après leurs commentaires, il semble que vous soyez censé pouvoir compter sur un ordre déterministe d'évaluation des expressions pour les instructions CASE." Microsoft reconnaît que le bogue peut se produire pour les agrégats, mais pas pour les scalaires.
Douglas
1
Point juste @Douglas. Je me souviens avoir lu au cours des deux derniers mois un article de l'un des blogueurs de marque sur les courts-circuits CASE qui était fiable au moment de la compilation, même si le séquençage à l'exécution était respecté. Cependant, je ne trouve pas cette référence pour le moment. Ma motivation était d'avertir la communauté que cela pouvait être un problème, en fonction de leur version / édition / niveau de patch et j'ai utilisé ce lien comme exemple, c'est tout.
Michael Green
1
@MichaelGreen: D'accord; À mon humble avis, il s'agit / était une grave lacune dans SQL Server qui limite considérablement son utilisation pour les scénarios nécessitant la validation des données.
Douglas
1
Pourquoi n'utilisez-vous pas BIGINT au milieu de la conversation?
alex
2

Cordialement.

J'ai écrit une fonction scalaire utile pour simuler la fonction TRY_CAST de SQL SERVER 2012 dans SQL Server 2008.

Vous pouvez le voir dans le lien suivant ci-dessous et nous nous entraidons pour l'améliorer. Fonction TRY_CAST pour SQL Server 2008 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb

Les deux principales différences sont que vous devez passer 3 paramètres et que vous devez en plus effectuer un CONVERT ou CAST explicite dans le champ. Cependant, il est toujours très utile car il vous permet de renvoyer une valeur par défaut si CAST n'est pas exécuté correctement.

dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)

Exemple:

SELECT   CASE WHEN dbo.TRY_CAST('6666666166666212', 'INT', DEFAULT) IS NULL   
                        THEN 'Cast failed'  
                        ELSE 'Cast succeeded'  
                    END AS Result; 

Pour l'instant ne prend en charge que les types de données INT, DATE, NUMERIC, BIT et FLOAT

J'espère que tu trouves cela utile.

CODE:

DECLARE @strSQL NVARCHAR(1000)
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[TRY_CAST]'))
    BEGIN
        SET @strSQL = 'CREATE FUNCTION [dbo].[TRY_CAST] () RETURNS INT AS BEGIN RETURN 0 END'
        EXEC sys.sp_executesql @strSQL
    END

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

/*
------------------------------------------------------------------------------------------------------------------------
    Description:    
                    Syntax 
                    ---------------
                    dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)

                    +---------------------------+-----------------------+
                    |   Expression              |   VARCHAR(8000)       |
                    +---------------------------+-----------------------+
                    |   Data_Type               |   VARCHAR(8000)       |
                    +---------------------------+-----------------------+
                    |   ReturnValueIfErrorCast  |   SQL_VARIANT = NULL  |
                    +---------------------------+-----------------------+


                    Arguments
                    ---------------
                    expression
                    The value to be cast. Any valid expression.

                    Data_Type
                    The data type into which to cast expression.

                    ReturnValueIfErrorCast
                    Value returned if cast fails or is not supported. Required. Set the DEFAULT value by default.


                    Return Type
                    ----------------
                    Returns value cast to SQL_VARIANT type if the cast succeeds; otherwise, returns null if the parameter @pReturnValueIfErrorCast is set to DEFAULT, 
                    or that the user indicates.


                    Remarks
                    ----------------
                    dbo.TRY_CAST function simulates the TRY_CAST function reserved of SQL SERVER 2012 for using in SQL SERVER 2008. 
                    dbo.TRY_CAST function takes the value passed to it and tries to convert it to the specified Data_Type. 
                    If the cast succeeds, dbo.TRY_CAST returns the value as SQL_VARIANT type; if the cast doesn´t succees, null is returned if the parameter @pReturnValueIfErrorCast is set to DEFAULT. 
                    If the Data_Type is unsupported will return @pReturnValueIfErrorCast.
                    dbo.TRY_CAST function requires user make an explicit CAST or CONVERT in ANY statements.
                    This version of dbo.TRY_CAST only supports CAST for INT, DATE, NUMERIC and BIT types.


                    Examples
                    ====================================================================================================

                    --A. Test TRY_CAST function returns null

                        SELECT   
                            CASE WHEN dbo.TRY_CAST('6666666166666212', 'INT', DEFAULT) IS NULL   
                            THEN 'Cast failed'  
                            ELSE 'Cast succeeded'  
                        END AS Result; 

                    GO

                    --B. Error Cast With User Value

                        SELECT   
                            dbo.TRY_CAST('2147483648', 'INT', DEFAULT) AS [Error Cast With DEFAULT],
                            dbo.TRY_CAST('2147483648', 'INT', -1) AS [Error Cast With User Value],
                            dbo.TRY_CAST('2147483648', 'INT', NULL) AS [Error Cast With User NULL Value]; 

                        GO 

                    --C. Additional CAST or CONVERT required in any assignment statement

                        DECLARE @IntegerVariable AS INT

                        SET @IntegerVariable = CAST(dbo.TRY_CAST(123, 'INT', DEFAULT) AS INT)

                        SELECT @IntegerVariable

                        GO 

                        IF OBJECT_ID('tempdb..#temp') IS NOT NULL
                            DROP TABLE #temp

                        CREATE TABLE #temp (
                            Id INT IDENTITY
                            , FieldNumeric NUMERIC(3, 1)
                            )

                        INSERT INTO dbo.#temp (FieldNumeric)
                        SELECT CAST(dbo.TRY_CAST(12.3, 'NUMERIC(3,1)', 0) AS NUMERIC(3, 1));--Need explicit CAST on INSERT statements

                        SELECT *
                        FROM #temp

                        DROP TABLE #temp

                        GO 

                    --D. Supports CAST for INT, DATE, NUMERIC and BIT types.

                        SELECT dbo.TRY_CAST(2147483648, 'INT', 0) AS [Cast failed]
                            , dbo.TRY_CAST(2147483647, 'INT', 0) AS [Cast succeeded]
                            , SQL_VARIANT_PROPERTY(dbo.TRY_CAST(212, 'INT', 0), 'BaseType') AS [BaseType];

                        SELECT dbo.TRY_CAST('AAAA0101', 'DATE', DEFAULT) AS [Cast failed]
                            , dbo.TRY_CAST('20160101', 'DATE', DEFAULT) AS [Cast succeeded]
                            , SQL_VARIANT_PROPERTY(dbo.TRY_CAST('2016-01-01', 'DATE', DEFAULT), 'BaseType') AS [BaseType];

                        SELECT dbo.TRY_CAST(1.23, 'NUMERIC(3,1)', DEFAULT) AS [Cast failed]
                            , dbo.TRY_CAST(12.3, 'NUMERIC(3,1)', DEFAULT) AS [Cast succeeded]
                            , SQL_VARIANT_PROPERTY(dbo.TRY_CAST(12.3, 'NUMERIC(3,1)', DEFAULT), 'BaseType') AS [BaseType];

                        SELECT dbo.TRY_CAST('A', 'BIT', DEFAULT) AS [Cast failed]
                            , dbo.TRY_CAST(1, 'BIT', DEFAULT) AS [Cast succeeded]
                            , SQL_VARIANT_PROPERTY(dbo.TRY_CAST('123', 'BIT', DEFAULT), 'BaseType') AS [BaseType];

                        GO 

                    --E. B. TRY_CAST return NULL on unsupported data_types

                        SELECT dbo.TRY_CAST(4, 'xml', DEFAULT) AS [unsupported];  

                        GO  

                    ====================================================================================================

------------------------------------------------------------------------------------------------------------------------
    Responsible:    Javier Pardo 
    Date:           diciembre 29/2016
    WB tests:       Javier Pardo 
------------------------------------------------------------------------------------------------------------------------
    Update by:      Javier Eduardo Pardo Moreno 
    Date:           febrero 16/2017
    Id update:      JEPM20170216
    Description:    Fix  ISNUMERIC function makes it unreliable. SELECT dbo.TRY_CAST('+', 'INT', 0) will yield Msg 8114, 
                    Level 16, State 5, Line 16 Error converting data type varchar to float.
                    ISNUMERIC() function treats few more characters as numeric, like: – (minus), + (plus), $ (dollar), \ (back slash), (.)dot and (,)comma
                    Collaborator aperiooculus (http://stackoverflow.com/users/3083382/aperiooculus )

                    Fix dbo.TRY_CAST('2013/09/20', 'datetime', DEFAULT) for supporting DATETIME format

    WB tests:       Javier Pardo 

------------------------------------------------------------------------------------------------------------------------
*/

ALTER FUNCTION dbo.TRY_CAST
(
    @pExpression AS VARCHAR(8000),
    @pData_Type AS VARCHAR(8000),
    @pReturnValueIfErrorCast AS SQL_VARIANT = NULL
)
RETURNS SQL_VARIANT
AS
BEGIN
    --------------------------------------------------------------------------------
    --  INT 
    --------------------------------------------------------------------------------

    IF @pData_Type = 'INT'
    BEGIN
        IF ISNUMERIC(@pExpression) = 1 AND @pExpression NOT IN ('-','+','$','.',',','\')    --JEPM20170216
        BEGIN
            DECLARE @pExpressionINT AS FLOAT = CAST(@pExpression AS FLOAT)

            IF @pExpressionINT BETWEEN - 2147483648.0 AND 2147483647.0
            BEGIN
                RETURN CAST(@pExpressionINT as INT)
            END
            ELSE
            BEGIN
                RETURN @pReturnValueIfErrorCast
            END --FIN IF @pExpressionINT BETWEEN - 2147483648.0 AND 2147483647.0
        END
        ELSE
        BEGIN
            RETURN @pReturnValueIfErrorCast
        END -- FIN IF ISNUMERIC(@pExpression) = 1
    END -- FIN IF @pData_Type = 'INT'

    --------------------------------------------------------------------------------
    --  DATE    
    --------------------------------------------------------------------------------

    IF @pData_Type IN ('DATE','DATETIME')
    BEGIN
        IF ISDATE(@pExpression) = 1
        BEGIN

            DECLARE @pExpressionDATE AS DATETIME = cast(@pExpression AS DATETIME)

            IF @pData_Type = 'DATE'
            BEGIN
                RETURN cast(@pExpressionDATE as DATE)
            END

            IF @pData_Type = 'DATETIME'
            BEGIN
                RETURN cast(@pExpressionDATE as DATETIME)
            END

        END
        ELSE 
        BEGIN

            DECLARE @pExpressionDATEReplaced AS VARCHAR(50) = REPLACE(REPLACE(REPLACE(@pExpression,'\',''),'/',''),'-','')

            IF ISDATE(@pExpressionDATEReplaced) = 1
            BEGIN
                IF @pData_Type = 'DATE'
                BEGIN
                    RETURN cast(@pExpressionDATEReplaced as DATE)
                END

                IF @pData_Type = 'DATETIME'
                BEGIN
                    RETURN cast(@pExpressionDATEReplaced as DATETIME)
                END

            END
            ELSE
            BEGIN
                RETURN @pReturnValueIfErrorCast
            END
        END --FIN IF ISDATE(@pExpression) = 1
    END --FIN IF @pData_Type = 'DATE'

    --------------------------------------------------------------------------------
    --  NUMERIC 
    --------------------------------------------------------------------------------

    IF @pData_Type LIKE 'NUMERIC%'
    BEGIN

        IF ISNUMERIC(@pExpression) = 1
        BEGIN

            DECLARE @TotalDigitsOfType AS INT = SUBSTRING(@pData_Type,CHARINDEX('(',@pData_Type)+1,  CHARINDEX(',',@pData_Type) - CHARINDEX('(',@pData_Type) - 1)
                , @TotalDecimalsOfType AS INT = SUBSTRING(@pData_Type,CHARINDEX(',',@pData_Type)+1,  CHARINDEX(')',@pData_Type) - CHARINDEX(',',@pData_Type) - 1)
                , @TotalDigitsOfValue AS INT 
                , @TotalDecimalsOfValue AS INT 
                , @TotalWholeDigitsOfType AS INT 
                , @TotalWholeDigitsOfValue AS INT 

            SET @pExpression = REPLACE(@pExpression, ',','.')

            SET @TotalDigitsOfValue = LEN(REPLACE(@pExpression, '.',''))
            SET @TotalDecimalsOfValue = CASE Charindex('.', @pExpression)
                                        WHEN 0
                                            THEN 0
                                        ELSE Len(Cast(Cast(Reverse(CONVERT(VARCHAR(50), @pExpression, 128)) AS FLOAT) AS BIGINT))
                                        END 
            SET @TotalWholeDigitsOfType = @TotalDigitsOfType - @TotalDecimalsOfType
            SET @TotalWholeDigitsOfValue = @TotalDigitsOfValue - @TotalDecimalsOfValue

            -- The total digits can not be greater than the p part of NUMERIC (p, s)
            -- The total of decimals can not be greater than the part s of NUMERIC (p, s)
            -- The total digits of the whole part can not be greater than the subtraction between p and s
            IF (@TotalDigitsOfValue <= @TotalDigitsOfType) AND (@TotalDecimalsOfValue <= @TotalDecimalsOfType) AND (@TotalWholeDigitsOfValue <= @TotalWholeDigitsOfType)
            BEGIN
                DECLARE @pExpressionNUMERIC AS FLOAT
                SET @pExpressionNUMERIC = CAST (ROUND(@pExpression, @TotalDecimalsOfValue) AS FLOAT) 

                RETURN @pExpressionNUMERIC --Returns type FLOAT
            END 
            else
            BEGIN
                RETURN @pReturnValueIfErrorCast
            END-- FIN IF (@TotalDigitisOfValue <= @TotalDigits) AND (@TotalDecimalsOfValue <= @TotalDecimals) 

        END
        ELSE 
        BEGIN
            RETURN @pReturnValueIfErrorCast
        END --FIN IF ISNUMERIC(@pExpression) = 1
    END --IF @pData_Type LIKE 'NUMERIC%'

    --------------------------------------------------------------------------------
    --  BIT 
    --------------------------------------------------------------------------------

    IF @pData_Type LIKE 'BIT'
    BEGIN
        IF ISNUMERIC(@pExpression) = 1
        BEGIN
            RETURN CAST(@pExpression AS BIT) 
        END
        ELSE 
        BEGIN
            RETURN @pReturnValueIfErrorCast
        END --FIN IF ISNUMERIC(@pExpression) = 1
    END --IF @pData_Type LIKE 'BIT'


    --------------------------------------------------------------------------------
    --  FLOAT   
    --------------------------------------------------------------------------------

    IF @pData_Type LIKE 'FLOAT'
    BEGIN
        IF ISNUMERIC(REPLACE(REPLACE(@pExpression, CHAR(13), ''), CHAR(10), '')) = 1
        BEGIN

            RETURN CAST(@pExpression AS FLOAT) 
        END
        ELSE 
        BEGIN

            IF REPLACE(@pExpression, CHAR(13), '') = '' --Only white spaces are replaced, not new lines
            BEGIN
                RETURN 0
            END
            ELSE 
            BEGIN
                RETURN @pReturnValueIfErrorCast
            END --IF REPLACE(@pExpression, CHAR(13), '') = '' 

        END --FIN IF ISNUMERIC(@pExpression) = 1
    END --IF @pData_Type LIKE 'FLOAT'

    --------------------------------------------------------------------------------
    --  Any other unsupported data type will return NULL or the value assigned by the user to @pReturnValueIfErrorCast  
    --------------------------------------------------------------------------------

    RETURN @pReturnValueIfErrorCast



END
JotaPardo
la source
1
Quelqu'un peut-il me dire pourquoi la rétroaction négative?
JotaPardo
1
Je ne comprends pas comment ils mettent des points négatifs et n'en ont même pas mentionné la raison. De cette façon, vous ne pouvez pas apprendre. S'il vous plaît feed back
JotaPardo
2
Vous voudrez peut-être inclure la mise en œuvre dans votre réponse.
AperioOculus
1
@AperioOculus merci beaucoup pour vos commentaires. J'ai mis à jour le code! Merci encore!
JotaPardo
1

La réponse de Joseph a souligné que ISNUMERIC gère également la notation scientifique comme «1.3e + 3» mais sa réponse ne gère pas ce format de nombre.

La conversion en argent ou en flottant traite d'abord à la fois la monnaie et les questions scientifiques:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TryConvertInt]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[TryConvertInt]
GO

CREATE FUNCTION dbo.TryConvertInt(@Value varchar(18))
RETURNS bigint
AS
BEGIN
    DECLARE @IntValue bigint;

    IF (ISNUMERIC(@Value) = 1)
        IF (@Value like '%e%')
            SET @IntValue = CAST(Cast(@Value as float) as bigint);
        ELSE
            SET @IntValue = CAST(CAST(@Value as money) as bigint);
    ELSE
        SET @IntValue = NULL;

    RETURN @IntValue;
END

La fonction échouera si le nombre est plus grand qu'un bigint.

Si vous souhaitez renvoyer une valeur par défaut différente, laissez cette fonction pour qu'elle soit générique et remplacez la valeur nulle par la suite:

SELECT IsNull(dbo.TryConvertInt('nan') , 1000);
Stephen Turner
la source
1

Je sais que ce n'est pas joli mais c'est simple. Essaye ça:

declare @AlpaNumber nvarchar(50) = 'ABC'
declare @MyNumber int = 0
begin Try
select @MyNumber = case when ISNUMERIC(@AlpaNumber) = 1 then cast(@AlpaNumber as int) else 0 end
End Try
Begin Catch
    -- Do nothing
End Catch 

if exists(select * from mytable where mynumber = @MyNumber)
Begin
print 'Found'
End
Else
Begin
 print 'Not Found'
End
Homme Ramblen
la source
1

Ma solution à ce problème était de créer la fonction ci-dessous. Mes exigences incluaient que le nombre devait être un entier standard, pas un BIGINT, et je devais autoriser les nombres négatifs et positifs. Je n'ai trouvé aucune circonstance où cela échoue.

CREATE FUNCTION [dbo].[udfIsInteger]
(
    -- Add the parameters for the function here
    @Value nvarchar(max)
)
RETURNS int
AS
BEGIN
    -- Declare the return variable here
    DECLARE @Result int = 0

    -- Add the T-SQL statements to compute the return value here
    DECLARE @MinValue nvarchar(11) = '-2147483648'
    DECLARE @MaxValue nvarchar(10) = '2147483647'

    SET @Value = ISNULL(@Value,'')

    IF LEN(@Value)=0 OR 
      ISNUMERIC(@Value)<>1 OR
      (LEFT(@Value,1)='-' AND LEN(@Value)>11) OR
      (LEFT(@Value,1)='-' AND LEN(@Value)=11 AND @Value>@MinValue) OR
      (LEFT(@Value,1)<>'-' AND LEN(@Value)>10) OR
      (LEFT(@Value,1)<>'-' AND LEN(@Value)=10 AND @Value>@MaxValue)
      GOTO FINISHED

    DECLARE @cnt int = 0
    WHILE @cnt<LEN(@Value)
    BEGIN
      SET @cnt=@cnt+1
      IF SUBSTRING(@Value,@cnt,1) NOT IN ('-','0','1','2','3','4','5','6','7','8','9') GOTO FINISHED
    END
    SET @Result=1

FINISHED:
    -- Return the result of the function
    RETURN @Result

END
Brian Payne
la source