PostgreSQL: пользовательская логика для определения отдельных строк? - PullRequest
0 голосов
/ 29 августа 2010

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

Теперь предположим, что я хочу выбрать ровно одну строку для каждого человека.В целях приложения считается, что две строки относятся к одному и тому же лицу, если а) их идентификационные номера совпадают или б) их имена совпадают, а идентификационный номер одного или обоих равен 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.

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

Ответы [ 2 ]

1 голос
/ 29 августа 2010

с функциями окна

--
-- 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()) имя для каждого ненулевого идентификатора:

...
UNION ALL
  SELECT MIN(name) AS name, id
    FROM persons
   WHERE id IS NOT NULL
GROUP BY id
0 голосов
/ 29 августа 2010

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

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

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

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

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

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

...