Настройка.

У меня есть таблица, в которой хранится список физических предметов для игры. Элементы также имеют иерархический список категорий. Пример базовой таблицы:

Предметы

id | parent_id | is_category | name    | description  
-- | --------- | ----------- | ------- | -----------  
1  | 0         | 1           | Weapon  | Something intended to cause damage
2  | 1         | 1           | Ranged  | Attack from a distance
3  | 1         | 1           | Melee   | Must be able to reach the target with arm
4  | 2         | 0           | Musket  | Shoots hot lead.
5  | 2         | 0           | Bomb    | Fire damage over area
6  | 0         | 1           | Mount   | Something that carries a load.
7  | 6         | 0           | Horse   | It has no name.
8  | 6         | 0           | Donkey  | Don't assume or you become one.

Система в настоящее время работает на PHP и SQLite, но серверная часть базы данных является гибкой и может использовать MySQL, а интерфейсная часть может в конечном итоге использовать javascript или Object-C / Swift.

Проблема.

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

Как лучше всего пометить уровни верхнего уровня в коде для особой обработки?

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

Мои мысли.

  1. Я могу использовать строковое соответствие имени и сохранить идентификатор во внутренней константе при первом выполнении. В лучшем случае уродливое решение, которого я бы хотел избежать.
  2. Я могу сохранить идентификатор во внутренней константе во время установки. лучше, но все же не совсем то, что я предпочитаю.
  3. Я могу хранить массив в коде элементов верхнего уровня вместо того, чтобы помещать их в таблицу. Это создает множество сложностей, например, как дочерний элемент указывает на родительский элемент верхнего уровня. В таблицу необходимо добавить еще один идентификатор, который используется примерно 100 из 10К строк.
  4. Я могу сохранить массив в коде и включить вставку идентификатора во время установки, чтобы добавить элементы верхнего уровня, разделяющие идентификатор статического массива. Наверное, моя лучшая идея, но мне не очень нравится идея вставки личности, она просто не кажется мне «базой данных». Также что, если появится новый элемент верхнего уровня. Может быть, начать с 1 миллиона для этих категорий?
  5. Я могу добавить столбец флага «varchar (1) top_category» или «int top_category» с символом или битовой картой, указывающей значение. Опять же, столбец используется примерно на 10 из 10 тыс. Строк.

Как программист, я склонен к прекрасным программным решениям, поэтому мне любопытно, больше ли это решение типа БД.

0
danielson317 15 Ноя 2019 в 18:55
Разве «высшая категория» не определяется тем, что у нее нет родителя? Таким образом, для них parent_id должен иметь значение NULL, и вы можете легко определить его по значению null в этом столбце.
 – 
a_horse_with_no_name
15 Ноя 2019 в 18:59
Очень нравится ручка! Верно, но особая обработка различна для каждого элемента верхнего уровня. Так что мне все равно нужно отличать их друг от друга. Попробую уточнить вопрос, чтобы включить эту информацию.
 – 
danielson317
15 Ноя 2019 в 19:00
Можете ли вы создавать другие таблицы и / или изменять существующую? или вы работаете с предопределенной схемой? Если у вас есть возможность создавать дополнительные таблицы, я бы создал таблицу, в которой хранится информация о сценарии обработки, а затем связал бы их с помощью ссылки fkey в вашей текущей таблице.
 – 
Daileyo
15 Ноя 2019 в 19:08
Это небольшой личный проект, поэтому я могу сделать все, что нужно. Если дополнительные таблицы помогут, хотелось бы услышать, как :). Моя первоначальная идея собиралась отделить item_type от item, но это создало другой набор проблем, поскольку игрок мог использовать конкретный предмет или что-нибудь в категории, и мне не нравилось хранить эти идентификаторы отдельно.
 – 
danielson317
15 Ноя 2019 в 19:09
Вы можете оставить все предметы в одной таблице. Я думаю, вы просто создаете новую таблицу, например, действия. Ссылаясь на то, что с помощью fkey в вашей таблице элементов, вы можете гибко определять действия для всех элементов, а не только для трех верхних. Затем из вашего кода ваши модели данных могут быть специфичными для трех верхних, чтобы включать данные, на которые ссылается fkey, но не для остальных.
 – 
Daileyo
15 Ноя 2019 в 19:14

2 ответа

Исходная таблица с присоединением к действиям.

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

IMO , разбиение данных на таблицы имеет больше смысла. Вот еще один пример того, как я бы начал подходить к разработке схемы для некоторых из сценарии, которые вы описали.

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

tl; dr аналогия впереди

База данных - это не список одежды, упорядоченный по строкам. Здесь вы храните детские кроватки, из которых состоит наряд.

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

Думайте о своей схеме как о комоде. Выдвижные ящики - это столы. Если у вас всего несколько носков и нижнего белья, лучше всего положить их все в один ящик. Но как только у вас будет достаточно носков, вам может стать неприятно хранить их все в одном ящике с нижним бельем. У вас есть модельные носки, носки для экипажа, носки до щиколоток, носки с мехом. Вы кладете их в другой ящик. Когда у вас есть рубашки, шорты, брюки, вы тоже начинаете складывать их в ящики.

