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 BY
和ORDER BY
中的列在表中是否唯一。
在您的情况下,PRODUCT_ID, SYSTEM_DATE, TIME
应该是唯一的,否则视图不是确定性的,并且会在重复查询时返回不同的结果 - 这可能不是您所期望的。
【讨论】:
以上是关于SQL查询从数据中获取最新价格?的主要内容,如果未能解决你的问题,请参考以下文章