Пометить всех руководителей на собрании на основе дерева организации

Моя задача — отметить участников «уровня начальника» в данных, собранных на собраниях, на основе заданной организационной структуры.

Другими словами, необходимо определить человека(ов) с самым высоким рейтингом на данном собрании, «босса» в рамках линии подчинения (ветви или поддерева в организационном дереве).

Если у участников нет начальника (в пределах их поддерева/структуры отчетности), они становятся начальником.

Если присутствует начальник (внутри его поддерева/структуры отчетности), он не является начальником.

На собраниях может быть несколько начальников (если они не находятся на одной ветке дерева).

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

Инструменты: SQL Server (версия 2019 или новее).

Дерево организационной диаграммы: Организационная диаграмма

Данные организационной диаграммы:

Имя Начальство Корень НУЛЕВОЙ Билли Корень Джейкоб Билли Сьюзен Билли Трейси Билли Шери Билли Карлос Трейси Эндрю Трейси Охотник Шери Чарли Корень Ларри Чарли Гарри Чарли Тони Чарли Крис Ларри Майкл Гарри Анита Тони

Данные встречи и ожидаемые результаты (Босс):

Встреча Имя Встреча с боссом (ожидаемые результаты) Встреча 1 Трейси Да Встреча 1 Охотник Да Встреча 2 Билли Да Встреча 2 Чарли Да Встреча 2 Охотник Н Встреча 2 Ларри Н Встреча 3 Билли Да Встреча 3 Охотник Н Встреча 3 Анита Да Встреча 4 Анита Да Встреча 4 Билли Да Встреча 5 Анита Да Встреча 6 Билли Да Встреча 6 Чарли Да

Я предполагаю, что здесь может потребоваться рекурсивный CTE, но интересно, есть ли более простые методы для этого. Также интересует общепринятая терминология для описания этой проблемы. Спасибо!


Данные испытаний:

Select *
From
(
    values
        ('Root', NULL),
        ('Billy', 'Root'),
        ('Jacob', 'Billy'),
        ('Susan', 'Billy'),
        ('Tracy', 'Billy'),
        ('Sheri', 'Billy'),
        ('Carlos', 'Tracy'),
        ('Andrew', 'Tracy'),
        ('Hunter', 'Sheri'),
        ('Charly', 'Root'),
        ('Larry', 'Charl'),
        ('Harry', 'Charly'),
        ('Toni', 'Charly'),
        ('Chris', 'Larry'),
        ('Michael', 'Harry'),
        ('Anita', 'Toni')
) A (Name, Superior)


Select *
From
(
    values
        ('Meeting 1', 'Tracy', 'Y'),
        ('Meeting 1', 'Hunter', 'Y'),
        ('Meeting 2', 'Billy', 'Y'),
        ('Meeting 2', 'Charly', 'Y'),
        ('Meeting 2', 'Hunter', 'N'),
        ('Meeting 2', 'Larry', 'N'),
        ('Meeting 3', 'Billy', 'Y'),
        ('Meeting 3', 'Hunter', 'N'),
        ('Meeting 3', 'Anita', 'Y'),
        ('Meeting 4', 'Anita', 'Y'),
        ('Meeting 4', 'Billy', 'Y'),
        ('Meeting 5', 'Anita', 'Y'),
        ('Meeting 6', 'Billy', 'Y'),
        ('Meeting 6', 'Charly', 'Y')
) A ([Meeting], [Name], [Meeting Boss  (expected results)])

Эту проблему легко решить с помощью WHERE EXISTS против рекурсивного объекта, имеющего иерархию сотрудников. Если вы предоставите иерархию в формате текстовой таблицы, это упростит написание ответа.

siggemannen 29.08.2024 00:33

Каковы ваши ожидаемые результаты? Можете ли вы объяснить ваши правила лучше? Будет ли ответ таков: на собрании также присутствует любой участник собрания, у которого нет начальника (кто-либо выше него в иерархии организационной структуры)? Это может означать, что на собраниях может быть более одного «начальника». Заменяет ли флаг «Босс» другую логику, означая, что все участвующие боссы включены в результаты, даже если один подчиняется другому?

T N 29.08.2024 02:37

