TSQL - ส่งสตริงเป็นจำนวนเต็มหรือส่งคืนค่าเริ่มต้น


116

มีวิธีใน T-SQL ในการแคสต์ nvarchar เป็น int และส่งคืนค่าเริ่มต้นหรือ NULL หากการแปลงล้มเหลว?

คำตอบ:


66

หากคุณใช้ SQL Server 2012 (หรือใหม่กว่า):

ใช้ฟังก์ชั่น TRY_CONVERT

ถ้าคุณใช้ SQL Server 2005, 2008 หรือ 2008 R2:

สร้างฟังก์ชันที่ผู้ใช้กำหนด สิ่งนี้จะหลีกเลี่ยงปัญหาที่Fedor Hajdu กล่าวถึงเกี่ยวกับสกุลเงินตัวเลขเศษส่วน ฯลฯ :

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

ข้อมูลอ้างอิง:ฉันใช้หน้านี้อย่างกว้างขวางเมื่อสร้างโซลูชันของฉัน


1
ใครได้รับสิ่งนี้ไปทำงาน? ฉันสร้างฟังก์ชันตามที่แนะนำและพบว่ามีข้อผิดพลาดในการแปลงโดยใช้ฟังก์ชันทดสอบที่เสนอ ฉันคิดว่าฉันพลาดอะไรบางอย่างที่นี่ ...
Keith Hoffman

1
@JosephSturtevant นี่สวย
StelioK

ใช่งานดี ... ช่วยให้ฉันมีงานทำมากมาย ... ฉันไม่ได้อยู่ในปี 2012 หรือสูงกว่าเนื่องจากลูกค้า ... อย่าให้เครดิตฉัน :) ฉันเก่งในการค้นหาคำตอบที่ได้ผลเท่านั้น ฉัน ... แม้ว่าฉันจะเปลี่ยนจากการคืนค่าว่างเป็นศูนย์เนื่องจาก varchar โง่ควรเป็นคอลัมน์ int โดยมีค่าเริ่มต้นเป็นศูนย์ :)
CA Martin

134

ครับ :). ลองสิ่งนี้:

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()มีไม่กี่ประเด็นชี้โดยชาเลีย Hajdu

มันกลับเป็นจริงสำหรับสตริงเช่น$(สกุลเงิน) ,หรือ.(ทั้งสองเป็นตัวคั่น) และ+-


3
ขอบคุณสำหรับคำตอบ. ฉันหวังว่าจะมีอะไรที่สง่างามกว่านี้
Oliver Hanappi

4
จะเกิดอะไรขึ้นเมื่อค่าข้อความเป็นตัวเลข แต่มีจำนวนมากกว่าคอลัมน์ int ที่สามารถจัดเก็บได้
Tom Mayfield

8
-1: น่าเสียดายที่มีปัญหามากเกินไปISNUMERICสำหรับคำตอบนี้ที่จะใช้กับข้อมูลที่ไม่ได้รับการISNUMERICตรวจสอบ(และคุณไม่จำเป็นต้องตรวจสอบตั้งแต่แรกสำหรับข้อมูลที่ได้รับการตรวจสอบอย่างถูกต้อง) ผู้เขียนยอมรับว่ามีปัญหาเหล่านี้ แต่ไม่ได้กล่าวถึงประเด็นเหล่านี้
ดักลาส

7
ตามที่โจเซฟกล่าวถึงผู้ใช้ SQL 2012+ ควรใช้TRY_CONVERT/ TRY_CASTซึ่งมีจุดประสงค์เพื่อแก้ไขคำถามของ OP อย่างชัดเจน คำตอบนี้เขียนขึ้นก่อนที่ SQL 2012 จะเผยแพร่
Brian

3
ฉันลงคะแนนเนื่องจาก ISNUMERIC ('1,1,1,1,1') = 1 และสิ่งนี้ไม่เคยส่งเป็น int
mischka

14

ฉันอยากจะสร้างฟังก์ชันเช่น TryParse หรือใช้TRY-CATCHบล็อกT-SQL เพื่อให้ได้สิ่งที่คุณต้องการ

ISNUMERIC ไม่ทำงานตามที่ตั้งใจไว้เสมอไป รหัสที่ให้ไว้ก่อนหน้านี้จะล้มเหลวหากคุณทำ:

SET @text = '$'

เครื่องหมาย $ สามารถแปลงเป็นประเภทข้อมูลเงินได้ดังนั้นจึงISNUMERIC()คืนค่าจริงในกรณีนั้น มันจะทำเช่นเดียวกันสำหรับ '-' (ลบ), ',' (ลูกน้ำ) และ '.' ตัวละคร


