Почему я получаю эти разные результаты из двух запросов 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. Я не могу притворяться, что знаю, какая комбинация настроек связанного сервера и неявного приведения приводит к этому. Надеюсь, кто-то с большим знанием в этой области может присоединиться!

Другие вопросы по тегам