基于 SQL 中下一条和上一条记录的复杂排序
Complex sorting based on next and previous records in SQL
这是
的后续问题
但现在它变得有点复杂,例如:
- 如果1的任意一个字母与2的任意一个字母匹配,我想改变顺序,使该字母与下面的记录匹配。
- 如果未找到匹配项,则应按字母进行正常排序。
- ID 可能不成功,并且记录的顺序不正确。 [SQLFiddle Demo]
[Create script and SQL Fiddle demo]
create table Parent (
id [bigint] IDENTITY(1,2),
number bigint NOT NULL,
PRIMARY KEY (id)
)
GO
create table Child (
id [bigint] IDENTITY(1,2),
parentId BIGINT,
letter VARCHAR(1) NOT NULL,
PRIMARY KEY (id),
UNIQUE (parentId, Letter),
FOREIGN KEY (parentId) REFERENCES Parent(id)
)
GO
DECLARE @ParentIdentity BIGINT
INSERT Parent (number) VALUES (2)
SET @ParentIdentity = @@IDENTITY
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'C')
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'B')
INSERT Parent (number) VALUES (3)
SET @ParentIdentity = @@IDENTITY
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'D')
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'B')
INSERT Parent (number) VALUES (1)
SET @ParentIdentity = @@IDENTITY
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'C')
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'A')
GO
当前查询
目前我正在使用此查询进行排序:
;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
), 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 B
2 C
3 B
3 D
预期结果集
为了阐明我真正想做什么,这里是预期的结果集:
number letter
-------------------- ------
1 A
1 C
2 C
2 B
3 B
3 D
其他要求和问题
- 它必须在 SQL Server 2005 中工作。
- 有一种情况是每个数字使用 3 个字母,如果它只使用最佳匹配我很高兴。
任何人都可以指出正确的方向来处理这种情况吗?
如果我理解你的要求是对的,你有 parentId
的某些部分,你希望每个部分都以 letter
开始,那些在前一部分中,并以 letter
结束那些在下一部分,如果是,试试这个:
;WITH t AS (
SELECT
c.id,
c.parentId,
c.letter,
dt.parentSeq
FROM
Child c
JOIN (
SELECT
ci.parentId, ROW_NUMBER() OVER (ORDER BY p.number) parentSeq
FROM
Child ci
JOIN
Parent p ON ci.parentId = p.id
GROUP BY
ci.parentId, p.number) dt ON c.parentId = dt.parentId
)
SELECT
p.number,
t.letter
FROM
t
JOIN
Parent p ON t.parentId = p.id
ORDER BY
p.number,
CASE WHEN t.letter IN (SELECT ti.letter FROM t ti WHERE ti.parentSeq = t.parentSeq - 1) THEN 0
WHEN t.letter IN (SELECT ti.letter FROM t ti WHERE ti.parentSeq = t.parentSeq + 1) THEN 2
ELSE 1 END,
t.letter
这个问题我用Row_Number
函数希望对你有帮助。
SELECT Number, Letter
FROM (
SELECT number, letter, ROW_NUMBER()over(Partition by letter,Number order by Number) as R
FROM Child C
JOIN Parent P
ON P.id = C.parentId) x
ORDER BY number, R desc
我不确定这是否适用于一些复杂的真实数据,但您可以检查:
;WITH cte AS(SELECT ci.id, ci.parentId, ci.letter, p.number,
DENSE_RANK() OVER (ORDER BY p.number) rn
FROM Child ci
JOIN Parent p ON ci.parentId = p.id)
SELECT t1.number, t1.letter
FROM cte t1
LEFT JOIN cte t2 ON t2.rn = t1.rn - 1 AND t1.letter = t2.letter
LEFT JOIN cte t3 ON t1.rn = t3.rn - 1 AND t1.letter = t3.letter
ORDER BY t1.number, t1.letter + t2.letter DESC, t1.letter + t3.letter, t1.letter
如果这不起作用,您可以切换到@shA.t 的巧妙排序:
;WITH cte AS(SELECT ci.id, ci.parentId, ci.letter, p.number,
DENSE_RANK() OVER (ORDER BY p.number) rn
FROM Child ci
JOIN Parent p ON ci.parentId = p.id)
SELECT number, letter
FROM cte
ORDER BY
number,
CASE WHEN letter IN (SELECT ti.letter FROM cte ti WHERE ti.rn = cte.rn - 1) THEN 0
WHEN letter IN (SELECT ti.letter FROM cte ti WHERE ti.rn = cte.rn + 1) THEN 2
ELSE 1 END,
letter
这是
但现在它变得有点复杂,例如:
- 如果1的任意一个字母与2的任意一个字母匹配,我想改变顺序,使该字母与下面的记录匹配。
- 如果未找到匹配项,则应按字母进行正常排序。
- ID 可能不成功,并且记录的顺序不正确。 [SQLFiddle Demo]
[Create script and SQL Fiddle demo]
create table Parent (
id [bigint] IDENTITY(1,2),
number bigint NOT NULL,
PRIMARY KEY (id)
)
GO
create table Child (
id [bigint] IDENTITY(1,2),
parentId BIGINT,
letter VARCHAR(1) NOT NULL,
PRIMARY KEY (id),
UNIQUE (parentId, Letter),
FOREIGN KEY (parentId) REFERENCES Parent(id)
)
GO
DECLARE @ParentIdentity BIGINT
INSERT Parent (number) VALUES (2)
SET @ParentIdentity = @@IDENTITY
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'C')
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'B')
INSERT Parent (number) VALUES (3)
SET @ParentIdentity = @@IDENTITY
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'D')
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'B')
INSERT Parent (number) VALUES (1)
SET @ParentIdentity = @@IDENTITY
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'C')
INSERT Child (parentId, letter) VALUES (@ParentIdentity, 'A')
GO
当前查询
目前我正在使用此查询进行排序:
;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
), 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 B
2 C
3 B
3 D
预期结果集
为了阐明我真正想做什么,这里是预期的结果集:
number letter
-------------------- ------
1 A
1 C
2 C
2 B
3 B
3 D
其他要求和问题
- 它必须在 SQL Server 2005 中工作。
- 有一种情况是每个数字使用 3 个字母,如果它只使用最佳匹配我很高兴。
任何人都可以指出正确的方向来处理这种情况吗?
如果我理解你的要求是对的,你有 parentId
的某些部分,你希望每个部分都以 letter
开始,那些在前一部分中,并以 letter
结束那些在下一部分,如果是,试试这个:
;WITH t AS (
SELECT
c.id,
c.parentId,
c.letter,
dt.parentSeq
FROM
Child c
JOIN (
SELECT
ci.parentId, ROW_NUMBER() OVER (ORDER BY p.number) parentSeq
FROM
Child ci
JOIN
Parent p ON ci.parentId = p.id
GROUP BY
ci.parentId, p.number) dt ON c.parentId = dt.parentId
)
SELECT
p.number,
t.letter
FROM
t
JOIN
Parent p ON t.parentId = p.id
ORDER BY
p.number,
CASE WHEN t.letter IN (SELECT ti.letter FROM t ti WHERE ti.parentSeq = t.parentSeq - 1) THEN 0
WHEN t.letter IN (SELECT ti.letter FROM t ti WHERE ti.parentSeq = t.parentSeq + 1) THEN 2
ELSE 1 END,
t.letter
这个问题我用Row_Number
函数希望对你有帮助。
SELECT Number, Letter
FROM (
SELECT number, letter, ROW_NUMBER()over(Partition by letter,Number order by Number) as R
FROM Child C
JOIN Parent P
ON P.id = C.parentId) x
ORDER BY number, R desc
我不确定这是否适用于一些复杂的真实数据,但您可以检查:
;WITH cte AS(SELECT ci.id, ci.parentId, ci.letter, p.number,
DENSE_RANK() OVER (ORDER BY p.number) rn
FROM Child ci
JOIN Parent p ON ci.parentId = p.id)
SELECT t1.number, t1.letter
FROM cte t1
LEFT JOIN cte t2 ON t2.rn = t1.rn - 1 AND t1.letter = t2.letter
LEFT JOIN cte t3 ON t1.rn = t3.rn - 1 AND t1.letter = t3.letter
ORDER BY t1.number, t1.letter + t2.letter DESC, t1.letter + t3.letter, t1.letter
如果这不起作用,您可以切换到@shA.t 的巧妙排序:
;WITH cte AS(SELECT ci.id, ci.parentId, ci.letter, p.number,
DENSE_RANK() OVER (ORDER BY p.number) rn
FROM Child ci
JOIN Parent p ON ci.parentId = p.id)
SELECT number, letter
FROM cte
ORDER BY
number,
CASE WHEN letter IN (SELECT ti.letter FROM cte ti WHERE ti.rn = cte.rn - 1) THEN 0
WHEN letter IN (SELECT ti.letter FROM cte ti WHERE ti.rn = cte.rn + 1) THEN 2
ELSE 1 END,
letter