Как таблица может нарушать собственный индекс первичного ключа? - PullRequest
6 голосов
/ 23 апреля 2011

У меня есть база данных PostgreSQL, в которой есть таблица с первичным ключом, примененным к трем столбцам.В соответствии с базой данных, существует индекс ключа:

Indexes:
    "full_log_pkey" PRIMARY KEY, btree (server_name, line_number, log_generation)

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

select count(*) from full_log;
  count
----------
 60644405

select count(*) from 
    (select distinct server_name, 
                     line_number, 
                     log_generation 
            from     full_log) as foo;
  count
----------
 60636564

Очевидно, что есть меньше отдельных строк (на основепо первичному ключу) чем есть строки.У меня вопрос, как это возможно?

Редактировать: полное определение таблицы таково:

                 Table "public.full_log"
     Column     |            Type             | Modifiers
----------------+-----------------------------+-----------
 activity       | character(1)                |
 archivaldate   | timestamp without time zone |
 media_type     | character varying(5)        |
 vsn            | text                        |
 archive_set    | character varying(20)       |
 copy           | smallint                    |
 file_start     | integer                     |
 file_offset    | integer                     |
 fs_name        | character varying(20)       |
 inode          | double precision            |
 file_length    | bigint                      |
 file_type      | character(1)                |
 overflow       | integer                     |
 device_number  | integer                     |
 server_name    | text                        | not null
 path           | text                        |
 line_number    | integer                     | not null
 log_generation | integer                     | not null
Indexes:
    "full_log_pkey" PRIMARY KEY, btree (server_name, line_number, log_generation)
Foreign-key constraints:
    "full_log_server_name_fkey" FOREIGN KEY (server_name) REFERENCES servers(server_name)
Rules:
    insert_update_full_log AS
    ON INSERT TO full_log
   WHERE (EXISTS ( SELECT full_log.activity, full_log.archivaldate, full_log.media_type, full_log.vsn, full_log.archive_set, full_log.copy, full_log.file_start, full_log.file_offset, full_log.fs_name, full_log.inode, full_log.file_length, full_log.file_type, full_log.overflow, full_log.device_number, full_log.server_name, full_log.path, full_log.line_number, full_log.log_generation
           FROM full_log
          WHERE full_log.server_name = new.server_name AND full_log.line_number = new.line_number AND full_log.log_generation = new.log_generation)) DO INSTEAD  UPDATE full_log SET activity = new.activity, archivaldate = new.archivaldate, media_type = new.media_type, vsn = new.vsn, archive_set = new.archive_set, copy = new.copy, file_start = new.file_start, file_offset = new.file_offset, fs_name = new.fs_name, inode = new.inode, file_length = new.file_length, file_type = new.file_type, overflow = new.overflow, device_number = new.device_number, path = new.path
  WHERE full_log.server_name = new.server_name AND full_log.line_number = new.line_number AND full_log.log_generation = new.log_generation

Для примера повторяющихся строк:

 select * from full_log where line_number = 6332986;
 activity |    archivaldate     | media_type |  vsn   | archive_set | copy | file_start | file_offset | fs_name |   inode    | file_length | file_type | overflow | device_number | server_name |                                           path                                            | line_number | log_generation
----------+---------------------+------------+--------+-------------+------+------------+-------------+---------+------------+-------------+-----------+----------+---------------+-------------+-------------------------------------------------------------------------------------------+-------------+----------------
 A        | 2010-10-13 10:49:49 | ti         | Z00711 | lcbp_rel    |    1 |     226237 |      779099 | lcbp    | 21798068.3 |    31198108 | f         |        0 |          8511 | redact      | wdl/delivery/irishparis_2010_09/MSE2_Histoire des rois d'Angleterre/MSE2_239.TIF          |     6332986 |              1
 A        | 2010-10-13 10:49:49 | ti         | Z00711 | lcbp_rel    |    1 |     226237 |      779099 | lcbp    | 21798068.3 |    31198108 | f         |        0 |          8511 | redact      | wdl/delivery/irishparis_2010_09/MSE2_Histoire des rois d'Angleterre/MSE2_239.TIF          |     6332986 |              1
(2 rows)

Ответы [ 4 ]

4 голосов
/ 23 апреля 2011

Что возвращает этот запрос?

select server_name, line_number, log_generation 
from full_log
group by server_name, line_number, log_generation
having count(*) > 1

Может быть полезно сравнить это с

select line_number, log_generation 
from full_log
group by line_number, log_generation
having count(*) > 1

, но это не так.Я думаю, что этот пункт

WHERE (EXISTS ( SELECT full_log.activity, 
                       full_log.archivaldate, 
                       full_log.media_type, 
                       full_log.vsn, 
                       full_log.archive_set, 
                       full_log.copy, 
                       full_log.file_start, 
                       full_log.file_offset, 
                       full_log.fs_name, 
                       full_log.inode, 
                       full_log.file_length, 
                       full_log.file_type, 
                       full_log.overflow, 
                       full_log.device_number, 
                       full_log.server_name, 
                       full_log.path, 
                       full_log.line_number, 
                       full_log.log_generation
               FROM full_log
               WHERE full_log.server_name = new.server_name 
                 AND full_log.line_number = new.line_number 
                 AND full_log.log_generation = new.log_generation)) 

может быть упрощен до этого пункта.(Хотя я не думаю, что это усугубляет проблему.)

WHERE (EXISTS ( SELECT full_log.server_name, 
                       full_log.line_number, 
                       full_log.log_generation
                FROM full_log
                WHERE full_log.server_name = new.server_name 
                  AND full_log.line_number = new.line_number 
                  AND full_log.log_generation = new.log_generation)) 

Вы сказали, что PostgreSQL удаляет и воссоздает индекс, когда вы изменяете тип данных неключевого столбца.Я не вижу, чтобы это происходило здесь, и я не уверен, что когда-либо видел, чтобы это случилось.Возможно, я не заметил, если изменение прошло успешно, и я не изменяю тип данных столбца.(Теперь, когда я это сказал, я не мог начать говорить вам в прошлый раз, когда я это сделал.) Теперь у меня есть PostgreSQL 9.0.2 здесь.

1 голос
/ 23 апреля 2011

Используете ли вы наследование таблиц? Если это так, PK не применяется в отношении детей (по крайней мере, не в 8.2).

0 голосов
/ 08 мая 2011

Я видел, как это происходит, когда ctid целевой строки изменяется в результате триггера перед.Не могу вспомнить точные шаги, но это было в основном проблемой.

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

Для чего подходят правила PostgreSQL?

Вывод: если при выполнении операторов вставки / обновления / удаления postgres не возвращает правильное количество затронутых строк, вы можете запуститьв странности.Правило (или использование вместо него триггера после) может помочь избавиться от них.

0 голосов
/ 23 апреля 2011

я бы заподозрил значения NULL. возможно запрос, чтобы видеть, есть ли какие-либо.

...