【发布时间】:2021-03-26 05:00:51
【问题描述】:
我正在使用 MS SQL 处理学生数据,并且需要遵循一些非常具体的规则。
样本表
CREATE TABLE students (
encounterId INT,
studentId INT,
positionId INT
);
INSERT INTO students
VALUES
(100,20,1),
(100,32,2),
(100,14,2),
(101,18,1),
(101,87,2),
(101,78,3),
(102,67,2),
(102,20,2),
(103,33,3),
(103,78,4),
(104,16,1),
(104,18,4),
(105,67,4),
(105,18,4),
(105,20,4);
表格规则
该表显示了学生被安排在 1 到 4 之间的位置。
一次遭遇中可以有多个学生。
一场遭遇战中,位置 1 只能有一名学生。
一场遭遇战中只能有一名学生处于位置 3。
但是,在一次遭遇战中,多名学生可能处于位置 2 和 4。
业务规则
每次遇到的业务规则如下:
- 如果遇到的学生在位置 1,则返回该遇到的 row(单数位置 1),删除该遇到的任何位置 2-4 行
- ELSE if no position 1 THEN return the meet's rows for students (can be multiple) in position 2, remove any position 3 or 4 for that meet
- ELSE 如果没有位置 1-2 THEN 为位置 3 的学生返回遭遇的行,删除该遭遇的任何位置 4 行
- ELSE 如果没有位置 1-3 THEN 为位置 4 的学生返回遭遇的行
不太好用
studentId 值的串联是可以接受的,但并不理想。我有这个半工作与一系列不稳定的联合和 string_aggs。 positionId=3 的行是有问题的,正如我在代码中所说的那样。
此外,这种联合/不像架构在我的小型开发数据库中有效,但在生产数据库中会出现严重的性能问题:
WITH tAll
AS ( SELECT
encounterId,
studentId,
positionId
FROM
students)
SELECT
encounterId,
CAST(studentId AS VARCHAR) AS [studentId],
1 AS [ord]
FROM
tAll
WHERE
positionId = 1
UNION
SELECT
encounterId,
CAST(studentId AS VARCHAR),
2 AS [ord]
FROM
(
SELECT
encounterId,
STRING_AGG(studentId, ',') AS [studentId],
STRING_AGG(positionId, ',') AS [positionId]
FROM
tAll
GROUP BY
encounterId
) t2
WHERE
positionId NOT LIKE '%1%'
AND positionId NOT LIKE '%3%'
AND positionId NOT LIKE '%4%'
UNION
SELECT
encounterId,
CAST(studentId AS VARCHAR),
3 AS [ord]
FROM
--tAll WHERE positionId=3
--Limiting to positionId=3 includes results (101,18,1) AND (101,78,3).. I just want (101,18,1)
--Using the below code instead, but this creates other problems
(
SELECT
encounterId,
STRING_AGG(studentId, ',') AS [studentId],
STRING_AGG(positionId, ',') AS [positionId]
FROM
tAll
GROUP BY
encounterId
) t3
WHERE
positionId NOT LIKE '%1%'
AND positionId NOT LIKE '%2%'
AND positionId NOT LIKE '%4%'
--This excludes 103 entirely since it has both positionId values of 3 AND 4... I just want (103,33,3)
UNION
SELECT
encounterId,
CAST(studentId AS VARCHAR),
4 AS [ord]
FROM
(
SELECT
encounterId,
STRING_AGG(studentId, ',') AS [studentId],
STRING_AGG(positionId, ',') AS [positionId]
FROM
tAll
GROUP BY
encounterId
) t4
WHERE
positionId NOT LIKE '%1%'
AND positionId NOT LIKE '%2%'
AND positionId NOT LIKE '%3%';
我想要返回的东西
| encounterId | studentId | ord |
|---|---|---|
| 100 | 20 | 1 |
| 101 | 18 | 1 |
| 102 | 67 | 2 |
| 102 | 20 | 2 |
| 103 | 33 | 3 |
| 104 | 16 | 1 |
| 105 | 67 | 4 |
| 105 | 18 | 4 |
| 105 | 20 | 4 |
【问题讨论】:
标签: sql sql-server subquery greatest-n-per-group sql-server-2017