TSQL - Truyền chuỗi thành số nguyên hoặc trả về giá trị mặc định


116

Có cách nào trong T-SQL để truyền nvarchar thành int và trả về giá trị mặc định hoặc NULL nếu quá trình chuyển đổi không thành công không?

Câu trả lời:


66

Nếu bạn đang sử dụng SQL Server 2012 (hoặc mới hơn):

Sử dụng hàm TRY_CONVERT .

Nếu bạn đang sử dụng SQL Server 2005, 2008 hoặc 2008 R2:

Tạo một chức năng do người dùng xác định. Điều này sẽ tránh được các vấn đề mà Fedor Hajdu đã đề cập đến liên quan đến tiền tệ, số phân số, v.v.:

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

Tham khảo: Tôi đã sử dụng trang này nhiều khi tạo giải pháp của mình.


1
Bất cứ ai nhận được điều này để làm việc? Tôi đã tạo một chức năng như được đề xuất và thấy nó ném ra các lỗi chuyển đổi bằng cách sử dụng chức năng thử nghiệm được đề xuất. Tôi tưởng tượng tôi là thiếu một cái gì đó ở đây ...
Keith Hoffman

1
@JosephSturtevant cái này thật đẹp
StelioK

Vâng, công việc tốt ... Đã tiết kiệm cho tôi rất nhiều việc ... Tôi không ở năm 2012 trở lên do khách hàng ... Tuy nhiên, đừng cho tôi tín dụng :) Tôi chỉ giỏi tìm kiếm câu trả lời phù hợp với tôi ... Mặc dù tôi đã thay đổi nó từ trả về null thành trả về 0 vì varchar ngu ngốc phải là một cột int với giá trị mặc định là 0 :)
CA Martin

134

Đúng :). Thử cái này:

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()Fedor Hajdu đã chỉ ra một số vấn đề .

Nó trả về true cho các chuỗi như $(là tiền tệ) ,hoặc .(cả hai đều là dấu phân cách) +-.


3
Cảm ơn câu trả lời của bạn. Tôi hy vọng rằng có một cái gì đó thanh lịch hơn.
Oliver Hanappi

4
Còn khi giá trị văn bản là số, nhưng chứa một số lớn hơn số int có thể lưu trữ thì sao?
Tom Mayfield

8
-1: Thật không may, có quá nhiều vấn đề ISNUMERICkhiến câu trả lời này có thể được sử dụng trên dữ liệu chưa được xác thực (và bạn sẽ không cần ISNUMERICkiểm tra ngay từ đầu để có dữ liệu được xác thực đúng cách). Tác giả thừa nhận sự tồn tại của những vấn đề này nhưng không giải quyết chúng.
Douglas

7
Như Joseph Mentions , người dùng SQL 2012+ nên sử dụng TRY_CONVERT/ TRY_CAST, mục đích rõ ràng là để giải quyết câu hỏi của OP. Câu trả lời này đã được viết trước khi SQL 2012 được phát hành.
Brian

3
Tôi phản đối vì ISNUMERIC ('1,1,1,1,1') = 1 và điều này không bao giờ chuyển thành int.
mischka

14

Tôi thà tạo một hàm như TryParse hoặc sử dụng TRY-CATCHkhối T-SQL để có được những gì bạn muốn.

ISNUMERIC không phải lúc nào cũng hoạt động như dự định. Mã được cung cấp trước đó sẽ không thành công nếu bạn thực hiện:

SET @text = '$'

Dấu $ có thể được chuyển đổi thành kiểu dữ liệu tiền, vì vậy ISNUMERIC()trả về true trong trường hợp đó. Nó sẽ làm tương tự cho '-' (dấu trừ), ',' (dấu phẩy) và '.' nhân vật.


3
Đúng :(. Cũng ISNUMERIC()trả về 1cho ,..
Grzegorz Gierlik

3
... ngoại trừ bạn không thể sử dụng try-catch bên trong một hàm (ít nhất là trong cơ sở dữ liệu SQL 2005 của tôi) ... liên kết
Etherman

12

Như đã được đề cập, bạn có thể gặp một số vấn đề nếu sử dụng 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

Nếu bạn muốn một chuyển đổi đáng tin cậy, bạn sẽ cần phải tự mình viết mã một chuyển đổi.

Cập nhật : Đề xuất mới của tôi là sử dụng chuyển đổi thử nghiệm trung gian FLOATđể xác thực số. Cách tiếp cận này dựa trên nhận xét của adrianm . Logic có thể được định nghĩa là một hàm có giá trị bảng nội tuyến:

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]
)

Một số bài kiểm tra:

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]

Kết quả tương tự như câu trả lời của Joseph Sturtevant , với những điểm khác biệt chính sau:

  • Logic của tôi không chấp nhận sự xuất hiện của .hoặc ,để bắt chước hành vi của INTchuyển đổi gốc . '1,234''1234.0'quay trở lại NULL.
  • Vì nó không sử dụng các biến cục bộ, nên hàm của tôi có thể được định nghĩa là một hàm có giá trị bảng nội tuyến, cho phép tối ưu hóa truy vấn tốt hơn.
  • Câu trả lời của Joseph có thể dẫn đến kết quả không chính xác do lập luận bị cắt ngắn một cách im lặng; '00000000000000001234'đánh giá 12. Việc tăng độ dài tham số sẽ dẫn đến lỗi trên các số bị tràn BIGINT, chẳng hạn như BBAN (số tài khoản ngân hàng cơ bản) '212110090000000235698741'.

