SQL - 复杂排名

SQL - Complex RANK

这里有一个小问题:

平台是MSSQL2008,但问题一般

我有一个包含 3 列的 table table: 客户,日期,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

实际上我同时要做两件事:

第1题很简单,又是一道经典题:

从每组 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

2 - 现在是困难的部分。我无法解决,需要一些建议:

如果 DESTINATION_PREFERENCE 是 3 我仍然应该包括行 DESTINATION_PREFERENCE 等于 6.

因此结果 table 值将多出一行(第 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 的行。

等...

规则很多。

提前感谢您的提示!

我假设你的意思是如果 最小 目的地偏好是 3,则包括 6。这意味着将“6”视为与“3”相同,但前提是一个“3”。

您可以使用 window 函数通过放入“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 ;

您可以使用 CTE 添加额外的列,您只需将 6 替换为 3,将 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

您可以在您的订单中添加一个案例陈述,排名如下:

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 

由于评分不足,无法对 Gordon Linoff post 发表评论。然而,如果只有 3 个是最小目的地,他的解决方案将起作用。如果 minimum = 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)