【问题标题】:SELECT TOP PERCENT, VaR, Expected Shortfall in MySQLSELECT TOP PERCENT、VaR、MySQL 中的预期不足
【发布时间】:2021-06-14 23:51:41
【问题描述】:

我想在 MySQL 中实现 SELECT TOP PERCENT

我在Select TOP X (or bottom) percent for numeric values in MySQL中使用了Victor Sorokin的思路,得到如下查询:

SELECT x.log AS Login, 
       AVG(x.PROFIT) AS 'Expected Shortfall', 
       MAX(x.PROFIT) AS '40%VaR'
  FROM
  (SELECT t.PROFIT, 
          @counter := @counter +1 AS counter, 
          t.LOGIN AS log 
     FROM (SELECT @counter:=0) initvar, trades AS t
     WHERE t.LOGIN IN (100,101)
     ORDER BY t.PROFIT) AS x
  WHERE x.counter <= (40/100 * @counter)
GROUP BY x.log

返回以下结果:

Login Expected Shortfall 40%VaR
101 -85 -70

当我将 WHERE t.LOGIN IN (100,101) 更改为像 WHERE t.LOGIN=100 这样的单个值时,此方法有效。因此它将为每个登录返回我的值,如下所示:

Login Expected Shortfall 40%VaR
100 -4.5 -4
Login Expected Shortfall 40%VaR
101 -95 -90

我不太确定发生了什么,我想知道是否有一种方法可以对多个帐户使用查询,或者有更好的方法来解决这个问题?是否在考虑 LOOP 语句?

我目前使用的是 MySQL 5.7.34 版。如果需要任何澄清,请随时告诉我。任何想法将不胜感激!

编辑:复制问题:

CREATE TABLE trades (
TICKET int(11) PRIMARY KEY,
LOGIN int(11),
PROFIT double)

INSERT INTO trades (TICKET,LOGIN,PROFIT)
VALUES
(1,100,-5),
(2,100,-4),
(3,100,-3),
(4,100,-2),
(5,100,-1),
(6,101,-100),
(7,101,-90),
(8,101,-80),
(9,101,-70),
(10,101,-60),
(11,101,-50),
(12,101,500)

预期的输出就像您分别运行查询 100 和 101 时得到的输出一样:

预期输出

LOGIN ES 40%VAR
100 -4.5 -4
101 -95 -90

Expected Output

【问题讨论】:

  • edit your question 并提供Minimal, reproducible example .. 例如样本数据和预期输出。
  • 不推荐在SELECT 中分配@variables。您已经找到了造成这种情况的原因之一。
  • 感谢@FaNo_FN,我已经编辑了问题并提供了一个可重复的示例。请注意,我已将 1/100 * at counter 更改为 40/100 * at counter 以使示例最小化。如果您还有什么需要我澄清的,请告诉我。
  • 好的...我提到的解决方案是在 8 年前发布的,这就是原因。谢谢@RickJames
  • 您使用的是哪个 MySQL 版本?您可以运行SELECT @@version 进行检查。

标签: mysql


【解决方案1】:

最终结果不是按照单值查询的原因是@row_number赋值造成的。单独运行基本查询(子查询)将返回以下结果:

PROFIT counter log
-100 1 101
-90 2 101
-80 3 101
-70 4 101
-60 5 101
-50 6 101
-5 7 100
-4 8 100
-3 9 100
-2 10 100
-1 11 100
500 12 101

如您所见,使用@row_number 生成的counter 值为表中的所有数据提供了一个运行编号,而不管它是log 值。下面的结果显示了使用单个 log 值的查询的差异:

PROFIT counter log
-5 1 100
-4 2 100
-3 3 100
-2 4 100
-1 5 100

在这里您可以看到,如果使用log=100,您将获得从1-5 生成的counter(@row_number),而不是从组合7-11 中的7-11 生成的log IN (100,101)。这就是为什么 WHERE x.counter &lt;= (40/100*v.ctr) 在最终查询中只取 log=101 的原因,因为它是唯一符合条件的。您正在寻找的是由log 分隔的counter 值。在支持窗口函数的 MySQL 8.0+(或 MariaDB 10.2+)上,这可以通过使用 ROW_NUMBER() 来完成。但是,由于 OP 使用的是旧版本,I found a way to emulate 相应地使用了ROW_NUMBER() 的功能。

这是生成的最终查询:

SELECT x.log AS Login,
        AVG(x.PROFIT) AS 'Expected Shortfall', 
       MAX(x.PROFIT) AS '40%VaR'
  FROM
  (SELECT t.PROFIT,
    @row_number:=CASE
        WHEN @id = LOGIN THEN @row_number + 1
          ELSE 1 END AS counter,
    @id:=LOGIN ID, t.LOGIN AS log 
FROM trades t 
CROSS JOIN (SELECT @id:=0,@row_number:=0) as n
ORDER BY LOGIN) AS x 
  JOIN (SELECT Login,COUNT(*) ctr FROM trades GROUP BY login) AS v
  ON x.log=v.login
  WHERE x.counter <= (40/100*v.ctr)
  GROUP BY x.log
  ORDER BY x.log;

这里是 MySQL 8.0+ 查询中的demo fiddle (inclusive of ROW_NUMBER())

【讨论】:

  • 再次感谢。我会要求升级。
猜你喜欢
  • 1970-01-01
  • 2010-09-15
  • 1970-01-01
  • 2016-12-14
  • 2011-11-26
  • 2016-07-04
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
相关资源
最近更新 更多