3
. ทรู :( ISNUMERIC()ผลตอบแทน1ยัง,และ..
Grzegorz Gierlik

3
... ยกเว้นคุณไม่สามารถใช้ TRY-CATCH ภายในฟังก์ชันได้ (อย่างน้อยก็ไม่ใช่ในฐานข้อมูล SQL 2005 ของฉัน) ... ลิงค์
Etherman

12

ดังที่ได้กล่าวไปแล้วคุณอาจพบปัญหาหลายประการหากคุณใช้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

หากคุณต้องการการแปลงที่เชื่อถือได้คุณจะต้องเขียนโค้ดด้วยตัวเอง

อัปเดต : คำแนะนำใหม่ของฉันคือการใช้การแปลงการทดสอบตัวกลางFLOATเพื่อตรวจสอบหมายเลข วิธีการนี้จะขึ้นอยู่กับความคิดเห็นของ adrianm ตรรกะสามารถกำหนดเป็นฟังก์ชันค่าตารางแบบอินไลน์ได้:

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

การทดสอบบางอย่าง:

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]

ผลลัพธ์จะคล้ายกับ คำตอบของ Joseph Sturtevantโดยมีข้อแตกต่างหลัก ๆ ดังนี้

  • ตรรกะของฉันไม่ยอมให้เกิดเหตุการณ์.หรือ,เพื่อเลียนแบบพฤติกรรมของINTการแปลงเนทีฟ '1,234'และ'1234.0'NULLผลตอบแทน
  • เนื่องจากไม่ได้ใช้ตัวแปรภายในฟังก์ชันของฉันจึงสามารถกำหนดเป็นฟังก์ชันที่มีมูลค่าตารางแบบอินไลน์ทำให้สามารถเพิ่มประสิทธิภาพการสืบค้นได้ดีขึ้น
  • คำตอบของโจเซฟอาจนำไปสู่ผลลัพธ์ที่ไม่ถูกต้องเนื่องจากการตัดทอนข้อโต้แย้งอย่างเงียบ ๆ ประเมิน'00000000000000001234' 12เพิ่มความยาวพารามิเตอร์จะส่งผลให้เกิดข้อผิดพลาดเกี่ยวกับตัวเลขที่ล้นBIGINTเช่นBBANs (หมายเลขบัญชีธนาคารพื้นฐาน) '212110090000000235698741'เช่น

ถอน : แนวทางด้านล่างไม่แนะนำอีกต่อไปเนื่องจากเหลือไว้เพียงเพื่อการอ้างอิง

ตัวอย่างด้านล่างใช้กับจำนวนเต็มที่ไม่ใช่จำนวนเต็มลบ ตรวจสอบว่าสตริงของคุณไม่มีอักขระที่ไม่ใช่ตัวเลขไม่ว่างเปล่าและไม่ล้น (โดยเกินค่าสูงสุดสำหรับintประเภท) อย่างไรก็ตามยังให้NULLสำหรับจำนวนเต็มที่ถูกต้องซึ่งมีความยาวเกิน 10 อักขระเนื่องจากเลขศูนย์นำหน้า

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 

