简化一系列计算

huangapple go评论77阅读模式
英文:

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 &quot;diff&quot;
  CASE WHEN diff &gt; 0 THEN diff ELSE 0 END AS gain,
  CASE WHEN diff &lt; 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 = &#39;AAPL&#39;;

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 &gt; 0 THEN dt.diff ELSE 0 END AS gain,
    CASE WHEN diff &lt; 0 THEN diff ELSE 0 END AS loss
    FROM (SELECT
    time,
    close - LAG(close) OVER (ORDER BY time) AS &quot;diff&quot;
    FROM candles_5min
    WHERE symbol = &#39;AAPL&#39;) 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);

fiddle

你可以用专用的 (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 = &#39;AAPL&#39;
   ) dt
WINDOW w AS (ORDER BY time ROWS 40 PRECEDING);

fiddle

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 &quot;diff&quot;,
CASE WHEN close - LAG(close) OVER (ORDER BY time)  &gt; 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END AS gain,
CASE WHEN close - LAG(close) OVER (ORDER BY time)  &lt; 0 THEN close - LAG(close) OVER (ORDER BY time) ELSE 0 END AS loss,
AVG(CASE WHEN close - LAG(close) OVER (ORDER BY time)  &gt; 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)  &lt; 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)  &gt; 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)  &lt; 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)  &gt; 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)  &lt; 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 = &#39;AAPL&#39;;

you should benchmark it

huangapple
  • 本文由 发表于 2023年6月22日 06:39:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76527576.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定