【问题标题】:select range when value is higher than in previous value当值高于先前值时选择范围
【发布时间】:2014-01-07 21:53:18
【问题描述】:

如何获取值高于先前值的所有记录。 例如,下表中的第一个范围从 id 1 开始,到 id 6 结束,下一个范围是从 7 到 10,等等...

id  Open
1   1.30077
2   1.30088
3   1.30115
4   1.30132
5   1.30135
6   1.30144
7   1.30132
8   1.30137
9   1.30152
10  1.30158
11  1.30149
12  ...

【问题讨论】:

  • ID 是否保证是连续的,还是必须处理间隙?

标签: mysql trend forex


【解决方案1】:

您的样本数据

USE test
DROP TABLE IF EXISTS rangedata;
CREATE TABLE rangedata
(
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  open FLOAT
) ENGINE=MyISAM;
INSERT INTO rangedata (open) VALUES
(1.30077),(1.30088),(1.30115),(1.30132),
(1.30135),(1.30144),(1.30132),(1.30137),
(1.30152),(1.30158),(1.30149),
(1.30077),(1.30088),(1.30115),(1.30132),
(1.30135),(1.30144),(1.30132),(1.30137),
(1.30152),(1.30158),(1.30149),
(1.30077),(1.30088),(1.30115),(1.30132),
(1.30135),(1.30144),(1.30132),(1.30137),
(1.30152),(1.30158),(1.30149);

您的样本数据已加载

mysql>     USE test
Database changed
mysql>     DROP TABLE IF EXISTS rangedata;
Query OK, 0 rows affected (0.01 sec)

mysql>     CREATE TABLE rangedata
    ->     (
    ->       id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    ->       open FLOAT
    ->     ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.09 sec)

mysql>     INSERT INTO rangedata (open) VALUES
    ->     (1.30077),(1.30088),(1.30115),(1.30132),
    ->     (1.30135),(1.30144),(1.30132),(1.30137),
    ->     (1.30152),(1.30158),(1.30149),
    ->     (1.30077),(1.30088),(1.30115),(1.30132),
    ->     (1.30135),(1.30144),(1.30132),(1.30137),
    ->     (1.30152),(1.30158),(1.30149),
    ->     (1.30077),(1.30088),(1.30115),(1.30132),
    ->     (1.30135),(1.30144),(1.30132),(1.30137),
    ->     (1.30152),(1.30158),(1.30149);
Query OK, 33 rows affected (0.00 sec)
Records: 33  Duplicates: 0  Warnings: 0

mysql>

使用联接查询

这是 LEFT JOIN 查询

SET @grp = 1;
SELECT A.open prev,(@grp:=@grp+IF(A.open<B.open,1,0)) group_number
FROM rangedata A LEFT JOIN rangedata B ON A.id= B.id+1;

这是它的输出

mysql> SELECT A.open prev,(@grp:=@grp+IF(A.open<B.open,1,0)) group_number
    -> FROM rangedata A LEFT JOIN rangedata B ON A.id= B.id+1;
+---------+--------------+
| prev    | group_number |
+---------+--------------+
| 1.30088 |            1 |
| 1.30115 |            1 |
| 1.30132 |            1 |
| 1.30135 |            1 |
| 1.30144 |            1 |
| 1.30132 |            2 |
| 1.30137 |            2 |
| 1.30152 |            2 |
| 1.30158 |            2 |
| 1.30149 |            3 |
| 1.30077 |            4 |
| 1.30088 |            4 |
| 1.30115 |            4 |
| 1.30132 |            4 |
| 1.30135 |            4 |
| 1.30144 |            4 |
| 1.30132 |            5 |
| 1.30137 |            5 |
| 1.30152 |            5 |
| 1.30158 |            5 |
| 1.30149 |            6 |
| 1.30077 |            7 |
| 1.30088 |            7 |
| 1.30115 |            7 |
| 1.30132 |            7 |
| 1.30135 |            7 |
| 1.30144 |            7 |
| 1.30132 |            8 |
| 1.30137 |            8 |
| 1.30152 |            8 |
| 1.30158 |            8 |
| 1.30149 |            9 |
| 1.30077 |            9 |
+---------+--------------+
33 rows in set (0.01 sec)

无连接查询

使用用户定义的变量,您只需监控每一行并查看前一个值何时更大。准备好查询了吗?这里是:

SET @prev = '0.00000';
SET @grp = 1;
SELECT id,open,(@grp:=@grp+increasing) group_number FROM
(SELECT id,open,IF(@prev<=open,0,1) increasing,(@prev:=open) FROM rangedata) A;

