Почему я получаю эти разные результаты из двух запросов SQL?
Это убивало меня весь день =D. Пожалуйста помоги!
Сценарий 1: две базы данных на одном сервере (A, B) и A имеют три таблицы. Запрос выполнен из B.
Сценарий 2: один сервер двух БД связан с другим. (B имеет ссылку A), а A имеет три таблицы. Запрос выполнен из B.
В сценарии 1 (несвязанный сервер):
SET @val = ''
SELECT @val = @val + 'Hello, my name is ' + [name] + '!' + CHAR(10) + CHAR(13)
FROM A.sys.tables
Возвращает: Здравствуйте, меня зовут Table1! Здравствуйте, меня зовут Table2! Здравствуйте, меня зовут Table3!
В сценарии 2 (связанный сервер):
SET @val = ''
SELECT @val = @val + 'Hello, my name is ' + [name] + '!' + CHAR(10) + CHAR(13)
FROM LINKED.A.sys.tables
Возвращает: Здравствуйте, меня зовут Table3!
Почему они разные? Если я использую openquery() на связанном сервере, результаты будут такими же, как в сценарии 1. Я стараюсь по возможности избегать использования openquery(). Спасибо!
1 ответ
К сожалению, это ненадежный метод конкатенации строк в SQL Server. Я бы избегал этого во всех случаях, кроме самых тривиальных. В этой КБ содержится дополнительная информация: план выполнения и результаты агрегированных конкатенационных запросов в зависимости от местоположения выражения.
Тем не менее, я смог дублировать вашу проблему и обеспечить обходной путь в моей среде:
SET @val = ''
SELECT @val = @val + 'Hello, my name is ' + replace([name], '', '') + '!' + CHAR(10) + CHAR(13)
FROM LINKED.A.sys.tables
Обратите внимание, что я добавил пустую функцию замены в выражение. Хотя он ничего не должен делать с выводом, он добавляет локальный шаг "вычислить скаляр" в план запроса. Похоже, что это возвращает все данные из столбца имени для последующей обработки локально, а не просто позволяет удаленному запросу вернуть то, что он считает необходимым.
Я не уверен, есть ли лучшая функция для использования, кроме replace
с пустыми аргументами. Возможно двойной reverse
или что-то. Просто обязательно приведите к максимальному типу данных, если необходимо, как указано в документации.
ОБНОВИТЬ
Просто декларируя @var
как varchar(max)
скорее, чем nvarchar(max)
устраняет проблему, поскольку затем возвращает весь столбец имени (типа sysname - или nvarchar(128) - я полагаю) для локальной обработки, как это сделала функция replace. Я не могу притворяться, что знаю, какая комбинация настроек связанного сервера и неявного приведения приводит к этому. Надеюсь, кто-то с большим знанием в этой области может присоединиться!