如何在存储过程中包含日期时间参数以及字符串查询?

Har*_*run 5 sql-server stored-procedures dynamic-sql

我的存储过程如下,

    -- Add the parameters for the stored procedure here 
   @FromDate datetime,
   @ToDate datetime

       --Select query
      DECLARE @query nvarchar(max)

      set @query='SELECT [col1] 
                  FROM [Table1]              
                  WHERE ([col2] BETWEEN '''+@FromDate+''' AND'''+@ToDate+''')'

       execute sp_executesql @query
Run Code Online (Sandbox Code Playgroud)

执行此字符串查询会导致以下错误,

"从字符串转换日期和/或时间时转换失败。 "

哪位大神帮我解决一下...

bil*_*nkc 11

根本问题是 TSQL 不能将日期时间(或整数或浮点数)隐式转换为字符数据类型。它实际上是倒退的,它尝试根据数据类型优先规则将字符数据隐式转换为日期时间(整数/浮点值)。这是您的错误消息顺便告诉您的,它无法将字符串转换为日期时间值。您必须明确要求它将值混搭为字符串。

这个例子用整数演示了主体,因为它们更容易理解预期值。

DECLARE 
    @stringInt nvarchar(3)
,   @intint int

SELECT
    @stringInt = N'3'
,   @intint = 5

SELECT 
    @stringInt + @intint AS implicit_conversion
,   @stringInt + CAST(@intint AS nvarchar(5)) AS explicit_conversion
Run Code Online (Sandbox Code Playgroud)

隐式转换的值显示 @stringint 首先转换为整数,然后 + 被视为数字加法,结果为 8。@intint 显式转换为字符数据类型导致 + 符号被视为与返回 35 的字符串

implicit_conversion explicit_conversion
------------------- -------------------
8                   35
Run Code Online (Sandbox Code Playgroud)

要解决您提供的问题,您必须将日期时间值显式转换为字符类型,以便可以按预期连接查询字符串。

set @query='SELECT [col1] 
FROM [Table1]              
WHERE ([col2] BETWEEN ''' + CONVERT(nvarchar(24), @FromDate, 121) +''' AND'''+ CONVERT(nvarchar(24), @ToDate, 121) +''')'
Run Code Online (Sandbox Code Playgroud)

但是如上所述,出于多种原因,您确实不想这样做,SQL 注入就是其中之一。当您在 TSQL 中对查询字符串进行切片和切块时,它还会使您的维护变得更加困难。

更好的方法是参数化您的查询并使用sp_executesql的强大功能。sp_executesql 参数的一个好处是您不必使用提供的所有参数。根据您真正想做的事情,这可能是有益的。

示例表和数据

CREATE TABLE 
dbo.table1
(col1 int, col2 datetime)

INSERT INTO
    dbo.table1
SELECT
3, '2009-04-06'
UNION ALL SELECT
1, '2001-09-11'
Run Code Online (Sandbox Code Playgroud)

使用参数演示

DECLARE
    @FromDate datetime,
    @ToDate datetime

SELECT    
    @FromDate = '2005-03-17'
,   @ToDate = current_timestamp

DECLARE
    @query nvarchar(max)
SET @query = N'    
SELECT [col1] 
FROM [Table1]              
WHERE ([col2] BETWEEN @start AND @end)'

-- gratuitous use of parameter assignment here
-- could just as easily used @FromDate and @ToDate
-- in the @query and the parameter list
EXECUTE sp_executesql 
    @query
,   N'@start datetime, @end datetime'
,   @start = @FromDate
,   @end = @ToDate
Run Code Online (Sandbox Code Playgroud)

结果

col1
3
Run Code Online (Sandbox Code Playgroud)