这是您的样本数据增加了三倍:

这是查询的执行:

mysql> SET @prev = '0.00000';
Query OK, 0 rows affected (0.00 sec)

mysql> SET @grp = 1;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT id,open,(@grp:=@grp+increasing) group_number FROM
    -> (SELECT id,open,IF(@prev<=open,0,1) increasing,(@prev:=open) FROM rangedata) A;
+----+---------+--------------+
| id | open    | group_number |
+----+---------+--------------+
|  1 | 1.30077 |            1 |
|  2 | 1.30088 |            1 |
|  3 | 1.30115 |            1 |
|  4 | 1.30132 |            1 |
|  5 | 1.30135 |            1 |
|  6 | 1.30144 |            1 |
|  7 | 1.30132 |            2 |
|  8 | 1.30137 |            2 |
|  9 | 1.30152 |            2 |
| 10 | 1.30158 |            2 |
| 11 | 1.30149 |            3 |
| 12 | 1.30077 |            4 |
| 13 | 1.30088 |            4 |
| 14 | 1.30115 |            4 |
| 15 | 1.30132 |            4 |
| 16 | 1.30135 |            4 |
| 17 | 1.30144 |            4 |
| 18 | 1.30132 |            5 |
| 19 | 1.30137 |            5 |
| 20 | 1.30152 |            5 |
| 21 | 1.30158 |            5 |
| 22 | 1.30149 |            6 |
| 23 | 1.30077 |            7 |
| 24 | 1.30088 |            7 |
| 25 | 1.30115 |            7 |
| 26 | 1.30132 |            7 |
| 27 | 1.30135 |            7 |
| 28 | 1.30144 |            7 |
| 29 | 1.30132 |            8 |
| 30 | 1.30137 |            8 |
| 31 | 1.30152 |            8 |
| 32 | 1.30158 |            8 |
| 33 | 1.30149 |            9 |
+----+---------+--------------+
33 rows in set (0.00 sec)

关键是:每次出现一个新的组号时,都会告诉您下一个丢弃的值。

请注意,两个查询的输出相同

CAVEAT:如果prevopen 之间存在一些浮点问题,第二个查询并不是一个完美的解决方案。如果它们彼此离得太近,那可能是不对的。这是在编写存储过程之外的最佳尝试。

【讨论】:

    【解决方案2】:

    可以使用此查询对范围进行编号:

    SELECT id, open, range_number
    FROM( 
      SELECT *,
             if(@lastopen<open,@grp,@grp:=@grp+1) range_number,
             @lastopen:=open
      FROM table1,
      (select @lastopen:=null,@grp:=0) qqq
      ORDER BY id
    ) qqq;
    

    演示:http://www.sqlfiddle.com/#!2/b1bb8/9

    | ID |           OPEN | RANGE_NUMBER |
    |----|----------------|--------------|
    |  1 | 1.300770044327 |            1 |
    |  2 | 1.300879955292 |            1 |
    |  3 | 1.301149964333 |            1 |
    |  4 | 1.301319956779 |            1 |
    |  5 |  1.30134999752 |            1 |
    |  6 | 1.301440000534 |            1 |
    |  7 | 1.301319956779 |            2 |
    |  8 | 1.301370024681 |            2 |
    |  9 | 1.301519989967 |            2 |
    | 10 |  1.30157995224 |            2 |
    | 11 | 1.301489949226 |            3 |
    

    【讨论】:

      【解决方案3】:
      SELECT startid,MAX(id) FROM (
          SELECT 
             @currentid := IF(@previous <= open,@currentid,id) as startid,
             @previous := open,
             id
          FROM ranges 
          JOIN (SELECT @currentid := MIN(id), @previous := MIN(open) FROM ranges) as variables
          ORDER BY id) runningscan
      GROUP BY startid ORDER BY startid + 0;
      

      在 SQLFiddle 上查看:http://sqlfiddle.com/#!2/e3cea/3

      它的作用是:在 runningscan 子查询中,它遍历表一次,如果 open 大于或小于前一个 open (存储在 @previous 变量中),则保持点击。这将为您提供一个列表包含所有 id,以及开始中断的更高(或等于)“运行”的 id。从那里,我们只需要找到起始 id 的最高 id,因此我们将其放在子查询中以实现简单的max 构造.id列的空隙是没有问题的。如果你单行不能是范围(即:open连续降低两次或多次),在外层查询中添加WHERE startid &lt; id子句。如果你需要限定的最小行数范围是大于 1 的范围,添加 HAVING COUNT(*) &gt; your_desired_minimum

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多