@siggemannen, спасибо! Я добавил дерево организации в виде текстовой таблицы.

DataDude 29.08.2024 03:12

@TN Извините за неточность, я добавил в пост больше жаргона. Может быть, очень просто: необходимо отметить сотрудников с самым высоким рейтингом, присутствующих на собрании, но только в пределах их подчиненности.

DataDude 29.08.2024 03:14

Добавлен sql для создания тестовых данных.

DataDude 29.08.2024 04:02

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

GuidoG 29.08.2024 07:39

@GuidoG проверьте поле [Встреча с руководителем (ожидаемые результаты)] во второй таблице

DataDude 29.08.2024 17:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
83
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я могу придумать два подхода, но оба потребуют рекурсивного CTE для обхода иерархии.

Один из подходов — начать с каждого участника и использовать рекурсивный CTE для обхода иерархии в поисках начальника, который также присутствует на том же собрании. Если он найден, то начинающий участник не является боссом. Если ни один не найден, этот участник является боссом.

WITH AttendeeSuperiors AS (
    SELECT M.Meeting, M.Participant, M.Expected, O.Superior
    FROM MeetingData M
    JOIN OrgChart O
        ON O.Employee = M.Participant

    UNION ALL

    SELECT ATS.Meeting, ATS.Participant, ATS.Expected, O.Superior
    FROM AttendeeSuperiors ATS
    JOIN OrgChart O
        ON O.Employee = ATS.Superior
)
SELECT
    ATS.Meeting, ATS.Participant, ATS.Expected,
    CASE WHEN MAX(M2.Participant) IS NULL THEN 'Y' ELSE 'N' END AS Boss
FROM AttendeeSuperiors ATS
LEFT JOIN MeetingData M2
    ON M2.Meeting = ATS.Meeting
    AND M2.Participant = ATS.Superior
GROUP BY ATS.Meeting, ATS.Participant, ATS.Expected
ORDER BY ATS.Meeting, ATS.Participant;

(Может быть небольшое преимущество в производительности при проверке превосходного участия в CTE, так что мы можем остановить рекурсию при первом совпадении. См. связанную демонстрацию для модифицированной версии.)

Другой подход — использовать рекурсивный CTE для создания полного списка всех руководителей для каждого сотрудника. (В идеале это можно записать в таблицу и проиндексировать для эффективного повторного использования.) Затем для каждого участника собрания мы будем искать в этом превосходном списке того, кто также является участником того же собрания. Если он найден, то начинающий участник не является боссом. Если ни один не найден, этот участник является боссом.

WITH EmployeeSuperiors AS (
    SELECT O.Employee, O.Superior
    FROM OrgChart O
    --WHERE O.Employee IN (SELECT DISTINCT Participant FROM MeetingData) 

    UNION ALL

    SELECT S.Employee, O.Superior
    FROM EmployeeSuperiors S
    JOIN OrgChart O
        ON O.Employee = S.Superior
)
SELECT
    M.*,
    CASE WHEN NOT EXISTS(
            SELECT *
            FROM EmployeeSuperiors S
            JOIN MeetingData M2
                ON M2.Meeting = M.Meeting
                AND M2.Participant = S.Superior
            WHERE S.Employee = M.Participant
        ) THEN 'Y' ELSE 'N' END AS Boss
FROM MeetingData M
ORDER BY M.Meeting, M.Participant;

Если мы вычисляем EmployeeSuperiors на лету для каждого выполнения (и не сохраняем эти данные), мы можем ограничить этот расчет только теми сотрудниками, которые участвуют в собраниях, раскомментировав условие WHERE выше.

Что касается того, какой подход более эффективен, я считаю, что первый вариант может быть лучше для небольшого количества встреч и участников по отношению к общему количеству сотрудников. Второй подход может быть лучше, если вы будете обрабатывать множество собраний и иметь возможность определять, заполнять, индексировать и поддерживать постоянную таблицу EmployeeSuperiors. В любом случае я предлагаю вам провести собственные тесты производительности.

См. эту db<>fiddle для демонстрации.

Спасибо, работает потрясающе! Для моего сценария последний вариант кажется наиболее подходящим! Очень ценю!

DataDude 29.08.2024 16:58

Другие вопросы по теме