Oracle sql 或 pl/sql:根据前一行值和日期列计算

Oracle sql or pl/sql: Compute based on previous row values and on date column

我在 Python 技术上找到了很多与我的问题相关的解决方案,但在 Oracle SQL 或 PL/SQL 上找不到很多解决方案。所以在 Oracle SQL 上使用 Oracle Application Express 进行尝试。

我的数据库 table 具有以下 8 天的股票样本数据。每天收市后都会插入新记录。我有查询要查找价格连续 increase/decrease 的股票。现在我想再添加一列,我可以在其中看到一个基于当前日期和之前日期值的称为看涨或看跌的模式。

+-------+------------+--------+-------+-------+--------+--------+
| Stock |   Cdate    |  Open  | High  |  Low  | Close  | Volume |
+-------+------------+--------+-------+-------+--------+--------+
| XYZ   | 01-01-2021 |     40 |  40.5 |  38.5 |   38.8 |  83057 |
| XYZ   | 02-01-2021 |   39.2 |  39.2 |  37.2 |   37.8 | 181814 |
| XYZ   | 03-01-2021 |     38 |  38.5 |  36.5 |     37 | 117378 |
| XYZ   | 04-01-2021 |     36 |  36.1 |  35.6 |   35.7 |  93737 |
| XYZ   | 05-01-2021 |  35.35 |  36.8 |  35.1 |   36.6 | 169106 |
| XYZ   | 06-01-2021 |   36.5 |  38.5 |  36.5 |     38 | 123179 |
| XYZ   | 07-01-2021 |   37.5 |  39.5 |  37.3 |   39.4 | 282986 |
| XYZ   | 08-01-2021 |     39 |  40.5 |  38.5 |     40 | 117437 |
|       |            |        |       |       |        |        |
| DDD   | 01-01-2021 | 135.35 | 136.8 | 135.1 |  136.6 |  16454 |
| DDD   | 02-01-2021 |  136.5 | 138.5 | 136.5 |    138 | 281461 |
| DDD   | 03-01-2021 |  137.5 | 139.5 | 137.3 |  139.4 |  77334 |
| DDD   | 04-01-2021 |    139 | 140.5 | 138.5 |    140 | 321684 |
| DDD   | 05-01-2021 |  139.7 | 139.8 | 139.3 |  139.4 | 873009 |
| DDD   | 06-01-2021 |  139.2 | 139.2 | 137.2 |  137.8 |  62522 |
| DDD   | 07-01-2021 |    138 | 138.5 | 136.5 |    137 | 114826 |
| DDD   | 08-01-2021 |  136.6 | 136.8 | 135.1 | 135.35 |  27317 |
+-------+------------+--------+-------+-------+--------+--------+

这是图表查找股票 XYZ 的方式:

2021 年 1 月 5 日,趋势发生了变化,我想根据以下公式添加新的“看涨”栏:

O1 > C1 and C > O and C > H1 and O < L1

其中,

O1 = Previous day Open price  
C1 = Previous day Close price  
C  = Today's Close price  
O  = Today's Open price  
H1 = Previous day High price  
L1 = Previous day Low price  

预期输出将是一个新列,在列模式下显示“看涨”:

+-------+------------+-------+------+------+-------+--------+---------+
| Stock |   Cdate    | Open  | High | Low  | Close | Volume | Pattern |
+-------+------------+-------+------+------+-------+--------+---------+
| XYZ   | 01-01-2021 |    40 | 40.5 | 38.5 |  38.8 |  83057 |         |
| XYZ   | 02-01-2021 |  39.2 | 39.2 | 37.2 |  37.8 | 181814 |         |
| XYZ   | 03-01-2021 |    38 | 38.5 | 36.5 |    37 | 117378 |         |
| XYZ   | 04-01-2021 |    36 | 36.1 | 35.6 |  35.7 |  93737 |         |
| XYZ   | 05-01-2021 | 35.35 | 36.8 | 35.1 |  36.6 | 169106 | Bullish |
| XYZ   | 06-01-2021 |  36.5 | 38.5 | 36.5 |    38 | 123179 |         |
| XYZ   | 07-01-2021 |  37.5 | 39.5 | 37.3 |  39.4 | 282986 |         |
| XYZ   | 08-01-2021 |    39 | 40.5 | 38.5 |    40 | 117437 |         |
+-------+------------+-------+------+------+-------+--------+---------+

