Сложный SQL Присоединяйтесь, помогите! - PullRequest
1 голос
/ 01 июля 2010

(Для быстрого ознакомления, пожалуйста, посмотрите разделы, озаглавленные, ДАННЫЕ. Могу получить результаты только на РИСУНОК А, а РИСУНОК Б, то, что я хочу, - ЭТО ЖЕЛАЕМЫЕ РЕЗУЛЬТАТЫ, попробовал все, что опубликовано в разделе ответов.)

ВсеЯ усердно трудился, чтобы начать использовать соединения в своем приложении.Мальчик, это заняло у меня какое-то время, но я думаю, что понял это, но я не могу заставить этот запрос работать.

Пожалуйста, помогите мне!Вот соединение как есть:

SELECT     TOP (100) PERCENT a.Id, a.DateCreated, a.DateModified, a.LastUpdatedBy, a.AccomplishmentTypeId, a.Title, a.Description, a.ApplicableDate, 
                  a.LocalId, at.Name AS AccomplishmentTypeName, a.Name AS AreaName, u.FirstName, u.LastName, ug.Name AS UserGroupName, 
                  ugo.Name AS OtherUserGroupName
FROM         dbo.Accomplishment AS a INNER JOIN
                  dbo.AccomplishmentType AS at ON at.Id = a.AccomplishmentTypeId INNER JOIN
                  dbo.AccomplishmentArea AS aal ON aal.AccomplishmentId = a.Id INNER JOIN
                  dbo.Area AS al ON al.Id = aal.AreaId INNER JOIN
                  dbo.UserAccomplishment AS ua ON ua.AccomplishmentId = a.Id INNER JOIN
                  dbo.[User] AS u ON u.Id = ua.UserId INNER JOIN
                  dbo.UserUserGroup AS uug ON uug.UserId = u.Id INNER JOIN
                  dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId INNER JOIN
                  dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId INNER JOIN
                  dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id LEFT OUTER JOIN
                  dbo.UserGroup AS ugo ON ugo.Id = uugo.UserGroupId LEFT OUTER JOIN
                  dbo.UserGroupType AS ugto ON ugto.Id = ugo.UserGroupTypeId
WHERE     (ug.LocalId = 2) AND (ugo.LocalId <> 2) AND (ugto.LocalId = 4)
ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName

Теперь это довольно сложно, но я делаю выбор информации из всех этих связанных таблиц, где есть достижение, с которым связан пользователь, гдеу этого пользователя локальная группа userId = 2, но затем, если он у него есть, я также хочу вернуть все пользовательские группы, в которых находится usertype.localid = 4.

Позвольте мне привести некоторые примеры данных длянадеюсь, уточнить это, прямо сейчас у меня есть 3 достижения в базе данных, которые выглядят так:

------- (ДАННЫЕ)

Accomplishment 1:
   User: John
      Usergroups: Group A (Of UserGroupType 2), Group B (Of UserGroupType 3), 
                  Group C (Of UserGroupType 4), Group D (Of UsergroupType 4)
Accomplishment 2:
   User: John
      Usergroups: Group A (Of UserGroupType 2), Group B (Of UserGroupType 3), 
                  Group C (Of UserGroupType 4), Group D (Of UsergroupType 4)
Accomplishment 3:
   User: Sue
      Usergroups: Group A (Of UserGroupType 2)

Теперь мой выше Присоединиться результаты 4 строки:

------- (РИСУНОК A):

 Accomplishment 1, John, Group A, Group C
 Accomplishment 1, John, Group A, Group D
 Accomplishment 2, John, Group A, Group C
 Accomplishment 2, John, Group A, Group D

Теперь это правильно, поскольку для каждой пользовательской группы есть строка, в которой пользователь имеет usergrouptype.localid, равный 4, однако, как я могу сделать так, чтобы он также отображал Выполнение 3 ??Я сначала начал с того, что все объединения были внутренними соединениями, а затем подумал, что создание последних нескольких оставленных объединений позаботится о том, чтобы вернуть пользователя, даже если у него нет групп пользователей типа usergroup localid 4, но это не так.По сути, я хочу, чтобы он возвращал все достижения пользователя, входящего в группу А, и, если у них есть какие-либо группы пользователей, относящиеся к типу группы 4, возвращаются также все эти группы пользователей.

Есть мысли?И если я не совсем уверен, пожалуйста, дайте мне знать, это очень сложно, и я, возможно, недостаточно объяснил.

РЕДАКТИРОВАТЬ:

(Для быстрого резюме, пожалуйста,см. разделы под названием «ДАННЫЕ». Могу получить результаты только на РИСУНОК А, а на РИС. В, то, что мне нужно, - ЭТО ЖЕЛАЕМЫЕ РЕЗУЛЬТАТЫ, перепробовал все, что размещено в разделе ответов.)

