В postgresql, как я могу заполнить пропущенные значения в столбце?

Я пытаюсь выяснить, как заполнить значения, отсутствующие в одном столбце, неотсутствующими значениями из других строк, которые имеют такое же значение в данном столбце. Например, в приведенном ниже примере я бы хотел, чтобы все значения «1» были равны Бобу, а все значения «2» были равны Джону.

ID #   | Name
-------|-----
1      | Bob 
1      | (null)
1      | (null)
2      | John
2      | (null)
2      | (null)
`

РЕДАКТИРОВАТЬ: Одно предостережение заключается в том, что я использую postgresql 8.4 с Greenplum, поэтому коррелированные подзапросы не поддерживаются.


person d_a_c321    schedule 21.07.2012    source источник
comment
Пожалуйста, назовите вашу конкретную реализацию. Какую версию вы используете? Кроме того, могут ли быть случаи идентификаторов с более чем одним различным именем? Как с этим справиться? Сначала выбрали по алфавиту?   -  person Erwin Brandstetter    schedule 21.07.2012
comment
@dchandler: Postgres 8.4 поддерживает коррелированные подзапросы (на самом деле это было целую вечность). Тогда Greenplum должен быть основан на действительно старой версии.   -  person a_horse_with_no_name    schedule 22.07.2012
comment
@a_horse_with_no_name, интересно узнать. Похоже, это Гринплам! какой позор   -  person d_a_c321    schedule 23.07.2012


Ответы (3)


CREATE TABLE bobjohn
        ( ID INTEGER NOT NULL
        , zname varchar
        );
INSERT INTO bobjohn(id, zname) VALUES
 (1,'Bob') ,(1, NULL) ,(1, NULL)
,(2,'John') ,(2, NULL) ,(2, NULL)
        ;

UPDATE bobjohn dst
SET zname = src.zname
FROM bobjohn src
WHERE dst.id = src.id
AND dst.zname IS NULL
AND src.zname IS NOT NULL
        ;

SELECT * FROM bobjohn;

ПРИМЕЧАНИЕ. Этот запрос завершится ошибкой, если для данного идентификатора существует более одного имени. (и это не коснется записей, для которых нет ненулевого имени)

Если вы используете версию postgres>-9, вы можете использовать CTE для извлечения исходных кортежей (это эквивалентно подзапросу, но его легче писать и читать (ИМХО). CTE также решает проблему повторяющихся значений ( довольно грубо):

        --
        -- CTE's dont work in update queries for Postgres version below 9
        --
WITH uniq AS (
        SELECT DISTINCT id
        -- if there are more than one names for a given Id: pick the lowest
        , min(zname) as zname
        FROM bobjohn
        WHERE zname IS NOT NULL
        GROUP BY id
        )
UPDATE bobjohn dst
SET zname = src.zname
FROM uniq src
WHERE dst.id = src.id
AND dst.zname IS NULL
        ;

SELECT * FROM bobjohn;
person wildplasser    schedule 21.07.2012

UPDATE tbl
SET    name = x.name
FROM  (
    SELECT DISTINCT ON (id) id, name
    FROM   tbl
    WHERE  name IS NOT NULL
    ORDER  BY id, name
    ) x
WHERE  x.id = tbl.id
AND    tbl.name IS NULL;

DISTINCT ON делает работу в одиночку. Отсутствие необходимости в дополнительной агрегации.

В случае нескольких значений для name выбирается первое в алфавитном порядке (в соответствии с текущей локалью) - для этого предназначен ORDER BY id, name. Если name однозначно, вы можете опустить эту строку.

Кроме того, если есть хотя бы одно ненулевое значение для id, вы можете опустить WHERE name IS NOT NULL.

person Erwin Brandstetter    schedule 21.07.2012

Если вы наверняка знаете, что нет конфликтующих значений (несколько строк с одинаковым идентификатором, но разными ненулевыми именами), тогда что-то вроде этого обновит таблицу соответствующим образом:

UPDATE some_table AS t1
SET name = (
    SELECT name
    FROM some_table AS t2
    WHERE t1.id = t2.id
      AND name IS NOT NULL
    LIMIT 1
)
WHERE name IS NULL;

Если вы хотите только запросить таблицу и заполнить эту информацию на лету, вы можете использовать аналогичный запрос:

SELECT
    t1.id,
    (
        SELECT name
        FROM some_table AS t2
        WHERE t1.id = t2.id
          AND name IS NOT NULL
        LIMIT 1
    ) AS name

FROM some_table AS t1;
person cdhowie    schedule 21.07.2012
comment
Я использую конкретную реализацию, которая не поддерживает коррелированные подзапросы. Есть ли другой способ справиться с этим? - person d_a_c321; 21.07.2012