我知道这是可能的,但不确定如何设置。 基本上,我需要为每位员工提取数据,但前提是这些数据必须满足某些基于不同日期的条件。
例如,如果员工在6/1之前被分配到公司,他们将被自动计数。
如果员工在6/1之后被分配到公司,则只有在分配日期之后他们对该公司进行了审核(即,他们在6/25被分配并在7 / 1 ...这应该算在内。例如,如果他们在6/25接受调查,而审核在6/15进行,则不会计入该员工的人数
如果员工在4/1之前从公司离职,则不计算在内。如果它们在4/1或之后被删除,则算在内。
因此,关键列是审核的“创建日期”,“员工-客户”表中的“开始日期”和“结束日期”。
我认为这将需要是某种子查询,该子查询返回该雇员与该客户的雇员的开始日期,然后根据评估该日期的Case语句将审查日期与审查日期进行比较,但是我不确定确切如何为此。
任何帮助将不胜感激。
编辑:下面的表结构/数据:
员工-客户表
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
因此,在此示例中,我想要一个查询,该查询带回以下客户ID以及基于条件的数据:
希望这种解释和细分是有道理的。
更新:这是表脚本和预期结果:
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')
预期结果:
答案 0 :(得分:5)
我不确定我是否理解您的所有要求。实际上,我错过了一些东西,因为我获得的结果与您不完全相同。 我准备的代码:
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%明确的,因为这些示例基于现实生活中的常见情况。有必要明确确定必须应用的业务规则和需要应用的顺序(顺序)。 因此,根据我的猜测,我构建了以下解决方案。该解决方案的优点是非常容易调试为:
:
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;
此方法的最好之处在于,它计算并显示已应用的规则,因此,当查询显示已应用哪个规则时,可以非常容易地对其进行调试。如果规则或某个规则的顺序不正确,则可以非常快速地检测到并修复它。将来的更改也是如此,因为通常这些基于日期的规则确实经常更改,因此我们需要一种简单的方法来维护代码。 最后,我希望该练习将为将来的开发提供一些思路,因为在创建代码时可追溯性和可支持性非常重要。
答案 1 :(得分:2)
对于这种复杂的逻辑,我建议使用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元组的结果,就可以添加所需的任何信息。
答案 2 :(得分:0)
我处理此查询的方式将对通用表表达式和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
)
答案 3 :(得分:0)
我的方法是使用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
答案 4 :(得分:0)
也许这会有所帮助。
PS C:\> $notepad = Start-Process notepad++ -Wait -PassThru
<close the application at this point>
PS C:\> $notepad.exitcode
PS C:\> 0