SQL - 复杂的RANK

时间:2015-01-20 12:51:06

标签: sql sql-server ranking

这是一个小小的破坏者:

平台是MS SQL 2008,但问题很普遍。

我有一个包含3列的表格表: CLIENT,DATE,DESTINATION_PREFERENCE

TABLE1
-------------------------------------------------------
CLIENT      |DATE       |DESTINATION_PREFERENCE
-------------------------------------------------------
Akme        |2014-01    |1
Akme        |2014-02    |6
Akme        |2014-02    |3
Akme        |2014-03    |5
Yutani      |2014-01    |5
Yutani      |2014-02    |8
Yutani      |2014-03    |3
Yutani      |2014-03    |5

我要做的事实上是两件事:

第一是非常简单,也是一个经典问题:

从每组CLIENT和DATE中选择一个最小DESTINATION_PREFERENCE的行。

换句话说,我们在CLIENT,DATE上进行GROUP BY,然后选择DESTINATION_PREFERENCE最低的行。

注意:我在DATE只使用YEAR和MONTH。

这可以通过RANK轻松解决:

    SELECT 
    CLIENT,DATE,DESTINATION_PREFERENCE 
    FROM
    (
        SELECT 
        CLIENT,DATE,DESTINATION_PREFERENCE,
        RANK() OVER (PARTITION BY CLIENT, DATE ORDER BY DESTINATION_PREFERENCE ASC) AS RANKING
        FROM
        #table1
    ) sq
    WHERE
    RANKING = 1 

结果很好,我们选择每行CLIENT和DATE(YEAR,MONTH)中DESTINATION_PREFERENCE最低的行:

    CLIENT  DATE    DESTINATION_PREFERENCE
    Akma    2014-01 1
    Akma    2014-02 3
    Akma    2014-03 5
    Yutani  2014-01 5
    Yutani  2014-02 8
    Yutani  2014-03 3

第二 - 现在来了困难的部分。我无法解决,需要一些建议:

如果DESTINATION_PREFERENCE为3,我仍然应该在其中包含行 DESTINATION_PREFERENCE等于6。

因此结果表值将有一个额外的行(第2行):

CLIENT  DATE    DESTINATION_PREFERENCE
Akma    2014-01 1
Akme    2014-02 6
Akma    2014-02 3
Akma    2014-03 5
Yutani  2014-01 5
Yutani  2014-02 8
Yutani  2014-03 3   

我怎样才能将RANK()扩展到包含这样的任意规则? 要实施的示例规则:

如果组中DESTINATION_PREFERENCE的最高值为3,则包含来自同一组的值为6的行。

如果组中DESTINATION_PREFERENCE的最高值为9,则包含来自同一组的值为2的行。

如果组中DESTINATION_PREFERENCE的最高值为128,则包含来自同一组的值为312的行。

等等......

有很多规则。

提前感谢您提示!

4 个答案:

答案 0 :(得分:1)

我认为你的意思是如果最小目的地偏好是3,那么包括6.这意味着将“6”视为“3”,但仅限于“3”。

您可以使用窗口函数执行此操作,方法是输入“6-flag”:

SELECT CLIENT, DATE, DESTINATION_PREFERENCE 
FROM (SELECT t1.*
             RANK() OVER (PARTITION BY CLIENT, DATE
                          ORDER BY (CASE WHEN NumThrees > 0 AND DESTINATION_PREFERENCE = 6 THEN 3
                                    ELSE DESTINATION_PREFERENCE END) ASC
                         ) AS RANKING
      FROM (SELECT t1.*,
                   SUM(CASE WHEN DESTINATION_PREFERENCE = 3 THEN 1 ELSE 0 END) as NumThrees
            FROM #table1 t1
           ) t1
    ) sq
WHERE RANKING = 1 ;

如果你想在所有情况下将“6”视为“3”,那么你不需要子查询:

SELECT CLIENT, DATE, DESTINATION_PREFERENCE 
FROM (SELECT t1.*
             RANK() OVER (PARTITION BY CLIENT, DATE
                          ORDER BY (CASE WHEN DESTINATION_PREFERENCE = 6 THEN 3
                                    ELSE DESTINATION_PREFERENCE END) ASC
                         ) AS RANKING
      FROM #table1 t1
    ) sq
WHERE RANKING = 1 ;

答案 1 :(得分:1)

您可以使用CTE添加额外的列,只需将3替换为6,9替换为2等。

DECLARE @t TABLE
    (
      client NVARCHAR(MAX) ,
      date DATETIME ,
      dest INT
    )

INSERT  INTO @t
VALUES  ( 'Akme', '20140101', 1 ),
        ( 'Akme', '20140102', 3 ),
        ( 'Akme', '20140102', 6 ),
        ( 'Akme', '20140103', 5 ),
        ( 'Yutani', '20140104', 2 ),
        ( 'Yutani', '20140104', 7 ),
        ( 'Yutani', '20140104', 9 ),
        ( 'Yutani', '20140105', 7 );
WITH    cte
          AS ( SELECT   client ,
                        date ,
                        dest ,
                        CASE dest
                          WHEN 6 THEN 3
                          WHEN 9 THEN 2
                          ELSE dest
                        END AS rndest
               FROM     @t
             )
    SELECT  CLIENT ,
            DATE ,
            dest
    FROM    ( SELECT    CLIENT ,
                        DATE ,
                        dest ,
                        RANK() OVER ( PARTITION BY CLIENT, DATE ORDER BY rndest ASC ) AS RANKING
              FROM      cte
            ) sq
    WHERE   RANKING = 1 

输出:

CLIENT  DATE    dest
Akme    2014-01-01 00:00:00.000 1
Akme    2014-01-02 00:00:00.000 3
Akme    2014-01-02 00:00:00.000 6
Akme    2014-01-03 00:00:00.000 5
Yutani  2014-01-04 00:00:00.000 2
Yutani  2014-01-04 00:00:00.000 9
Yutani  2014-01-05 00:00:00.000 7

答案 2 :(得分:1)

您可以在等级中按顺序放置一个案例陈述:

SELECT 
    CLIENT,DATE,DESTINATION_PREFERENCE 
FROM
(
    SELECT 
    CLIENT,DATE,DESTINATION_PREFERENCE,
    RANK() OVER (PARTITION BY CLIENT, DATE ORDER BY Case DESTINATION_PREFERENCE when 3 then 6 when 9 then 2 when 128 then 312 else DESTINATION_PREFERENCE END ASC) 
AS RANKING
    FROM
    #table1
) sq
WHERE
RANKING = 1 

答案 3 :(得分:1)

由于缺乏评级,不能对Gordon Linoff的帖子发表评论。但是,如果只有3是最小目的地,他的解决方案将起作用。如果最小值= 1且它将同时具有3和6,则它也将显示为6。

SELECT 
CLIENT,DATE,DESTINATION_PREFERENCE  
FROM
(
    SELECT 
    CLIENT,DATE,DESTINATION_PREFERENCE ,
    RANK() OVER (PARTITION BY CLIENT, DATE ORDER BY DESTINATION_PREFERENCE  ASC) AS RANKING,
    MIN(DESTINATION_PREFERENCE ) OVER (PARTITION BY CLIENT, DATE) AS min_3
    FROM
    #table1
) sq
WHERE
RANKING = 1 
OR (min_3 = 3 AND DESTINATION_PREFERENCE  =6)