【发布时间】:2020-07-12 11:25:30
【问题描述】:
我正在尝试评估商店访客对 COVID-19 传播的影响。
这是一个简单的场景:
- VisitorA 走进商店并在时间 = 0 时与 Employee1 会面。
- VisitorA 然后会见 Employee2 @ Time = 1。
- VisitorB 走进商店并在时间 = 1 时与 Employee1 会面。
- VisitorB 然后会见 Employee3 @ Time = 2。
- VisitorA 离开商店。
当我收集所有访客数据以及他们在一段时间内遇到的人时,数据集如下所示:
表visitorByEmployee:
| VisitorID | EmployeeID | Contact |
+-----------+------------+-------------------+
| 100 | X123 | 3/11/2020 1:00 |
| 100 | X124 | 3/11/2020 1:10 |
| 101 | X123 | 3/12/2020 1:11 |
| 101 | X125 | 3/11/2020 1:20 |
| 102 | X126 | 3/12/2020 10:00 |
| 102 | X124 | 3/12/2020 10:00 |
| 103 | X123 | 3/12/2020 11:00 |
| 104 | X124 | 3/12/2020 12:00 |
| 104 | X126 | 3/12/2020 12:00 |
| 105 | X126 | 3/12/2020 12:00 |
我想根据这些数据构建一个层次结构,最终可以表示如下:
每棵树都代表访客对病毒传播的影响:
100
--> X123
--> 101
--> X125
--> 103
--> X124
--> 104
102
--> X126
--> 104
--> 105
--> X124
--> 104
--> X126
我尝试通过首先找到根节点(不受之前访问者和/或他们看到的员工影响的根访问者)来做到这一点。这些是 100 和 102。
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY EmployeeID ORDER BY Contact) AS SeenOrder
INTO
#SeenOrder
FROM
visitorByEmployee
SELECT *
INTO #RootVisitors
FROM #SeenOrder
WHERE SeenOrder = 1
从#RootVisitors 和#SeenOrder 开始,我想构建一个表格,可以告诉我影响的层次结构,并可能导致如下结果:
| InitVisitorID | HLevel | EmployeeID | VisitorID |
+---------------+------------+-------------------+-------------+
| 100 | 0 | X123 | 100 |
| 100 | 0 | X124 | 100 |
| 100 | 1 | X123 | 101 |
| 100 | 1 | X123 | 103 |
| 100 | 1 | X124 | 104 |
| 100 | 2 | X125 | 101 |
| 102 | 0 | X126 | 102 |
| 102 | 0 | X124 | 102 |
| 102 | 1 | X126 | 104 |
| 102 | 1 | X126 | 105 |
| 102 | 1 | X124 | 104 |
| 102 | 2 | X126 | 104 |
这是可以使用递归 CTE 完成的吗?我试图这样做,但由于从访客到员工到访客到员工的层次结构不断变化,我很难创建递归 CTE。
更新 这是我正在研究的递归 CTE。它还不起作用,但我正在分享这种方法:
; WITH exposure_tree AS (
/* == Anchor with the root visitors == */
/* == You can think of this: The Employees who were exposed by the Visior == */
SELECT re.VisitorID InitVisitor,
1 as Level,
CASE WHEN 1%2=1 THEN 'Visitor' ELSE 'Employee' END ExposerType,
re.VisitorID Exposer,
re.EmployeeID Exposee,
re.SeenOrder,
re.InitialContact
FROM #SeenOrder re
WHERE re.SeenOrder = 1
/* == Recursive Part #1 ==
Get the visitors who were exposed next by the exposed employees
*/
UNION ALL
SELECT et.VisitorID InitVisitor,
Level + 1,
CASE WHEN (Level+1)%2=1 THEN 'Visitor' ELSE 'Employee' END ExposerType,
re.EmployeeID,
re.VisitorID, -- These are switched from the anchor.
re.SeenOrder,
re.InitialContact
FROM #SeenOrder re
JOIN exposure_tree et ON et.Exposee = re.EmployeeID AND re.SeenOrder > 1 AND re.InitialContact > et.InitialContact
UNION ALL
/* == Recursive Part #2 ==
Get the next employees who were exposed the second level exposed visitors
*/
SELECT et.VisitorID InitVisitor,
Level + 2,
CASE WHEN (Level+2)%2=1 THEN 'Visitor' ELSE 'Employee' END ExposerType,
re.VisitorID,
re.EmployeeID,
re.SeenOrder,
re.InitialContact
FROM #ROOT_EXPOSURES re
JOIN exposure_tree et ON re.VisitorID = et.Exposer and re.SeenOrder > 1 AND re.InitialContact > et.InitialContact
)
select top 1000 * from exposure_tree ORDER BY InitVisitor, Level
【问题讨论】:
-
好问题。 +1 让我尝试一些事情...
-
为什么在您的示例中“根访问者”是 100 和 102?
101与X125的会议是该员工和该访客的第一次会议。还是日期突然变为11而不是12是错误的。相反,访客 102 第一次见到员工 X124 - 但 X124 已经见过访客 100,那么为什么 102 是“根”? -
你说得对,Martin 101 是一个根。 102 是 root,因为他们第一次遇到的是 X126。
-
他们第一次见面是在
3/12/2020 10:00同时与两名员工
标签: sql sql-server-2016 common-table-expression recursive-cte