Выберите значение из более низкого ранга, когда ноль - PullRequest
0 голосов
/ 23 мая 2018

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

Ответы на опросдля одного и того же участника:

┌─────────────────────────────────────┐
| p_id | rank | val_a | val_b | val_c |
|    2 |    1 |     1 |     2 |     3 |
|    2 |    2 |     2 |       |       |
|    2 |    3 |     4 |     4 |     1 |
|    2 |    4 |     4 |     3 |       |
└─────────────────────────────────────┘

Desired output:
┌──────────────────────────────┐
| p_id | val_a | val_b | val_c |
|    2 | 3     | 1     |    -2 |
└──────────────────────────────┘

a = row4 - row1
b = row4 - row1 
c = row3 - row1 (uses the rank3 value since rank4 has none)

Результат должен показать разницу между значением столбца строки с наивысшим рейтингом, который не является нулевым, и значением того же столбца в первой строке, который долженникогда не будет нулевым.

Пока у меня есть код для разницы между двумя строками, но я не могу понять, как учитывать нулевые значения, когда в более низком ранге есть ненулевое значение, которое можно использовать,

SELECT
    p_id,
    CASE WHEN ("p_val_a" IS NOT null) AND (rank != 1) AND ("val_a" IS NOT null) THEN "val_a" - "p_val_a" ELSE NULL END as "diff_val_a",
    CASE WHEN ("p_val_b" IS NOT null) AND (rank != 1) AND ("val_b" IS NOT null) THEN "val_b" - "p_val_b" ELSE NULL END AS "diff_val_b",
    CASE WHEN ("p_val_c" IS NOT null) AND (rank != 1) AND ("val_c" IS NOT null) THEN "val_c" - "p_val_c" ELSE NULL END AS "diff_val_c"
FROM
    (
        SELECT
            p_id,
            "val_a",
            "val_b",
            "val_c",
            LAG("val_a") OVER w AS "p_val_a",
            LAG("val_b") OVER w as "p_val_b",
            LAG("val_c") OVER w as "p_val_c"
        FROM
            dataset WINDOW w AS (
                PARTITION BY 
                    p_id
                ORDER BY
                    rank
            )
    ) t;

В приведенном выше примере, если я запрашиваю только первую и последнюю строки, val_a и val_b дают правильные результаты.Но val_c выдаст null вместо -2, как я хочу.

Как сравнить значения первой строки со значением того же столбца в строке с последним ненулевым значением?

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

Я бы использовал first_value() и last_value():

select distinct p_id,
       (first_value(val_a) over (partition by p_id order by (val_a is not null)::int desc, rank desc) -
        first_value(val_a) over (partition by p_id order by (val_a is not null)::int desc, rank asc)
       ) as a_diff,
       (first_value(val_b) over (partition by p_id order by (val_b is not null)::int desc, rank desc) -
        first_value(val_b) over (partition by p_id order by (val_b is not null)::int desc, rank asc)
       ) as b_diff,
       (first_value(val_c) over (partition by p_id order by (val_c is not null)::int desc, rank desc) -
        first_value(val_c) over (partition by p_id order by (val_c is not null)::int desc, rank asc)
       ) as c_diff
from t;

Здесь - это скрипта SQL.

0 голосов
/ 23 мая 2018

Аналитические функции могут использоваться для определения того, какая строка (ранг) имеет первое вхождение значения, отличного от NULL, и снова для определения окончательного вхождения.

Затем условная агрегация может выбрать этизначения.

http://sqlfiddle.com/#!17/78886/9

WITH
  analysed
AS
(
  SELECT
    *,
    MIN(CASE WHEN val_a IS NOT NULL THEN rank END) OVER ranked_pid   AS first_a_pos,
    MIN(CASE WHEN val_b IS NOT NULL THEN rank END) OVER ranked_pid   AS first_b_pos,
    MIN(CASE WHEN val_c IS NOT NULL THEN rank END) OVER ranked_pid   AS first_c_pos,
    MAX(CASE WHEN val_a IS NOT NULL THEN rank END) OVER ranked_pid   AS final_a_pos,
    MAX(CASE WHEN val_b IS NOT NULL THEN rank END) OVER ranked_pid   AS final_b_pos,
    MAX(CASE WHEN val_c IS NOT NULL THEN rank END) OVER ranked_pid   AS final_c_pos
  FROM
    test
  WINDOW
    ranked_pid AS (
      PARTITION BY p_id
  --      ORDER BY rank
  --  ROWS BETWEEN unbounded preceding
  --           AND unbounded following
    )
)
SELECT
  p_id,
  MAX(CASE WHEN rank = final_a_pos THEN val_a END) - MAX(CASE WHEN rank = first_a_pos THEN val_a END)  AS change_in_a,
  MAX(CASE WHEN rank = final_b_pos THEN val_b END) - MAX(CASE WHEN rank = first_b_pos THEN val_b END)  AS change_in_b,
  MAX(CASE WHEN rank = final_c_pos THEN val_c END) - MAX(CASE WHEN rank = first_c_pos THEN val_c END)  AS change_in_c
FROM
  analysed
GROUP BY
  p_id
ORDER BY
  p_id

РЕДАКТИРОВАТЬ:

Закомментированная часть определения окна, которая не нужна,Это было там, когда я играл с FIRST_VALUE() и LAST_VALUE() (но postgreSQL не поддерживает IGNORE NULLS)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...