我在互联网上搜索了解决方案,找到的最接近的是 this。我稍微修改了查询,但无法添加公式或任何其他新列。下面是我试过的查询:

WITH StockRow AS (SELECT stock, close, cdate,
                         ROW_NUMBER() OVER(PARTITION BY stock
                                           ORDER BY cdate) rn
                  FROM sampletable where volume > 200000),

     RunGroup AS (SELECT Base.stock, Base.cdate, base.close,
                         MAX(Restart.rn) OVER(PARTITION BY Base.stock
                                              ORDER BY Base.cdate) groupingId
                  FROM StockRow Base
                  LEFT JOIN StockRow Restart
                         ON Restart.stock = Base.stock
                            AND Restart.rn = Base.rn - 1
                            AND Restart.CLOSE < Base.CLOSE)

SELECT stock, 
       COUNT(*) AS consecutiveCount, 
       MIN(cdate) AS startDate, MAX(cdate) AS endDate, min(close) as latestclose
FROM RunGroup
GROUP BY stock, groupingId
HAVING COUNT(*) >= 3
and MAX(cdate) = (select max(cdate) from sampletable)
ORDER BY CONSECUTIVECOUNT desc, stock, startDate

与股票类似,下面的“DDD”是图表的外观和最终数据:

2021 年 1 月 5 日,趋势发生了变化,我想根据以下公式添加新的“看跌”列:

C < O and C < C1 and O < C1 and O1 < C1 and C > O1 and O < C1

其中,

O1 = Previous day Open price  
C1 = Previous day Close price  
C = Today's Close price  
O = Today's Open price  


+-------+------------+--------+-------+-------+--------+--------+---------+
| Stock |   Cdate    |  Open  | High  |  Low  | Close  | Volume | Pattern |
+-------+------------+--------+-------+-------+--------+--------+---------+
| XYZ   | 01-01-2021 |     40 |  40.5 |  38.5 |   38.8 |  83057 |         |
| XYZ   | 02-01-2021 |   39.2 |  39.2 |  37.2 |   37.8 | 181814 |         |
| XYZ   | 03-01-2021 |     38 |  38.5 |  36.5 |     37 | 117378 |         |
| XYZ   | 04-01-2021 |     36 |  36.1 |  35.6 |   35.7 |  93737 |         |
| XYZ   | 05-01-2021 |  35.35 |  36.8 |  35.1 |   36.6 | 169106 | Bullish |
| XYZ   | 06-01-2021 |   36.5 |  38.5 |  36.5 |     38 | 123179 |         |
| XYZ   | 07-01-2021 |   37.5 |  39.5 |  37.3 |   39.4 | 282986 |         |
| XYZ   | 08-01-2021 |     39 |  40.5 |  38.5 |     40 | 117437 |         |
|       |            |        |       |       |        |        |         |
| DDD   | 01-01-2021 | 135.35 | 136.8 | 135.1 |  136.6 |  16454 |         |
| DDD   | 02-01-2021 |  136.5 | 138.5 | 136.5 |    138 | 281461 |         |
| DDD   | 03-01-2021 |  137.5 | 139.5 | 137.3 |  139.4 |  77334 |         |
| DDD   | 04-01-2021 |    139 | 140.5 | 138.5 |    140 | 321684 |         |
| DDD   | 05-01-2021 |  139.7 | 139.8 | 139.3 |  139.4 | 873009 | Bearish |
| DDD   | 06-01-2021 |  139.2 | 139.2 | 137.2 |  137.8 |  62522 |         |
| DDD   | 07-01-2021 |    138 | 138.5 | 136.5 |    137 | 114826 |         |
| DDD   | 08-01-2021 |  136.6 | 136.8 | 135.1 | 135.35 |  27317 |         |
+-------+------------+--------+-------+-------+--------+--------+---------+

