Коли використовувати успадковані таблиці в PostgreSQL?


84

У яких ситуаціях слід використовувати успадковані таблиці? Я намагався використовувати їх дуже коротко, і спадкування здавалося не таким, як у світі ООП.

Я думав, це працює так:

Таблиця, usersяка містить усі поля, необхідні для всіх рівнів користувача. Столи подобаються moderators, admins, bloggersі т.д. , але поля НЕ перевіряються від батьків. Наприклад, usersє поле електронної пошти та успадковане bloggersвоно також зараз, але воно не є унікальним для обох usersі bloggersодночасно. тобто так само, як я додаю поле електронної пошти до обох таблиць.

Єдине використання, про яке я міг подумати, це поля, які зазвичай використовуються, наприклад row_is_deleted , created_at , modified_at . Це єдине використання для успадкованих таблиць?

Відповіді:


111

Є кілька основних причин використання успадкування таблиць у postgres.

Скажімо, у нас є кілька таблиць, необхідних для статистики, які створюються та заповнюються щомісяця:

statistics
    - statistics_2010_04 (inherits statistics)
    - statistics_2010_05 (inherits statistics)

У цьому зразку ми маємо 2 000 000 рядків у кожній таблиці. Кожна таблиця має обмеження CHECK, щоб переконатися, що в ній зберігаються лише дані відповідного місяця.

То що ж робить спадщину цікавою функцією - чому класно розділяти дані?

  • ЕФЕКТИВНІСТЬ: При виборі даних ми ВИБРИВАЄМО * ЗІ статистики ДЕ, де МІЖ x та Y, а Postgres використовує лише таблиці, де це має сенс. Напр. ВИБЕРІТЬ * ВІД статистики, де дата МІЖ '2010-04-01' І '2010-04-15' сканує лише таблицю statistics_2010_04, всі інші таблиці не торкнуться - швидко!
  • Розмір індексу: У нас немає великої таблиці жиру з великим індексом жиру на дату стовпця. У нас є невеликі таблиці на місяць, з невеликими індексами - швидше читання.
  • Технічне обслуговування: Ми можемо запускати вакуумний повний, переіндексований, кластерний на кожній місячній таблиці, не блокуючи всі інші дані

Щоб правильно використати успадкування таблиці як прискорювач продуктивності, зверніться до керівництва postgresql. Вам потрібно встановити обмеження CHECK у кожній таблиці, щоб повідомити базу даних, за яким ключем ваші дані поділяються (розділяються).

Я активно використовую успадкування таблиці, особливо коли мова йде про збереження даних журналу, згрупованих за місяцями. Підказка: Якщо ви зберігаєте дані, які ніколи не зміняться (дані журналу), створіть або індексуйте за допомогою CREATE INDEX ON () WITH (fillfactor = 100); Це означає, що в індексі не буде зарезервовано місця для оновлень - індекс менший на диску.

ОНОВЛЕННЯ: за замовчуванням fillfactor - 100, з http://www.postgresql.org/docs/9.1/static/sql-createtable.html :

Коефіцієнт заповнення таблиці становить відсоток від 10 до 100. За замовчуванням 100 (повне упакування)


13
Ще один приклад
розділення

4
Як у вашому пункті 1, як Postgres розуміє, яку з таблиць потрібно шукати? Ви вибираєте з батьківської таблиці, а діапазон дат - лише зручний приклад поділу. Батьківська таблиця не може знати цієї логіки. Або я помиляюся?
Олександр Паламарчук

4
Виконання запиту на батьківській таблиці фактично те саме, що виконання запиту на UNION ALL у кожній таблиці нащадків у загальних рядках. Планувальник запитів знає обмеження перевірки, які визначають кожен розділ, і поки вони не перекривають розділи, використовує їх, щоб визначити, що він може пропустити таблиці перевірки, для яких ПЕРЕВІРКИ вказують, що рядки не повертаються. Документи Postgres про це
zxq9

