web-dev-qa-db-ja.com

文字列クエリと共にストアドプロシージャ内に日時パラメーターを含める方法は?

私のストアドプロシージャは次のとおりです。

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

この文字列クエリを実行すると、次のエラーが発生します。

"日付や時刻を文字列から変換するときに変換に失敗しました。"

誰でも私がこれを整理するのを手伝ってください...

5
Harun

基本的な問題は、TSQLが暗黙的に日付時刻(または整数または浮動小数点)を文字データ型に変換できないことです。実際には逆方向に進み、データ型の優先規則に基づいて文字データを暗黙的に日時(整数/浮動小数点値)に変換しようとします。ちなみに、これはエラーメッセージが伝えていることです。文字列を日時値に変換できませんでした。 explicitly値を文字列にマッシュアップするように要求する必要があります。

この例では、期待値を理解しやすい整数を使用したプリンシパルを示しています。

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

暗黙的な変換の値は、@ stringintが最初に整数に変換され、次に+が数値の加算として処理され、結果が8になることを示しています。@ intintを文字データ型に明示的に変換すると、+記号が35の文字列を返しました

implicit_conversion explicit_conversion
------------------- -------------------
8                   35

提供された問題を解決するには、日時の値を文字型に明示的にキャストして、クエリ文字列を期待どおりに連結できるようにする必要があります。

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

しかし、上記のように、SQLインジェクションがその理由の1つであるという理由で、実際にはそれを実行したくありません。また、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'

パラメータ使用のデモ

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

結果

col1
3
11
billinkc

このエラーとSQLインジェクションの脆弱性を回避するには、クエリを次のように書き直す必要があります。

@FromDate datetime,
@ToDate datetime

--Select query
 SELECT [col1] 
   FROM [Table1]              
  WHERE [col2] BETWEEN @FromDate AND @ToDate
3
datagod