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)
这里有一个小问题:
平台是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)