SQL ORDER BY - 保持具有相同值的列分组

时间:2017-08-23 15:10:37

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

我有很多困难用文字表达我想要的东西(这个标题是我能带来的最好的标题),使用模式更容易,但我会首先尝试解释自己。

我想使用此规则通过查询订购:

  1. 按查询“组”列对查询进行分组。
  2. 将每个群组的“主要”放在他们的群组之上。
  3. 如果行与主要行具有相同的“名称”,请将其放在主要行之后。
  4. 按名称
  5. 放置其余行

    第1,2和4点是微不足道的。一个简单的ORDER BY技巧。但我从未见过第3点的查询。

    我已经阅读了一些关于RANK()ROW_NUMBER()函数的内容,尝试了它们,但我还没有设法创建我想要的输出。我开始质疑是否有可能这样做。

    无论如何,这里有一大块SQL来测试它。任何帮助表示赞赏。如果您能找到更好的术语来描述它,请随意纠正。

    CREATE TABLE #TEMP
    (
        COL_GROUP INT,
        COL_PRIMARY BIT,
        COL_NAME VARCHAR(3)
    )
    
    INSERT INTO 
        #TEMP 
    VALUES
        (1,1,'AAA'),
        (2,0,'BBB'),
        (2,1,'BBB'),
        (1,0,'BBB'),
        (1,0,'AAA'),
        (2,0,'AAA')
    
    SELECT
         *
    FROM 
        #TEMP
    ORDER BY 
        COL_GROUP, 
        COL_PRIMARY DESC, 
        COL_NAME
    
    DROP TABLE #TEMP
    

    这给出了这个输出:

    COL_GROUP   COL_PRIMARY   COL_NAME
    =========   ===========   ========
    1           1             AAA  
    1           0             AAA
    1           0             BBB
    2           1             BBB
    2           0             AAA
    2           0             BBB
    

    我想要的是这个输出:

    COL_GROUP   COL_PRIMARY   COL_NAME
    =========   ===========   ========
    1           1             AAA  
    1           0             AAA
    1           0             BBB
    2           1             BBB
    2           0             BBB       -- The ones with the same name as the primary first
    2           0             AAA
    

4 个答案:

答案 0 :(得分:2)

根据评论解决方案

  

如果名称等于主要名称,您可以进行自我加入和测试,如果是,则指定例如1否则为0,然后在名称列之前按顺序使用该列。

SELECT
     T1.*
FROM 
    #TEMP T1 JOIN #TEMP T2 
    ON T1.COL_GROUP=T2.COL_GROUP AND T2.COL_PRIMARY=1
ORDER BY 
    T1.COL_GROUP, 
    T1.COL_PRIMARY DESC,
    CASE WHEN T1.COL_NAME=T2.COL_NAME THEN 1 ELSE 0 END DESC,
    T1.COL_NAME

See demo here

答案 1 :(得分:1)

使用max窗口函数获取col_primary = 1的col_name,并在case中的order by表达式中使用它来获得所需的顺序。

SELECT * FROM (
SELECT
     t.*,max(case when col_primary=1 then col_name end) over(partition by col_group) as prim_col_name
FROM 
    #TEMP t
) t
ORDER BY 
    COL_GROUP, 
    COL_PRIMARY DESC, 
    case when COL_NAME = prim_col_name then 1 else 2 end,
    COL_NAME

这假设每个col_primary只能有一行=每个col_group一个。

答案 2 :(得分:1)

我认为你正在寻找这个:

SELECT 
    *
FROM #TEMP o
ORDER BY COL_GROUP, COL_PRIMARY DESC, CASE WHEN COL_NAME = (SELECT COL_Name FROM #Temp i WHERE i.COL_PRimary = 1 AND i.Col_Group = o.Col_Group) THEN 0 ELSE 1 END, COL_NAME;

基本上我正在对同一数据集的内部引用进行嵌套选择,但外部但限制范围说:"我想要名称与组的领导者匹配,然后将其设为0,否则为1& #34 ;.这对于小集合可以正常工作,但正如另一个用户所示,您可能希望将其分解为CTE,如果它是一个大型数据集,则稍后再调用它。如果你只做几千行或更少,这应该没问题。

答案 3 :(得分:0)

在桌子上使用左连接将确保在组中没有主要项目时不会丢失任何记录(Maby不是问题)。

这也会简单地通过添加链接列T2.COL_NAME来简化“订单”(如果COL_NAME与主要列相同,则不会为空。)

SELECT
         T1.*
FROM 
        #TEMP T1
        left join #TEMP T2 on T2.COL_GROUP = T1.COL_GROUP and T2.COL_NAME  = T1.COL_NAME and T2.COL_PRIMARY = 1
ORDER BY 
        T1.COL_GROUP, 
        T1.COL_PRIMARY DESC, 
        T2.COL_NAME DESC,
        T1.COL_NAME