关于 sp_executesql 的错误
A bug about sp_executesql
CREATE TABLE [dbo].[MyTable]([id] [int] IDENTITY(1,1) NOT NULL,
[MyNumericColumn] [decimal](18, 2) NOT NULL,[insert_time] [datetime] NOT NULL CONSTRAINT [DF__MyTable__insert___0F975522] DEFAULT (getdate()),
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED
([id] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]
GO
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values ( @P0, getdate()),( @P1, getdate())',N'@P0 decimal(38,1),@P1 decimal(38,2)',10.0,0.99
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values ( @P0, getdate()),( @P1, getdate()),( @P2, getdate()) ',N'@P0 decimal(38,2),@P1 decimal(38,1),@P2 decimal(38,3)',0.99,10.00,0.999
select * from [MyTable];
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values( @P0, getdate()),( @P1, getdate()), ( @P2, getdate())',N'@P0 decimal(38,2),@P1 decimal(38,2),@P2 decimal(38,3)',0.99,10.00,0.999
select * from [MyTable];
while @P0 decimal(38,2),@P1 decimal(38,1),@P2 decimal(38,3)为什么所有数据都是decimal(38,1)..
这种舍入行为是预期的,与 sp_executesql
无关。在组合不同类型的表达式时,行构造函数的行为类似于 UNION
和 UNION ALL
。 the documentation.
中描述了组合不同精度和小数位数的小数类型的规则
使用带有局部变量的 UNION ALL SELECT
查询考虑此重现:
DECLARE
@P0 decimal(38,1) = 10.0
,@P1 decimal(38,2) = 0.99
SELECT @P0
UNION ALL
SELECT @P1;
结果:
+------------------+
| 10.0 |
| 1.0 |
+------------------+
使用 sp_describe_first_reesult_set
执行查询显示结果类型是 decimal(38,1)
而不是人们可能预期的 decimal(38,2)
,导致 0.99 值四舍五入为 1.00。这样做是为了避免在指定较大值时截断整数部分。
EXEC sp_describe_first_result_set N'
DECLARE
@P0 decimal(38,1) = 10.0
,@P1 decimal(38,2) = 0.99
SELECT @P0
UNION ALL
SELECT @P1;
';
如果我们将小数位数减小到 37,则结果类型为 decimal(38,2)
,并且这些值不会四舍五入。但是,最佳做法是声明参数类型以匹配目标 table 的参数类型,并理解精度更高的值将被四舍五入:
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values ( @P0, getdate()), ( @P1, getdate())',N'@P0 decimal(38,2),@P1 decimal(38,2)',10.0,0.99
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values ( @P0, getdate()), ( @P1, getdate()), ( @P2, getdate()) ',N'@P0 decimal(38,2),@P1 decimal(38,2),@P2 decimal(38,2)',0.99,10.00,0.999
select * from [MyTable];
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values( @P0, getdate()),( @P1, getdate()), ( @P2, getdate())',N'@P0 decimal(38,2),@P1 decimal(38,2),@P2 decimal(38,2)',0.99,10.00,0.999
select * from [MyTable];
CREATE TABLE [dbo].[MyTable]([id] [int] IDENTITY(1,1) NOT NULL,
[MyNumericColumn] [decimal](18, 2) NOT NULL,[insert_time] [datetime] NOT NULL CONSTRAINT [DF__MyTable__insert___0F975522] DEFAULT (getdate()),
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED
([id] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]
GO
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values ( @P0, getdate()),( @P1, getdate())',N'@P0 decimal(38,1),@P1 decimal(38,2)',10.0,0.99
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values ( @P0, getdate()),( @P1, getdate()),( @P2, getdate()) ',N'@P0 decimal(38,2),@P1 decimal(38,1),@P2 decimal(38,3)',0.99,10.00,0.999
select * from [MyTable];
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values( @P0, getdate()),( @P1, getdate()), ( @P2, getdate())',N'@P0 decimal(38,2),@P1 decimal(38,2),@P2 decimal(38,3)',0.99,10.00,0.999
select * from [MyTable];
while @P0 decimal(38,2),@P1 decimal(38,1),@P2 decimal(38,3)为什么所有数据都是decimal(38,1)..
这种舍入行为是预期的,与 sp_executesql
无关。在组合不同类型的表达式时,行构造函数的行为类似于 UNION
和 UNION ALL
。 the documentation.
使用带有局部变量的 UNION ALL SELECT
查询考虑此重现:
DECLARE
@P0 decimal(38,1) = 10.0
,@P1 decimal(38,2) = 0.99
SELECT @P0
UNION ALL
SELECT @P1;
结果:
+------------------+
| 10.0 |
| 1.0 |
+------------------+
使用 sp_describe_first_reesult_set
执行查询显示结果类型是 decimal(38,1)
而不是人们可能预期的 decimal(38,2)
,导致 0.99 值四舍五入为 1.00。这样做是为了避免在指定较大值时截断整数部分。
EXEC sp_describe_first_result_set N'
DECLARE
@P0 decimal(38,1) = 10.0
,@P1 decimal(38,2) = 0.99
SELECT @P0
UNION ALL
SELECT @P1;
';
如果我们将小数位数减小到 37,则结果类型为 decimal(38,2)
,并且这些值不会四舍五入。但是,最佳做法是声明参数类型以匹配目标 table 的参数类型,并理解精度更高的值将被四舍五入:
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values ( @P0, getdate()), ( @P1, getdate())',N'@P0 decimal(38,2),@P1 decimal(38,2)',10.0,0.99
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values ( @P0, getdate()), ( @P1, getdate()), ( @P2, getdate()) ',N'@P0 decimal(38,2),@P1 decimal(38,2),@P2 decimal(38,2)',0.99,10.00,0.999
select * from [MyTable];
exec sp_executesql N'insert into MyTable ( MyNumericColumn, insert_time ) values( @P0, getdate()),( @P1, getdate()), ( @P2, getdate())',N'@P0 decimal(38,2),@P1 decimal(38,2),@P2 decimal(38,2)',0.99,10.00,0.999
select * from [MyTable];