根据 SQL 中的下一条和上一条记录排序
Sorting based on next and previous records in SQL
我正在尝试通过考虑下一条和上一条记录来对特定查询进行排序,但我似乎无法完成。我想按数字和字母排序,但是,例如,如果数字 1 的最后一个字母等于数字 2 的字母之一,我想更改排序,以便字母与以下匹配记录。
Create script and SQL fiddle demo
create table Parent (
id [bigint] IDENTITY(1,1),
number bigint NOT NULL,
PRIMARY KEY (id)
)
GO
create table Child (
id [bigint] IDENTITY(1,1),
parentId BIGINT,
letter VARCHAR(1) NOT NULL,
PRIMARY KEY (id),
UNIQUE (parentId, Letter),
FOREIGN KEY (parentId) REFERENCES Parent(id)
)
GO
INSERT Parent (number) VALUES (1)
INSERT Parent (number) VALUES (2)
INSERT Parent (number) VALUES (3)
INSERT Child (parentId, letter) VALUES (1, 'A')
INSERT Child (parentId, letter) VALUES (1, 'C')
INSERT Child (parentId, letter) VALUES (2, 'B')
INSERT Child (parentId, letter) VALUES (2, 'C')
INSERT Child (parentId, letter) VALUES (3, 'B')
INSERT Child (parentId, letter) VALUES (3, 'D')
当前查询
目前我正在使用此查询进行排序:
SELECT P.number, C.letter
FROM Child C
JOIN Parent P ON C.parentId = P.id
ORDER BY P.number, C.letter
当前结果集
number letter
-------------------- ------
1 A
1 C
2 B
2 C
3 B
3 D
预期结果集
为了阐明我真正想做的事情,这里是预期的结果集(交换了 2 号的 C 和 B)。
number letter
-------------------- ------
1 A
1 C
2 C --switched
2 B --switched
3 B
3 D
其他要求和问题
- 它必须在 SQL SERVER 2005 中工作。
- 有一种情况是每个数字使用 3 个字母,如果它只使用最佳匹配我很高兴。
- 其实我也对SQLServer(学习用)的后续版本的解决方案感兴趣,但是那些并没有回答我的问题。
任何人都可以为我指明正确的方向吗?
你可以这样做。
- 使用
ROW_NUMBER()
和 PARTITION BY
识别每个 parent 的第一个和最后一个字母
- 将前一个
id
的最后一条记录与下一个 id
的第一条记录匹配。
- 检查第二个 parent id 是否有任何字母与上面选择的字母相匹配
- 使用
LEFT JOIN
并使用 CASE
或 ISNULL
为字母匹配的 id
记录设置更高的优先级
查询
;WITH CTE AS
(
SELECT id,ParentID,letter,
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID) first_element,
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID DESC) Last_element
FROM Child
), CTE2 AS
(
SELECT c1.id,c1.parentid,c1.letter,c2.parentid as c2parentid
FROM CTE c1
INNER JOIN CTE c2
ON c1.last_element = 1
AND c2.first_element = 1
AND c1.id +1 = c2.id
), CTE3 AS
(
SELECT C.parentid,C.id
FROM CTE2
INNER JOIN child C ON CTE2.c2parentid = C.parentid
AND C.letter = CTE2.letter
)
SELECT P.number, C.letter
FROM Child C
JOIN Parent P ON C.parentId = P.id
LEFT JOIN CTE3 ON CTE3.id = C.id
ORDER BY P.number, ISNULL(CTE3.id,0) DESC, C.letter
输出
number letter
1 A
1 C
2 C
2 B
3 B
3 D
编辑
如果您的 ids
不是连续的,您可以像这样更改 CTE1
和 CTE2
以利用 ROW_NUMBER()OVER(ORDER BY ID) seq_id
.
;WITH CTE AS
(
SELECT id,ParentID,letter,
ROW_NUMBER()OVER(ORDER BY ID) seq_id,
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID) first_element,
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID DESC) Last_element
FROM Child
), CTE2 AS
(
SELECT c1.id,c1.parentid,c1.letter,c2.parentid as c2parentid
FROM CTE c1
INNER JOIN CTE c2
ON c1.last_element = 1
AND c2.first_element = 1
AND c1.seq_id + 1 = c2.seq_id
)
其余代码保持不变。
我正在尝试通过考虑下一条和上一条记录来对特定查询进行排序,但我似乎无法完成。我想按数字和字母排序,但是,例如,如果数字 1 的最后一个字母等于数字 2 的字母之一,我想更改排序,以便字母与以下匹配记录。
Create script and SQL fiddle demo
create table Parent (
id [bigint] IDENTITY(1,1),
number bigint NOT NULL,
PRIMARY KEY (id)
)
GO
create table Child (
id [bigint] IDENTITY(1,1),
parentId BIGINT,
letter VARCHAR(1) NOT NULL,
PRIMARY KEY (id),
UNIQUE (parentId, Letter),
FOREIGN KEY (parentId) REFERENCES Parent(id)
)
GO
INSERT Parent (number) VALUES (1)
INSERT Parent (number) VALUES (2)
INSERT Parent (number) VALUES (3)
INSERT Child (parentId, letter) VALUES (1, 'A')
INSERT Child (parentId, letter) VALUES (1, 'C')
INSERT Child (parentId, letter) VALUES (2, 'B')
INSERT Child (parentId, letter) VALUES (2, 'C')
INSERT Child (parentId, letter) VALUES (3, 'B')
INSERT Child (parentId, letter) VALUES (3, 'D')
当前查询
目前我正在使用此查询进行排序:
SELECT P.number, C.letter
FROM Child C
JOIN Parent P ON C.parentId = P.id
ORDER BY P.number, C.letter
当前结果集
number letter
-------------------- ------
1 A
1 C
2 B
2 C
3 B
3 D
预期结果集
为了阐明我真正想做的事情,这里是预期的结果集(交换了 2 号的 C 和 B)。
number letter
-------------------- ------
1 A
1 C
2 C --switched
2 B --switched
3 B
3 D
其他要求和问题
- 它必须在 SQL SERVER 2005 中工作。
- 有一种情况是每个数字使用 3 个字母,如果它只使用最佳匹配我很高兴。
- 其实我也对SQLServer(学习用)的后续版本的解决方案感兴趣,但是那些并没有回答我的问题。
任何人都可以为我指明正确的方向吗?
你可以这样做。
- 使用
ROW_NUMBER()
和PARTITION BY
识别每个 parent 的第一个和最后一个字母
- 将前一个
id
的最后一条记录与下一个id
的第一条记录匹配。 - 检查第二个 parent id 是否有任何字母与上面选择的字母相匹配
- 使用
LEFT JOIN
并使用CASE
或ISNULL
为字母匹配的id
记录设置更高的优先级
查询
;WITH CTE AS
(
SELECT id,ParentID,letter,
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID) first_element,
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID DESC) Last_element
FROM Child
), CTE2 AS
(
SELECT c1.id,c1.parentid,c1.letter,c2.parentid as c2parentid
FROM CTE c1
INNER JOIN CTE c2
ON c1.last_element = 1
AND c2.first_element = 1
AND c1.id +1 = c2.id
), CTE3 AS
(
SELECT C.parentid,C.id
FROM CTE2
INNER JOIN child C ON CTE2.c2parentid = C.parentid
AND C.letter = CTE2.letter
)
SELECT P.number, C.letter
FROM Child C
JOIN Parent P ON C.parentId = P.id
LEFT JOIN CTE3 ON CTE3.id = C.id
ORDER BY P.number, ISNULL(CTE3.id,0) DESC, C.letter
输出
number letter
1 A
1 C
2 C
2 B
3 B
3 D
编辑
如果您的 ids
不是连续的,您可以像这样更改 CTE1
和 CTE2
以利用 ROW_NUMBER()OVER(ORDER BY ID) seq_id
.
;WITH CTE AS
(
SELECT id,ParentID,letter,
ROW_NUMBER()OVER(ORDER BY ID) seq_id,
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID) first_element,
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID DESC) Last_element
FROM Child
), CTE2 AS
(
SELECT c1.id,c1.parentid,c1.letter,c2.parentid as c2parentid
FROM CTE c1
INNER JOIN CTE c2
ON c1.last_element = 1
AND c2.first_element = 1
AND c1.seq_id + 1 = c2.seq_id
)
其余代码保持不变。