2010-08-29 2 views
0

Вот моя проблема. Предположим, у меня есть таблица под названием persons, содержащая, помимо прочего, поля для имени человека и национального идентификационного номера, причем последний является необязательным. Для каждого фактического человека может быть несколько строк.PostgreSQL: настраиваемая логика для определения отдельных строк?

Теперь предположим, что я хочу выбрать ровно одну строку для каждого фактического человека. Для целей приложения две строки считаются относящимися к одному и тому же лицу, если: a) совпадают их идентификационные номера или b) совпадают их имена, а идентификационный номер одного или обоих - NULL. SELECT DISTINCT здесь не годится: я не могу сделать DISTINCT ON (name, id), потому что тогда две строки с тем же именем, где идентификатор одного - NULL, не совпадают (что неверно, их следует считать одинаковыми). Я не могу сделать DISTINCT ON (name), потому что строки с таким же именем, но с разными идентификаторами будут совпадать (опять-таки неправильно, их следует считать разными). И я не могу сделать DISTINCT ON (id), потому что тогда все строки, где ID NULL, будут считаться одинаковыми (явно неверными).

Есть ли способ переопределить, как PostgreSQL сравнивает строки, чтобы определить, идентичны ли они или нет? Я думаю, что поведение по умолчанию для DISTINCT ON (name, id) было бы чем-то вроде IF a.name = b.name AND a.id = b.id THEN IDENTICAL ELSE DISTINCT. Я бы хотел пересмотреть это как-то вроде IF a.id = b.id OR (a.name = b.name AND (a.id IS NULL OR b.id IS NULL)) THEN IDENTICAL ELSE DISTINCT.

Это довольно поздно, и я мог пропустить что-то очевидное, поэтому другие предложения о том, как достичь того, что я хочу, также будут приветствоваться. Все, что позволяет мне выбирать отдельные строки на основе более сложных критериев, чем простой список столбцов. Заранее спасибо.

ответ

1

с функций окна

-- 
-- First, SELECT those names with NULL national IDs not shadowed by the same 
-- name with a national ID. Each one is a unique person. 
-- 
SELECT name, id 
FROM persons 
WHERE NOT EXISTS (SELECT 1 
        FROM persons p 
        WHERE p.name = persons.name AND p.id IS NOT NULL) 
-- 
-- Second, collapse each national ID into the "first" row with that ID, 
-- whatever the name. Each ID is a unique person. 
-- 
UNION ALL 
SELECT name, id 
    FROM (SELECT name, id, ROW_NUMBER() OVER (PARTITION BY id) 
      FROM persons 
     WHERE id IS NOT NULL) d 
WHERE d.row_number = 1; 

Без функций окна

Заменить выше UNION с GROUP BY первым (MIN()) имя для каждого не NULL ID:

... 
UNION ALL 
    SELECT MIN(name) AS name, id 
    FROM persons 
    WHERE id IS NOT NULL 
GROUP BY id 
+0

Спасибо за предложение. Тем не менее, я на PostgreSQL 8.1, который, AFAIK, не имеет оконных функций. – Indrek

+0

Не думаю, что вам нужны оконные функции: ... объединение всех выбрать уникальное имя, идентификатор из лиц , где идентификатор не является нулевым – Corey

+0

@Corey, которая не для следующей пары '(имя, идентификатор) 'tuples *, представляющие одно и то же лицо *:' ('Bob Jones', 123) ',' ('Robert A. Jones', 123) '. – pilcrow

0

Похоже, главная проблема заключается в макете вашей базы данных. Я не знаю подробностей вашего конкретного приложения, но наличие нескольких строк и нулевых идентификаторов для одного и того же человека обычно является плохой идеей. Если возможно, вам может потребоваться создать отдельную таблицу для любой информации, требующей нескольких строк, с persons, содержащей только одну строку на человека и уникальный идентификатор для каждой строки.

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

В чем проблема с:

select distinct name, id 
from persons 
where id is not null 

У вас есть несколько людей, которые имеют имя, но не идентификатор? Или вам нужны некоторые конкретные данные из других строк?

Вот еще одна проблема: если есть две строки с одинаковыми именами и идентификаторами нулей, а также несколько людей с одинаковыми именами и разными идентификаторами, как узнать, с какими людьми соответствуют нулевые строки?

+0

Да, структура базы данных не является оптимальной, и я фактически меняю ее. Таблица 'people' должна содержать одну строку на человека, как вы описали, но для этого мне нужен способ сконденсировать все существующие строки в один. Отсюда вопрос. И да, у меня есть люди, у которых есть имя, но не идентификатор. Как я уже сказал выше, поле ID является необязательным. В противном случае я просто 'SELECT DISTINCT ON (id)'. – Indrek

+0

«Еще одна проблема: если есть две строки с одинаковыми именами и идентификаторами нулей, а также несколько людей с одинаковым именем и разными идентификаторами, как узнать, с какими людьми соответствуют нулевые строки?» Нет таких строк, поэтому это спорный вопрос. – Indrek

Смежные вопросы