SQL查询从数据中获取最新价格?

Posted

技术标签:

【中文标题】SQL查询从数据中获取最新价格?【英文标题】:SQL query to get the latest price from the data? 【发布时间】:2020-02-15 08:09:44 【问题描述】:

我的 Oracle 表中有这些数据:

在此表中,我有多个产品,在特定时间具有 product_id 和价格。

我想编写一个 SQL 查询,根据TIME 列(Oracle - Long 类型)获取每种产品的最新价格。

但问题出在这里,因为我们对产品的表价监控从前一天的 23:00 开始。

需要输出:当我将 2019 年 12 月 2 日作为参数传递给 SQL 查询时

如何使用 SQL 查询来做到这一点?还是我需要为它写一个存储过程,基于 if 和 else 条件?

提前致谢。

【问题讨论】:

But here is the problem ... 您似乎在 12 月 19 日晚上 11 点前后都获得了结果。请解释你的逻辑。 @Tim 日期不是 12 月 19 日,而是 2019 年 抱歉,现在是 2019 年 12 月 2 日,但我的问题仍然存在。 @Tim 让我解释一下逻辑:我想要每个产品的最新价格。现在我将在我的查询中传递日期作为参数。但是,我需要考虑时间超过 230000(hhmmss) 和上一个日期的所有行。你可以看到我展示的输出。对于产品 ID 13520,时间是 231150,local_date 是 1-Dec 。因此,当我在 sql 查询中将 2-Dec 作为参数传递时,您可以看到 13520 id 没有其他行,我想将该行视为 230000 之后的最新行。 以防万一,如果不清楚,我会用更多的数据来解释.. 【参考方案1】:

我们可以尝试使用ROW_NUMBER 来满足这个要求:

WITH cte AS (
    SELECT t.*, ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY time DESC) rn
    FROM yourTable t
    WHERE system_date = date '2019-12-19'
)

SELECT local_date, system_date, currency, product_id, time, current_price
FROM cte
WHERE rn = 1;

【讨论】:

【参考方案2】:

哦,我明白了。 ORDER BY中可以使用两个参数:

SELECT local_date, system_date, currency, product_id, time, current_price
FROM (SELECT t.*,
             ROW_NUMBER() OVER (PARTITION BY product_id
                                ORDER BY local_date DESC, time DESC
                               ) as seqnum
      FROM t
      WHERE system_date = @parameter
     ) t
WHERE rn = 1;

我不确定你是否真的想要这样的日期过滤。你可能想要:

      WHERE system_date <= @parameter

或:

      WHERE system_date < @parameter + interval '1' day

如果在参数指定的日期没有数据,这些将允许您从早期获取数据。如果system_date 具有时间组件(Oracle 中允许date 数据类型使用),第二个版本也可以工作。

【讨论】:

【参考方案3】:

首先,您将日期和时间分开存储,这使得处理日期和时间比需要的复杂。

将两者结合的一种方法:

local_date + interval '1' second * (to_number(substr(time, 5, 2)) +
                                    to_number(substr(time, 3, 2)) * 60 +
                                    to_number(substr(time, 1, 2)) * 3600)

另一个:

to_date(to_char(local_date, 'yyyymmdd') || time, 'yyyymmddhh24miss')

有了这个日期时间,你想要一些类似的东西

where the_time >= timestamp '2019-12-01 23:00:00' and the_time < date '2019-12-03'

对于运行查询时将提供的一些未知日期:

where the_time >= :date - interval '1' hour and the_time < :date + interval '1' day

当日,假设表中没有未来数据:

where the_time >= trunc(sysdate) - interval '1' hour

至于更喜欢当前的日期时间而不是较旧的日期时间,请使用窗口函数,例如MAX OVER.

with rows_with_datetime as
(
  select
    mytable.*, 
    to_date(to_char(local_date, 'yyyymmdd') || time, 'yyyymmddhh24miss') as dt
  from mytable
)
, two_days_with_maxdatetime as
(
  select
    rows_with_datetime.*,
    max(dt) over (partition by product_id order by dt) as max_dt
  from rows_with_datetime
  where dt >= trunc(sysdate) - interval '1' hour
)
select *
from two_days_with_maxdatetime
where dt = max_dt
order by product_id;

随着日期时间的计算,此处不会使用索引,因此查询可能会很慢。因此,您最好将数据模型更改为具有可以轻松索引和使用的日期时间,而不是单独的日期和时间。如果不能,您可能希望仅在日期上应用额外的冗余条件:

where local_date >= trunc(sysdate) - interval '1' day

【讨论】:

【参考方案4】:

您可以从其他答案扩展这个想法(使用ROW_NUMBER)来定义一个视图,只提供每天最后一次价格的记录。

您必须在 PARTITION BY 子句中为您提供维度(product_id 和 day),这适用于任何产品和任何一天 - 仅选择具有每天最高时间和产品的行

create view last_price_per_day as 
with last_price as (
select 
  SYSTEM_DATE, PRODUCT_ID, TIME, CURRENT_PRICE,
  row_number() over (partition by PRODUCT_ID, SYSTEM_DATE order by TIME desc) as rn
from tab)
select 
  SYSTEM_DATE, PRODUCT_ID, TIME, CURRENT_PRICE
from last_price
where rn = 1;

请注意,这种方法优于在相关子查询中使用 MAX(TIME) 的替代方法,因为即使出现平局(更多行具有相同的最高时间),它也能正常工作。

如果这是一个相关主题,您可以在ORDER BY 子句中添加更多列,例如通过添加CURRENT_PRICE DESC,您将从绑定的行中获得最高价格。

对此类最后一行视图的一个很好的检查是验证PARTITIEN BYORDER BY 中的列在表中是否唯一。

在您的情况下,PRODUCT_ID, SYSTEM_DATE, TIME 应该是唯一的,否则视图不是确定性的,并且会在重复查询时返回不同的结果 - 这可能不是您所期望的。

【讨论】:

以上是关于SQL查询从数据中获取最新价格?的主要内容,如果未能解决你的问题,请参考以下文章

使用SQL查询物料最新采购价格的示例

Oracle SQL 查询从一行中的多个列中获取最新数据

如何在 mysql 查询中获取最新的商品价格?

如何从价格变化的 SQL 表中获取列表

通过一个 SQL 查询从多个类别中检索最常见的项目名称及其最高价格

SQL查询获取特定日期和列中最新日期之间的数据