Как связать множество таблиц, содержащих одни и те же продукты, с разными столбцами и идентификаторами

У меня есть несколько таблиц от разных поставщиков, содержащих информацию о поставляемых ими продуктах. Форматы таблиц различаются - разные идентификаторы, различное описание и т.д. - поскольку каждый поставщик хранит отдельную информацию о своих продуктах. Некоторые продукты в разных таблицах могут быть одинаковыми, но я не знаю, что в то время, когда строки вставлены; Я должен пройти через них вручную, чтобы определить, какие продукты одинаковы.

Например, рассмотрим следующие таблицы (SQL Fiddle):

CREATE TABLE A (
  id char(10)PRIMARY KEY,
  name char(16),
  color char(16),
  weight float
  );

CREATE TABLE B (
  id int(11) PRIMARY KEY,
  name varchar(60),
  color char(3)
  );

Каждая таблица может иметь разные значения ID, столбцы и четные значения для одного и того же столбца; например:.

Table A:
+------------+-----------------+---------------+-----------------+
|         ID |            NAME |         COLOR |          WEIGHT |
+------------+-----------------+---------------+-----------------+
| RFY-55-001 |  Wagon, Classic |           Red | 15.199999809265 |
| RFY-62-001 | Trike, My First |           Red |   8.60000038147 |
| RFY-64-001 |  Trike, 12 Inch |           Red |  15.39999961853 |
| SWN-35-001 | Trike, Roadster | Metallic Blue | 20.700000762939 |
| SWN-35-002 | Trike, Roadster |        Silver | 20.700000762939 |
| SWN-35-003 | Trike, Roadster |    Cherry Red | 20.700000762939 |
+------------+-----------------+---------------+-----------------+

Table B:
+-------+--------------------------------------------+--------+
|    ID |                                       NAME |  COLOR |
+-------+--------------------------------------------+--------+
| 10560 |                  Schwinn Roadster Tricycle |    BLU |
| 10685 |              Radio Flyer Classic Red Wagon | (null) |
| 10880 | Radio Flyer Classic Red Dual Deck Tricycle | (null) |
| 12008 |         Fisher-Price I Can Play Basketball | (null) |
+-------+--------------------------------------------+--------+

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

Строки вставляются автоматически, и я не могу проверить каждый продукт до его вставки. Я могу только перебирать их время от времени и обновлять ссылки, которые я нахожу вручную. Например, небольшое исследование показывает, что трехколесный велосипед Radio Flyer Classic Red Dual Deck имеет 12-дюймовое переднее колесо и весит 15,4 фунта, что указывает на то, что элемент RFY-64-001 в таблице A является тем же продуктом, что и элемент 10880 в таблице B.

Какие отношения я использовал бы для привязки строк в разных таблицах, если они ссылаются на один и тот же продукт, когда-то после их вставки, чтобы у меня был один ключ для каждого уникального продукта?

Очевидным решением было бы добавить таблицу подключения с моим ключом и использовать триггеры при вставке в другие таблицы. Проблема с этим решением заключается в том, что процесс "sync" становится довольно ручным и подвержен ошибкам.

Ответы

Ответ 1

Хорошо; поэтому у вас есть несколько таблиц, происходящих из разных источников, каждый из которых реализует один и тот же объект (Продукт) со своей собственной логической структурой. Любая заданная запись может существовать в нескольких таблицах, и ваша задача состоит в том, чтобы согласовать эти разные источники данных, используя какой-либо ручной процесс для определения того, какие записи одинаковы/идентичны между таблицами.

Это похоже на процесс, который мы проводим в Cal/EPA для согласования данных из разных источников при создании инвентаризации загрязнения воздуха в масштабе штата. Эти источники включают в себя несколько различных групп моделирования, каждая из которых работает с отдельным набором входных и выходных параметров; прямые измерения с станций мониторинга; ежегодные отчеты локальных и муниципальных учреждений; и в некоторых случаях данные поступают непосредственно нам частными лицами и предприятиями. Эти данные поступают к нам во многих разных форматах, и большая часть их перекрывается.

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

