none
数据类型的疑问, 有些没搞懂 RRS feed

  • 问题

  • SET NOCOUNT ON;
    GO
    CREATE TABLE #tb( id int );
    INSERT #tb VALUES( 1 );
    go
    
    SET STATISTICS PROFILE ON;
    GO
    DECLARE @a int = 1;
    SELECT
    	1.,					-- [Expr1004]=(1.)
    	1.0,					-- [Expr1005]=(1.0)
    	FLOOR( 1.1 * 1 ),			-- [Expr1006]=(1.)
    	FLOOR( 1.1 * @a ),			-- [Expr1007]=floor((1.1)*CONVERT_IMPLICIT(numeric(10,0),[@a],0))
    	CONVERT( numeric(10, 0), 1),		-- [Expr1008]=(1.)
    	CONVERT( decimal(10, 0), 1)		-- [Expr1009]=(1.)
    FROM #tb
    /*--
      |--Compute Scalar(DEFINE:([Expr1004]=(1.), [Expr1005]=(1.0), [Expr1006]=(1.), [Expr1007]=floor((1.1)*CONVERT_IMPLICIT(numeric(10,0),[@a],0)), [Expr1008]=(1.), [Expr1009]=(1.)))
           |--Table Scan(OBJECT:([tempdb].[dbo].[#tb]))--+ACo-/
    --*/
    GO
    SET STATISTICS PROFILE OFF;
    GO
    
    PRINT '1.';
    GO
    SELECT TOP( 1. ) * FROM #tb;
    GO
    
    PRINT '1.0';
    GO
    SELECT TOP( 1.0 ) * FROM #tb;
    /*--
    消息 1060,级别 15,状态 1,第 1 行
    为 TOP 或 FETCH 子句行计数参数提供的行数必须是整数。
    --*/
    GO
    
    PRINT 'FLOOR( 1.1 * 1 )';
    GO
    SELECT TOP( FLOOR( 1.1 * 1 ) ) * FROM #tb;
    GO
    
    PRINT 'FLOOR( 1.1 * @a )';
    GO
    DECLARE @a int = 1;
    SELECT TOP( FLOOR( 1.1 * @a ) ) * FROM #tb;
    /*--
    消息 1060,级别 15,状态 1,第 2 行
    为 TOP 或 FETCH 子句行计数参数提供的行数必须是整数。
    --*/
    GO
    
    PRINT 'CONVERT( numeric(10, 0), 1)';
    GO
    SELECT TOP( CONVERT( numeric(10, 0), 1) ) * FROM #tb;
    GO
    
    PRINT 'CONVERT( decimal(10, 0), 1)';
    GO
    SELECT TOP( CONVERT( decimal(10, 0), 1) ) * FROM #tb;
    /*--
    消息 1060,级别 15,状态 1,第 1 行
    为 TOP 或 FETCH 子句行计数参数提供的行数必须是整数。
    --*/
    GO
    
    DROP TABLE #tb;
    

    在这个查询中, 有三个查询错误

    第一个错误, 差别在于小数点后有没有0, 无0表示小数位为0, 有0表示小数位为1

       这个错误似乎说明, 如果小数位为0的时候,  TOP 是可以成功的

    但第三个错误(最后一个查询)的小数位也是0, 同样失败, 难道 cecimal 和  numeric 有差别 (从联机帮助上没看到有任何差别的说明)

        暂时接受, 只有 numeric 类型, 0位小数才能用于 top

    可是第二个错误又和上面的结论冲突了, 这个表达式的结果类型也是 numeric, 而且是 0 位小数, 为啥又错了呢

    到了这里就彻底郁闷了, 这个数据类型到底是怎么评估和控制的呢?

    另外, 针对变量, 就算对最终结果强制加一个数据类型转换也出错

    DECLARE @a int = 1;
    SELECT TOP( CONVERT( numeric(10, 0), FLOOR( 1.1 * @a )) ) * FROM #tb;

    但是转换为 int 就没有问题

    DECLARE @a int = 1;
    SELECT TOP( CONVERT( int, FLOOR( 1.1 * @a )) ) * FROM #tb;

    2014年6月12日 8:50

全部回复

  • Hi,

    从 numeric 向 float 或 real , int 转换会导致精度损失。从 int、smallint、tinyint、float、real、money 或 smallmoney 向 decimal 或 numeric 转换会导致溢出.就像你说的:

    DECLARE @a int = 1; SELECT CONVERT( numeric(10, 0), FLOOR( 1.1 * @a ))

    SELECT CONVERT( int, FLOOR( 1.1 * @a ))

    都是转换ok的, 但是当我们加上TOP() 的时候, 第一个select 就会出现错误,那是因为top

    ()的参数必须是int类型,即使top(1.0) 也会报错, 那是因为sql会自动解析它为numeric类型,而不会将他的值转换为int. 所以我们应该在使用TOP之前转换为int.

    2014年6月13日 7:02
    版主
  • Hi,

    从 numeric 向 float 或 real , int 转换会导致精度损失。从 int、smallint、tinyint、float、real、money 或 smallmoney 向 decimal 或 numeric 转换会导致溢出.就像你说的:

    DECLARE @a int = 1; SELECT CONVERT( numeric(10, 0), FLOOR( 1.1 * @a ))

    SELECT CONVERT( int, FLOOR( 1.1 * @a ))

    都是转换ok的, 但是当我们加上TOP() 的时候, 第一个select 就会出现错误,那是因为top

    ()的参数必须是int类型,即使top(1.0) 也会报错, 那是因为sql会自动解析它为numeric类型,而不会将他的值转换为int. 所以我们应该在使用TOP之前转换为int.

    你没有仔细看吧? 这个不会出错啊, 这个也是 numeric 嘛

    SELECT TOP( 1. ) * FROM #tb;

    这个也不会出错, 这个是显式转换为 numeric 的, 按照你说的, 它至少不会被自动转换为 intSELECT TOP( CONVERT( numeric(10, 0), 1) ) * FROM #tb;

    2014年6月13日 7:26
  • 如果是常量表达式折叠, 那么按照脚本前面 SET STATISTICS PROFILE ON 后面的那句的测试, 除了变量和 1.0 的那句, 其他的语句执行的时候都是用的 (1.) 这个值, 那么应该只会有两句出错, 但实际上出错的是 3 句
    2014年6月13日 7:31