SQL-запрос, чтобы вернуть только строку на основе текучих условий - PullRequest
0 голосов
/ 25 декабря 2018

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

Например, если сотрудник был назначен в компанию до 6/1, он учитывается автоматически.

Если сотрудник был назначен в компанию после 6/1, он учитывается только в том случае, если у него есть проверка с этой компанией после назначенной даты (т. Е. Он назначен 6/25 и имеет проверку7/1 ... это следует учитывать. Если, например, они были назначены на 6/25, а проверка произошла 6/15, они не будут учитываться для этого сотрудника)

Если сотрудник отстранен от должностикомпания до 4/1 они не учитываются.Если они удалены 4/1 или позже, это считается.

Таким образом, ключевыми столбцами являются Дата создания обзора, Дата начала и Дата окончания из таблицы сотрудник-клиент.

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

Любая помощь будет оценена.

РЕДАКТИРОВАТЬ: Структура таблицы / данные ниже:

Таблица сотрудника-клиента

ID    EmpID   CustID  StartDate   EndDate
1       4       10    10/1/2017   2/21/2018
2       4       11    10/1/2017   7/31/2018
3       4       15    10/1/2017   4/8/2018
4       4       17    6/1/2018    NULL (means still active with this employee)
5       4       19    5/18/2018   NULL

Таблица данных клиента

ID    CustID   ActivityDate   Task
1       10       1/13/2018    Review
3       15       4/2/2018     Review
4       17       6/25/2018    Review
5       17       6/13/2018    Client Engagement
6       17       6/29/2018    Client Engagement
7       19       5/25/2018    Client Engagement
8       19       6/28/2018    Review

Итак, для этого примера я хочу запрос, который возвращает следующие идентификаторы клиента с данными, основанными на критериях:

  • 10: Этот клиент НЕ возвращается, потому что клиент был отстранен от сотрудника до даты прекращения 4/1.
  • 11: Этот клиент ДЕЛАЕТ получитьвернулся, потому что еУ клиента mployee был срок истечения 5/31, хотя для клиента нет отзыва
  • 15: Этот клиент DOES возвращается, потому что у сотрудника был клиент, прошедший 4/ 1 предельная дата до того, как она была удалена из них.
  • 17: Обручение клиента с 29.06.2008 DOES возвращается, но обручение клиента с 13.06.2008 делает НЕ получить обратно, потому что это произошло ДО того, как проверка была проведена с этим клиентом (фактически, когда Дата начала работы сотрудника для клиента - ПРОШЛОЕ 5/31, действие считается только ПОСЛЕ того, как у него была проверка с этим клиентом - вседействие, которое происходит до этой даты проверки, игнорируется)
  • 19: в этом случае возвращается Обязательство клиента DOES , поскольку сотрудник был назначен им до 6/1, поэтому любая операцияучитывается независимо от того, была ли сделана проверка до появления другого элемента.

Надеюсь, это объяснение и разбивка имеют смысл.

ОБНОВЛЕНИЕ: Вот сценарии таблицы и ожидаемые результаты:

