TSQL: подсчет внешних объединенных таблиц дает неверные результаты

Создание отчета по SQL Server 2008. Я объединил одну таблицу с пятью другими, используя LEFT OUTER JOIN. Когда я выполняю подсчет этих других таблиц, я получаю неверные данные. Я знаю, ПОЧЕМУ, но я не знаю, как это исправить.

Запрос отслеживает поступающих в школу. По мере продвижения по процессу они помечаются на каждом основном этапе процесса. Что мне нужно, так это подсчитать, сколько людей прошли определенный этап в определенный период (год и месяц). В основном это работает. Однако, если кандидат прошел ЛЮБУЮ стадию в периоде, этот кандидат также «учитывается» на предыдущих стадиях, даже если они произошли до запрашиваемого периода. Отличным примером является AD_35, где для конкретной академической программы должен быть один человек, но в выводе указано 2. Когда я запрашиваю ТОЛЬКО таблицу AD_35, я получаю правильную информацию. Итак, я знаю, что проблема связана с внешним соединением, но я не уверен, как решить эту проблему (попробовав различные критерии в подзапросе, который генерирует мои именованные результаты). Кому-то должно быть легко... Заранее спасибо, код ниже. :Year и :Month предназначены для ввода пользователем и будут заполнены числовыми значениями (например, 2015 1)

CW

SELECT DISTINCT 
   ad_candidacy.prog_cde,   
   ad_candidacy.stageyr,   
   ad_candidacy.stagemo,   
   Count (case when (ad_02.stageyr in (:Year, :Year -1, :Year-2) and ad_02.stagemo <= :month) then 1 else null end) as Inquiry,   
   Count (case when (ad_05.stageyr in (:Year, :Year -1, :Year-2) and ad_05.stagemo <= :month) then 1 else null end) as Applied,   
   Count (case when (ad_35.stageyr in (:Year, :Year -1, :Year-2) and ad_35.stagemo <= :month and ad_35.id_num = ad_candidacy.id_num and ad_35.stageyr = ad_candidacy.stageyr and ad_35.stagemo=ad_candidacy.stagemo) then 1 else null end) as Accepted,   
   Count (case when (ad_50.stageyr in (:Year, :Year -1, :Year-2) and ad_50.stagemo <= :month) then 1 else null end) as Matriculated,   
   Count (case when (ad_enroll.stageyr in (:Year, :Year -1, :Year-2) and ad_enroll.stagemo <= :month) then 1 else null end) as Enrolled,   
   ad_candidacy.stagemo_long  
FROM 
   ad_candidacy 
LEFT OUTER JOIN 
   ad_02 ON ad_candidacy.id_num = ad_02.id_num 
LEFT OUTER JOIN 
   ad_05 ON ad_candidacy.id_num = ad_05.id_num 
LEFT OUTER JOIN 
   ad_35 ON ad_candidacy.id_num = ad_35.id_num 
LEFT OUTER JOIN 
   ad_enroll ON ad_candidacy.id_num = ad_enroll.id_num 
LEFT OUTER JOIN 
   ad_50 ON ad_candidacy.id_num = ad_50.id_num  
WHERE 
   (ad_candidacy.stageyr in (:Year, :Year -1, :Year-2) ) 
   AND ( ad_candidacy.stagemo <= :Month )   
GROUP BY 
   ad_candidacy.prog_cde,   
   ad_candidacy.stageyr,   
   ad_candidacy.stagemo,   
   ad_candidacy.stagemo_long  
ORDER BY 
   ad_candidacy.stageyr ASC  