感谢任何帮助。

更新:对不起,如果我要求太多。这就是我想要实现的目标。 简而言之,我想识别以下屏幕截图中以黄色突出显示的区域,仅当它位于 upmove/uptrend 或 downmove/downtrend.

中时

XYZ 股票,2021 年 5 月 1 日看涨

股票 DDD,2021 年 5 月 1 日看跌

谢谢,
里查

从 Oracle 12c 开始,您可以使用 MATCH_RECOGNIZE:

SELECT *
FROM   table_name
MATCH_RECOGNIZE(
  PARTITION BY stock
  ORDER     BY cdate
  MEASURES
    CLASSIFIER() AS pttrn
  ALL ROWS PER MATCH
  PATTERN (bullish|bearish|other)
  DEFINE
    bullish AS  PREV(open) > PREV(close)
            AND Close > Open
            AND Close > PREV(High)
            AND Open  < PREV(Low),
    bearish AS  Close < Open
            AND Close < PREV(Close)
            AND Open  < PREV(Close)
            AND PREV(Open) < PREV(Close)
            AND Close > PREV(Open)
            AND Open  < PREV(Close)
)

其中,对于示例数据:

CREATE TABLE table_name (Stock, Cdate, Open, High, Low, Close, Volume ) AS
SELECT 'XYZ', DATE '2021-01-01',  40.00,  40.50,  38.50,  38.80,  83057 FROM DUAL UNION ALL
SELECT 'XYZ', DATE '2021-01-02',  39.20,  39.20,  37.20,  37.80, 181814 FROM DUAL UNION ALL
SELECT 'XYZ', DATE '2021-01-03',  38.00,  38.50,  36.50,  37.00, 117378 FROM DUAL UNION ALL
SELECT 'XYZ', DATE '2021-01-04',  36.00,  36.10,  35.60,  35.70,  93737 FROM DUAL UNION ALL
SELECT 'XYZ', DATE '2021-01-05',  35.35,  36.80,  35.10,  36.60, 169106 FROM DUAL UNION ALL
SELECT 'XYZ', DATE '2021-01-06',  36.50,  38.50,  36.50,  38.00, 123179 FROM DUAL UNION ALL
SELECT 'XYZ', DATE '2021-01-07',  37.50,  39.50,  37.30,  39.40, 282986 FROM DUAL UNION ALL
SELECT 'XYZ', DATE '2021-01-08',  39.00,  40.50,  38.50,  40.00, 117437 FROM DUAL UNION ALL
SELECT 'DDD', DATE '2021-01-01', 135.35, 136.80, 135.10, 136.60,  16454 FROM DUAL UNION ALL
SELECT 'DDD', DATE '2021-01-02', 136.50, 138.50, 136.50, 138.00, 281461 FROM DUAL UNION ALL
SELECT 'DDD', DATE '2021-01-03', 137.50, 139.50, 137.30, 139.40,  77334 FROM DUAL UNION ALL
SELECT 'DDD', DATE '2021-01-04', 139.00, 140.50, 138.50, 140.00, 321684 FROM DUAL UNION ALL
SELECT 'DDD', DATE '2021-01-05', 139.70, 139.80, 139.30, 139.40, 873009 FROM DUAL UNION ALL
SELECT 'DDD', DATE '2021-01-06', 139.20, 139.20, 137.20, 137.80,  62522 FROM DUAL UNION ALL
SELECT 'DDD', DATE '2021-01-07', 138.00, 138.50, 136.50, 137.00, 114826 FROM DUAL UNION ALL
SELECT 'DDD', DATE '2021-01-08', 136.60, 136.80, 135.10, 135.35,  27317 FROM DUAL;

输出:

