Добавление предложения WHERE во внешний запрос приводит к ошибке внутреннего запроса - PullRequest
0 голосов
/ 22 октября 2019

Следующий запрос работал до тех пор, пока я не добавил предложение WHERE.

SELECT
    PersonTable.FullName,
    View_PersonToHead.DirectorId
FROM PersonTable
LEFT JOIN View_PersonToDirector ON PersonTable.PersonId = View_PersonToDirector.PersonId
WHERE View_PersonToDirector.DirectorId = 12345 --No error if this line is removed

Сообщение об ошибке:

Недопустимый параметр длины, переданный в функцию RIGHT.

Это наводит меня на мысль, что произошла ошибка в том, как я написал View_PersonToDirector представление. Что-то в добавлении предложения WHERE приводит к тому, что запрос оценивается / оптимизируется по-другому, в результате чего возникает некоторая проблема.

Внутренние компоненты View_PersonToDirector:

WITH items AS (
    SELECT
        PersonId,
        0 AS [Level],
        CAST(PersonId AS VARCHAR(255)) AS [Path]
    FROM PersonTable
    UNION ALL
    SELECT
        e.PersonId,
        [Level] + 1,
        CAST([Path] + ' < ' + CAST(e.PersonId AS VARCHAR(255)) AS VARCHAR(255))
    FROM PersonTable e
    INNER JOIN items itms ON itms.PersonId = e.ManagerId
)

SELECT
    A.PersonId,
    CASE
        WHEN A.[Level] = 1
        THEN A.PersonId
        ELSE CAST(LEFT(A.PathToDirector, CHARINDEX(' ', A.PathToDirector)) AS INT)
    END AS DirectorId
FROM (
    SELECT
        items.PersonId,
        items.[Level],
        RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector
    FROM items

    WHERE Path LIKE '1111 < %' --Id of director
) A

Я подозреваю, что наличие оператора WITHВ представлении cte оптимизатор запросов работает по-разному, применяя фильтрацию WHERE в другом порядке. Это плохая практика?

Ответы [ 3 ]

2 голосов
/ 22 октября 2019

Одна из ваших проблем определенно в этом коде:

RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector

Я ценю , что вы думаете, что это предложение WHERE решает проблему:

WHERE Path LIKE '1111 < %' 

Но это не так. Проблема в том, что SQL Server не гарантирует порядок вычисления выражений. Это верно даже для подзапросов, CTE и представлений. Их можно оценить в любом порядке. Фактически, SQL Server иногда отправляет оценку узлу, который читает из таблицы - вот почему вы получаете ошибку.

Лично я считаю, что это ошибка в SQL Server. Те полномочия, которые будут не согласны. Вот два решения:

(CASE WHEN Path LIKE '1111 < %' THEN RIGHT(items.[Path], LEN(items.[Path])-7) END) AS PathToDirector

Или:

(CASE WHEN Path LIKE '1111 < %' THEN RIGHT(items.[Path], LEN(items.[Path] + '1234567')-7) END AS PathToDirector

У вас может быть такая же проблема с CHARINDEX().

1 голос
/ 22 октября 2019

Базовая часть вашего rCTE имеет:

CAST(PersonId AS VARCHAR(255)) AS [Path]

Это означает, что базовые строки rCTE будут содержать такие значения, как 1234, которые короче 7 символов и приводят к сбою RIGHT(x, LEN(x) - 7). Условие:

RIGHT(items.[Path], LEN(items.[Path])-7) AS PathToDirector

Может быть безопасно записано как:

SUBSTRING(items.[Path], NULLIF(CHARINDEX(' < ', items.[Path]), 0) + 3, LEN(items.[Path]))
1 голос
/ 22 октября 2019

Использование условия where, относящегося к столбцу из левой таблицы соединений, сгенерированного и неявного внутреннего соединения
В этом случае добавьте условие в предложение ON, чтобы левое соединение работало

SELECT
  PersonTable.FullName,
  View_PersonToHead.DirectorId
FROM PersonTable
LEFT JOIN View_PersonToDirector ON PersonTable.PersonId = View_PersonToDirector.PersonId
   AND  View_PersonToDirector.DirectorId = 12345 

дляпроблема длины попробуйте использовать условные, например, case

SELECT
    items.PersonId,
    items.[Level],
    case when LEN(items.[Path]) > 7 then  RIGHT(items.[Path], LEN(items.[Path])-7) 
       ELSE items.[Path] END AS PathToDirector
FROM items
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...