Во-первых, сборка перекрывающихся данных, которая существует во многих разных форматах, отстой. Наши рабочие места были бы намного проще, если бы все использовали одни и те же структуры таблиц и оставались друг от друга. Тем не менее, это реальность, с которой нам приходится иметь дело; вот почему у нас есть рабочие места. Различные организации и отдельные лица работают по-разному, периоду, и большую часть времени у вас не будет возможности изменить способ работы.

Вам нужен план, иначе будут сделаны ошибки - не случайные ошибки, а постоянные. Один широкий подход, который вы можете предпринять, заключается в следующем: я буду принимать данные из всех этих источников во всех этих форматах и ​​помещать их в промежуточную базу данных, где я буду манипулировать и агрегировать и измельчать, удалять и калечить данные по мере необходимости, чтобы поместить его в мой блестящая, хорошо управляемая база данных, где есть только одна физическая модель, и все замечательно. Затем я могу забыть обо всех невыразимых вещах, которые я сделал с данными, чтобы они были в нем в первую очередь, и жизнь будет замечательной... пока я не буду делать это снова и снова. Это называется ETL, что означает Извлечь, Преобразовать, Загрузить или, возможно, Entrap, Torture, Lobotomize в зависимости от того, насколько вы веселитесь. Вы можете прочитать об этом в Википедии, если хотите (или здесь, на SO, или здесь, на DBA.SE, или в это руководство от компании, которая знает что-то или другое о пытках).

Хорошая вещь о ETL заключается в том, что вы попадаете туда, где чувствуете себя хорошо. Не очень приятная вещь в ETL заключается в том, что, преобразовывая данные в соответствии с вашими конкретными потребностями, вы часто теряете определенную степень детализации или полезности, которые присутствовали в источнике. С другой стороны, если вы создаете очень обобщенную модель данных, чтобы как можно больше извлекать такие исходные данные и избегать деструктивных преобразований, вы получаете высоко обобщенную модель данных, что подразумевает большую документацию и обслуживание, даже если ваш модель идеальна, которой она никогда не бывает.

Теперь, похоже, вы либо не желаете, либо не можете преобразовать исходные данные в соответствии с разумно сфокусированной моделью. И вы не должны втискивать все в одну таблицу, полную повторяющихся столбцов и NULL, чтобы ваш быстрый спуск в безумие не прерывался видениями Ктулху в его доме в R'lyeh:

+-----+---------+------------+---------+-----------------+-------------+-----+
| id  | sup1_id | sup1_color | sup2_id | sup2_color      | sup2_weight | ... |
+-----+---------+------------+---------+-----------------+-------------+-----+
| 1   | 7124    | brn        | 93      | Burnt Sienna    | 0.65        | ... |
| 2   | 415     | yel        | 8552    | Bananas Foster  | 12.50       | ... |
| 3   | NULL    | NULL       | 51      | Mostly Red      | 2.00        | ... |
| 4   | 159     | wht        | NULL    | NULL            | NULL        | ... |
| 5   | NULL    | NULL       | NULL    | NULL            | NULL        | NULL NULLNULLNULLUNNLUNUL gratuitous Unicode diacritics, you get the idea

Это вообще не относится к реляционной базе данных; это парадигма плоского файла/таблицы, которая должна быть ограничена не-базами данных, такими как Excel. (Или доступ. Zing!)

Что вам осталось, если вам нужно сохранить разрозненные таблицы в их различных формах, но все же хотите связать их друг с другом и сохранить какую-то "основную" запись, создает дополнительные таблицы, чтобы охарактеризовать эти отношения и сохраните свой "один ключ, чтобы править ими всеми". Теперь мы направляемся в сторону разговоров о подтипах и ролях. У вас есть один объект (Продукт), который представлен различными способами; если каждый экземпляр продукта существует только в одном представлении, то вы имеете дело с подтипами сущностей. В случае, когда есть перекрытие, как здесь, лучше подумать о ролях, которые экземпляры могут взять на себя с каждым поставщиком. Здесь приведен пример из Data Modeling, Руководство для начинающих:

