oracle取不同日期一列的平均值
oracle take mean value of one column across different dates
有一列名为 Price
,另一列名为 Date_1
,其中包含从现在到大约一年后的数据。
我想找到 Price
在不同日期的平均值。例如,2 周后、1 个月后、6 个月后...
我可以使用 Case When
功能吗?
鉴于:
Location_id | Date_1 | Price
------------+-------------+------
L_1 | 20-JUL-2016 | 105
L_1 | 21-JUL-2016 | 117
... | ... | ...
L_1 | 16-MAY-2017 | 103
L_2 | 20-JUL-2016 | 99
L_2 | 21-JUL-2016 | 106
... | ... | ...
L_2 | 16-MAY-2017 | 120
获得:
Location_id | Period | Average_Price
------------+----------+--------------
L_1 | 2 weeks | ...
L_1 | 6 months | ...
L_1 | 1 year | ...
L_2 | 2 weeks | ...
L_2 | 6 months | ...
L_2 | 1 year | ...
在 "Period" 中,“2 周”表示从开始日期 (sysdate) 开始的 2 周。 "Average_Price" 是那个时期的价格平均值。
谢谢!这个问题解决了。我又发现了一个:
还有一个 table 包含日期信息:
Location_id | Ex_start_date | Ex_end_date
------------+-----------------+--------------
L_1 | 08-JUN-16 | 30-AUG-16
L_1 | 21-SEP-16 | 25-SEP-16
L_1 | 08-MAY-17 | 12-MAY-17
L_2 | 08-AUG-16 | 21-AUG-16
L_2 | 24-OCT-16 | 29-OCT-16
L_2 | 15-MAR-17 | 19-MAR-17
"Ex_Start_date" 和 "Ex_End_date" 之后是 'Non_Ex' 时期。在获得 2 周和 6 个月的平均信息后,我想再添加一列,以获得上述 'Non_Ex' 和 'Ex' 条件的平均价格。
希望能得到如下的table:
Location_id | Period | Ex_Condition | Average_Price
------------+----------------+----------------------------------
L_1 | 2 weeks | Ex period | ...
L_1 | 2 weeks | Non-Ex period | ...
L_1 | 6 months | Ex period | ...
L_1 | 6 months | Non-Ex period | ...
L_2 | 2 weeks | Ex period | ...
L_2 | 2 weeks | Non-Ex period | ...
L_2 | 6 months | Ex period | ...
L_2 | 6 months | Non-Ex period | ...
如果没有日期在 EX Period 或 Non-Ex Period 内,平均价格将 return 'null'。
我怎样才能做到这一点?谢谢!
你可以这样做:
select location_id,
period,
sum(in_period * price) / nullif(sum(in_period), 0) as avg_price
from (select location_id,
price,
period,
case when mydate - days < sysdate then 1 else 0 end in_period
from localprice,
( select '2 weeks' as period, 14 as days from dual
union
select '6 months', 183 from dual
) intervals
) detail
group by location_id,
period
将 localprice 替换为您的 table 的名称(您没有在问题中提供其名称)。
将 mydate 替换为日期列的实际名称。我不希望你将它命名为 date,因为这是一个保留字并且需要你始终引用它——不要那样做:选择另一个名称。
dual
是 Oracle 中可用的标准对象,可用于在查询中引入行 - table 某处没有的行。
或者,您可以创建一个 table,其中包含您感兴趣的所有时间段(2 周、4 周...,以及它们代表的天数),并使用它代替并集 select对偶。
这是一个SQL fiddle。请注意,它在 Postgres 上运行,因为此时 Oracle 实例不可用。出于这个原因,我明确创建了 dual
并使用 current_date
而不是 sysdate
。但其余的都是一样的。
未测试,因为 OP 未提供可用格式的输入数据。
您可能想要类似
的东西
select location_id, '2 weeks' as period, avg(price) as average_price
from base_table
where price is not null
and
"date" between SYSDATE and SYSDATE + 13
-- or however you want to define the two week interval
group by location_id
union all
select location_id, '6 months' as period, avg(price) as average_price
from base_table
where price is not null
and
"date" between SYSDATE and add_months(SYSDATE, 6) - 1
-- or however you want to define the six month interval
group by location_id
;
请注意,date
是 Oracle 保留关键字,不应用作列名;如果你这样做,你将不得不使用双引号,完全匹配大小写(大小写),你可能仍然 运行 进入各种问题。最好只使用 table 和非保留字的列名。
这是@trincot 回答的重新措辞版本。它应该在更大的数据集上更快。
- 不需要的行被跳过,不归零和使用。如果没有符合间隔条件的本地价格,您将不会再获得结果行。
- 与@mathguy 的回答不同,它仍然只扫描一次 localprice。
- 如果当地实际价格在日期上有高选择性的指数,那么可以使用。
- 取消注释
WHERE
子句中的行将有助于尽早丢弃行,即在考虑间隔 table 之前。 ORDERED
提示在现实生活中可能是不必要的,但它演示了将此行与此数据一起使用时的正确解释计划。
- 在粘合将要唯一的行时,使用
UNION ALL
而不是 UNION
。
像往常一样,在您根据自己的情况证明答案之前,不要相信任何答案。
WITH
localprice AS
( SELECT 'L_1' Location_id, TO_DATE('20-JUN-2016') "DATE", 105 Price FROM DUAL
UNION ALL
SELECT 'L_1' Location_id, TO_DATE('16-MAY-2017') "DATE", 103 Price FROM DUAL
UNION ALL
SELECT 'L_2' Location_id, TO_DATE('20-JUN-2016') "DATE", 99 Price FROM DUAL
UNION ALL
SELECT 'L_2' Location_id, TO_DATE('16-MAY-2017') "DATE", 120 Price FROM DUAL
),
intervals AS
( SELECT '2 weeks' AS period, 14 AS days FROM dual
UNION ALL
SELECT '6 months', 183 FROM dual
)
SELECT /*+ ORDERED */
location_id, period,
AVG(price) AS avg_price
FROM
localprice
CROSS JOIN
intervals
WHERE "DATE" >= SYSDATE - days
-- AND "DATE" >= SYSDATE - (SELECT MAX(days) FROM intervals)
GROUP BY location_id, period
有一列名为 Price
,另一列名为 Date_1
,其中包含从现在到大约一年后的数据。
我想找到 Price
在不同日期的平均值。例如,2 周后、1 个月后、6 个月后...
我可以使用 Case When
功能吗?
鉴于:
Location_id | Date_1 | Price
------------+-------------+------
L_1 | 20-JUL-2016 | 105
L_1 | 21-JUL-2016 | 117
... | ... | ...
L_1 | 16-MAY-2017 | 103
L_2 | 20-JUL-2016 | 99
L_2 | 21-JUL-2016 | 106
... | ... | ...
L_2 | 16-MAY-2017 | 120
获得:
Location_id | Period | Average_Price
------------+----------+--------------
L_1 | 2 weeks | ...
L_1 | 6 months | ...
L_1 | 1 year | ...
L_2 | 2 weeks | ...
L_2 | 6 months | ...
L_2 | 1 year | ...
在 "Period" 中,“2 周”表示从开始日期 (sysdate) 开始的 2 周。 "Average_Price" 是那个时期的价格平均值。
谢谢!这个问题解决了。我又发现了一个:
还有一个 table 包含日期信息:
Location_id | Ex_start_date | Ex_end_date
------------+-----------------+--------------
L_1 | 08-JUN-16 | 30-AUG-16
L_1 | 21-SEP-16 | 25-SEP-16
L_1 | 08-MAY-17 | 12-MAY-17
L_2 | 08-AUG-16 | 21-AUG-16
L_2 | 24-OCT-16 | 29-OCT-16
L_2 | 15-MAR-17 | 19-MAR-17
"Ex_Start_date" 和 "Ex_End_date" 之后是 'Non_Ex' 时期。在获得 2 周和 6 个月的平均信息后,我想再添加一列,以获得上述 'Non_Ex' 和 'Ex' 条件的平均价格。
希望能得到如下的table:
Location_id | Period | Ex_Condition | Average_Price
------------+----------------+----------------------------------
L_1 | 2 weeks | Ex period | ...
L_1 | 2 weeks | Non-Ex period | ...
L_1 | 6 months | Ex period | ...
L_1 | 6 months | Non-Ex period | ...
L_2 | 2 weeks | Ex period | ...
L_2 | 2 weeks | Non-Ex period | ...
L_2 | 6 months | Ex period | ...
L_2 | 6 months | Non-Ex period | ...
如果没有日期在 EX Period 或 Non-Ex Period 内,平均价格将 return 'null'。
我怎样才能做到这一点?谢谢!
你可以这样做:
select location_id,
period,
sum(in_period * price) / nullif(sum(in_period), 0) as avg_price
from (select location_id,
price,
period,
case when mydate - days < sysdate then 1 else 0 end in_period
from localprice,
( select '2 weeks' as period, 14 as days from dual
union
select '6 months', 183 from dual
) intervals
) detail
group by location_id,
period
将 localprice 替换为您的 table 的名称(您没有在问题中提供其名称)。
将 mydate 替换为日期列的实际名称。我不希望你将它命名为 date,因为这是一个保留字并且需要你始终引用它——不要那样做:选择另一个名称。
dual
是 Oracle 中可用的标准对象,可用于在查询中引入行 - table 某处没有的行。
或者,您可以创建一个 table,其中包含您感兴趣的所有时间段(2 周、4 周...,以及它们代表的天数),并使用它代替并集 select对偶。
这是一个SQL fiddle。请注意,它在 Postgres 上运行,因为此时 Oracle 实例不可用。出于这个原因,我明确创建了 dual
并使用 current_date
而不是 sysdate
。但其余的都是一样的。
未测试,因为 OP 未提供可用格式的输入数据。
您可能想要类似
的东西select location_id, '2 weeks' as period, avg(price) as average_price
from base_table
where price is not null
and
"date" between SYSDATE and SYSDATE + 13
-- or however you want to define the two week interval
group by location_id
union all
select location_id, '6 months' as period, avg(price) as average_price
from base_table
where price is not null
and
"date" between SYSDATE and add_months(SYSDATE, 6) - 1
-- or however you want to define the six month interval
group by location_id
;
请注意,date
是 Oracle 保留关键字,不应用作列名;如果你这样做,你将不得不使用双引号,完全匹配大小写(大小写),你可能仍然 运行 进入各种问题。最好只使用 table 和非保留字的列名。
这是@trincot 回答的重新措辞版本。它应该在更大的数据集上更快。
- 不需要的行被跳过,不归零和使用。如果没有符合间隔条件的本地价格,您将不会再获得结果行。
- 与@mathguy 的回答不同,它仍然只扫描一次 localprice。
- 如果当地实际价格在日期上有高选择性的指数,那么可以使用。
- 取消注释
WHERE
子句中的行将有助于尽早丢弃行,即在考虑间隔 table 之前。ORDERED
提示在现实生活中可能是不必要的,但它演示了将此行与此数据一起使用时的正确解释计划。 - 在粘合将要唯一的行时,使用
UNION ALL
而不是UNION
。
像往常一样,在您根据自己的情况证明答案之前,不要相信任何答案。
WITH
localprice AS
( SELECT 'L_1' Location_id, TO_DATE('20-JUN-2016') "DATE", 105 Price FROM DUAL
UNION ALL
SELECT 'L_1' Location_id, TO_DATE('16-MAY-2017') "DATE", 103 Price FROM DUAL
UNION ALL
SELECT 'L_2' Location_id, TO_DATE('20-JUN-2016') "DATE", 99 Price FROM DUAL
UNION ALL
SELECT 'L_2' Location_id, TO_DATE('16-MAY-2017') "DATE", 120 Price FROM DUAL
),
intervals AS
( SELECT '2 weeks' AS period, 14 AS days FROM dual
UNION ALL
SELECT '6 months', 183 FROM dual
)
SELECT /*+ ORDERED */
location_id, period,
AVG(price) AS avg_price
FROM
localprice
CROSS JOIN
intervals
WHERE "DATE" >= SYSDATE - days
-- AND "DATE" >= SYSDATE - (SELECT MAX(days) FROM intervals)
GROUP BY location_id, period