SQL Server LEFT JOIN не соответствует строки без подсказки JOIN - PullRequest
4 голосов
/ 20 марта 2012

У меня есть то, что кажется поврежденным индексом?

Вот что происходит. У меня есть две табличные функции, первая из которых - набор наблюдений, а вторая - набор осведомленных дат. Эти два набора имеют отношение 1 (регистр) к 0 или 1 (дата осведомленности). Обычно я запрашиваю их как;

SELECT c.CaseID, a.AwareDate  
FROM Cases(@date) AS c  
LEFT JOIN AwareDates(@date) AS a ON c.CaseID = a.CaseID;

Беда в том, что не все строки из AwareDates, которые соответствуют, кажутся JOIN'd. Если я добавлю подсказку присоединения, они тогда сделают. говорят,

SELECT c.CaseID, a.AwareDate  
FROM Cases(@date) AS c  
LEFT MERGE JOIN AwareDates(@date) AS a ON c.CaseID = a.CaseID;

Что я заметил в плане запросов, так это то, что добавление подсказки о соединении добавляет данные AwareDate своего рода перед соединением, которых в противном случае нет. Кроме того, планировщик запросов переворачивает соединение в ПРАВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ, когда нет подсказки, и, конечно, сохраняет левое соединение там, где есть подсказка.

Я сделал следующее без ошибок;

DBCC UPDATEUSAGE (0) WITH INFO_MESSAGES, COUNT_ROWS;  
EXECUTE sp_updatestats 'resample';  
DBCC CHECKDB (0) WITH ALL_ERRORMSGS, EXTENDED_LOGICAL_CHECKS;  

Я в тупике ... есть идеи?

Вот определения UDF

ALTER FUNCTION dbo.Cases( @day date ) RETURNS TABLE
WITH SCHEMABINDING
AS RETURN (
SELECT 
    CaseID -- other 42 columns ommitted
FROM (
    SELECT
        ROW_NUMBER() OVER (PARTITION BY CaseID ORDER BY UpdateDate DESC, UpdateNumber DESC) AS RecordAge,
        CaseID, 
        Action
    FROM
        dbo.CaseAudit
    WHERE
        convert(date,UpdateDate) <= @day
    ) AS History
WHERE
    RecordAge = 1                    -- only the most current record version
    AND isnull(Action,'') != N'DEL'  -- only include cases that have not been deleted
)

ALTER FUNCTION dbo.AwareDates( @day date ) RETURNS TABLE
WITH SCHEMABINDING
AS RETURN (
WITH 
    History AS (
        SELECT row_number() OVER (PARTITION BY CaseID, ContactID ORDER BY UpdateDate DESC, UpdateNumber DESC) AS RecordAge,
            CaseID, InfoReceived, ReceiveDate, ResetClock, Action
        FROM dbo.ContactLogAudit WITH (NOLOCK)
        WHERE convert(date,UpdateDate) <= @day
        ),
    Notes AS (
        SELECT 
            CaseID,
            convert(date,ReceiveDate,112) AS ReceiveDate,
            ResetClock
        FROM History 
        WHERE RecordAge = 1                -- only the most current record version
        AND isnull(Action,'') != N'DEL'    -- only include notes that have not been deleted
        AND InfoReceived = N'Y'            -- only include notes that have Info Rec'd checked 
        AND len(ReceiveDate) = 8 AND isnumeric(ReceiveDate) = 1 AND isdate(ReceiveDate) = 1 -- only include those with a valid aware date
        ),
    Initials AS (
        SELECT CaseID, min(ReceiveDate) AS ReceiveDate
        FROM Notes 
        GROUP BY CaseID
        ),
    Resets AS (
        SELECT CaseID, max(ReceiveDate) AS ReceiveDate
        FROM Notes 
        WHERE ResetClock = N'Y'
        GROUP BY CaseID
        )
SELECT 
    i.CaseID                              AS CaseID,
    i.ReceiveDate                         AS InitialAwareDate, -- the oldest valid aware date value (must have AE Info Reveived checked and a received date)
    coalesce(r.ReceiveDate,i.ReceiveDate) AS AwareDate  -- either the newest valid aware date value with the Reset Clock checked, otherwise the initial aware date value
FROM Initials AS i
LEFT JOIN Resets AS r 
    ON i.CaseID = r.CaseID
);