@avesus хех ... Взятий вище код сам по собі гідний такого сарказму. Типово загорнути подібні речі у певний порядок технічного обслуговування. Це може бути так просто, як збережена процедура, яка піклується про неї за певних умов, завдання cron або що завгодно. Це часто для розділу за датою, але я виявив, що час від часу також розподіляю за допомогою розподілу табличної області, і для цього потрібна якась зовнішня інформація - 30 хвилин, необхідних для написання розділу няні, того варте для контролю це дає вам.
zxq9

Хм Ви впевнені, що це не блокує? У мене подібне налаштування, але коли я запускаю команду CLUSTER на одному розділі, оператор SELECT щодо даних, що зберігаються іншими блоками розділів!
Е. ван Путтен

37

"Наслідування таблиці" означає щось інше, ніж "успадкування класу", і вони слугують різним цілям.

Postgres - це все про визначення даних. Іноді справді складні визначення даних. OOP (у загальноприйнятому для Java розумінні речей) - це підпорядкування поведінки визначенням даних в одній атомній структурі. Призначення і значення слова «спадщина» тут суттєво різняться.

В ООП землі я міг би визначити (будучи тут дуже вільним із синтаксисом та семантикою):

import life

class Animal(life.Autonomous):
  metabolism = biofunc(alive=True)

  def die(self):
    self.metabolism = False

class Mammal(Animal):
  hair_color = color(foo=bar)

  def gray(self, mate):
    self.hair_color = age_effect('hair', self.age)

class Human(Mammal):
  alcoholic = vice_boolean(baz=balls)

Таблиці для цього можуть виглядати так:

CREATE TABLE animal
  (name       varchar(20) PRIMARY KEY,
   metabolism boolean NOT NULL);

CREATE TABLE mammal
  (hair_color  varchar(20) REFERENCES hair_color(code) NOT NULL,
   PRIMARY KEY (name))
  INHERITS (animal);

CREATE TABLE human
  (alcoholic  boolean NOT NULL,
   FOREIGN KEY (hair_color) REFERENCES hair_color(code),
   PRIMARY KEY (name))
  INHERITS (mammal);

Але де поведінка? Вони нікуди не поміщаються. Це не мета "об'єктів", як вони обговорюються у світі баз даних, оскільки бази даних стосуються даних, а не процесуального коду. Ви можете писати функції в базі даних, щоб робити для вас обчислення (часто дуже гарна ідея, але насправді не те, що підходить для цього випадку), але функції - це не те саме, що методи - методи, зрозумілі у формі ООП, про який ви говорите приблизно навмисно менш гнучкі.

Існує ще одна річ, на яку слід звернути увагу щодо успадкування як схематичного пристрою: станом на Postgres 9.2 немає жодного способу посилатися на обмеження зовнішнього ключа для всіх членів сімейства розділів / таблиці одночасно. Ви можете писати чеки, щоб зробити це, або обійти це іншим способом, але це не вбудована функція (насправді це зводиться до проблем зі складною індексацією, і ніхто не написав біти, необхідні для автоматичного здійснення). Замість того, щоб використовувати для цієї мети успадкування таблиці, часто кращим збігом у базі даних для успадкування об’єктів є створення схематичних розширень таблиць. Щось на зразок цього:

CREATE TABLE animal
  (name       varchar(20) PRIMARY KEY,
   ilk        varchar(20) REFERENCES animal_ilk NOT NULL,
   metabolism boolean NOT NULL);

CREATE TABLE mammal
  (animal      varchar(20) REFERENCES animal PRIMARY KEY,
   ilk         varchar(20) REFERENCES mammal_ilk NOT NULL,
   hair_color  varchar(20) REFERENCES hair_color(code) NOT NULL);


CREATE TABLE human
  (mammal     varchar(20) REFERENCES mammal PRIMARY KEY,
   alcoholic  boolean NOT NULL);

