SQL Определить команды, имеющие одинаковых членов - PullRequest
3 голосов
/ 04 мая 2019

У меня интересная проблема.

У меня есть несколько команд с лидером команды, хранящимся в одной таблице, а члены команд хранятся в дочерней таблице. Я хочу определить команды, которые имеют одинаковых членов.

TEAMS
TEAM_ID    LEADER_ID
1   1
2   1
3   2
4   2

MEMBERS
TEAM_ID MEMBER_ID
1   2
1   3
1   4
2   3
2   4
2   5
3   1
3   3
3   4
4   5
4   6
4   7

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

SELECT
    TEAM_ID,
    (
        SELECT
            CONVERT (VARCHAR, MEMBER_ID) + ', '
        FROM
            (
                SELECT
                    TEAM_ID,
                    LEADER_ID AS MEMBER_ID
                FROM
                    TEAMS
                UNION ALL
                    SELECT
                        TEAM_ID,
                        MEMBER_ID
                    FROM
                        MEMBERS
            ) FORMATIONS
        WHERE
            TEAM_ID = MT.TEAM_ID
        ORDER BY
            MEMBER_ID FOR XML PATH ('')
    ) AS MEMBERS
FROM
    TEAMS MT

Поскольку очевидно, что идентификаторы команд 1 и 3 одинаковы, как можно получить самый низкий идентификатор дублирующих команд.

т.е.. запрос должен возвращать список TEAM_ID, которые являются наименьшими для дублирующейся группы (и только если они дубликаты)

В этом сценарии должен быть возвращен идентификатор 1.

http://sqlfiddle.com/#!18/c845a/5

Ответы [ 3 ]

2 голосов
/ 04 мая 2019

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

Все, что вам нужно сделать, это объединить элементы из двух таблиц, а затем использовать это для логики:

with m as (
       select team_id, member_id
       from members
       union  -- on purpose to remove duplicates
       select team_id, leader_id
       from teams
      )
select *
from (select team_id, members, count(*) over (partition by members) as num_teams
      from (select t.team_id,
                   stuff( (select concat(',', m.member_id)
                           from m
                           where m.team_id = t.team_id
                           order by m.member_id
                           for xml path ('')
                          ), 1, 1, ''
                        ) as members
            from teams t
           ) t
     ) t
where num_teams > 1
order by members;

Здесь - это ваша скрипта SQL.

Обратите внимание, что сравнение строк отлично подходит для этого случая, который представляет собой точное совпадение членов.Для отношений надмножества это не так хорошо.

0 голосов
/ 04 мая 2019

Использование чистого SQL.

Основная идея состоит в том, что два набора A и B, равные, определяются тем, что A является подмножеством B, а B является подмножеством A.

И мыможно проверить, является ли B подмножеством A, получив члены B, которые находятся в A, посчитав их и проверив, равно ли это счету в A.

Поскольку это довольно сложный шаг, я просто сделал этопутем перекрестного применения подзапроса, отфильтрованного к командам A и B. Может быть более элегантный способ.

WITH MembersAll AS
(
  SELECT Team_Id, Member_Id FROM Members
  UNION
  -- Consider leaders as members.
  SELECT Team_Id, Leader_Id AS Member_Id FROM Teams
),
-- Teams and any teams which are a subset of that team:
TeamSubsetTeam AS (
  SELECT
  ThisTeam.Team_Id,
  OtherTeam.Team_Id AS SubsetTeam_Id
  FROM Teams AS ThisTeam
  CROSS JOIN Teams AS OtherTeam -- Considering all pairs of teams.
  CROSS APPLY (
    -- Get the members in both teams,
    -- left join so that we have all members from a given team
    -- and all of the members in the other team that are in the given team
    -- then filter on the counts of these being the same.
    SELECT
    COUNT(MembersThisTeam.Member_Id) AS MemberCountThisTeam,
    COUNT(MembersOtherTeamInThisTeam.Member_Id) AS MemberCountOtherTeamInThisTeam
    FROM MembersAll AS MembersThisTeam
    LEFT JOIN MembersAll AS MembersOtherTeamInThisTeam
      ON MembersThisTeam.Member_Id = MembersOtherTeamInThisTeam.Member_Id
      AND MembersOtherTeamInThisTeam.Team_Id = OtherTeam.Team_Id
    WHERE MembersThisTeam.Team_Id = ThisTeam.Team_Id
  ) MemberCounts
  WHERE MemberCounts.MemberCountThisTeam = MemberCounts.MemberCountOtherTeamInThisTeam
),
-- Teams and any teams which are equivalent to that team (including itself):
TeamEquivalentTeam AS (
  -- From set theory, team A is equivalent to team B if
  -- team A is a subset of team B and
  -- team B is a subset of team A.
  SELECT
  Team_Id,
  SubsetTeam_Id AS EquivalentTeamId
  FROM TeamSubsetTeam
  WHERE Team_Id IN (
    SELECT SubsetTeam_Id FROM TeamSubsetTeam AS SubsetTeamSubsetTeam
    WHERE SubsetTeamSubsetTeam.Team_Id = TeamSubsetTeam.SubsetTeam_Id
  )
)
-- The specified post-processing step.
-- Doesn't seem particularly useful but you can do whatever you like
-- now you have the information in TeamEquivalentTeam.
SELECT DISTINCT MIN(EquivalentTeamId) AS FirstEquivalentTeam
FROM TeamEquivalentTeam
GROUP BY Team_Id

Возвращает:

FirstEquivalentTeam
1
2
4
0 голосов
/ 04 мая 2019

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

Позже он используется с group by для определения наименьшего идентификатора команд с одинаковыми членами.Чтобы гарантировать, что будут только дублированные команды, я использовал предложение having.

;with cte as (
    select team_id,
           (select cast(member_id as varchar(5)) + ',' from @members innerMembers
            where team_id = m.team_id 
            and not exists(select 1 from @TEAMS
                           where leader_id = innerMembers.member_id)
            order by member_id
            for xml path('')) members
    from @members m
    group by team_id
)

select min(team_id), members from cte
group by members
having count(*) > 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...