Rút lại : Cách tiếp cận dưới đây không còn được khuyến khích nữa, mà chỉ để tham khảo.

Đoạn mã dưới đây hoạt động trên các số nguyên không âm. Nó kiểm tra xem chuỗi của bạn không chứa bất kỳ ký tự không phải chữ số nào, không trống và không tràn (bằng cách vượt quá giá trị tối đa cho intkiểu). Tuy nhiên, nó cũng cung cấp NULLcho các số nguyên hợp lệ có độ dài vượt quá 10 ký tự do các số không ở đầu.

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 

Nếu bạn muốn hỗ trợ bất kỳ số 0 ở đầu nào, hãy sử dụng phần bên dưới. Các CASEcâu lệnh lồng nhau , mặc dù khó sử dụng, được yêu cầu để thúc đẩy đánh giá ngắn mạch và giảm khả năng xảy ra lỗi (ví dụ: phát sinh từ việc chuyển một độ dài âm sang 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

Nếu bạn muốn hỗ trợ số nguyên dương âm với bất kỳ số 0 nào ở đầu:

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

3
Để nhấn mạnh vấn đề, hiện tượng đoản mạch không được đảm bảo , ngay cả với câu lệnh CASE.
Michael Green

1
@MichaelGreen: Tôi đã đọc bài báo đó; bản cập nhật cuối cùng cho biết: "Chủ sở hữu của mã này [?] đã đánh dấu lỗi này là đã được sửa. Từ nhận xét của họ, có vẻ như bạn được cho là có thể dựa vào thứ tự xác định của đánh giá biểu thức cho các câu lệnh CASE." Microsoft thừa nhận rằng lỗi có thể xảy ra đối với tổng hợp, nhưng không xảy ra đối với các đại lượng vô hướng.
Douglas

1
Điểm khá @Douglas. Tôi nhớ mình đã đọc trong vòng vài tháng trước một bài đăng của một trong những blogger có thương hiệu trên CASE ngắn gọn cho rằng đáng tin cậy tại thời điểm biên dịch ngay cả khi việc giải trình tự thời gian chạy được tôn vinh. Tuy nhiên, tôi không thể tìm thấy tham chiếu đó vào lúc này. Động lực của tôi là cảnh báo Cộng đồng rằng đây có thể là một vấn đề, tùy thuộc vào mức độ phiên bản / ấn bản / bản vá của họ và tôi đã sử dụng liên kết đó như một examplar, tất cả.
Michael Green

1
@MichaelGreen: Đồng ý; IMHO, đây là một thiếu sót nghiêm trọng trong SQL Server hạn chế nghiêm trọng khả năng sử dụng của nó đối với các tình huống yêu cầu xác thực dữ liệu.
Douglas

1
Tại sao bạn không sử dụng BIGINT ở giữa cuộc trò chuyện?
alex

2

Trân trọng.

Tôi đã viết một hàm vô hướng hữu ích để mô phỏng hàm TRY_CAST của SQL SERVER 2012 trong SQL Server 2008.

Bạn có thể xem nó trong liên kết tiếp theo bên dưới và chúng ta giúp nhau cải thiện nó. Hàm TRY_CAST cho SQL Server 2008 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb

Hai điểm khác biệt chính là bạn phải chuyển 3 tham số và bạn phải thực hiện thêm một CHUYỂN ĐỔI hoặc CAST rõ ràng cho trường. Tuy nhiên, nó vẫn rất hữu ích vì nó cho phép bạn trả về giá trị mặc định nếu CAST không được thực hiện chính xác.

dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)

Thí dụ:

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

Hiện tại chỉ hỗ trợ các loại dữ liệu INT, DATE, NUMERIC, BIT và FLOAT

Tôi hy vọng bạn thấy nó hữu dụng.

MÃ:

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

1
Ai đó có thể cho tôi biết tại sao các phản hồi tiêu cực?
JotaPardo

1
Tôi không hiểu họ đặt điểm tiêu cực như thế nào và thậm chí còn không nêu lý do. Bằng cách đó bạn không thể học được. Vui lòng cung cấp lại
JotaPardo

2
Bạn có thể muốn bao gồm việc triển khai trong câu trả lời của mình.
AperioOculus

1
@AperioOculus cảm ơn bạn rất nhiều vì phản hồi của bạn. Tôi đã cập nhật mã! Cám ơn bạn một lần nữa!
JotaPardo

1

Câu trả lời của Joseph chỉ ra ISNUMERIC cũng xử lý ký hiệu khoa học như '1.3e + 3' nhưng câu trả lời của anh ấy không xử lý định dạng số này.

Việc chuyển thành tiền hoặc float trước hết xử lý cả vấn đề tiền tệ và khoa học:

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

Hàm sẽ không thành công nếu số lớn hơn bigint.

Nếu bạn muốn trả về một giá trị mặc định khác, hãy để nguyên hàm này để nó là chung và thay thế giá trị null sau đó:

SELECT IsNull(dbo.TryConvertInt('nan') , 1000);

1

Tôi biết nó không đẹp nhưng nó đơn giản. Thử cái này:

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

1

Giải pháp của tôi cho vấn đề này là tạo chức năng được hiển thị bên dưới. Yêu cầu của tôi bao gồm rằng số phải là số nguyên tiêu chuẩn, không phải là SỐ LỚN và tôi cần cho phép số âm và số dương. Tôi đã không tìm thấy một trường hợp mà điều này không thành công.

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.