Тепер у нас є канонічне посилання на екземпляр тварини, яке ми можемо надійно використовувати як посилання на зовнішній ключ, і ми маємо стовпець "ilk", який посилається на таблицю визначень xxx_ilk, яка вказує на "наступну" таблицю розширених даних ( або вказує, що його немає, якщо ilk є самим родовим типом). Написати функції таблиці, подання тощо для такого роду схем настільки просто, що більшість фреймворків ORM роблять саме такі речі у фоновому режимі, коли вдаються до успадкування класу в стилі ООП для створення сімейств типів об'єктів.


Що, якби ви додавали всіх відомих ссавців? Ви успадкували б від ссавця чи мали б зовнішній ключ, як тут? У мене проблема з зовнішніми ключами полягає в тому, що в підсумку вам доводиться робити стільки приєднань.
пук

1
@puk Спочатку потрібно вирішити, чому ви додавали всіх відомих ссавців. Форма даних визначатиметься способом їх використання (у цьому випадку, мабуть, не потрібно мати таблицю на тварину - розгляньте бази даних для ігрових бестіаріїв, де у вас дійсно є кожен тип натовпу ). У наведеному вище випадку я зазвичай додаю подання, яке є найпоширенішим випадком mammal JOIN human, просто тому, що написання об’єднання кожного разу дратує. Але не уникайте приєднання . Об’єднання - це те, що ставить R у СУБД. Якщо вам не подобаються об'єднання, вам слід використовувати інший тип db.
zxq9

@ zxq9: Я здогадуюсь, що масові, неефективні об’єднання через великі таблиці - це місце, де вступають у реальність матеріалізовані погляди? (Я так довго не використовую Postgres)
Mark K Cowan

1
@MarkKCowan Joins не є неефективними. Неефективним є намагання приєднатися до неіндексованих, не-унікальних полів (оскільки схема не наближається до нормованості) через неакуратний дизайн. У цих випадках може бути корисним матеріалізований погляд. Матеріалізовані подання також корисні в тому випадку, якщо вам потрібні нормалізовані дані як вашої схематичної основи (часто справжні), але також потрібні кілька робочих, денормалізованих подань, з якими легше працювати або для ефективності обробки (фронтальне завантаження обчислень), або для когнітивної ефективності. Якщо ви пишете більше, ніж читаєте, це песимізація.
zxq9

1
@MarkKCowan "Повільний" - це відносний термін. У великих бізнес-системах та ігрових серверах, де ми можемо прийняти ~ 50 мс, щоб повернути запит, 20 об’єднань таблиць ніколи не було проблемою (у будь-якому випадку в Postgres 8+), на мій досвід. Але в тих випадках, коли керівництво бажає відповідей <1 мс на> 10b рядкових об'єднань у 5+ таблицях з неіндексованими даними (або похідними значеннями!) ... жодна система у світі не почуватиметься "швидкою", окрім як зробити це приєднання минулого місяця та затримати його у швидкому магазині K / V (що, по суті, є матеріалізованим видом, який може діяти, як за особливих обставин). Не вдається уникнути компромісу ні під час написання, ні під час читання.
zxq9

6

Спадщину можна використовувати в парадигмі ООП, якщо вам не потрібно створювати зовнішні ключі на батьківській таблиці. Наприклад, якщо у вас є транспортний засіб абстрактного класу, що зберігається в таблиці транспортного засобу, і настільний автомобіль, який успадковується від нього, усі автомобілі будуть видно в таблиці транспортного засобу, але зовнішній ключ таблиці водія на столі транспортного засобу не буде відповідати тезам записи.

Спадщина може також використовуватися як інструмент розділення . Це особливо корисно, коли у вас є таблиці, які мають рости вічно (таблиці журналів тощо).