person Craig    schedule 03.02.2015    source источник
comment
Вы говорите, что если кандидат считается принятым, то он также считается подавшим заявку, даже если он подал заявку более 2 лет назад? Я не понимаю, как. Или, если вы говорите, что кандидат подал заявку в прошлом году и был принят в этом году, тогда имеет смысл учитывать его дважды.   -  person artm    schedule 03.02.2015
comment
Я говорю второе. Почему это имеет смысл? (да, мне нужно немного поучиться здесь, ха!) Кандидат был принят в декабре 2014 года и зачислен в январе 2015 года. Если я запрошу таблицу AD_35 (принято), я увижу декабрьскую дату. Если я запрошу и ограничусь только январём 2015 года, кандидат не появится. Однако, когда я запускаю приведенный выше запрос, который включает в себя присоединение, есть кандидат с принятием и регистрацией в январе 2015 года. Я думаю, что подзапрос в операторе Count (case... разрешит это, но нет.   -  person Craig    schedule 03.02.2015


Ответы (2)


Объединение нескольких таблиц требует продумывания условий соединения. Возможно, что вторая таблица имеет несколько строк для одной и той же строки. Чтобы гарантировать, что в конечном итоге вы получите дубликаты, вы можете выполнить поиск во второй таблице в подзапросе перед присоединением к первой таблице.

SELECT a.Name, 
       b.Total
FROM table1 as a
LEFT OUTER JOIN ( SELECT table1Id, Total = COUNT(b.some_measure) from table2 group by table1Id)as b ON a.table1Id = b.table1Id
person Ako    schedule 04.02.2015
comment
Договорились подумать об условиях присоединения. Только запрос второй таблицы дает мне правильную информацию. Однако ваш пример кода дал мне дополнительную информацию для переработки логики OUTER JOIN, и сейчас я работаю над ее вариантами. - person Craig; 05.02.2015

Ответ Ако указал мне правильное направление. Я использовал подзапросы, но его пример привел к правильному выводу. Ниже приведена рабочая версия кода. Спасибо!

SELECT DISTINCT 
   ad_candidacy.prog_cde,   
   ad_candidacy.stageyr,   
   ad_candidacy.stagemo, 
   ad_candidacy.StageMo_Long,
   COUNT (case when (Inquiry IS NOT NULL) then 1 else null end) as Inquiry,
   COUNT (case when (Applied IS NOT NULL) then 1 else null end) as Applied,
   count (case when (Accepted is not null) then 1 else null end) as Accepted,
   COUNT (case when (Matriculated IS NOT NULL) then 1 else null end) as Matriculated,
   count (case when (Enrolled is not null) then 1 else null end) as Enrolled
FROM 
   ad_candidacy
LEFT OUTER JOIN
 (select id_num, Inquiry = COUNT (id_num) from ad_02 where stageyr in (:year, :year-1, :year-2) and StageMo <= :month group by id_num) as ad_02 on ad_candidacy.id_num = ad_02.id_num     
LEFT OUTER JOIN
 (select id_num, Accepted = COUNT (id_num) from ad_35 where stageyr in (:year, :year-1, :year-2) and StageMo <= :month group by id_num) as ad_35 on ad_candidacy.id_num = ad_35.id_num    
LEFT OUTER JOIN
 (select id_num, Applied = COUNT (id_num) from ad_05 where stageyr in (:year, :year-1, :year-2) and StageMo <= :month group by id_num) as ad_05 on ad_candidacy.id_num = ad_05.id_num    
LEFT OUTER JOIN
 (select id_num, Matriculated = COUNT (id_num) from ad_50 where stageyr in (:year, :year-1, :year-2) and StageMo <= :month group by id_num) as ad_50 on ad_candidacy.id_num = ad_50.id_num   
LEFT OUTER JOIN
 (select id_num, Enrolled = COUNT (id_num) from ad_enroll where stageyr in (:year, :year-1, :year-2) and StageMo <= :month group by id_num) as ad_enroll on ad_candidacy.id_num = ad_enroll.id_num  
WHERE 
   (ad_candidacy.stageyr in (:year, :year-1, :year-2) ) 
   AND ( ad_candidacy.stagemo <= :month )   
GROUP BY 
   ad_candidacy.prog_cde,   
   ad_candidacy.stageyr,   
   ad_candidacy.stagemo,   
   ad_candidacy.stagemo_long
person Craig    schedule 05.02.2015