英文:
Simplify a chain of computations
问题
以下是翻译好的 SQL 查询部分:
原始查询:
SELECT
time,
close - LAG(close) OVER (ORDER BY time) AS "diff"
CASE WHEN diff > 0 THEN diff ELSE 0 END AS gain,
CASE WHEN diff < 0 THEN diff ELSE 0 END AS loss,
AVG(gain) OVER (ORDER BY time ROWS 40 PRECEDING) as avg_gain,
AVG(loss) OVER (ORDER BY time ROWS 40 PRECEDING) AS avg_loss
avg_gain / avg_loss AS rs,
100 - (100 / NULLIF(1+rst.rs, 0)) as rsi
FROM candles_5min
WHERE symbol = 'AAPL';
简化后的查询:
SELECT
rst.time,
100 - (100 / NULLIF((1+rst.rs), 0)) as rsi
FROM (
SELECT
avgs.time,
avgs.avg_gain / NULLIF(avgs.avg_loss, 0) AS rs
FROM (
SELECT
glt.time,
AVG(glt.gain) OVER (ORDER BY time ROWS 40 PRECEDING) as avg_gain,
AVG(glt.loss) OVER (ORDER BY time ROWS 40 PRECEDING) AS avg_loss
FROM (
SELECT
dt.time,
CASE WHEN dt.diff > 0 THEN dt.diff ELSE 0 END AS gain,
CASE WHEN dt.diff < 0 THEN dt.diff ELSE 0 END AS loss
FROM (
SELECT
time,
close - LAG(close) OVER (ORDER BY time) AS "diff"
FROM candles_5min
WHERE symbol = 'AAPL'
) AS dt
) AS glt
) AS avgs
) AS rst
请注意,这些查询已经按照你的要求进行了翻译,只返回翻译好的 SQL 查询部分。
英文:
I would like to make a query similar to this:
SELECT
time,
close - LAG(close) OVER (ORDER BY time) AS "diff"
CASE WHEN diff > 0 THEN diff ELSE 0 END AS gain,
CASE WHEN diff < 0 THEN diff ELSE 0 END AS loss,
AVG(gain) OVER (ORDER BY time ROWS 40 PRECEDING) as avg_gain,
AVG(loss) OVER (ORDER BY time ROWS 40 PRECEDING) AS avg_loss
avg_gain / avg_loss AS rs,
100 - (100 / NULLIF(1+rst.rs, 0)) as rsi
FROM candles_5min
WHERE symbol = 'AAPL';
But from what I can tell, SQL doesn't allow references to columns created within the same SELECT
. So I have to do something like:
SELECT rst.time, 100 - (100 / NULLIF((1+rst.rs), 0)) as rsi
FROM (SELECT
avgs.time,
avgs.avg_gain / NULLIF(avgs.avg_loss, 0) AS rs
FROM (SELECT glt.time, AVG(glt.gain) OVER (ORDER BY time ROWS 40 PRECEDING) as avg_gain,
AVG(glt.loss) OVER (ORDER BY time ROWS 40 PRECEDING) AS avg_loss
FROM (SELECT
dt.time,
CASE WHEN dt.diff > 0 THEN dt.diff ELSE 0 END AS gain,
CASE WHEN diff < 0 THEN diff ELSE 0 END AS loss
FROM (SELECT
time,
close - LAG(close) OVER (ORDER BY time) AS "diff"
FROM candles_5min
WHERE symbol = 'AAPL') AS dt) AS glt) AS avgs) AS rst
Is there any way to simplify a query like this one? I'm using PostgreSQL.
答案1
得分: 1
有一个基于另一个窗口函数的窗口函数,不能在相同的 SELECT
列表中嵌套。所以你需要至少两个查询级别。但这个最小值似乎是可行的:
SELECT time
, 100 - (100 / NULLIF((1 + AVG(GREATEST(diff, 0)) OVER w
/ NULLIF(AVG(LEAST(diff, 0)) OVER w, 0)), 0)) AS rsi
FROM (
SELECT time
, close - LAG(close) OVER (ORDER BY time) AS diff
FROM candles_5min
WHERE symbol = 'AAPL'
) dt
WINDOW w AS (ORDER BY time ROWS 40 PRECEDING);
你可以用专用的 (100% 等效的) GREATEST()
和 LEAST()
替代冗长的 CASE
表达式。
你可以使用 WINDOW
子句 避免在相同的 SELECT
列表中为多个窗口函数重复指定相同的窗口帧。在同一个查询计划中产生相同的结果,所以对性能没有影响。 手册链接:
> ... 当需要在一个以上的窗口函数中使用相同的窗口定义时,WINDOW
子句可以节省打字时间。
总的来说,我预计我的重写只会稍微快一点。但你要求简化查询,所以没关系。
英文:
There is a window function based on another window function, which cannot be nested in the same SELECT
list. So you need a minimum of two query levels. But that minimum seems feasible:
SELECT time
, 100 - (100 / NULLIF((1 + AVG(GREATEST(diff, 0)) OVER w
/ NULLIF(AVG(LEAST(diff, 0)) OVER w, 0)), 0)) AS rsi
FROM (
SELECT time
, close - LAG(close) OVER (ORDER BY time) AS diff
FROM candles_5min
WHERE symbol = 'AAPL'
) dt
WINDOW w AS (ORDER BY time ROWS 40 PRECEDING);
You can replace the lengthy CASE
expressions with dedicated (100 % equivalent) GREATEST()
and LEAST()
.
You can use a WINDOW
clause to avoid spelling out the same frame for multiple window functions in the same SELECT
list. Results in the same query plan, so no effect on performance. The manual:
> ... the WINDOW
clause saves typing when the same window definition is needed for more than one window function.
Overall, I expect my rewrite to be only slightly faster. But you asked for a simplified query, not performance, so that's ok.
答案2
得分: 0
你可以使用公共表达式(CTEs)来重写它,而不是子查询,这将使代码更易阅读,也更容易调试。
但你可以这样做:
SELECT
time,
close - LAG(close) OVER (ORDER BY time) AS "diff",
CASE WHEN close - LAG(close) OVER (ORDER BY time) > 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END AS gain,
CASE WHEN close - LAG(close) OVER (ORDER BY time) < 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END AS loss,
AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) > 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END) OVER (ORDER BY time ROWS 40 PRECEDING) as avg_gain,
AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) < 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END) OVER (ORDER BY time ROWS 40 PRECEDING) AS avg_loss,
AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) > 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END) OVER (ORDER BY time ROWS 40 PRECEDING)
/ AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) < 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END) OVER (ORDER BY time ROWS 40 PRECEDING) AS rs,
100 - (100 / NULLIF(1+
(AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) > 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END) OVER (ORDER BY time ROWS 40 PRECEDING)
/ AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) < 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END) OVER (ORDER BY time ROWS 40 PRECEDING)), 0)) as rsi
FROM candles_5min
WHERE symbol = 'AAPL';
你应该对它进行性能基准测试。
英文:
You could rewrite it with ctes instead of subqueries, that woul keep it better readable and you cann better debug it.
But you can do
SELECT
time,
close - LAG(close) OVER (ORDER BY time) AS "diff",
CASE WHEN close - LAG(close) OVER (ORDER BY time) > 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END AS gain,
CASE WHEN close - LAG(close) OVER (ORDER BY time) < 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END AS loss,
AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) > 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END ) OVER (ORDER BY time ROWS 40 PRECEDING) as avg_gain,
AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) < 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END) OVER (ORDER BY time ROWS 40 PRECEDING) AS avg_loss,
AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) > 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END ) OVER (ORDER BY time ROWS 40 PRECEDING)
/ AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) < 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END) OVER (ORDER BY time ROWS 40 PRECEDING) AS rs,
100 - (100 / NULLIF(1+
(AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) > 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END ) OVER (ORDER BY time ROWS 40 PRECEDING)
/ AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time) < 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END) OVER (ORDER BY time ROWS 40 PRECEDING)), 0)) as rsi
FROM candles_5min
WHERE symbol = 'AAPL';
you should benchmark it
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论