Результаты HLGEM и Tom Hs одинаковы,и ближе к тому что мне нужно, но все же выключено.Теперь я получаю 9 результатов на рисунке B.

------- (РИСУНОК B):

Accomplishment 1, John, Group A, Group C
Accomplishment 1, John, Group A, Group D
Accomplishment 1, John, Group A, Null
Accomplishment 1, John, Group A, Group B (Group B is UsergroupType 3)
Accomplishment 3, Sue, Group A, Null
Accomplishment 2, John, Group A, Group C
Accomplishment 2, John, Group A, Group D
Accomplishment 2, John, Group A, Null
Accomplishment 2, John, Group A, Group B (Group B is UsergroupType 3)

Так что дляпо какой-то причине он включает в себя пустую строку для Джона, даже если у него есть группы пользователей, которые имеют тип пользовательской группы 4, и это включает в себя строку с группой B, которая имеет тип пользовательской группы 3 и не должна отображаться.

ВНОВЬ РЕДАКТИРУЙТЕ:

(Для быстрого ознакомления, пожалуйста, смотрите разделы, озаглавленные «ДАННЫЕ». Можно получить результаты только на РИСУНКЕ А, а на РИС. Б, то, что я хочу, это «Желаемые результаты», перепробовал все, что было опубликовано в разделе ответов).)

Да, я действительно озадачен этим, я попробовал почти каждую комбинацию, помещая некоторые вещи в предложение where, и это либо 4 строки без выполнения 3, либо 9 строк, где пользователь Джон получает два дополнительныхстрок на выполнение или 6 строк без выполнения 3 и дополнительная строка для группы пользователей типа 3.Что еще более озадачивает, так это то, что добавление ugto.localid = 4 к последней строке, похоже, не влияет на результаты.Он показывает мою группу пользователей с типом группы 3, и я не вижу нигде в запросе, где это нормально.

РЕДАКТИРОВАТЬ 5/02/10

(Длябыстрое резюме, пожалуйста, смотрите разделы под названием «ДАННЫЕ. Могу получить результаты только на РИСУНОК А, а РИСУНОК Б, то, что я хочу, - ЖЕЛАЕМЫЕ РЕЗУЛЬТАТЫ», попробовал все, что опубликовано в разделе ответов.)

Я думаю, что я хочу, чтобы мойбыть невозможным с тем, как это настроено ??В любом случае, я хочу получить следующие 5 строк:

------- (желаемый результат):

Accomplishment 1, John, Group A, Group C
Accomplishment 1, John, Group A, Group D
Accomplishment 2, John, Group A, Group C
Accomplishment 2, John, Group A, Group D
Accomplishment 3, Sue, Group A, Null

Здесьэто список всего, что я пробовал, и результатов, которые они дают: (Пожалуйста, простите за дерьмовое форматирование SSMS)

Все ниже основано на этом базовом запросе:

Запуск запроса / попытки1:

SELECT     TOP (100) PERCENT a.Id, a.DateCreated, a.DateModified, a.LastUpdatedBy, a.AccomplishmentTypeId, a.Title, a.Description, a.ApplicableDate, 
                  a.LocalId, at.Name AS AccomplishmentTypeName, al.Name AS AreaName, u.FirstName, u.LastName, ug.Name AS UserGroupName, 
                  ugo.Name AS OtherUsergroupName
  FROM         dbo.Accomplishment AS a INNER JOIN
                  dbo.AccomplishmentType AS at ON at.Id = a.AccomplishmentTypeId INNER    JOIN
                  dbo.AccomplishmentArea AS aal ON aal.AccomplishmentId = a.Id INNER JOIN
                  dbo.Area AS al ON al.Id = aal.AreaId INNER JOIN
                  dbo.UserAccomplishment AS ua ON ua.AccomplishmentId = a.Id INNER JOIN
                  dbo.[User] AS u ON u.Id = ua.UserId INNER JOIN
                  dbo.UserUserGroup AS uug ON uug.UserId = u.Id INNER JOIN
                  dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId INNER JOIN
                  dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId INNER JOIN
                  dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id LEFT OUTER JOIN
                  dbo.UserGroup AS ugo ON ugo.Id = uugo.UserGroupId AND ugo.LocalId <> 2 LEFT OUTER JOIN
                  dbo.UserGroupType AS ugto ON ugto.Id = ugo.UserGroupTypeId AND ugto.LocalId = 4
 WHERE     (ug.LocalId = 2)
 ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName

Результат: Рисунок B (см. Выше) Почему это плохо: Появляются нулевые значения для Джона и появляется группа пользователей типа 3.

Попытка 2:

Попытка 1, за вычетом фильтрации всех встроенных соединений (по-прежнему сохраняется соединение слева в последних двух) и следующий оператор where:

WHERE (ug.LocalId = 2)
  AND (ugo.LocalId <> 2 OR ugo.LocalId is null)
  AND (ugto.LocalId = 4 OR ugto.LocalId is null) 

Результат: Рисунок A (см. Выше) Почему это плохо: Не включает в себя достижение 3

Попытка 3:

То же, что и попытка 1 с изменениями, начинающимися под этой строкой:

 dbo.[User] AS u ON u.Id = ua.UserId INNER JOIN

Изменения под этой строкой:

                  dbo.UserUserGroup AS uug ON uug.UserId = u.Id INNER JOIN
                  dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId AND ug.LocalId = 2 INNER JOIN
                  dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId INNER JOIN
                  dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id LEFT OUTER JOIN
                  dbo.UserGroup AS ugo ON ugo.Id = uugo.UserGroupId AND ugo.LocalId <> 2 LEFT OUTER JOIN
                  dbo.UserGroupType AS ugto ON ugto.Id = ugo.UserGroupTypeId
WHERE     (ugto.LocalId = 4)
ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName

(Для быстрого ознакомления, пожалуйста, смотрите разделы, озаглавленные «ДАННЫЕ». Можно получить результаты только на РИСУНКЕ А, а на РИС. В, то, что я хочу, это «Желаемые результаты», перепробовал все, что размещено в разделе ответов.)

Ответы [ 5 ]

3 голосов
/ 01 июля 2010

@ Комментарий ChaosPandion о переформатировании - хорошая идея, но, возможно, вам нужна помощь, чтобы понять, что такое хорошее форматирование.Ну, это, наверное, для всех разное :-) но если бы я писал ваш запрос, я бы отформатировал его следующим образом:

SELECT  TOP (100) PERCENT
          a.Id,
          a.DateCreated,
          a.DateModified,
          a.LastUpdatedBy,
          a.AccomplishmentTypeId,
          a.Title,
          a.Description,
          a.ApplicableDate,  
          a.LocalId,
          at.Name AS AccomplishmentTypeName,
          a.Name AS AreaName,
          u.FirstName,
          u.LastName,
          ug.Name AS UserGroupName,  
          ugo.Name AS OtherUserGroupName 
FROM dbo.Accomplishment AS a
INNER JOIN dbo.AccomplishmentType AS at
  ON at.Id = a.AccomplishmentTypeId
INNER JOIN dbo.AccomplishmentArea AS aal
  ON aal.AccomplishmentId = a.Id
INNER JOIN dbo.Area AS al
  ON al.Id = aal.AreaId
INNER JOIN dbo.UserAccomplishment AS ua
  ON ua.AccomplishmentId = a.Id
INNER JOIN dbo.[User] AS u
  ON u.Id = ua.UserId
INNER JOIN dbo.UserUserGroup AS uug
  ON uug.UserId = u.Id
INNER JOIN dbo.UserGroup AS ug
  ON ug.Id = uug.UserGroupId
INNER JOIN dbo.UserGroupType AS ugt
  ON ugt.Id = ug.UserGroupTypeId
INNER JOIN dbo.UserUserGroup AS uugo
  ON uugo.UserId = u.Id
LEFT OUTER JOIN dbo.UserGroup AS ugo
  ON ugo.Id = uugo.UserGroupId
LEFT OUTER JOIN dbo.UserGroupType AS ugto
  ON ugto.Id = ugo.UserGroupTypeId 
WHERE ug.LocalId = 2 AND
      ugo.LocalId <> 2 AND
      ugto.LocalId = 4
ORDER BY a.DateCreated DESC,
         u.LastName,
         u.FirstName,
         u.Id,
         UserGroupName

Я думаю, что это форматирование облегчает чтение.

Поделитесь и наслаждайтесь.

1 голос
/ 01 июля 2010

Вы допустили классическую ошибку при изучении левых объединений, вы поставили в условие where условия, которые превращают их во внутренние объединения

Попробуйте это

SELECT     a.Id, a.DateCreated, a.DateModified, a.LastUpdatedBy, a.AccomplishmentTypeId, a.Title,
a.Description, a.ApplicableDate,  a.LocalId, at.Name AS AccomplishmentTypeName, 
a.Name AS AreaName, u.FirstName, u.LastName, ug.Name AS UserGroupName, ugo.Name AS OtherUserGroupName 
FROM        dbo.Accomplishment AS a 
INNER JOIN  dbo.AccomplishmentType AS at ON at.Id = a.AccomplishmentTypeId 
INNER JOIN  dbo.AccomplishmentArea AS aal ON aal.AccomplishmentId = a.Id 
INNER JOIN  dbo.Area AS al ON al.Id = aal.AreaId 
INNER JOIN  dbo.UserAccomplishment AS ua ON ua.AccomplishmentId = a.Id 
INNER JOIN  dbo.[User] AS u ON u.Id = ua.UserId 
INNER JOIN  dbo.UserUserGroup AS uug ON uug.UserId = u.Id 
INNER JOIN  dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId 
INNER JOIN  dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId 
INNER JOIN  dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id 
LEFT OUTER JOIN  dbo.UserGroup AS ugo ON ugo.Id = uugo.UserGroupId  AND ugo.LocalId <> 2
LEFT OUTER JOIN  dbo.UserGroupType AS ugto ON ugto.Id = ugo.UserGroupTypeId  AND ugto.LocalId = 4
WHERE     ug.LocalId = 2 
ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName 

