Вычитание в динамическом SQL - PullRequest
0 голосов
/ 08 ноября 2018

Я обновлял хранимую процедуру, чтобы изменить время отчета на часовой пояс клиентов. Хранимая процедура состоит из динамического SQL, который содержит параметр time @timeoffset типа данных smallint.

  DECLARE @sql VARCHAR(MAX) = 
  N'SELECT DISTINCT cl.ClientId, 
  CASE WHEN DATEADD(HOUR,'+CONVERT(CHAR(2),@timeoffset)+', x.changedate) >= '''+ CONVERT(varchar, @start_date) +''' 
  AND DATEADD(HOUR,'+CONVERT(CHAR(2),@timeoffset)+', x.changedate) < '''+ CONVERT(varchar, @end_date_plus1) +''' 
  THEN DATEADD(HOUR,'+CONVERT(CHAR(2),@timeoffset)+',x.changedate)' 

Чтобы изменить время на часовой пояс клиентов, мне нужно вычесть @timeoffset. Отрицательный результат не меняет результат. Попытка добавить (-) перед преобразованием приведет к ошибке, так как оператор вычитания недопустим для varchar. Пишу это без конвертации возникает ошибка 'Ошибка преобразования при преобразовании значения nvarchar в тип данных smallint.

Может кто-нибудь помочь мне с этим, пожалуйста? Благодарю.

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Я заметил несколько проблем в коде, который вы разместили:

  1. Вы объявляете @Sql как varchar(max), но перед установкой его значения вы используете префикс N. Это необходимо только при работе с данными Unicode (nchar, nvarcar). Это очень важно, но вы должны знать об этом.

  2. Вы используете convert без указания параметра style. Это не так плохо при преобразовании целых чисел в строки, но это может вызвать неожиданное поведение при работе с датами. Всякий раз, когда вам нужно использовать строковое представление для значений даты / даты и времени, вы всегда должны использовать стандарт ISO8601, поскольку гарантируется, что сервер Sql всегда будет правильно преобразовывать его в дату, независимо от локальных настроек. Чтобы преобразовать значение datetime в стандарт ISO8061, используйте стиль 126 в операторе convert.

  3. Вы используете varchar без указания длины. Это плохая привычка, поскольку в SQL Server значения по умолчанию отличаются от длины в зависимости от контекста. Это 1 при объявлении переменной, но 30 при использовании в cast и convert.

Я внес некоторые изменения в ваш код, включая изменение char(2), которое вы использовали, на varchar(11) (поскольку 11 символов будут содержать даже минимальное значение типа данных int, равное -2147483648) для @timeoffset, и не было проблем с его отрицательным значением.

Вот мой тест:

DECLARE @timeOffset int = -10, 
        @start_date datetime = getdate(), 
        @sql nvarchar(max)

SET @sql = 
N'SELECT '''+ convert(char(23), @start_date, 126) +''' As GetDate, 
       DATEADD(HOUR, '+ CAST(@timeOffset as varchar(11)) +', '''+ convert(char(23), @start_date, 126) +''') As DateAdd';

SELECT @Sql

EXEC(@sql)

Результаты:

SELECT '2018-11-08T20:33:31.670' As GetDate, 
       DATEADD(HOUR, -10, '2018-11-08T20:33:31.670') As DateAdd

GetDate                     DateAdd
2018-11-08T20:33:31.670     08.11.2018 10:33:3
0 голосов
/ 08 ноября 2018

Вы можете попробовать передать отрицательное число в качестве другой переменной:

DECLARE @neg INT = -1;
DECLARE @sql VARCHAR(MAX) = 
N'SELECT DISTINCT cl.ClientId, 
CASE WHEN DATEADD(HOUR,'+CONVERT(CHAR(2),@timeoffset)*@neg+', x.changedate) >= '''+ 
CONVERT(varchar, @start_date) +''' 
AND DATEADD(HOUR,'+CONVERT(CHAR(2),@timeoffset)*@neg+', x.changedate) < '''+ 
CONVERT(varchar, @end_date_plus1) +''' 
THEN DATEADD(HOUR,'+CONVERT(CHAR(2),@timeoffset)*@neg+',x.changedate)' 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...