【问题标题】:Oracle: Get the price based on a variable and history tableOracle:根据变量和历史表获取价格
【发布时间】:2020-12-16 14:44:15
【问题描述】:

我有一个表“variables”和一个表“variables_history”,如下

create table variables
( 
    variables_id number,
    variables_name varchar2(50),
    variables_value varchar2(50),
    variables_updated_at timestamp
);


create table variables_history
( 
    variables_id number,
    variables_name varchar2(50),
    variables_value varchar2(50),
    variables_hist_updated_at timestamp
);

历史记录由触发器生成,如下所示。

CREATE OR REPLACE EDITIONABLE TRIGGER "myuser"."trigger_variables_update" 
  AFTER UPDATE ON myuser.variables
  REFERENCING NEW AS NEW OLD AS OLD
  FOR EACH ROW

BEGIN
  IF :old.variables_value <> :new.variables_value THEN
    INSERT INTO myuser.variables_history
      (variables_id,
       variables_name,
       variables_value,
       variables_hist_updated_at
    VALUES
      (variables_id,
       :old.variables_name,
       :old.variables_value,
       old.variables_updated_at);
  END IF;
END trigger_variables_update;

我还有一张桌子,上面有所有的维护保养

create table maintenance
( 
    maintenance_id number,
    maintenance_status varchar2(20),
    maintenance_date timestamp
);

我需要根据 maintenance_date 和 variables_updated_at 或 variables_hist_updated_at 的变量生成带有 maintenance_price 的输出

喜欢这个


WITH variables_data as
( SELECT 1 variables_id, 'maintenance_price' variables_name, '30.00' variables_value, '2020-08-01 05:00:00.000' variables_updated_at from dual),

variables_history_data as
(
SELECT 1 variables_id, 'maintenance_price' variables_name, '15.90' variables_value, '2019-10-01 11:30:00.000' variables_hist_updated_at from dual union all
SELECT 1 variables_id, 'maintenance_price' variables_name, '10.50' variables_value, '2020-01-01 01:00:00.000' variables_hist_updated_at from dual union all
SELECT 1 variables_id, 'maintenance_price' variables_name, '20.30' variables_value, '2020-05-01 12:30:00.000' variables_hist_updated_at from dual
),

maintenance_data as
(
SELECT 1 maintenance_id, 'COMPLETE' maintenance_status, '2019-02-01 00:30:00.000' maintenance_date from dual union all
SELECT 2 maintenance_id, 'COMPLETE' maintenance_status, '2019-05-01 01:30:00.000' maintenance_date from dual union all
SELECT 3 maintenance_id, 'COMPLETE' maintenance_status, '2019-11-01 02:30:00.000' maintenance_date from dual union all
SELECT 4 maintenance_id, 'COMPLETE' maintenance_status, '2020-07-10 05:30:00.000' maintenance_date from dual union all
SELECT 5 maintenance_id, 'FAILED' maintenance_status, '2020-08-02 11:30:00.000' maintenance_date from dual
SELECT 6 maintenance_id, 'COMPLETE' maintenance_status, '2020-08-20 11:30:00.000' maintenance_date from dual
)

Select 
    m.maintenance_id,
    to_char(m.maintenance_date, 'yyyy/mm/dd') as maintenance_date
    v.variables_value
from 
    maintenances m
    join variables v on m.maintenance_date >= v.variables_updated_at
    join variables_history vh on m.maintenance_date < variables_hist_updated_at
        where maintenance_status = 'COMPLETE';

这个查询只是一个例子,我知道它错了

我需要这样的输出(并考虑变量可能有新的更新)。 “variable_value”需要是生成维护时的值。

maintenance_id | maintenance_date | variables_value |
---------------+------------------+-----------------+
            1  |        2019-02-01|           15.90 |
---------------+------------------+-----------------+
            2  |        2019-05-01|           15.90 |
---------------+------------------+-----------------+
            3  |        2019-11-01|           10.50 |
---------------+------------------+-----------------+
            4  |        2020-07-10|           20.30 |
---------------+------------------+-----------------+
            6  |        2020-08-20|           30.00 |
---------------+------------------+-----------------+

【问题讨论】:

  • 您的数据模型使这变得不必要地困难。您的历史记录表应包含该历史记录生效的时间戳。
  • 为此,我需要在“variables_history”表(如“variables_created_at”)上创建一个新的时间戳列并更改触发器以保存 :old.variables_updated_at 值?
  • 或者也许只是切换到触发器来保存 ":old.variables_updated_at" 值而不是设置当前的 "systimestamp"?
  • 我正在考虑切换触发器以保存 :old.variables_updated_at 而不是 systimestamp。如果你想拥有“effective_from”(:old.variables_updated_at)和“effective_to”(systimestamp),拥有这两列并没有什么坏处。但你至少应该在历史表中拥有有效的起始日期才能做到这一点更容易。
  • 谢谢。我会试试这样的。这是一个旧系统,处理起来有点复杂。

标签: sql oracle datetime lateral-join


【解决方案1】:

据我了解(并在 Matthew McPeak 的帮助下),历史表存储价格过时的日期,而另一方面,“实时”表存储价格生效的日期。

你可以通过两个横向连接来解决这个问题:

select 
    m.maintenance_id,
    to_char(m.maintenance_date, 'yyyy/mm/dd') as maintenance_date,
    v.*,
    vh.*,
    coalesce(v.variables_value, vh.variables_value) as variables_value
from maintenances m
outer apply(
    select v.variables_value
    from variables_data v
    where v.variables_updated_at <= m.maintenance_date
) v
outer apply (
    select vh.variables_value
    from variables_history_data vh
    where vh.variables_hist_updated_at > m.maintenance_date
    order by vh.variables_hist_updated_at
    fetch first 1 row only
) vh
where m.maintenance_status = 'COMPLETE'
order by 1;

对于您的示例数据,the query returns:

MAINTENANCE_ID |维护_日期 | VARIABLES_VALUE -------------: | :--------------- | --------------: 1 | 2019/02/01 | 15.9 2 | 2019/05/01 | 15.9 3 | 2019/11/01 | 10.5 4 | 2020/07/10 | 6 | 2020/08/20 | 30

请注意,您的示例数据存在故障。历史表中应该有一行的时间戳对应于实时数据的当前时间戳 - 因此,maintenance_id 4 与任何内容都不匹配。

【讨论】:

  • 很好的答案,除了 OP 的数据模型很糟糕。如果我在上午 11 点将一个值从 100 更新到 200,我大概会在历史表中得到一行,表示值 = 100 在上午 11 点更新,而主表中的一行表示值是 200 在上午 11 点更新。如果这是对正在发生的事情的正确理解,我不确定你的帖子中是否充分体现了这一点,尽管我完全同意 CROSS APPLY 方法。
  • @MatthewMcPeak:啊,是的,你是对的......当我开始回答时,我的脑海里有这个,但我在某个地方丢失了它。我稍微更改了查询以适应它。现在我开始认为使用outer apply 会一样好,或者更好。
  • 我认为仍然不存在(将返回最新的历史记录,而不是上次更改后维护日期的当前记录)。我为 OP 添加了关于他的数据模型的评论。这不应该这么难。
  • @MatthewMcPeak:你又是对的!我改为进行 2 个横向连接(并添加了一个小提琴......)。谢谢!
  • @RafaelFrancisco 这不是免费的代码编写服务。如果您在尝试调整 SQL 时遇到特定问题,我建议您将其作为一个新问题发布,其中包含数据模型、示例数据、您尝试了什么以及您的确切问题是什么。
【解决方案2】:

您可以使用UNION ALL 组合表格,然后使用LAG/LEAD 查找最新(或下一个)值:

SELECT *
FROM   (
  SELECT maintenance_id,
         COALESCE(
           variables_value,
           LAG( variables_value, 1 ) IGNORE NULLS OVER ( ORDER BY maintenance_date ),
           LEAD( variables_value, 1 ) IGNORE NULLS OVER ( ORDER BY maintenance_date )
         ) AS variables_value,
         maintenance_date
  FROM   (
    SELECT maintenance_id,
           NULL AS variables_value,
           maintenance_date
    FROM   maintenances
    WHERE  maintenance_status = 'COMPLETE'
    UNION ALL
    SELECT NULL,
           variables_value,
           variables_updated_at
    FROM   variables
    UNION ALL
    SELECT NULL,
           variables_value,
           variables_hist_updated_at
    FROM   variables_history
  )
)
WHERE maintenance_id IS NOT NULL;

其中,对于您的示例数据:

CREATE TABLE variables ( variables_id, variables_name, variables_value, variables_updated_at ) as
SELECT 1, 'maintenance_price', 30.00, TIMESTAMP '2020-08-01 05:00:00.000' from dual;

CREATE TABLE variables_history ( variables_id, variables_name, variables_value, variables_hist_updated_at ) as
SELECT 1, 'maintenance_price', 15.90, TIMESTAMP '2019-10-01 11:30:00.000' from dual union all
SELECT 1, 'maintenance_price', 10.50, TIMESTAMP '2020-01-01 01:00:00.000' from dual union all
SELECT 1, 'maintenance_price', 20.30, TIMESTAMP '2020-05-01 12:30:00.000' from dual;

CREATE TABLE maintenances ( maintenance_id, maintenance_status, maintenance_date ) as
SELECT 1, 'COMPLETE', TIMESTAMP '2019-02-01 00:30:00.000' from dual union all
SELECT 2, 'COMPLETE', TIMESTAMP '2019-05-01 01:30:00.000' from dual union all
SELECT 3, 'COMPLETE', TIMESTAMP '2019-11-01 02:30:00.000' from dual union all
SELECT 4, 'COMPLETE', TIMESTAMP '2020-07-10 05:30:00.000' from dual union all
SELECT 5, 'FAILED',   TIMESTAMP '2020-08-02 11:30:00.000' from dual union all
SELECT 6, 'COMPLETE', TIMESTAMP '2020-08-20 11:30:00.000' from dual;

输出:

MAINTENANCE_ID |变量值 |维护_DATE -------------: | --------------: | :---------------------------- 1 | 15.9 | 01-FEB-19 00.30.00.000000000 2 | 15.9 | 19 年 5 月 1 日 01.30.00.000000000 3 | 15.9 | 19 年 11 月 1 日 02.30.00.000000000 4 | 20.3 | 20 年 7 月 10 日 05.30.00.000000000 6 | 30 | 20-8-20 11.30.00.000000000

如果您交换 LAGLEAD(更喜欢下一个值而不是前一个值):

SELECT *
FROM   (
  SELECT maintenance_id,
         COALESCE(
           variables_value,
           LEAD( variables_value, 1 ) IGNORE NULLS OVER ( ORDER BY maintenance_date ),
           LAG( variables_value, 1 ) IGNORE NULLS OVER ( ORDER BY maintenance_date )
         ) AS variables_value,
         maintenance_date
  FROM   (
    SELECT maintenance_id,
           NULL AS variables_value,
           maintenance_date
    FROM   maintenances
    WHERE  maintenance_status = 'COMPLETE'
    UNION ALL
    SELECT NULL,
           variables_value,
           variables_updated_at
    FROM   variables
    UNION ALL
    SELECT NULL,
           variables_value,
           variables_hist_updated_at
    FROM   variables_history
  )
)
WHERE maintenance_id IS NOT NULL;

那么输出是:

MAINTENANCE_ID |变量值 |维护_DATE -------------: | --------------: | :---------------------------- 1 | 15.9 | 01-FEB-19 00.30.00.000000000 2 | 15.9 | 19 年 5 月 1 日 01.30.00.000000000 3 | 10.5 | 19 年 11 月 1 日 02.30.00.000000000 4 | 30 | 20 年 7 月 10 日 05.30.00.000000000 6 | 30 | 20-8-20 11.30.00.000000000

db小提琴here

【讨论】:

    猜你喜欢
    • 2011-03-23
    • 1970-01-01
    • 2016-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    相关资源
    最近更新 更多