TSQL - Transmisikan string ke integer atau kembalikan nilai default

116

Apakah ada cara di T-SQL untuk mentransmisikan nvarchar ke int dan mengembalikan nilai default atau NULL jika konversi gagal?

Oliver Hanappi
sumber

Jawaban:

66

Jika Anda menggunakan SQL Server 2012 (atau yang lebih baru):

Gunakan fungsi TRY_CONVERT .

Jika Anda menggunakan SQL Server 2005, 2008, atau 2008 R2:

Buat fungsi yang ditentukan pengguna. Ini akan menghindari masalah yang disebutkan Fedor Hajdu berkaitan dengan mata uang, angka pecahan, dll:

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

Referensi: Saya menggunakan halaman ini secara ekstensif saat membuat solusi saya.

Joseph Sturtevant
sumber
1
Ada yang bisa membuat ini bekerja? Saya membuat fungsi seperti yang disarankan dan menemukan itu melempar kesalahan konversi menggunakan fungsi pengujian yang diusulkan. Aku membayangkan aku melewatkan sesuatu di sini ...
Keith Hoffman
1
@JosephSturtevant ini indah
StelioK
Ya kerja bagus ... Menyelamatkan saya banyak pekerjaan ... Saya tidak pada tahun 2012 atau lebih tinggi karena pelanggan ... Tapi jangan beri saya kredit :) Saya hanya pandai mencari jawaban yang berhasil saya ... Meskipun saya mengubahnya dari mengembalikan nol menjadi mengembalikan nol karena varchar bodoh harus menjadi kolom int dengan default nol :)
CA Martin
134

Iya :). Coba ini:

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()memiliki beberapa masalah yang ditunjukkan oleh Fedor Hajdu .

Ini mengembalikan nilai true untuk string seperti $(adalah mata uang), ,atau .(keduanya adalah pemisah), +dan -.

Grzegorz Gierlik
sumber
3
Terima kasih atas jawaban anda. Saya berharap ada sesuatu yang lebih elegan.
Oliver Hanappi
4
Bagaimana jika nilai teks adalah numerik, tetapi berisi angka yang lebih besar dari yang bisa disimpan kolom int?
Tom Mayfield
8
-1: Sayangnya, ada terlalu banyak masalah dengan ISNUMERICjawaban ini untuk digunakan pada data yang tidak divalidasi (dan Anda tidak perlu ISNUMERICmemeriksa terlebih dahulu untuk data yang divalidasi dengan benar). Penulis mengakui adanya masalah ini tetapi tidak menanganinya.
Douglas
7
Seperti yang disebutkan Joseph , pengguna SQL 2012+ harus menggunakan TRY_CONVERT/ TRY_CAST, yang secara eksplisit dimaksudkan untuk menyelesaikan pertanyaan OP. Jawaban ini ditulis sebelum SQL 2012 dirilis.
Brian
3
Saya tidak suka karena ISNUMERIC ('1,1,1,1,1') = 1 dan ini tidak pernah dilemparkan ke int.
mischka
14

Saya lebih suka membuat fungsi seperti TryParse atau menggunakan TRY-CATCHblok T-SQL untuk mendapatkan apa yang Anda inginkan.

ISNUMERIK tidak selalu berfungsi sebagaimana mestinya. Kode yang diberikan sebelumnya akan gagal jika Anda melakukan:

SET @text = '$'

$ sign dapat dikonversi ke tipe data uang, jadi ISNUMERIC()mengembalikan true dalam kasus itu. Ini akan melakukan hal yang sama untuk '-' (minus), ',' (koma) dan '.' karakter.

Fedor Hajdu
sumber
3
. Benar :( ISNUMERIC()kembali 1juga untuk ,dan ..
Grzegorz Gierlik
3
... kecuali Anda tidak dapat menggunakan TRY-CATCH di dalam fungsi (setidaknya tidak dalam database SQL 2005 saya) ... link
Etherman
12

Seperti yang telah disebutkan, Anda mungkin mengalami beberapa masalah jika Anda menggunakan 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

Jika Anda menginginkan konversi yang andal, Anda harus membuat kode sendiri.

Pembaruan : Rekomendasi baru saya akan menggunakan konversi uji perantara FLOATuntuk memvalidasi nomor. Pendekatan ini berdasarkan komentar adrianm . Logikanya dapat didefinisikan sebagai fungsi nilai tabel sebaris:

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

Beberapa tes:

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]

Hasilnya mirip dengan jawaban Joseph Sturtevant , dengan perbedaan utama berikut:

  • Logika saya tidak mentolerir kemunculan .atau ,untuk meniru perilaku pemeluk INTagama asli . '1,234'dan '1234.0'kembali NULL.
  • Karena tidak menggunakan variabel lokal, fungsi saya dapat didefinisikan sebagai fungsi nilai tabel sebaris, memungkinkan pengoptimalan kueri yang lebih baik.
  • Jawaban Joseph dapat menyebabkan hasil yang salah karena pemotongan argumen diam-diam; '00000000000000001234'mengevaluasi ke 12. Menambah panjang parameter akan mengakibatkan kesalahan pada angka yang melimpah BIGINT, seperti BBAN (nomor rekening bank dasar) seperti '212110090000000235698741'.