1
Обмеження таблиці не успадковуються, тому це більше, ніж просто зовнішні ключі. Ви можете застосувати обмеження таблиці до дочірньої таблиці (таблиць), коли вони створені у вашому DDL, або ви можете написати тригери для здійснення тих самих обмежень.
Wexxor

3

Основне використання спадщини - для розділення, але іноді це корисно в інших ситуаціях. У моїй базі даних багато таблиць, що відрізняються лише зовнішнім ключем. Моє зображення "абстрактного класу" таблиці "містить" ідентифікатор (первинний ключ для нього повинен бути в кожній таблиці) і растр PostGIS 2.0. Успадковані таблиці, такі як "site_map" або "artifact_drawing", мають стовпець зовнішнього ключа (текстовий стовпець "site_name" для "site_map", "artifact_id" цілий стовпець для таблиці "artifact_drawing" тощо) та обмеження первинного та зовнішнього ключа; решта успадковується з таблиці "зображення". Я підозрюю, що в майбутньому, можливо, доведеться додати стовпець "опис" до всіх таблиць зображень, тому це може заощадити мені досить багато роботи, не створюючи реальних проблем (ну,

EDIT: ще одне корисне використання: при роботі з двома таблицями незареєстрованих користувачів інші СУБД мають проблеми з обробкою двох таблиць, але в PostgreSQL це легко - просто додайте, ONLYколи вас не цікавлять дані в успадкованій таблиці "незареєстрованого користувача".


2

Єдиний досвід роботи з успадкованими таблицями - це розділення. Це чудово працює, але це не найскладніша та проста у використанні частина PostgreSQL.

Минулого тижня ми розглядали ту саму проблему ООП, але у нас було занадто багато проблем із Hibernate (не сподобалось наше налаштування), тому ми не використовували спадкування в PostgreSQL.


0

Я використовую спадщину, коли маю зв’язок між таблицями більше 1 на 1.

Приклад: припустимо, ви хочете зберігати розташування об'єктів на карті з атрибутами x, y, обертання, масштаб.

Тепер припустимо, у вас є кілька різних видів об’єктів для відображення на карті, і кожен об’єкт має свої власні параметри розташування карти, і параметри карти ніколи не використовуються повторно.

У цих випадках наслідування таблиць було б дуже корисним, щоб уникнути необхідності підтримувати ненормалізовані таблиці або створювати ідентифікатори розташування та перехресних посилань на них з іншими таблицями.


-4

Використовуйте його якомога менше. І це, як правило, означає ніколи, це зводиться до способу створення структур, які порушують реляційну модель, наприклад, порушуючи інформаційний принцип і створюючи мішки замість відносин.

Натомість використовуйте розділення таблиць у поєднанні з правильним реляційним моделюванням, включаючи подальші звичайні форми.


4
Неправда, що функція успадкування PostgreSQL порушує реляційну модель, порушуючи принцип інформації. Принцип інформації говорить, що всі дані в реляційній базі даних представлені значеннями даних у відносинах, а всі результати запиту знову представлені як відношення. ( En.wikipedia.org/wiki/Relational_model ) Це завжди так, оскільки всі таблиці , які успадковують іншу таблицю, знову є простими таблицями. З цієї причини також не існує поняття "мішок", що б це не означало.
Роланд,

2
Ну, Вікіпедія навряд чи є посиланням щодо реляційної моделі; він відмовляється визнати, що SQL порушує реляційну модель. Сумка - це стіл без ключа, оскільки потенційно він має дублікати, отже, не є відношенням; відношення має бути набором.
Леандро

Це не проблема самої функції, а те, як вона використовується. Якщо ви працюєте з uuids як ідентифікаторами, ви будете мати унікальні ключі над усіма підтаблицями.
Роланд

Ви маєте сенс, але справа тут у тому, що успадкування змушує модельєра ігнорувати реляційну модель. UUID - це не справжні ключі, а сурогатні. Ще потрібно оголосити природні ключі.
Леандро
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.