STOCK CDATE PTTRN OPEN HIGH LOW CLOSE VOLUME
DDD 01-JAN-21 OTHER 135.35 136.8 135.1 136.6 16454
DDD 02-JAN-21 OTHER 136.5 138.5 136.5 138 281461
DDD 03-JAN-21 OTHER 137.5 139.5 137.3 139.4 77334
DDD 04-JAN-21 OTHER 139 140.5 138.5 140 321684
DDD 05-JAN-21 BEARISH 139.7 139.8 139.3 139.4 873009
DDD 06-JAN-21 OTHER 139.2 139.2 137.2 137.8 62522
DDD 07-JAN-21 OTHER 138 138.5 136.5 137 114826
DDD 08-JAN-21 OTHER 136.6 136.8 135.1 135.35 27317
XYZ 01-JAN-21 OTHER 40 40.5 38.5 38.8 83057
XYZ 02-JAN-21 OTHER 39.2 39.2 37.2 37.8 181814
XYZ 03-JAN-21 OTHER 38 38.5 36.5 37 117378
XYZ 04-JAN-21 OTHER 36 36.1 35.6 35.7 93737
XYZ 05-JAN-21 BULLISH 35.35 36.8 35.1 36.6 169106
XYZ 06-JAN-21 OTHER 36.5 38.5 36.5 38 123179
XYZ 07-JAN-21 OTHER 37.5 39.5 37.3 39.4 282986
XYZ 08-JAN-21 OTHER 39 40.5 38.5 40 117437

您还可以使用 LAG 分析函数(在 Oracle 12 之前可用):

SELECT t.*,
       CASE
       WHEN LAG(open) OVER (PARTITION BY stock ORDER BY cdate)
            > LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Close > Open
       AND  Close > LAG(high) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Open  < LAG(low) OVER (PARTITION BY stock ORDER BY cdate)
       THEN 'BULLISH'
       WHEN Close < Open
       AND  Close < LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Open  < LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  LAG(open) OVER (PARTITION BY stock ORDER BY cdate)
            < LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Close > LAG(open) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Open  < LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       THEN 'BEARISH'
       ELSE 'OTHER'
       END AS pttrn
FROM   table_name t

(给出类似的输出。)

db<>fiddle here


更新:向上和向下跟踪:

同样,这就是 MATCH_RECOGNIZE 的用途:

SELECT *
FROM   table_name
MATCH_RECOGNIZE(
  PARTITION BY stock
  ORDER     BY cdate
  MEASURES
    CLASSIFIER() AS pttrn
  ALL ROWS PER MATCH
  PATTERN (^initial_value|bullish|bearish|up|down|other)
  DEFINE
    bullish AS  PREV(open) > PREV(close)
            AND Close > Open
            AND Close > PREV(High)
            AND Open  < PREV(Low),
    bearish AS  Close < Open
            AND Close < PREV(Close)
            AND Open  < PREV(Close)
            AND PREV(Open) < PREV(Close)
            AND Close > PREV(Open)
            AND Open  < PREV(Close),
    up      AS  close > PREV(close)
            AND open  > PREV(open),
    down    AS  close < PREV(close)
            AND open  < PREV(open)
)

但是你可以用 LAG 做同样的事情:

SELECT t.*,
       CASE
       WHEN ROW_NUMBER() OVER (PARTITION BY stock ORDER BY cdate) = 1
       THEN 'INITIAL_VALUE'
       WHEN LAG(open) OVER (PARTITION BY stock ORDER BY cdate)
            > LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Close > Open
       AND  Close > LAG(high) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Open  < LAG(low) OVER (PARTITION BY stock ORDER BY cdate)
       THEN 'BULLISH'
       WHEN Close < Open
       AND  Close < LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Open  < LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  LAG(open) OVER (PARTITION BY stock ORDER BY cdate)
            < LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Close > LAG(open) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Open  < LAG(close) OVER (PARTITION BY stock ORDER BY cdate)
       THEN 'BEARISH'
       WHEN Close > LAG(Close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Open  > LAG(Open) OVER (PARTITION BY stock ORDER BY cdate)
       THEN 'UP'
       WHEN Close < LAG(Close) OVER (PARTITION BY stock ORDER BY cdate)
       AND  Open  < LAG(Open) OVER (PARTITION BY stock ORDER BY cdate)
       THEN 'DOWN'
       ELSE 'OTHER'
       END AS pttrn
FROM   table_name t

db<>fiddle here