Стремление поместить все данные в единую таблицу часто определяется тем, как вы собираетесь их использовать.

Предполагая, что ваш комод полностью укомплектован и аккуратно уложен, у вас есть несколько потенциально уникальных нарядов; все аккуратно разложено в вашем комоде. Вам просто нужно собрать их вместе. Выберите и присоединитесь, вы бы собрали эти наряды. Тот факт, что ваша любимая комбинация джинсов / футболок / носков находится не в одном ящике, не делает ее неуклюжей или неэффективной. Тот факт, что они разделены и организованы, позволяет вам: 1. Быстро узнать, где взять каждый предмет 2. Увидеть другие возможные новые любимые комбинации 3. Быстро увидеть, что у вас есть от каждого компонента вашего снаряжения.

Нет ничего плохого в том, чтобы сначала подумать о снаряжении, а потом о том, как вы его отложите. Если у вас только одна одежда, сложить все в один ящик намного проще, чем класть каждый кусок в отдельный ящик. Однако по мере того, как вы расширяете свой гардероб, единственный ящик для всего начинает становиться неэффективным.

Обычно вы хотите планировать расширение и универсальность. Ваша программа может собирать данные так, как вам это нужно. Хорошо организованная схема может сделать это за вас. Используете ли вы ORM и храните ли данные на основе модели; или начните со схемы, а затем построите модели на основе схемы; чем сложнее становятся ваши требования к данным; тем более похожими становятся оба подхода.

1
Daileyo 16 Ноя 2019 в 12:03

Реляционная база данных предназначена для хранения сущностей в таблицах, которые связаны друг с другом. Очень часто вы увидите примеры базы данных компании, состоящей из отделов, сотрудников, должностей и т. Д., Или магазинов, в которых хранятся продукты, клиенты, заказы и поставщики.

Очень легко запросить такую ​​базу данных и, например, получить всех сотрудников, которые имеют определенную работу в определенном отделе:

select *
from employees
where job_id = (select id from job where name = 'accountant')
and dept_id = select id from departments where name = 'buying');

С другой стороны, у вас есть только одна таблица, содержащая «вещи». Одна строка может относиться к другой, что означает «относится к типу». Вы могли бы назвать эту таблицу «чем-то». И если бы речь шла о данных компании, мы бы получили задание так:

select * 
from something
where description = 'accountant'
and parent_id = (select id from something where description = 'job');

И отдел таким образом:

select * 
from something
where description = 'buying'
and parent_id = (select id from something where description = 'department');

Эти двое все равно должны быть связаны людьми, работающими в отделе на работе. Тогда простого «is type» недостаточно. Короткий запрос, который я показал выше, станет довольно большим и сложным для вашего типа базы данных. Представьте себе то же самое с более сложным запросом.

И ваше приложение либо ничего не знает о том, что оно выбирает (ну, оно должно знать, что это что-то определенного типа, а другое - что-то определенного типа и человека (если вы дойдете до (чтобы ввести таблицу сотрудников) каким-то образом связан с этими двумя вещами), или он должен знать, что означает описание «отдел» и что означает описание «работа».

Ваша база данных слепая. Он не знает, что такое «что-то». Если вы когда-нибудь допустите ошибку в программировании (что делает большинство из нас), вы можете даже сохранить неправильные отношения (Осел относится к типу Musket и, следовательно, «стреляет по горячему свинцу», пока вы можете кататься на нем), и ваше приложение может в какой-то момент дать сбой или другой не может обработать результат запроса.

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

Отделить объект от данных . Пока что ваши сущности - это оружие и средства передвижения. Это должны быть таблицы. Затем у вас есть экземпляры (строки) этих сущностей с определенными атрибутами. Например, бомба - это оружие с определенным радиусом действия.

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

  • человек (person_id, name, Strength_points, ...)
  • оружие (weapon_id, name, range_from, range_to, weight, force_points, ...)
  • person_weapon (person_id, weapon_id)
  • mount (mount_id, имя, скорость, выносливость, ...)
  • person_mount (person_id, mount_id)
  • еда (food_id, имя, вес, energy_points, ...)
  • person_food (person_id, food_id)
  • броня (Arm_id, имя, protection_points, ...)
  • person_armor <= таблица для m: n или просто person.id_armor для 1: n
  • ...

Это всего лишь пример, но он ясно показывает, с какими объектами имеет дело ваше приложение. Он знает, что оружие и еда - это то, что носит человек, поэтому они могут иметь максимальный общий вес только для человека. Крепление - это то, что можно использовать для транспортировки, и оно может заставить человека двигаться быстрее (или переносить вес, если ваше приложение и столы это позволяют). И т.п.

0
Thorsten Kettner 16 Ноя 2019 в 13:14