Создать флаговый столбец после сравнения двух таблиц - PullRequest
1 голос
/ 22 мая 2019

У меня есть две разные таблицы отчетов с датами и владельцем отчета.Я хотел бы выбрать людей, которые хотя бы раз написали отчеты.Мне также нужно вычисленное поле, которое показывает, какой номер отчета они написали.Отчет 1 имеет приоритет, поэтому, если в любое время кто-то создал отчет один, новый столбец report_number должен содержать 1, иначе 2 (для отчета 2).

'people' table
| person_id | full_name
--------------------------
| 1         | John L Smith
| 2         | Carl M Selt
| 3         | Another Person

'report_1' table
| report_1_id | author_person_id | date_entered | other_columns
---------------------------------------------------------------
| 1           | 1                | 2018-01-12   | foo
| 2           | 1                | 2018-02-18   | foo foo

'report_2' table
| report_2_id | author_person_id | date_entered | other_columns
---------------------------------------------------------------
| 1           | 1                | 2018-03-21   | bar
| 2           | 1                | 2018-03-28   | bar bar
| 3           | 2                | 2018-04-16   | baz
| 4           | 2                | 2018-04-30   | baz baz

Желаемые результаты:

| full_name    | report_number
---------------------------
| John L Smith | 1
| Carl M Smelt | 2

Обратите внимание, что report_number у Джона 1, хотя он также является автором отчета 2.

Отчет 1 и отчет 2 имеют разные дополнительные столбцы, даже если они выглядят одинаково выше.

То, что я пробовал:

    /* Get people from both reports */
WITH report_1_people AS (
    SELECT P.full_name
    FROM report_1 R1
    INNER JOIN people P ON R1.author_person_id = P.person_id
    WHERE P.full_name IS NOT NULL 
    AND P.full_name <> ''
), report_2_people AS (
    SELECT P2.full_name
    FROM report_2 R2
    INNER JOIN people P2 ON R2.author_person_id = P2.person_id
    WHERE P2.full_name IS NOT NULL 
    AND P2.full_name <> ''
)
SELECT 
    P.full_name,
    CASE WHEN P.full_name IN ( /* Check if in report 1 */
                    SELECT full_name
                    FROM report_1)
                    THEN 1
            ELSE 2
            END AS report_number
FROM people P
WHERE P.full_name IS NOT NULL AND P.full_name <> ''
/* Eliminate duplicate names */
GROUP BY P.full_name 
/* Filter only who either authored report 1 or report 2 */
HAVING P.full_name IN (SELECT full_name
                       FROM report_1_people)
OR P.full_name IN (SELECT full_name
                   FROM report_2_people)

Примечание. С таблицей людей есть GROUP BY, потому что по какой-то причине есть повторяющиеся записи.

Запрос занял так много времени, он отключился от базы данных (24+ часа), поэтому я думаю, что я делаю что-то не так.Есть ли лучший способ выполнить этот столбец, рассчитанный по флагу на основе двух таблиц?Относительно новичок в SQL, поэтому мне интересно, есть ли другой способ думать, что я перебираю логику SQL.

Ответы [ 3 ]

1 голос
/ 22 мая 2019

IN на CTE могут убить его мертвым.

Альтернативный способ - использовать EXISTS, чтобы проверить, написал ли человек отчет. Выражение CASE может обрабатывать приоритет.

SELECT p.full_name,
       CASE
         WHEN EXISTS (SELECT *
                             FROM report_1 r1
                             WHERE r1.author_person_id = p.person_id) THEN
           1
         WHEN EXISTS (SELECT *
                             FROM report_2 r2
                             WHERE r2.author_person_id = p.person_id) THEN
           2
       END report_number
       FROM people p
       WHERE EXISTS (SELECT *
                            FROM report_1 r1
                            WHERE r1.author_person_id = p.person_id)
              OR EXISTS (SELECT *
                                FROM report_2 r2
                                WHERE r2.author_person_id = p.person_id);

Для производительности попробуйте индексы на report_1 (author_person_id) и report_2 (author_person_id). Для people вы можете поэкспериментировать с индексом на person_id (который, вероятно, уже существует) или, возможно, с составным индексом на person_id и full_name.

1 голос
/ 22 мая 2019

Вы можете использовать OUTER APPLY:

SELECT person_id, full_name, COALESCE(ca1.report_num, ca2.report_num)
FROM people
OUTER APPLY (SELECT TOP (1) 1 FROM report_1 WHERE author_person_id = people.person_id) AS ca1(report_num)
OUTER APPLY (SELECT TOP (1) 2 FROM report_2 WHERE author_person_id = people.person_id) AS ca2(report_num)

Демонстрация на дб <> скрипка

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

Это просто еще один способ получить результат.

SELECT 
    P.full_name,
    MIN( R.Report_Number) AS report_number
FROM people P
OUTER APPLY (SELECT 1 WHERE EXISTS(SELECT * FROM report_1 R1 WHERE R1.author_person_id = P.person_id)
             UNION ALL
             SELECT 2 WHERE EXISTS(SELECT * FROM report_2 R2 WHERE R2.author_person_id = P.person_id)) AS R(Report_Number)
WHERE P.full_name IS NOT NULL AND P.full_name <> ''
/* Eliminate duplicate names */
GROUP BY P.full_name;
...