CREATE TABLE Cust_Employee(

Cust_Emp_ID int IDENTITY(1,1) NOT NULL,

Cust_ID int NOT NULL,

Emp_ID int NULL,

Start_Date datetime NULL,

End_Date datetime NULL,

CONSTRAINT PK_Cust_Employee PRIMARY KEY CLUSTERED

(

Cust_Emp_ID ASC

)WITH (PAD INEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON PRIMARY

)ON PRIMARY

GO

CREATE TABLE Cust_Data(

Cust_Data_ID int IDENTITY(1,1) NOT NULL,

Cust_ID int NULL,

Activity_Date datetime NULL,

Task VARCHAR(50) NULL

)

CONSTRAINT PK_Client_Data PRIMARY KEY CLUSTERED

(

Cust_Data_ID ASC

)WITH (PAD INEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON PRIMARY

)ON PRIMARY

GO

INSERT INTO Cust_Employee VALUES(4, 10, '10/1/2017', '2/21/2018')

INSERT INTO Cust_Employee VALUES(4, 11, '10/1/2017', '7/31/2018')

INSERT INTO Cust_Employee VALUES(4, 15, '10/1/2017', '4/8/2018')

INSERT INTO Cust_Employee VALUES(4, 17, '6/1/2018', NULL)

INSERT INTO Cust_Employee VALUES(4, 19, '5/18/2018', NULL)

INSERT INTO Cust _Data VALUES(10, '1/13/2018', 'Review')

INSERT INTO Cust _Data VALUES(15, '4/2/2018', 'Review')

INSERT INTO Cust _Data VALUES(17, '6/25/2018', 'Review')

INSERT INTO Cust _Data VALUES(17, '6/13/2018', 'Client Engagement')

INSERT INTO Cust _Data VALUES(17, '6/29/2018', 'Client Engagement')

INSERT INTO Cust _Data VALUES(19, '5/25/2018', 'Client Engagement')

INSERT INTO Cust _Data VALUES(19, '6/28/2018', 'Review')

Ожидаемые результаты:

enter image description here

Ответы [ 5 ]

0 голосов
/ 10 января 2019

Может быть, это поможет.

SELECT employee
FROM employee emp
JOIN employee-customer ecust ON ecust.empID = ecust.empID
JOIN customer cust ON cust.custID = ecust.custID and cust.Task = 'Review'
WHERE 
(emp.StartDate <= '01062019' and (emp.EndDate <= '01042019' or emp.EndDate = NULL) or 
( emp.StartDate <= cust.ActivityDate )
0 голосов
/ 10 января 2019

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

WITH Reviews AS (
    SELECT d1.Cust_ID, 'true' AS HasActiveReview
    FROM Cust_Data d1
    INNER JOIN Cust_Data d2 ON d1.Cust_ID = d2.Cust_id
    WHERE d1.Task = 'Review' 
        AND d2.Task = 'Client Engagement'
        AND d1.Activity_Date >= d2.Activity_Date
    ),
RuleData as (
SELECT e.Cust_Emp_ID,
    (CASE WHEN e.Start_Date >= '20180601' THEN 'true' ELSE 'false' END) AS StartAfter0601,
    (CASE WHEN e.End_Date <= '20180401' THEN 'true' ELSE 'false' END) as EndBefore0401,
    COALESCE(r.HasActiveReview, 'false') as HasReview
FROM Cust_Employee e
LEFT OUTER JOIN reviews r on e.Cust_ID = r.Cust_ID)
SELECT e.Emp_id, e.Cust_id, e.Start_Date, e.end_date, MAX(d.Activity_Date) AS Activity_Date, d.Task
FROM RuleData r
INNER JOIN Cust_Employee e on r.Cust_Emp_ID = e.Cust_Emp_ID
LEFT OUTER JOIN Cust_Data d on e.Cust_ID = d.Cust_ID
WHERE r.EndBefore0401 = 'false'
    AND ((r.StartAfter0601 = 'true' AND r.HasReview = 'true') OR r.StartAfter0601 = 'false')
GROUP BY e.Emp_id, e.Cust_id, e.Start_Date, e.end_date, d.Task, r.Cust_Emp_ID, r.StartAfter0601, r.EndBefore0401, r.HasReview
ORDER BY e.Emp_id, e.Cust_id;

Если вам нужно отладить, легко добавить данные правила в конецзапрос, чтобы увидеть, почему возвращается строка:

WITH Reviews AS (
    SELECT d1.Cust_ID, 'true' AS HasActiveReview
    FROM Cust_Data d1
    INNER JOIN Cust_Data d2 ON d1.Cust_ID = d2.Cust_id
    WHERE d1.Task = 'Review' 
        AND d2.Task = 'Client Engagement'
        AND d1.Activity_Date >= d2.Activity_Date
    ),
RuleData as (
SELECT e.Cust_Emp_ID,
    (CASE WHEN e.Start_Date >= '20180601' THEN 'true' ELSE 'false' END) AS StartAfter0601,
    (CASE WHEN e.End_Date <= '20180401' THEN 'true' ELSE 'false' END) as EndBefore0401,
    COALESCE(r.HasActiveReview, 'false') as HasReview
FROM Cust_Employee e
LEFT OUTER JOIN reviews r on e.Cust_ID = r.Cust_ID)
SELECT e.Emp_id, e.Cust_id, e.Start_Date, e.end_date, MAX(d.Activity_Date) AS Activity_Date, d.Task, r.Cust_Emp_ID, r.StartAfter0601, r.EndBefore0401, r.HasReview
FROM RuleData r
INNER JOIN Cust_Employee e on r.Cust_Emp_ID = e.Cust_Emp_ID
LEFT OUTER JOIN Cust_Data d on e.Cust_ID = d.Cust_ID
WHERE r.EndBefore0401 = 'false'
    AND ((r.StartAfter0601 = 'true' AND r.HasReview = 'true') OR r.StartAfter0601 = 'false')
GROUP BY e.Emp_id, e.Cust_id, e.Start_Date, e.end_date, d.Task, r.Cust_Emp_ID, r.StartAfter0601, r.EndBefore0401, r.HasReview
ORDER BY e.Emp_id, e.Cust_id;

Я использовал 'true' и false 'для представления логических значений, вы можете использовать 1 и 0 BIT, если хотите.

Запустив запрос, он возвращает дополнительную строку для Cust_Id 19, возвращая как запись «Обязательство клиента», так и запись «Просмотр».Я не уверен, почему это не должно делать это, когда вы хотите обе строки для Cust_Id 17, и кажется, что то же самое должно применяться к Cust_Id 19

Emp_Id,Cust_id,Start_Date,End_Date,Activity_Date,Task
4,11,2017-10-01 00:00:00.000,2018-07-31 00:00:00.000,NULL,NULL
4,15,2017-10-01 00:00:00.000,2018-04-08 00:00:00.000,2018-04-02 00:00:00.000,Review
4,17,2018-06-01 00:00:00.000,NULL,2018-06-29 00:00:00.000,Client Engagement
4,17,2018-06-01 00:00:00.000,NULL,2018-06-25 00:00:00.000,Review
4,19,2018-05-18 00:00:00.000,NULL,2018-05-25 00:00:00.000,Client Engagement
4,19,2018-05-18 00:00:00.000,NULL,2018-06-28 00:00:00.000,Review
0 голосов
/ 04 января 2019

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

;WITH AssignedBefore as
(
    --if the employee was assigned to a company before 6/1 they get counted automatically.
    SELECT Cust_ID, Emp_ID
    FROM Cust_Employee
    WHERE Start_Date <= '20180601'
),
AssignedReviewed as
(
    --If the employee was assigned to a company after 6/1 they only get counted IF they have a review with that company after the date they were assigned
    SELECT Cust_ID, Emp_ID
    FROM Cust_Employee E
    CROSS APPLY (
        SELECT 1 as Exist
        FROM Cust_Data D
        WHERE D.Cust_ID = E.Cust_ID
        AND D.Task = 'Review'
        AND D.Activity_Date > E.Start_Date
        ) C
    WHERE E.Start_Date > '20180601'
),
RemovedAfter as
(
    --If the employee gets removed from a company before 4/1 they dont get counted. If they are removed on or after 4/1 it counts.
    SELECT Cust_ID, Emp_ID
    FROM Cust_Employee
    WHERE End_Date >= '20180401'
),
RemovedBefore as
(
    --If the employee gets removed from a company before 4/1 they dont get counted. If they are removed on or after 4/1 it counts.
    SELECT Cust_ID, Emp_ID
    FROM Cust_Employee
    WHERE End_Date <= '20180401'
)
--Positive Rules
SELECT * FROM AssignedBefore
UNION
SELECT * FROM AssignedReviewed
UNION 
SELECT * FROM RemovedBefore
--Negative Rules
EXCEPT
SELECT * FROM RemovedBefore

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

0 голосов
/ 06 января 2019

Способ, которым я подойду к этому запросу, очень подойдет как для общих табличных выражений, так и для EXISTS / NOT EXISTS.

Я собрал для данного сотрудника, если до этого были какие-то взаимодействия с заказчикомСотрудник имел отзыв с этим клиентом, их следует игнорировать в рамках этого сотрудника.Для этого я реализовал общее табличное выражение (cte_engagements_to_ignore), чтобы отфильтровать их.Результатами этого общего табличного выражения являются записи employee / cust_data, которые следует игнорировать.Он работает, сначала отфильтровывая все задания, а затем отфильтровывая только те из них, где не было предыдущего обзора, который произошел после назначения сотрудника и до задания, с которым мы сравниваем.

Далее мызапросить таблицы данных о сотруднике / клиенте и автоматически указать, был ли клиент запущен до 6/1 ИЛИ, если существует проверка, которая произошла после того, как он был назначен сотруднику.Затем мы исключаем, если назначение не назначено до 4/1 или если задание является идентифицированным, которое мы определили, следует игнорировать для данного сотрудника.

Действительно очень загадочное!

WITH cte_engagements_to_ignore AS 
    ( -- filter out client engagements that happened prior to reviews
        SELECT 
            A.Emp_ID,
            B.Cust_Data_ID
        FROM Cust_Employee A 
        INNER JOIN Cust_Data B 
        ON      A.Cust_ID = B.Cust_ID
        WHERE   B.Task = 'Client Engagement' 
            AND NOT EXISTS 
                    ( -- exclude this client engagement if there was not a review for this customer prior to it
                        SELECT
                            *
                        FROM Cust_Data X1 
                        WHERE   A.Cust_ID = X1.Cust_ID
                            AND X1.Task = 'Review'
                            AND A.Start_Date < X1.Activity_Date -- review happened after assignment
                            AND B.Activity_Date > X1.Activity_Date -- review happened prior to engagement
                    )
    )
SELECT 
    A.Emp_ID,
    A.Cust_ID,
    A.Start_Date,
    A.End_Date,
    B.Activity_Date,
    B.Task
FROM Cust_Employee A 
LEFT JOIN Cust_Data B 
ON      A.Cust_ID = B.Cust_ID
WHERE   (
            -- included automatically if started before 6/1
            A.Start_Date < '2018-06-01' 
            -- or include if there is a review after assignment
        OR EXISTS 
                ( 
                    SELECT 
                        *
                    FROM Cust_Data X1 
                    WHERE   A.Cust_ID = X1.Cust_ID
                        AND A.Start_Date < X1.Activity_Date
                        AND X1.Task = 'Review'
                )
        )
        -- exclude if unassigned prior to 4/1
    AND ISNULL(A.End_Date, '2050-01-01') >= '2018-04-01'
        -- filter out engagements we identified should be ignored
    AND NOT EXISTS 
        (
            SELECT 
                *
            FROM cte_engagements_to_ignore X1 
            WHERE   A.Emp_ID = X1.Emp_ID
                AND B.Cust_Data_ID = X1.Cust_Data_ID
        )
0 голосов
/ 04 января 2019

Я не уверен, понял ли я всю вашу просьбу.На самом деле, я что-то упускаю, так как полученные результаты не совсем такие, как вы.Код, который я подготовил:

SELECT E.Cust_ID AS Emp_ID, E.Emp_ID AS Cust_ID, E.Start_Date, E.End_Date, 
        MAX(D.Activity_Date) AS Activity_Date, D.Task
    FROM Cust_Employee E
    LEFT OUTER JOIN Cust_Data D
        ON E.Emp_ID = D.Cust_ID
    WHERE COALESCE(E.End_Date, GETDATE()) > '20180401'
    GROUP BY 
            E.Cust_ID, E.Emp_ID, E.Start_Date, E.End_Date, 
            D.Task
    ORDER BY E.Cust_ID;[![enter image description here][1]][1]

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

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

Во-первых, мне нужнопризнать, что требования для меня не на 100% ясны, потому что основаны на примерах, что обычно в реальной жизни.Необходимо четко определить бизнес-правила, которые должны применяться, и последовательность (порядок), который необходимо применять.Итак, основываясь на моем предположении, я построил следующее решение.Преимущество этого решения состоит в том, что его очень легко отлаживать как:

  1. Каждое правило идентифицируется номером , который может отображаться в режиме отладки (опускается, если требуется впоследствии)и сообщает, какое правило было применено
  2. Правила применяются последовательно , поэтому, если одно правило создает запись для отображения, остальные правила не применяются.Это потому, что важно, чтобы порядок проверки правил
  3. Отрицательный номер для правила показывает, что правило означает, что запись не должна отображаться .

:

WITH CTE AS (
    SELECT E.Cust_ID AS Emp_ID, E.Emp_ID AS Cust_ID, 
           E.Start_Date, E.End_Date, 
        MAX(D.Activity_Date) AS Activity_Date, D.Task,
        CASE 
            -- RULE -1: Removed Prior to 4/1 cutoff date
            WHEN E.End_Date < '20180401'                        THEN -1

            --  RULE 1: If the employee has had the customer past the 5/31 cutoff date, even though there is no review for the customer
            WHEN E.End_Date > '20180531'                        THEN 1

            --  RULE 2: If the employee had the customer past the 4/1 cutoff date before it was removed from them
            WHEN D.Activity_Date > '20180401' AND D.Activity_Date <= E.End_Date THEN 2

            --   RULE -2: Client engagement from 6/13/2018 does NOT get returned because it happened BEFORE the review was done with this client
            WHEN D.Task = 'Client Engagement' 
             AND NOT EXISTS (SELECT 1 FROM Cust_Data D2 WHERE D2.Cust_ID = E.Emp_ID AND D2.Task = 'Review' AND D2.Activity_Date <= D.Activity_Date)
                THEN -2

            --   RULE 12: If the employee was assigned to a company before 6/1 they get counted automatically.
            WHEN E.Start_Date <= '20180601'                     THEN 12

            --  RULE 14: If EndDate later than June-1-2018
            WHEN  COALESCE(E.End_Date, GETDATE()) > '20180601'  THEN 14

            -- RULE 0: Other cases
            ELSE 0 

        END AS [Rule]
    FROM Cust_Employee E
    LEFT OUTER JOIN Cust_Data D
        ON E.Emp_ID = D.Cust_ID
        --AND D.Activity_Date > '20180401'

    GROUP BY 
            E.Cust_ID, E.Emp_ID, E.Start_Date, E.End_Date, 
            D.Task, D.Activity_Date
    ) 
SELECT Emp_ID, Cust_ID, Start_Date, End_Date, Activity_Date, Task, [Rule]
    FROM CTE
    WHERE [Rule] > 0
    ORDER BY Cust_ID, Start_Date, Activity_Date;

Преимущество этого метода в том, что он вычисляет и показывает примененное правило , поэтому его можно очень легко отладитькак запрос показывает, какое правило было применено.Если порядок правил или какое-то правило неверно, его можно обнаружить очень быстро и исправить.То же самое относится и к будущим изменениям, так как обычно эти правила, основанные на датах, меняются очень часто, и нам нужен простой способ поддержки кода.Наконец, я надеюсь, что это упражнение даст некоторые идеи для будущих разработок, поскольку отслеживаемость и поддержка очень важны при создании кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...