对SQL Server中的两列执行双重排序,主要排序不在前两个列的评估中

时间:2013-02-25 15:39:36

标签: sql-server sql-server-2008 tsql

我有一个使用SQL Server 2008的表,它有一个包含两个可排序列的表,一个是手动设置的,另一个是通过系统过程计算的(此过程将整体排序,并从10开始分配排序,直到最高行数乘以10)

ID    Manual     System
------------------------
1      null      300
2      2         380
3      null      500
4      null      200

我试图让它将id分类为4,2,1,3

我希望输出在应用时对系统进行手动排序。如果添加了另一行并且还需要考虑手动排序,则会使事情进一步复杂化。

ID    Manual     System
-----------------------
1      null      300
2      2         380
3      null      500
4      null      200
5      5         100

所以新的排序将是4,2,1,3,5

ID    Manual     System
-----------------------
4      null      200
2      2         380
1      null      300
3      null      200
5      5         100

有什么想法吗?我尝试过Rank,Dense_Rank,Row_Number等。

已经给出的解决方案似乎对我的例子是正确的。我忘了提到第三列personID也是这里的一个因素。

ID    Manual     System    PersonID
-------------------------------------
4      null      200         22
2      2         380         22
1      null      300         22
3      null      200         22
5      5         100         22
8      1         210         25
6      1         480         25
7      null      600         25
9      4         800         25
10     null      990         25

因此,我首先必须按人员订购,然后按手动排序,然后按排序。这似乎仍然给我一个问题。

3 个答案:

答案 0 :(得分:3)

我认为这就是你所需要的。这有点难以解释。

Basically inserting not null manual values as row index (or row number) to the record list ordered by system.

FIDDLE DEMO

;with cte as (
    select id, manual,system,
           convert(decimal(10,1),row_number() over(order by system)) rn
    from t
    where manual is null 
    union all
    select id, manual,system, convert(decimal(10,1),manual-0.5) rn
    from t
    where manual is not null
)
select id,manual,system
from cte
order by rn


| ID | MANUAL | SYSTEM |
------------------------
|  4 | (null) |    200 |
|  2 |      2 |    380 |
|  1 | (null) |    300 |
|  3 | (null) |    500 |
|  5 |      5 |    100 |

答案 1 :(得分:3)

如果我了解您的要求,您希望按System列排序,除非提供了Manual列,并且在这种情况下,请将其用作排序位置?如果是这样,那么这应该适用于CASEROW_NUMBER

SELECT Id, Manual, System
FROM (
  SELECT Id, 
    Manual, 
    System,
    ROW_NUMBER() OVER (ORDER BY Manual, System)  rn
  FROM YourTable) t
ORDER BY CASE WHEN Manual IS NULL THEN RN ELSE Manual END, COALESCE(Manual,RN+1)

SQL Fiddle Demo

答案 2 :(得分:3)

以下是我的解决方案:http://sqlfiddle.com/#!3/a32a0/1/0

SELECT *
FROM
(
  SELECT
    ID
    , ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY System)-.1 AS rn
    , Manual
    , System
    , PersonID
  FROM YourTable
) t0
ORDER BY PersonID
  , COALESCE(Manual, RN)

以下是解释:

  • 我们将行号作为基本行号。但是,由于我们首先按PersonID的高阶索引进行排序,因此我PARTITION BY...之前ORDER BY...会重置MANUAL的每个分组的索引
  • 如果ROW_NUMBERMANUAL排序的自然排序之间存在联系,我会减去.1((0,1)之间的任意数量)。如果出现平局,则优先考虑MANUAL
  • 在排序最终结果时,我先ORDER BY PARTITION BY值,先确保正确的分组,然后按MANUAL的第一个非空值进行排序, RN

试一试。 +指向前两个答案的起点。我用其中一个作为起点,并从那里重写。

编辑:删除了.1的减法并添加了一个新的排名函数,它“优化”优化器,使其优先于手动排名。我不知道在所有情况下这是否成立,或者优化器是否会在其他情况下无法按此顺序给出结果,但我想将这些结果包括在内以防它们有用。

我的更新查询如下:

SELECT *
  FROM
  (
    SELECT
      ID
      , ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY System) AS rn
      , ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY Manual) AS rn_throwaway
      , Manual
      , System
      , PersonID
    FROM YourTable
  ) t0
ORDER BY PersonID
  , COALESCE(Manual, RN)

正在使用的示例位于http://sqlfiddle.com/#!3/1831d/55/0http://sqlfiddle.com/#!3/a32a0/9/0