web-dev-qa-db-fra.com

Comment inclure un paramètre datetime dans une procédure stockée avec une requête de chaîne?

Ma procédure stockée est la suivante,

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

L'exécution de cette requête de chaîne entraîne l'erreur suivante,

"La conversion a échoué lors de la conversion de la date et/ou de l'heure à partir d'une chaîne de caractères."

Quelqu'un, s'il vous plaît, aidez-moi à résoudre ce problème ...

5
Harun

Le problème fondamental est que TSQL ne peut pas implicitement convertir datetime (ou entier ou virgule flottante) en types de données de caractères. Il recule en fait, il tente de convertir implicitement les données de caractère en datetime (valeur int/virgule flottante) en fonction des règles de priorité des types de données. C'est ce que votre message d'erreur vous dit en passant, il n'a pas pu convertir les chaînes en valeurs datetime. Vous devez explicitement lui demander d'écraser les valeurs dans des chaînes de caractères.

Cet exemple illustre le principal avec des entiers car ils sont plus faciles à comprendre les valeurs attendues.

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

La valeur de la conversion implicite montre que @stringint est d'abord converti en entier, puis le + est traité comme une addition numérique et résulte en 8. La conversion explicite de @intint en un type de données de caractères entraîne le signe + traité comme une concaténation avec le chaîne retournée de 35

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

Pour résoudre votre problème fourni, vous devez explicitement convertir vos valeurs datetime en un type de caractère afin que la chaîne de requête puisse être concaténée comme prévu.

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

Mais comme indiqué ci-dessus, vous ne voulez vraiment pas le faire pour un certain nombre de raisons, l'injection SQL étant l'une d'entre elles. Cela rend également votre maintenance beaucoup plus difficile lorsque vous découpez et découpez une chaîne de requête en TSQL.

Une meilleure approche consiste à paramétrer votre requête et à utiliser la puissance de sp_executesql . Une bonne chose à propos du paramètre sp_executesql est que vous n'avez pas à utiliser tous les paramètres fournis. Selon ce que vous essayez vraiment de faire, cela peut être bénéfique.

Exemple de tableau et de données

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

INSERT INTO
    dbo.table1
SELECT
3, '2009-04-06'
UNION ALL SELECT
1, '2001-09-11'

Démonstration de l'utilisation des paramètres

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

Résultats

col1
3
11
billinkc

Pour éviter cette erreur et les vulnérabilités de SQL Injection, vous devez réécrire votre requête comme suit:

@FromDate datetime,
@ToDate datetime

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