см. Эту ссылку для объясненияиз этого: http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN

1 голос
/ 01 июля 2010

Я не уверен, что полностью понимаю, что вы пытаетесь сделать, но я думаю, что вам нужно перенести свои критерии в ЛЕВЫЕ НАРУЖНЫЕ СОЕДИНЕНИЯ.Если вы поместите его в предложение WHERE, то, если LEFT OUTER JOIN не найдет совпадения, эти столбцы будут отображаться как NULL, поэтому они не пройдут проверку предложения WHERE.Вы фактически превращаете ЛЕВОЕ НАРУЖНОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ.

FROM
    dbo.Accomplishment AS a
INNER JOIN dbo.AccomplishmentType AS at ON
    at.Id = a.AccomplishmentTypeId
INNER JOIN dbo.AccomplishmentArea AS aal ON
    aal.AccomplishmentId = a.Id
INNER JOIN dbo.Area AS al ON
    al.Id = aal.AreaId
INNER JOIN dbo.UserAccomplishment AS ua ON
    ua.AccomplishmentId = a.Id
INNER JOIN dbo.[User] AS u ON
    u.Id = ua.UserId
INNER JOIN dbo.UserUserGroup AS uug ON
    uug.UserId = u.Id
INNER JOIN dbo.UserGroup AS ug ON
    ug.Id = uug.UserGroupId AND
    ug.localid = 2
INNER JOIN dbo.UserGroupType AS ugt ON
    ugt.Id = ug.UserGroupTypeId
INNER JOIN dbo.UserUserGroup AS uugo ON
    uugo.UserId = u.Id
LEFT OUTER JOIN dbo.UserGroup AS ugo ON
    ugo.Id = uugo.UserGroupId AND
    ugo.localid <> 2
LEFT OUTER JOIN dbo.UserGroupType AS ugto ON
    ugto.Id = ugo.UserGroupTypeId
WHERE
    ugto.localid = 4
ORDER BY
    a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName
0 голосов
/ 02 июля 2010

У вас все в порядке. Это фильтрация!

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

UGO и UGTO оба достигают LEFT JOIN

WHERE (ug.LocalId = 2)
  AND (ugo.LocalId <> 2 OR ugo.LocalId is null)
  AND (ugto.LocalId = 4 OR ugto.LocalId is null) 

PS - смешивание критериев JOIN и FILTERING (оба в ON или оба в WHERE) - это рецепт безумия. Оставайтесь в здравом уме!


Редактировать: обнаружена ошибка в объединении:

INNER JOIN dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id

Должно быть

LEFT JOIN dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id
0 голосов
/ 01 июля 2010

Вот что я переписал до сих пор:

SELECT a.Id, a.DateCreated, a.DateModified, a.LastUpdatedBy, a.AccomplishmentTypeId, 
       a.Title, a.Description, a.ApplicableDate, a.LocalId, 
       at.Name AS AccomplishmentTypeName, a.Name AS AreaName, u.FirstName, 
       u.LastName, ug.Name AS UserGroupName, ugo.Name AS OtherUserGroupName
  FROM dbo.Accomplishment a 
  JOIN dbo.AccomplishmentType AS at ON at.Id = a.AccomplishmentTypeId 
  JOIN dbo.AccomplishmentArea AS aal ON aal.AccomplishmentId = a.Id 
  JOIN dbo.Area AS al ON al.Id = aal.AreaId 
  JOIN dbo.UserAccomplishment AS ua ON ua.AccomplishmentId = a.Id 
  JOIN dbo.[User] AS u ON u.Id = ua.UserId 
  JOIN dbo.UserUserGroup AS uug ON uug.UserId = u.Id 
  JOIN dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId 
  JOIN dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId 
  JOIN dbo.UserGroupType AS ugto ON ugto.Id = ug.UserGroupTypeId
 ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName

У вас есть избыточное соединение с UserUserGroup, и ваши объединения с UserGroup могут быть консолидированы, потому что критерии LEFT JOIN отменяют то, что было сделано в предыдущем соединении с той же таблицей.

...