sql - Oracle中的Select语句产生重复行

标签 sql database oracle

我有一个超过 10 列的选择语句。我必须根据日期重复缺少数据的行。 要生成的行应该包含前面行中的数据,这些数据按日期升序排序。 要考虑的日期范围基于 id 的分组。

日期实际上是从 3 月 15 日到 4 月 16 日的范围,但为了示例我只取了有限的行。

例如数据如下图

    ID  Date    Type    Code  Location
   ==== ======  ===     ====   ====
    1   15-Mar  TG       RET    X1
    1   17-Mar  GG       CAN    S2
    1   20-Mar  DTR      ISS    D2
    2   14-Apr  YT       RR     F2
    2   16-Apr  F        FC     F1

异常输出:

    ID  Date    Type    Code  Location
    === ====    ====   ====  ======  
    1   15-Mar  TG      RET    X1
    *1  16-Mar  TG      RET    X1*
    1   17-Mar  GG      CAN    S2
    *1  18-Mar  GG      CAN    S2*
    *1  19-Mar  GG      CAN    S2*
    1   20-Mar  DTR     ISS    D2
    2   14-Apr  YT      RR     F2
    *2  15-Apr  YT      RR     F2*
    2   16-Apr  F       FC     F1

最佳答案

这是实现所需输出的可能方法的工作示例。我正在使用 Oracle 的 LAST_VALUE IGNORE NULLS 的解析函数选项和 ORDER BY条款。

测试数据:

CREATE TABLE so123 (
  id NUMBER,
  d DATE,
  type VARCHAR2(10),
  code VARCHAR2(10),
  location VARCHAR2(10)
);

INSERT INTO so123 VALUES (1, DATE '2015-05-15', 'TG', 'RET', 'X1');
INSERT INTO so123 VALUES (1, DATE '2015-05-17', 'GG', 'CAN', 'S2');
INSERT INTO so123 VALUES (1, DATE '2015-05-20', 'DTR', 'ISS', 'D2');
INSERT INTO so123 VALUES (2, DATE '2015-04-14', 'YT', 'RR', 'F2');
INSERT INTO so123 VALUES (2, DATE '2015-04-16', 'F', 'FC', 'F1');

COMMIT;

选择本身:

WITH
  dmm AS (
    SELECT MIN(d) min_d, MAX(d) max_d FROM so123
  )
SELECT
    NVL(s.id, LAST_VALUE(s.id) IGNORE NULLS OVER (ORDER BY dt.d)) AS id,
    dt.d,
    NVL(s.type, LAST_VALUE(s.type) IGNORE NULLS OVER (ORDER BY dt.d)) AS type,
    NVL(s.code, LAST_VALUE(s.code) IGNORE NULLS OVER (ORDER BY dt.d)) AS code,
    NVL(s.location, LAST_VALUE(s.location) IGNORE NULLS OVER (ORDER BY dt.d)) AS location
  FROM (
    SELECT min_d + level - 1 as d
      FROM dmm
    CONNECT BY min_d + level - 1 <= max_d
  ) dt LEFT JOIN so123 s ON (dt.d = s.d)
ORDER BY dt.d
;

输出:

        ID D                TYPE       CODE       LOCATION 
---------- ---------------- ---------- ---------- ----------
         2 14-04-2015 00:00 YT         RR         F2         
         2 15-04-2015 00:00 YT         RR         F2         
         2 16-04-2015 00:00 F          FC         F1         
         2 17-04-2015 00:00 F          FC         F1         
         2 18-04-2015 00:00 F          FC         F1         
         2 19-04-2015 00:00 F          FC         F1         
         2 20-04-2015 00:00 F          FC         F1         
         2 21-04-2015 00:00 F          FC         F1         
         2 22-04-2015 00:00 F          FC         F1         
         2 23-04-2015 00:00 F          FC         F1         
         2 24-04-2015 00:00 F          FC         F1         
         2 25-04-2015 00:00 F          FC         F1         
         2 26-04-2015 00:00 F          FC         F1         
         2 27-04-2015 00:00 F          FC         F1         
         2 28-04-2015 00:00 F          FC         F1         
         2 29-04-2015 00:00 F          FC         F1         
         2 30-04-2015 00:00 F          FC         F1         
         2 01-05-2015 00:00 F          FC         F1         
         2 02-05-2015 00:00 F          FC         F1         
         2 03-05-2015 00:00 F          FC         F1         
         2 04-05-2015 00:00 F          FC         F1         
         2 05-05-2015 00:00 F          FC         F1         
         2 06-05-2015 00:00 F          FC         F1         
         2 07-05-2015 00:00 F          FC         F1         
         2 08-05-2015 00:00 F          FC         F1         
         2 09-05-2015 00:00 F          FC         F1         
         2 10-05-2015 00:00 F          FC         F1         
         2 11-05-2015 00:00 F          FC         F1         
         2 12-05-2015 00:00 F          FC         F1         
         2 13-05-2015 00:00 F          FC         F1         
         2 14-05-2015 00:00 F          FC         F1         
         1 15-05-2015 00:00 TG         RET        X1         
         1 16-05-2015 00:00 TG         RET        X1         
         1 17-05-2015 00:00 GG         CAN        S2         
         1 18-05-2015 00:00 GG         CAN        S2         
         1 19-05-2015 00:00 GG         CAN        S2         
         1 20-05-2015 00:00 DTR        ISS        D2         

 37 rows selected 

这是如何运作的?我们从源表中生成 MIN 和 MAX 日期之间的所有日期。为此,我们使用 CONNECT BY使 Oracle 生成记录直到条件 min_d + level - 1 <= max_d 的子句不再持有。

然后,我们获取生成的记录和LEFT JOIN他们的源表。 LAST_VALUE 来了分析函数的魔力发挥作用。此函数使用指定的顺序搜索表中的最后一个非空值(IGNORE NULLS 选项)并填充缺失的字段。

您可以在此处阅读有关该功能的更多信息:

http://oracle-base.com/articles/misc/first-value-and-last-value-analytic-functions.php

SQLFiddle Demo

关于sql - Oracle中的Select语句产生重复行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30337449/

相关文章:

sql - 从 <子查询> 中删除

sql - "distinct on"与 postgres 组

mysql - 从连接的 2 个表中选择最大值

java - 创建包含数据库值的列表并将其存储在 DTO 中

python - MongoDB 的位置索引在什么单位?米?英里?

java - Oracle Database 11g 如何通过 HTTPS TLS1.1、TLS1.2 连接到 Web 服务

sql - 无法找出历史表记录的正确 SQL 查询

sql - 将字段存储为单一文本类型 JSON 字符串与拆分到单独表的性能影响

mysql - 大型数据集的数据库设计

oracle - 为什么 oracle 选择索引范围扫描而不是快速完整索引扫描