Ваш "главный" идентификатор и любая связанная информация будут принадлежать левому полю, которое я просто позвоню в продукт. Здесь вы хотите хранить атрибуты, которые всегда будут одинаковыми для данного продукта, независимо от поставщика; или "официальная" версия атрибута, который, как вы знаете, отличается от поставщиков, если таковой требуется. Например, если вы являетесь производителем, ваш MSRP для продукта не зависит от розничного продавца, несущего продукт; он должен быть в главной таблице. Он также может служить "официальной" ценой, в отличие от различных ценовых цен, связанных с разными розничными торговцами.

Ваши различные таблицы поставщиков справа. Это роли, которые приобретает продукт; здесь будет храниться любая информация, которую вы хотите изменить от поставщика к поставщику (в вашем примере, цвет продукта), или это связано с некоторыми поставщиками, а не с другими (в вашем примере, весом). Поскольку между продуктами и ролями существует много-много взаимосвязей, вы добавляете таблицу соединений между ними, где назначаются роли; вам не нужно иметь даты в вашей таблице соединений, как в приведенном выше изображении, если вы не храните историю, но это пример, когда информация о назначении роли была бы подходящей для хранения в таблице соединений.

Поскольку вы указали, что вам нужно связать записи из таблиц поставщика после их вставки, через ваш ручной процесс, ваш рабочий процесс под этим подходом, вероятно, будет примерно таким:

  • Записи вставляются в различные таблицы поставщиков.
  • Вы заметили, что новые записи были вставлены (здесь, где триггер может быть полезен).
  • Используя ваш ручной мануальный процесс, вы определяете, действительно ли каждая новая запись существует или не существует в Продукте.
  • Всякий раз, когда новая запись от поставщика не существует в Product, вы вставляете новую строку в Продукт, чтобы однозначно идентифицировать (и, возможно, охарактеризовать) этот элемент.
  • Вы вставляете новую строку в таблицу соединений для каждой новой записи от поставщика, связывая уникальную запись для этого элемента с дополнительной информацией, характеризующей ее в своей роли с этим поставщиком.

Обратите внимание, что, поскольку ваши внешние ключи находятся в таблице соединений, возможно наличие продуктов без роли/поставщика и поставщиков, не имеющих продуктов. Что касается соблюдения рабочего процесса и избежания ошибок, если вы не готовы предоставить нам гораздо больше информации о вашем процессе для связывания продуктов между таблицами поставщиков, я могу предложить лучше всего использовать триггеры AFTER INSERT на каждой из таблиц поставщика поместить имя таблицы и PK строки в таблицу, которая отслеживает новые продукты, пока они не пройдут процесс ручного связывания, и удалите эти строки по ходу процесса ручного связывания.

Вы можете использовать другой триггер в таблице соединений, чтобы очистить таблицу новых/несвязанных продуктов, но он более рискован, чтобы иметь триггер, обновляющий или удаляющий строки. Фактически, если вы можете заполнить таблицу новых/несвязанных продуктов, используя логику приложения вместо триггеров, это может быть предпочтительнее. Вы можете прочитать в блоге Триггеры, которые считаются вредными, считаются вредоносными для продуманного анализа обеих сторон триггерных дебатов, хотя это не относится к MySQL.

Ответ 2

Я бы создал таблицу основных продуктов с вашим собственным первичным ключом (id).

create table product_master (
product_id INTEGER(11) NOT NULL AUTO_INCREMENT,
product_name varchar(512),
...
);

Затем я бы добавил новое поле в каждую таблицу поставщика.

alter table a add product_id integer(11);
alter table b add product_id integer(11);
...

в идеале вы определяете product_id в таблице product_master в качестве первичного ключа и как внешний ключ в таблицах поставщиков.

Заполните мастер продукта содержимым 1-го поставщика, а затем через графический интерфейс каждого продукта либо к существующему продукту, либо к новому владельцу продукта. Не забудьте создать опцию "объединить" 2 мастера продукта в 1, на случай, если вы поймете слишком поздно. 2 продукта на самом деле одинаковы.

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

Объединение продукта с product_id 45 и 555 означает: 1) Обновите product_id от 45 до 555 в каждой таблице поставщиков, где присутствует 2) Удалите запись 555 в главной таблице продуктов

Очевидно, что для эффективного выполнения этой задачи необходим графический интерфейс.