ROW_NUMBER(), PARTITION_BY, МАКСИМУМ 2 При МАКСИМАЛЬНОЙ 1-й и последней позиции

#sql #postgresql #window-functions

#sql #postgresql #окно-функции

Вопрос:

У меня есть запрос, который использует ROW_NUMBER() и PARTITION BY и упорядочивает на основе даты col. Что я хотел бы знать, так это то, возможно ли ограничить это и возвращать только col1, которые соответствуют, если второе максимальное значение находится в позиции 1, а текущее максимальное значение находится в позиции 250.

 SELECT t1.col1, t1.col_date, t1.col_val, t1.rno FROM (
  SELECT col1, col_date, col_val, count(*), ROW_NUMBER() OVER (PARTITION BY col1 order by col_date ASC) as rno
FROM table_one
INNER JOIN
  table_two
ON
  table_one.id = table_one_id
WHERE col1 in (
  SELECT
    col1
  FROM
    table_one
  INNER JOIN table_two on table_one.id = table_one_id
  GROUP
    by table_one.id
  HAVING
    COUNT(*) >= 250
  ) 
  GROUP BY col1, col_date, col_val
) t1
WHERE t1.rno < 250
GROUP BY t1.col1, t1.col_date, t1.col_val, t1.rno
ORDER BY t1.col1, t1.col_date;
  

IE-

  col1 |           col_date           | col_val | rno
-------- ---------------------------- -------- -----
 ABC   | 2018-07-18 15:27:35.394051  |   999   |   1
...
 ABC   | 2019-03-24 15:27:34.78493   |   1000  | 250

 XYZ   | 2018-07-18 15:27:35.394051  |   900   |   1
...
 XYZ   | 2019-03-24 15:27:34.78493   |   1001  | 250
  

Будут ли возвращены оба значения, если col_val является вторым максимальным значением в rno 1, а col_val максимальным значением в rno 250.

[ОБНОВИТЬ]

Для наглядности: две таблицы. table_one — это список стандартных символов. table_two — это список исторических цен. Текущий запрос выбирает символы из table_one и объединяет исторические цены из table_two, которые содержат более 250 записей для каждого символа, а отсечение по rno равно 250, поэтому результат будет выглядеть следующим образом.

 symbol |     market_close_date      | close  | rno
-------- ---------------------------- -------- -----
 FAKE   | 2018-07-18 15:27:35.394051 |  250.0 |   1
 FAKE   | 2018-07-19 15:27:35.391866 |  249.0 |   2
 FAKE   | 2018-07-20 15:27:35.389615 |  248.0 |   3
 FAKE   | 2018-07-21 15:27:35.38741  |  247.0 |   4
 FAKE   | 2018-07-22 15:27:35.3852   |  246.0 |   5
 FAKE   | 2018-07-23 15:27:35.383099 |  245.0 |   6
 FAKE   | 2018-07-24 15:27:35.380934 |  244.0 |   7
 FAKE   | 2018-07-25 15:27:35.378828 |  243.0 |   8
 FAKE   | 2018-07-26 15:27:35.376769 |  242.0 |   9
...
 FAKE   | 2019-03-24 15:27:34.78493  | 1000.0 | 250
 TEST   | 2018-07-18 15:27:35.396232 |  250.0 |   1
 ...
 TEST   | 2018-07-18 15:27:35.64352  |  50.0 | 250
  

Если rno 1 > 2 … 249 и rno 1 … 249 < rno 250, это будет справедливо для FAKE, потому что rno 1 является наибольшим приближением к 249, а 250 — наибольшее приближение, ТЕСТ не будет соответствовать критериям.

Комментарии:

1. Это очень помогло бы вашему вопросу показать некоторые примеры данных.

2. В вашем самом внешнем WHERE предложении не могли бы вы сказать WHERE t1.rno =1 OR t1.rno = 250 ? Можете ли вы немного уточнить определение второго максимума?

3. Джей Спратт, позвольте мне уточнить. Я спрашиваю, возможно ли это сделать, это следующее. значение col_val в rno 1 больше всех значений через 249, а значение rno 250 является наибольшим значением от 1 до 250. Таким образом, все значения от 2 до 249 будут меньше 999 в первом экземпляре (2-й максимум), а значение в 250 (1000) будет максимальным значением. Любое значение col_val 2… 249, которое больше либо rno1, либо rno250, не соответствует критериям.

Ответ №1:

Я думаю, вы можете использовать здесь ключевые слова FILTER и PARTITION BY . Это не будет решением для копирования и вставки, но я думаю, что это укажет вам правильное направление. Я использовал 10 в качестве ограничения строки, потому что я не хотел создавать поддельные данные, но 250 там можно применить.

Таблица test в этом примере будет результатом вашего первоначального объединения между двумя таблицами. Выбор всех данных (или желаемых полей) и добавление ROW_NUMBER

Идея:

  1. Внутренний запрос: выберите нужные данные и примените ROW_NUMBER
  2. Средний запрос: примените ограничение строки к начальным результатам (где номер строки <= x ). Выберите максимум всего, что находится под пределом строки x , и максимум всего набора, включая предел строки x
  3. Самый внешний запрос: убедитесь, что номер строки 1 равен max (самый высокий close где rno < x ) и убедитесь, что номер строки x равен max в наборе (самый высокий close где rno = x )

 SELECT *
FROM
(
    SELECT  *
            , MAX(close) FILTER(WHERE rno < 10) OVER(PARTITION BY symbol) as "max"
            , MAX(close) OVER(PARTITION BY symbol) AS second_max
    FROM 
    (
        SELECT *, ROW_NUMBER() OVER(PARTITION BY symbol ORDER BY close_date) AS rno
        FROM test
    ) t
    WHERE t.rno <= 10
) t2
WHERE (t2.rno = 1 AND t2.close = t2.max)
   OR (t2.rno = 10 AND t2.close = t2.second_max)
;
  

Вот SQLFiddle, показывающий эту идею.

Комментарии:

1. Я просматривал это построчно, разбивая и корректируя по мере продвижения, чтобы приблизиться к тому, что я намереваюсь. Это было исключительно полезно для направления меня. Спасибо.