【问题标题】:MySQL rank by value for each dayMySQL每天按值排名
【发布时间】:2010-11-05 20:31:45
【问题描述】:

我正在尝试按 700 个符号中每天的回报百分比排名。

例如:

date     symbol    pct_return
-----------------------------
1100101  IBM       1.2
1100101  AAPL      2.1
1100101  HPQ       -0.5

日期 1100101 大约还有 700 个这样的条目

1100102  IBM       -.02
1100102  AAPL      -.6
1100102  HPQ       1.9

日期 1100102 大约还有 700 个这样的条目

我要做的是创建一个查询或存储过程来循环遍历每一天,然后对每天每个符号的百分比回报进行排名并插入排名值。

我想插入回报百分比升序和降序的排名值。

排名后仅包含 3 个符号的示例表如下所示:

date     symbol    pct_return    rank_asc   rank_desc
------------------------------------------------------
1100101  IBM       1.2             2         2
1100101  AAPL      2.1             3         1
1100101  HPQ       -0.5            1         3
1100102  IBM       -.02            2         2
1100102  AAPL      -.6             1         3
1100102  HPQ       1.9             3         1

【问题讨论】:

  • 请提供该表的 CREATE TABLE 语句。
  • CREATE TABLE T_0_ranked2 (date int(11) 默认 NULL,sym varchar(8) 默认 NULL,pct_returndecimal(8,2) 默认 NULL,event_id int (10) unsigned NOT NULL, PRIMARY KEY USING BTREE (event_id) ENGINE=MyISAM DEFAULT CHARSET=latin1;插入T_0_ranked2 值 (1100101,'IBM','1.20',1),(1100101,'AAPL','2.10',2),(1100101,'HPQ','-0.50',3),( 1100102,'IBM','-0.02',4),(1100102,'AAPL','-0.60',5),(1100102,'HPQ','1.90',6);

标签: sql mysql rank


【解决方案1】:

您可以使用此语法在您的选择中选择行号:

SELECT @row := @row + 1 as row, t.*
FROM table t, (SELECT @row := 0) r;

然后,您可以选择所有 ORDER BY 每天升序和降序的值,并将它们插入到您的表格中。

来源:http://snippets.dzone.com/posts/show/6831

例子:

INSERT INTO [your table]
SELECT date, symbol, pct_return, @row := @row + 1
FROM [your other table] t, (SELECT @row := 0) r
ORDER BY pct_return ASC;

要获取升序值,然后使用类似查询对同一张表进行更新以获取降序值。

【讨论】:

  • 布伦特,我最终在 PERL 中使用了您的代码的改编版。我发现它更具可扩展性和快速性。 PERL 是我管理需要经历很多天的循环的最简单方法。我将在附加评论中发布代码。
【解决方案2】:

这是组内聚合的典型问题,可以通过左自排除连接解决。

您不需要任何存储过程来获得您想要的结果,只需一个简单的INSERT INTO ... SELECT ... 查询就可以了。

这是一个包含所提供数据的示例脚本:

创建表 shuffled_symbols ( dat INT NOT NULL ,symbol VARCHAR(4) NOT NULL ,pct_return DECIMAL(4,2) NOT NULL ,PRIMARY KEY (dat ,符号) ); 创建表ranked_symbols ( dat INT NOT NULL ,symbol VARCHAR(4) NOT NULL ,pct_return DECIMAL(4,2) NOT NULL ,rank_asc INT UNSIGNED NOT NULL ,rank_desc INT UNSIGNED NOT NULL ); INSERT INTO shuffled_symbols (dat,symbol,pct_return) VALUES (1100101,'IBM',1.2); 插入 shuffled_symbols (dat,symbol,pct_return) VALUES (1100101,'AAPL',2.1); 插入 shuffled_symbols (dat,symbol,pct_return) 值 (1100101,'HPQ',-0.5); INSERT INTO shuffled_symbols (dat,symbol,pct_return) VALUES (1100102,'IBM',-0.02); 插入 shuffled_symbols (dat,symbol,pct_return) 值 (1100102,'AAPL',-0.6); INSERT INTO shuffled_symbols (dat,symbol,pct_return) VALUES (1100102,'HPQ',1.9);

这是计算排名的查询(抱歉格式错误,我无法在<pre> 标签内正确显示):

INSERT INTO ranked_symbols ( dat, symbol, pct_return, rank_asc, rank_desc ) SELECT ars.dat, ars.symbol, ars.pct_return, ars.rank_asc, COUNT(ss3.dat)+1 rank_desc FROM ( SELECT ss1.dat, ss1.symbol, ss1.pct_return, COUNT(ss2.dat)+1 rank_asc FROM shuffled_symbols ss1 LEFT JOIN shuffled_symbols ss2 ON ss2.dat = ss1.dat AND ss2.pct_return < ss1.pct_return GROUP BY ss1.dat, ss1.symbol ) ars LEFT JOIN shuffled_symbols ss3 ON ss3.dat = ars.dat AND ss3.pct_return > ars.pct_return GROUP BY ars.dat, ars.symbol ;

请注意,如果您在给定日期没有重复的符号,则此查询只会返回有效排名。这就是我使用PRIMARY KEY (dat ,symbol) 创建shuffled_symbols 表的原因。

在ranked_symbols 表中,您会得到以下结果:

选择 * FROM 排名符号; +---------+--------+------------+----------+------ -----+ |数据 |符号 | pct_return | rank_asc | rank_desc | +---------+--------+------------+----------+------ -----+ | 1100101 |苹果 | 2.10 | 3 | 1 | | 1100101 |高品质 | -0.50 | 1 | 3 | | 1100101 | IBM | 1.20 | 2 | 2 | | 1100102 |苹果 | -0.60 | 1 | 3 | | 1100102 |高品质 | 1.90 | 3 | 1 | | 1100102 | IBM | -0.02 | 2 | 2 | +---------+--------+------------+----------+------ -----+ 6 行一组(0.00 秒)

【讨论】:

  • 谢谢你,布鲁诺!有效。非常感激。你的笔记很到位,符号列表中没有重复,感谢主键的使用。
【解决方案3】:

以下是我如何扩展布伦特的 以 PERL 为例。这是非常 对我有帮助,我非常感谢 社区支持。

从命令行运行代码:

rank.pl FromTableNoRank ToTableWithRank pct_return DESC

#!/usr/bin/perl -w

use strict;
use warnings;
use Carp;

// connect to database here

// Not enough command-line arguments, helpful error message.
if(@ARGV!=4) {
    die("$0 requires four arguments. 1.FromTable 2.ToTable 3.Order by value(ex.pct_return) 4.ASC (low = 1) or DESC(high = 1)\n");
}

// Use variables for insert to minimize errors
// $OrderBy is the value to rank 
// $AscDesc declares which way to rank
my $FromTable = $ARGV[0];
my $ToTable   = $ARGV[1];
my $OrderBy   = $ARGV[2];
my $AscDesc   = $ARGV[3];


// DateTable is table of dates for use within the insert query. Used to loop through and rank individual days.
my $query7 = "SELECT dat FROM DateTable ORDER BY dat ASC";
my $sth7   = $dbh->prepare($query7) || carp DBI::errstr;
$sth7->execute() || carp DBI::errstr;

// Fetchrow_hashref holds all dates from date tables and while loop walks through one at a time
// The insert sorts by a value and then a row number is added to provide a rank of values
// The nested sth exists because need to reference $dateVar from fetchrow
while(my $ref = $sth7->fetchrow_hashref()) {
    my $dateVar = $ref->{dat};
    print "$dateVar \n";
    my $query6 = "INSERT INTO $ToTable 
        SELECT t.*,". '@rownum := @rownum +1' . " 
        FROM $FromTable t, ".'(SELECT @rownum := 0) r  
        WHERE dat ='."$dateVar
        ORDER BY $OrderBy $AscDesc";
    my $sth6 = $dbh->prepare($query6) || carp DBI::errstr;
    $sth6->execute() || carp DBI::errstr;
    $sth6->finish();
}

$sth7->finish();
$dbh->disconnect();

【讨论】:

  • 这个解决方案不起作用(查询,而不是 PERL 代码),因为它给你一个行号,而不是一个排名,即如果你有两个具有相同 pct_return 的符号,它会将它们排序随机顺序并为每个返回不同的排名。
  • 你的权利,布鲁诺。谢谢你指出这一点。当我的查询遇到一些规模问题并且它会挂起时,我的数据库中有超过 200,000 个条目。我编写了 PERL 代码以朝着正确的方向前进。我不确定如何优雅地提出可扩展的解决方案。想法?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-18
  • 2016-01-08
  • 1970-01-01
  • 1970-01-01
  • 2021-12-17
相关资源
最近更新 更多