2012-03-20 4 views
4

У меня есть то, что кажется коррумпированным индексом?SQL Server LEFT JOIN не соответствует строкам без подсказки JOIN

Вот что происходит. У меня есть две таблицы-функции, первая из которых представляет собой набор случаев, а вторая - набор значащих дат. Эти два набора имеют отношение 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 
); 

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


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

  • Случаи {Фактическое: 25891, Оценка: 19,071.9}
  • AwareDates {Действительные: 24,693, Оценено: 1,463.09}
    • Инициалы {Фактические: 24,693, Оценочные: 1,463.09}
    • Отдыхает {Фактический: 985, Оценено: 33,2671}
  • AwareDates соответствует 8,108 строкам случаев в join'd множества результатов

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

  • Случаи {Фактические: 25,891, Оценка : 19,071.9}
  • AwareDates {Действительные: 24,673, Оценочные: 1,837.67}
    • Инициалы {Фактическое: 24673, Оценено: 1,837.67}
    • Остатки {Актуально: 982, Предполагаемый: 42.6238}
  • AwareDates соответствует 24,673 строк случаев в join'd множества результатов

я дополнительно сократился объем выпуска. Я могу;

SELECT * FROM AwareDate(@date); 

и

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

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

+2

Можете ли вы поделиться определением своих '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' Самый большой красный флаг для меня - это то, что вы запрашиваете вывод функции, а не таблицы. – JNK

+0

+1 для настоящей тайны. Могу сказать, что я никогда не пытался присоединиться к двум функциям раньше.Поскольку вы спрашиваете о плохом индексе, я предполагаю, что функции получают данные из таблиц. Можете ли вы запросить таблицы непосредственно, а не выполнять функции? Можете ли вы взглянуть? –

+0

Я пробовал несколько других способов запроса данных, и если я помещаю вывод табличной функции в временную таблицу, а затем присоединяюсь к двум темповым таблицам без подсказки, он работает так, как ожидалось. Кроме того, если я (SELECT TOP 999999 * FROM AwareDate (@date) ORDER BY CaseID) AS a работает без подсказки. –

ответ

2

Вы не указываете конкретную версию SQL (версия @@), но это выглядит подозрительно, как bug that was fixed в накопительном обновлении 6 для SQL 2008 R2 (по-видимому, это также относится к SQL 2008).

KB 2433265
FIX: Вы можете получить неверный результат при выполнении запроса, который использует функцию row_number вместе с левым внешним соединением в SQL Server 2008

Пример в статья указывает DISTINCT. Статья, однако, сформулирована неоднозначно - неясно, нужен ли вам определенный или нет DISTINCT один из триггеров.

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


UPDATE

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

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

С другой стороны, если вы используете SQL 2008 R2, вы правы, что это было исправлено в CU 6, который был частью SP1. Но эта ошибка, похоже, всплыла. Посмотрите на Cumulative update package 4 for SQL Server 2008 R2 Service Pack 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 

В associated KB article 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 - фразировка предполагает отчетливый является только один из многих условий, которые могут вызывают поведение. Кажется, что параллельный план выполнения является виновником на этот раз.

+0

Эта проблема была впервые исправлена ​​в накопительном обновлении 6 (pre-sp1), и у меня установлен SP1, поэтому я думаю, что исправление будет включено. Кроме того, в удаленных столбцах из случаев нет различий. –

+1

@DennisAllen просто для того, чтобы быть полностью понятным, вам нужно либо '10.00.2804', либо более поздней версии 2008 года или' 10.50.1765.0' за 2008 год R2 –

+0

@ConradFrix - Право, я нахожусь на 2008 R2 (SP1) - 10.50.2500.0 –