【问题标题】:Complex sorting based on next and previous records in SQL基于 SQL 中下一条和上一条记录的复杂排序
【发布时间】:2015-05-27 10:58:58
【问题描述】:

这是Sorting based on next and previous records in SQL的后续问题

但现在它变得有点复杂了,例如:

  1. 如果任何字母 1 匹配任何字母 2,我想更改排序,使字母与以下记录匹配。
  2. 如果未找到匹配项,则应按字母进行正常排序。
  3. 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 个字母,如果它只使用最佳匹配,我很高兴。

谁能指出我如何处理这种情况的正确方向?

【问题讨论】:

  • 这在 SQL Server 2005 中将很难做到,因为对于给定的数字,您无法先验确定结果的顺序。你本质上是一个分层(递归)问题。
  • 最好澄清一下“数字 1 的最后一个字母”实际上是指“数字 1 的最大字母值”,而不是基于 Child 的最后一个 id 1人桌
  • @ughai 我实际上并不关心最大字母值。匹配的字母是我关心的,假设我们有:1A 1C | 2B 2A。我想要1C 1A | 2A 2B 的结果。我不知道如何在需求中描述这个
  • 所以你的意思是任何字母 1 匹配任何字母 2
  • @Ughai 我更新了我的答案。

标签: sql sql-server sql-server-2005 sql-order-by


【解决方案1】:

如果我正确理解您的要求,您有 parentId 的某些部分,并且您希望每个部分以 letters 开头,这些在前一部分中,并以 letters 结尾,在下一部分中,如果是的,试试这个:

;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

【讨论】:

  • 你很好理解,这似乎是当前场景下的工作。唯一不起作用的是 ID 不成功的情况。 sqlfiddle.com/#!3/43070/11号的B和D需要调换。
  • 谢谢,还请注意,我需要在Parentnumber 列上进行排序,而不是在parentId 上。我在小提琴中进行了编辑。
  • 看起来很有希望,即使加入了父母号码,我也不想亲自编辑你的答案,但这会让它可以接受:)
  • 完美运行,感谢您更改 CTE,它解决了下一个问题:)
【解决方案2】:

我不确定这是否适用于一些复杂的真实数据,但你可以检查一下:

;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

【讨论】:

    【解决方案3】:

    我使用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
    

    【讨论】:

    • 这似乎也适用于上述值,但使用不同的字母并且不成功插入,可能会破坏这一点。虽然接受的答案继续有效。
    • 嗨,尼克,您能否提供更多示例数据,以便我再次检查。
    猜你喜欢
    • 2013-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多