Ditarik : Pendekatan di bawah ini tidak lagi direkomendasikan, karena ditinggalkan hanya sebagai referensi.

Cuplikan di bawah ini berfungsi pada bilangan bulat non-negatif. Ia memeriksa bahwa string Anda tidak berisi karakter non-digit, tidak kosong, dan tidak meluap (dengan melebihi nilai maksimum untuk inttipe tersebut). Namun, ini juga memberikan NULLbilangan bulat valid yang panjangnya melebihi 10 karakter karena angka nol di depannya.

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 

Jika Anda ingin mendukung sejumlah nol di depan, gunakan di bawah ini. CASEPernyataan bertingkat , meskipun berat, diperlukan untuk mempromosikan evaluasi hubung singkat dan mengurangi kemungkinan kesalahan (timbul, misalnya, dari meneruskan panjang negatif ke 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

Jika Anda ingin mendukung bilangan bulat positif dan negatif dengan sejumlah nol di depan:

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
sumber
3
Untuk menekankan intinya, korsleting tidak dijamin , bahkan dengan pernyataan CASE.
Michael Green
1
@MichaelGreen: Saya telah membaca artikel itu; pembaruan terakhir menyatakan: "Pemilik kode ini [?] telah menandai bug ini sebagai telah diperbaiki. Dari komentar mereka, sepertinya Anda dapat mengandalkan urutan deterministik evaluasi ekspresi untuk pernyataan CASE." Microsoft mengakui bahwa bug mungkin terjadi untuk agregat, tetapi tidak untuk skalar.
Douglas
1
Poin adil @Douglas. Saya ingat pernah membaca dalam beberapa bulan terakhir sebuah posting oleh salah satu blogger terkenal tentang hubungan arus pendek CASE yang dapat diandalkan pada waktu kompilasi bahkan jika urutan run-time dihormati. Namun, saya tidak dapat menemukan referensi itu saat ini. Motivasi saya adalah untuk memperingatkan Komunitas bahwa ini mungkin menjadi masalah, tergantung pada versi / edisi / tingkat tambalan mereka dan saya menggunakan tautan itu sebagai contoh, itu saja.
Michael Green
1
@MichaelGreen: Setuju; IMHO, ini adalah kekurangan serius dalam SQL Server yang sangat membatasi kegunaannya untuk skenario yang memerlukan validasi data.
Douglas
1
Mengapa Anda tidak menggunakan BIGINT di tengah percakapan?
alex
2

Salam.

Saya menulis fungsi skalar yang berguna untuk mensimulasikan fungsi TRY_CAST dari SQL SERVER 2012 di SQL Server 2008.

Anda dapat melihatnya di tautan berikutnya di bawah dan kami saling membantu untuk memperbaikinya. Fungsi TRY_CAST untuk SQL Server 2008 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb

Dua perbedaan utama adalah Anda harus meneruskan 3 parameter dan Anda juga harus melakukan CONVERT atau CAST eksplisit ke kolom tersebut. Namun, ini masih sangat berguna karena memungkinkan Anda mengembalikan nilai default jika CAST tidak dijalankan dengan benar.

dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)

Contoh:

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

Untuk saat ini hanya mendukung tipe data INT, DATE, NUMERIC, BIT dan FLOAT

Saya harap Anda merasa berguna.

KODE:

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
sumber
1
Bisakah seseorang memberi tahu saya mengapa umpan balik negatif?
JotaPardo
1
Saya tidak mengerti bagaimana mereka menempatkan poin negatif dan bahkan tidak menyebutkan alasannya. Dengan begitu Anda tidak bisa belajar. Tolong umpan balik
JotaPardo
2
Anda mungkin ingin memasukkan implementasi dalam jawaban Anda.
AperioOculus
1
@AperioOculus terima kasih banyak atas tanggapan Anda. Saya memperbarui kodenya! Terima kasih lagi!
JotaPardo
1

Jawaban Joseph menunjukkan ISNUMERIC juga menangani notasi ilmiah seperti '1.3e + 3' tetapi jawabannya tidak menangani format angka ini.

Mentransmisikan uang atau float terlebih dahulu menangani masalah mata uang dan ilmiah:

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

Fungsi akan gagal jika angkanya lebih besar dari bigint.

Jika Anda ingin mengembalikan nilai default yang berbeda, biarkan fungsi ini jadi generik dan ganti null setelahnya:

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

Saya tahu ini tidak cantik tapi sederhana. Coba ini:

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
Ramblen Man
sumber
1

Solusi saya untuk masalah ini adalah membuat fungsi yang ditunjukkan di bawah ini. Persyaratan saya termasuk bahwa nomor tersebut harus berupa bilangan bulat standar, bukan BIGINT, dan saya harus mengizinkan bilangan negatif dan bilangan positif. Saya belum menemukan keadaan di mana ini gagal.

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
sumber