基于SQL中下一个和上一个记录的复杂排序

时间:2015-05-27 10:58:59

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

这是关于Sorting based on next and previous records in SQL

的后续问题

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

  1. 如果任何1的字母与2的任何字母匹配,我想更改顺序,以便该字母与以下记录匹配。
  2. 如果未找到匹配项,则应按字母顺序排序。
  3. ID可能不会成功,并且记录不会以正确的顺序存储。 [SQLFiddle Demo]
  4. [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个字母,如果它只使用最佳匹配,我很高兴。

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

3 个答案:

答案 0 :(得分:5)

如果我理解您的要求是正确的,那么您有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 s结尾在下一部分,如果是,试试这个:

<textarea type="text" name="sampletext" value="try this, this text will not have line breaks"></textarea>

答案 1 :(得分:3)

我不确定这是否适用于某些复杂的实际数据,但您可以查看:

;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

答案 2 :(得分:2)

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