【问题标题】:SQL: get top N for latest month and history for theseSQL:获取最近一个月的前 N ​​个和这些的历史记录
【发布时间】:2020-09-18 11:03:50
【问题描述】:

我想找到最近一段时间的前 N ​​个名称(具有最高值的名称)。对于这些名称,我想获得历史价值。

根据这个问题Select top 10 records for each category

我尝试了以下方法:

base_table (period, name, value)

CREATE VIEW TOP3 AS
SELECT DISTINCT a.*, COUNT(*) as rank
FROM base_table AS a
LEFT JOIN base_table AS a2 
ON a.period = a2.period and a.value <= a2.value
GROUP BY a.value
HAVING COUNT(*) <= 3
ORDER BY period, rank;

这让我在每个时期都排在前 3 名。

period | name | value | rank
092020   Joe    100      1
092020   Jane   90       2
092020   Doe    80       3

082020   Jan    100      1
082020   Doc    99       2
082020   Pete   98       3
.
.
.

但不是想要的结果。

假设这些是最近一个月的前 3 名:

period | name | value | rank
092020   Joe    100      1
092020   Jane   90       2
092020   Doe    80       3

现在我想获取这些的历史记录,即使它们在上个月的前 3 名之外,如下所示:

period | name | value | rank
092020   Joe    100      1
092020   Jane   90       2
092020   Doe    80       3

082020   Joe    95       4
082020   Jane   94       5
082020   Doe    7        50
.        .      .        .
.        .      .        .
.        .      .        .
092019  Joe     42       20
092019  Doe     34       35
092019  Jane    1        100

仅供参考,我在 SAS 中工作,使用 PROC SQL,因此我无法使用 CTE、窗口函数和其他东西等细节。

【问题讨论】:

  • 那些 PERIOD 值是什么? 092020 和 082020 是什么意思?那些应该是约会?这些值是否可以按原样进行排序,或者您是否首先需要将它们转换为实际日期,或者至少是另一个正确排序的字符串?
  • 只有傻瓜才会将日期存储为 MMYYYY,因为最新日期不是最高值的日期(122019 比 012020 的值更高)。为了任何不得不接触此数据或代码的人的喜爱,请使用 DATE 数据类型或将其编码为 YYYYMM。

标签: sql sas proc-sql


【解决方案1】:

你能用子查询试试吗?如果没有,您可以创建一个视图,而不是使用一些 row_number() / rank 函数排序来加入

SELECT b.period, b.name, b.value
FROM base_table b
INNER JOIN
(  
    -- subquery to get last period 
    SELECT TOP 3 name, period, value
    FROM base_table
    WHERE period = (SELECT MAX(period) FROM base_table)
    ORDER BY 3 DESC
) AS s ON s.name = b.name -- join them

【讨论】:

  • 欣赏它,不幸的是 TOP、row_number 或 rank 在 SAS 的 SQL 实现中都不可用。也不能在子查询中排序。所以这就是我在这里挣扎的部分原因
【解决方案2】:

你可以使用JOIN:

SELECT a.*, ar.rank
FROM base_table a JOIN
     (SELECT a.value, COUNT(*) as rank
      FROM base_table a JOIN
           base_table a2 
           ON a.period = a2.period and a.value <= a2.value JOIN
           (SELECT max(a3.period) as max_period
            FROM base_table a3
           ) a3
           ON a3.max_period = period
      GROUP BY a.value
      HAVING COUNT(*) <= 3
     ) ar
     ON ar.value = a.value
ORDER BY period, rank;

我怀疑使用data 步骤会更简单,至少对于分配等级而言。

【讨论】:

  • 感谢您的建议!似乎它仍然每个月都进入前三名,不管上个月谁在前三名。我在 SQL 而不是 SAS 数据语言方面有更多经验,因此我的第一次尝试是在 SQL 中。我也想让它成为一个视图,以便它动态更新。
  • @joddm 。 . .我懂了。我添加了一个新条件以仅获取最新期间的结果。
  • 不幸的是,操作选择将日期编码为 MMYYYY,因此 MAX() 不会得到最新的时间段:(
【解决方案3】:

不知道为什么要为此使用 SQL。这是一个使用 PROC SQL 的 OUTOBS 选项的方法。

首先让我们做一些实际的测试数据:

data have ;
  input period :ddmmyy. name $ value rank ;
  format period yymm7. ;
cards;
01092020   Joe    100      1
01092020   Jane   90       2
01092020   Doe    80       3
01082020   Joe    95       4
01082020   Jane   94       5
01082020   Doe    7        50
01092019  Joe     42       20
01092019  Doe     34       35
01092019  Jane    1        100
;

现在让我们将 N 设置为一个宏变量以使其更灵活一些。由于示例数据只有 3 个名称,因此我们使用 N of 2。

%let n=2;

所以首先找到最近 (MAX) 周期的前 N ​​个。

proc sql;
reset outobs=&n;
create table top&n as select name
  from have 
  having period=max(period)
  order by value desc
;

现在将 OUTOBS 重置为 MAX,并使用该 TOPn 列表从原始数据集中提取这些名称的所有数据。

reset outobs=max;
create table want as 
select * from have
where name in (select name from top&n)
;
quit;

让我们看看结果:

proc print data=want;
run;

这是:

Obs     period    name    value    rank

 1     2020M09    Joe      100        1
 2     2020M09    Jane      90        2
 3     2020M08    Joe       95        4
 4     2020M08    Jane      94        5
 5     2019M09    Joe       42       20
 6     2019M09    Jane       1      100

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-17
    • 2022-01-03
    相关资源
    最近更新 更多