Я также обнаружил, что если я опущу табличную подсказку WITH (NOLOCK), я получу правильные результаты. Также, если добавить подсказку о соединении в UTF AwareDates или даже добавить COLLATE Latin1_General_BIN в отношении LEFT JOIN между инициалами и сбросами.


Количество строк в плане запроса - без подсказки соединения (не работает)

  • Дела {Фактические: 25 891, Эстимейт: 19 071,9}
  • AwareDates {Фактический: 24 693, Расчетный: 1 463,09}
    • Инициалы {Фактические: 24 693, Расчетные: 1 463,09}
    • Отдыхает {Фактический: 985, Расчетный: 33.2671}
  • AwareDates соответствует 8,108 строк Cases в наборе результатов объединения

Количество строк в плане запроса - с подсказкой соединения (работает)

  • Дела {Фактические: 25 891, Эстимейт: 19 071,9}
  • AwareDates {Фактический: 24 673, Расчетный: 1 837,67}
    • Инициалы {Фактические: 24 673, Расчетные: 1 837,67}
    • Отдыхает {Фактический: 982, Расчетный: 42.6238}
  • AwareDates соответствует 24 673 строкам Cases в наборе результатов объединения

Я еще больше сократил суть вопроса. Я могу;

SELECT * FROM AwareDate(@date);  

и

SELECT * FROM AwareDate(@date) ORDER BY CaseID;  

С разным количеством строк.

1 Ответ

2 голосов
/ 23 марта 2012

Вы не указываете конкретную версию SQL (@@ version), но это подозрительно похоже на ошибку , которая была исправлена ​​ в Накопительном обновлении 6 для SQL 2008 R2 (очевидно, это также относится к SQL 2008).

KB 2433265
ИСПРАВЛЕНИЕ: вы можете получить неправильный результат при выполнении запроса, который использует Функция ROW_NUMBER вместе с левым внешним объединением в SQL Server 2008

В примере в статье указано DISTINCT. Статья, однако, сформулирована неоднозначно - неясно, НУЖНО ли вам отдельное или если DISTINCT является одним из триггеров.

Ваш пример не имеет ничего похожего на статью, но он кажется измененным ради того, чтобы задать вопрос (т. Е. Пропущено 42 столбца). Есть ли отличное? Также в AwareDates udf к тому времени, когда я спускаюсь в Initials CTE, вы делаете GROUP BY, которая может иметь тот же эффект, что и DISTINCT.


UPDATE

@ Деннис из вашего комментария Я до сих пор не могу сказать, используете ли вы SQL 20080 или 2008 R2.

Если вы работаете в 2008 году, в статье базы знаний говорится: «Решение этой проблемы было впервые выпущено в Накопительном обновлении 11 для SQL Server 2008 с пакетом обновления 1». Итак, пост SP1.

С другой стороны, если вы используете SQL 2008 R2, вы правы, что это было исправлено в CU 6, который был частью SP1. Но эта ошибка, похоже, всплыла. Посмотрите на Накопительное обновление 4 для SQL Server 2008 R2 с пакетом обновления 1 - выпущено после SP1.

970198      FIX: You receive an incorrect result when you run a 
            query that uses the row_number function in SQL Server 2008 
            or in SQL Server 2008 R2 

В статье , связанной с КБ MS отбросила ссылку на отличную:

Consider the following scenario. You run a query against a table that has a 
clustered index in Microsoft SQL Server 2008 or in Microsoft SQL Server 2008
R2. In the query, you use the row_number function. In this scenario, you 
receive an incorrect result when a parallel execution plan is used for the 
query. If you run the query many times, you may receive different results.

Это, кажется, подтверждает мое раннее прочтение KB 2433265 - формулировка предполагает, что отличительным является просто одно из многих условий, которые могут вызвать поведение. Кажется, что в этот раз виновником является план параллельного выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...