หากคุณต้องการสนับสนุนเลขศูนย์นำหน้าให้ใช้ด้านล่าง CASEคำสั่งที่ซ้อนกันแม้ว่าจะดูเทอะทะ แต่ก็จำเป็นเพื่อส่งเสริมการประเมินการลัดวงจรและลดโอกาสที่จะเกิดข้อผิดพลาด (เช่นจากการส่งผ่านความยาวเชิงลบไปจนถึง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

หากคุณต้องการสนับสนุนจำนวนเต็มบวกและลบด้วยเลขศูนย์นำหน้าจำนวนเท่าใดก็ได้:

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
เพื่อเน้นประเด็นนี้ไม่รับประกันการลัดวงจรแม้จะมีคำสั่ง CASE ก็ตาม
Michael Green

1
@ MichaelGreen: ฉันอ่านบทความนั้น; สถานะการอัปเดตล่าสุด: "เจ้าของรหัสนี้ [?] ได้ทำเครื่องหมายจุดบกพร่องนี้ว่าแก้ไขแล้วจากความคิดเห็นของพวกเขาดูเหมือนว่าคุณควรจะสามารถพึ่งพาลำดับการประเมินนิพจน์ที่กำหนดได้สำหรับคำสั่ง CASE" Microsoft ยอมรับว่าข้อบกพร่องอาจเกิดขึ้นสำหรับการรวม แต่ไม่ใช่สำหรับสเกลาร์
ดักลาส

1
จุดยุติธรรม @ ดักลาส ฉันจำได้ว่าได้อ่านโพสต์ของหนึ่งในบล็อกเกอร์ชื่อแบรนด์เกี่ยวกับการลัดวงจรของ CASE ในช่วงสองสามเดือนที่ผ่านมาซึ่งเชื่อถือได้ในเวลารวบรวมแม้ว่าจะมีการจัดลำดับเวลาทำงานก็ตาม อย่างไรก็ตามฉันไม่พบข้อมูลอ้างอิงนั้นในขณะนี้ แรงจูงใจของฉันคือเตือนชุมชนว่านี่อาจเป็นปัญหาขึ้นอยู่กับเวอร์ชัน / รุ่น / ระดับแพตช์ของพวกเขาและฉันใช้ลิงก์นั้นเป็นจุดตรวจสอบทั้งหมด
Michael Green

1
@MichaelGreen: เห็นด้วย; IMHO นี่คือ / เป็นข้อบกพร่องร้ายแรงใน SQL Server ที่ จำกัด การใช้งานอย่างรุนแรงสำหรับสถานการณ์ที่ต้องมีการตรวจสอบข้อมูล
Douglas

1
ทำไมคุณไม่ใช้ BIGINT ในระหว่างการสนทนา?
alex

2

ความนับถือ.

ฉันเขียนฟังก์ชันสเกลาร์ที่มีประโยชน์เพื่อจำลองฟังก์ชัน TRY_CAST ของ SQL SERVER 2012 ใน SQL Server 2008

คุณสามารถดูได้ในลิงค์ถัดไปด้านล่างและเราจะช่วยกันปรับปรุง TRY_CAST ฟังก์ชันสำหรับ SQL Server 2008 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb

ความแตกต่างหลักสองประการคือคุณต้องส่งพารามิเตอร์ 3 ตัวและคุณต้องทำการแปลงหรือแคสต์อย่างชัดเจนไปยังฟิลด์เพิ่มเติม อย่างไรก็ตามยังคงมีประโยชน์อย่างมากเนื่องจากช่วยให้คุณสามารถคืนค่าเริ่มต้นได้หาก CAST ทำงานไม่ถูกต้อง

dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)

ตัวอย่าง:

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

ตอนนี้รองรับเฉพาะประเภทข้อมูลINT, DATE, NUMERIC, BIT และ FLOAT

ฉันหวังว่าคุณพบว่ามีประโยชน์.

รหัส:

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
ใครช่วยบอกหน่อยได้ไหมว่าทำไมข้อเสนอแนะเชิงลบ
JotaPardo

1
ฉันไม่เข้าใจว่าพวกเขาใส่คะแนนเชิงลบอย่างไรและไม่ได้พูดถึงเหตุผล วิธีที่คุณไม่สามารถเรียนรู้ กรุณาย้อนกลับ
JotaPardo

2
คุณอาจต้องการรวมการใช้งานไว้ในคำตอบของคุณ
AperioOculus

1
@AperioOculus ขอบคุณมากสำหรับคำติชม ฉันอัปเดตรหัสแล้ว! ขอขอบคุณอีกครั้ง!
JotaPardo

1

คำตอบของโจเซฟชี้ให้เห็นว่า ISNUMERIC ยังจัดการสัญกรณ์ทางวิทยาศาสตร์เช่น '1.3e + 3' แต่คำตอบของเขาไม่ได้จัดการกับรูปแบบของตัวเลขนี้

การโยนเงินหรือลอยตัวก่อนจัดการทั้งเรื่องสกุลเงินและประเด็นทางวิทยาศาสตร์:

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

ฟังก์ชั่นจะล้มเหลวหากจำนวนมากกว่าค่า bigint

หากคุณต้องการส่งคืนค่าเริ่มต้นที่แตกต่างออกไปให้ปล่อยให้ฟังก์ชันนี้เป็นค่าทั่วไปและแทนที่ค่าว่างในภายหลัง:

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

1

ฉันรู้ว่ามันไม่สวย แต่มันเรียบง่าย ลองสิ่งนี้:

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

วิธีแก้ปัญหาของฉันคือการสร้างฟังก์ชันที่แสดงด้านล่าง ข้อกำหนดของฉันรวมถึงว่าจำนวนนั้นต้องเป็นจำนวนเต็มมาตรฐานไม่ใช่ BIGINT และฉันต้องอนุญาตให้ใช้จำนวนลบและจำนวนบวก ฉันไม่พบสถานการณ์ที่ล้มเหลว

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.