Архитектура таблиц
-
- Сообщения: 147
- Зарегистрирован: 2017.11.15, 23:54
Архитектура таблиц
Всем привет)
Прощу помощи и критики в архитектуре таблиц.
Небольшое слово о ТЗ.
Необходимо реализовать Календарь мероприятий с регистрацией на них. Примерно 800 мероприятий в год. Около 1000 заявок на каждый период. База mysql
Нюансы:
- мероприятия могут быть разовые, а могут быть переодичные (ежедневные, ежегодные, ежемесячные...). Период указывается датой и временем. Дубликаты мероприятий запрещены.
- На мероприятие есть несколько типов регистраций: соревнование, мастер-класс, батл. Типы регистрации закрепляются за периодом мероприятия.
- В личном кабинете участников выводится история участия. В личном кабинете регистратора выводится список заявок.
Вот что у меня получилось. https://dbdiagram.io/d/5cfad2ca09a99609d6145a6a
<spoiler title="Архитектура таблиц">
<code>
events (мероприятия)
- id
- organization_id (Организация)
- slug
- poster
- name
- url_json
- contact_email
- contact_phone
- description
- status
- view_count
evt_periods (периоды проведения)
-id
- event_id
- date_from //дата и время начала периода
- date_to
registrations системы регистраций
- id
- event_id //мероприятие
- period_id /на какой период
- name
- type
- date_from //дата открытия регистрации
- date_to //дата закрытия
- status
reg_turnt_requests //заявки на турнир
- id
- registration_id //ID системы регистрации
- number //номер участия
- organization_id
- discipline_id // дисциплина
- cancel_reason
- current_status
- status_json
- mentor_count
- participant_count
- created_at
reg_turnt_request_musics //Музыкальные треки заявки
- request_id
- file_name
- extension
- play_point //воспроизвести с точки
reg_turnt_mentors //наставники
- request_id
- person_id
reg_turnt_participants //участники
- request_id
- person_id
</code>
</spoiler>
————————————/
<b>В чем собствннно сложность?</b>
1. Это архитектура. На сколько правильно она выстроена. Есть ли какие-то косяки?
2. Запросы. Меня больше именно волнует этот фактор. Так как данные разбросаны по разным таблицам и будет возня по join-нами. Нужно будет много таблиц подгружать чтобы вывести, например, у участника список мероприятий, в которых он принимал участие, где нужно будет вывести название мероприятия, период, дисциплина... что посоветуете в этом случае?
Мои мысли это добавить event_id и period_id в таблицу с заявками. Так бы сразу будем напрямую получать название мероприяти и период. Если же оставить так, то нам придётся сначала делать запрос в систему регистраций, затем в периоды и только тогда мы по связи получим название мероприятия.
Посоветуйте что нибудь ещё. Давно голову ломаю( Очень нужна помощь специалистов!!!
Прощу помощи и критики в архитектуре таблиц.
Небольшое слово о ТЗ.
Необходимо реализовать Календарь мероприятий с регистрацией на них. Примерно 800 мероприятий в год. Около 1000 заявок на каждый период. База mysql
Нюансы:
- мероприятия могут быть разовые, а могут быть переодичные (ежедневные, ежегодные, ежемесячные...). Период указывается датой и временем. Дубликаты мероприятий запрещены.
- На мероприятие есть несколько типов регистраций: соревнование, мастер-класс, батл. Типы регистрации закрепляются за периодом мероприятия.
- В личном кабинете участников выводится история участия. В личном кабинете регистратора выводится список заявок.
Вот что у меня получилось. https://dbdiagram.io/d/5cfad2ca09a99609d6145a6a
<spoiler title="Архитектура таблиц">
<code>
events (мероприятия)
- id
- organization_id (Организация)
- slug
- poster
- name
- url_json
- contact_email
- contact_phone
- description
- status
- view_count
evt_periods (периоды проведения)
-id
- event_id
- date_from //дата и время начала периода
- date_to
registrations системы регистраций
- id
- event_id //мероприятие
- period_id /на какой период
- name
- type
- date_from //дата открытия регистрации
- date_to //дата закрытия
- status
reg_turnt_requests //заявки на турнир
- id
- registration_id //ID системы регистрации
- number //номер участия
- organization_id
- discipline_id // дисциплина
- cancel_reason
- current_status
- status_json
- mentor_count
- participant_count
- created_at
reg_turnt_request_musics //Музыкальные треки заявки
- request_id
- file_name
- extension
- play_point //воспроизвести с точки
reg_turnt_mentors //наставники
- request_id
- person_id
reg_turnt_participants //участники
- request_id
- person_id
</code>
</spoiler>
————————————/
<b>В чем собствннно сложность?</b>
1. Это архитектура. На сколько правильно она выстроена. Есть ли какие-то косяки?
2. Запросы. Меня больше именно волнует этот фактор. Так как данные разбросаны по разным таблицам и будет возня по join-нами. Нужно будет много таблиц подгружать чтобы вывести, например, у участника список мероприятий, в которых он принимал участие, где нужно будет вывести название мероприятия, период, дисциплина... что посоветуете в этом случае?
Мои мысли это добавить event_id и period_id в таблицу с заявками. Так бы сразу будем напрямую получать название мероприяти и период. Если же оставить так, то нам придётся сначала делать запрос в систему регистраций, затем в периоды и только тогда мы по связи получим название мероприятия.
Посоветуйте что нибудь ещё. Давно голову ломаю( Очень нужна помощь специалистов!!!
Последний раз редактировалось myks1992@mail.ru 2019.06.08, 12:25, всего редактировалось 2 раза.
-
- Сообщения: 977
- Зарегистрирован: 2014.08.27, 21:54
-
- Сообщения: 147
- Зарегистрирован: 2017.11.15, 23:54
Re: Архитектура таблиц
Код: Выделить всё
"Мои мысли это добавить event_id и period_id в таблицу с заявками."
-
- Сообщения: 147
- Зарегистрирован: 2017.11.15, 23:54
Re: Архитектура таблиц
То есть такая структура рабочая и ничего плохого в ней нет? Нормально будет от заявки персоне получать названия мероприятия через таблицу $request->registrations—>periods->events—>nameleonenco писал(а): ↑2019.06.08, 05:57Вы начинаете обратно возвращятся к дублированию записей. Это в свою очередь приведет к поддтягиванию новых моделей присохранении. Вы должны как можно детально нормализовать свои таблицы чтобы было как можно меньше дупликатов.Код: Выделить всё
"Мои мысли это добавить event_id и period_id в таблицу с заявками."
И тогда вопрос с таблицей регистрации. Там тоже получается дублирование id event_id. По периоду можно получить этот ИД. МОЖЕТ БЫТЬ ЕГО ТОЖЕ УБРАТЬ?
Есть ли ещё какие-то косяки?)
-
- Сообщения: 977
- Зарегистрирован: 2014.08.27, 21:54
Re: Архитектура таблиц
Мне не нравятся двойные связи туда и обратно.
Их же нужно как-то сохранять еще...
Архитектуру вы должны строить, держа в голове
Country - Region -> Town -> Address -> Person
В таблицу с персоной вы же не будете добавлять region_id и country_id, если нужно узнать из какой страны человек.
Второй момент - не полагайтесь сильно на AR.
Есть простые mysql запросы. Если нужно узнать, из какой страны человек, не нужно тянуть по связям регионы, города и адреса.
Пишете в Query нужные методы под конкретные задачи.
$model = Person::find()->withCountry()->one();
Их же нужно как-то сохранять еще...
Архитектуру вы должны строить, держа в голове
Country - Region -> Town -> Address -> Person
В таблицу с персоной вы же не будете добавлять region_id и country_id, если нужно узнать из какой страны человек.
Второй момент - не полагайтесь сильно на AR.
Есть простые mysql запросы. Если нужно узнать, из какой страны человек, не нужно тянуть по связям регионы, города и адреса.
Пишете в Query нужные методы под конкретные задачи.
$model = Person::find()->withCountry()->one();
-
- Сообщения: 147
- Зарегистрирован: 2017.11.15, 23:54
Re: Архитектура таблиц
Огромное спасибо за комментарий! Очень полезно)Loveorigami писал(а): ↑2019.06.08, 18:55 Мне не нравятся двойные связи туда и обратно.
Их же нужно как-то сохранять еще...
Архитектуру вы должны строить, держа в голове
Country - Region -> Town -> Address -> Person
В таблицу с персоной вы же не будете добавлять region_id и country_id, если нужно узнать из какой страны человек.
Второй момент - не полагайтесь сильно на AR.
Есть простые mysql запросы. Если нужно узнать, из какой страны человек, не нужно тянуть по связям регионы, города и адреса.
Пишете в Query нужные методы под конкретные задачи.
$model = Person::find()->withCountry()->one();
По поводу связей туда обратно немного не понял)) Вы имеете ввиду о таблице registrations? То что там ссылаюсь на event_id, когда его можно получить через таблицу event_periods... ?
Верно говорите по дублированию полей. Просто я думал, что лишняя таблица в запросе будет лишней связи. И поэтому ради оптимизации думал выносить эти id в другую связь. Логичнее же, конечно, что бы их не было. Чтобы с сохранениями не возиться сильно.
На AR особо не надеюсь. Я делаю везде свои repositories, которая служит обёрткой над AR. в любой момент можно будет написать туда хоть голый SQL. Так же в Query пишу методы типо active(), ->withCountry()... Это упрощает жизнь и косяки)
Меня именно больше интересует архитектура таблиц. Не будет ли проблем с запросами и получения связей. Так как не сильно разбираюсь в этом)) А судя по проекту таблиц будет много разбросанных. И к ним нужно будет обращаться большой связью)
-
- Сообщения: 147
- Зарегистрирован: 2017.11.15, 23:54
Re: Архитектура таблиц
Огромное спасибо за комментарий! Очень полезно)Loveorigami писал(а): ↑2019.06.08, 18:55 Мне не нравятся двойные связи туда и обратно.
Их же нужно как-то сохранять еще...
Архитектуру вы должны строить, держа в голове
Country - Region -> Town -> Address -> Person
В таблицу с персоной вы же не будете добавлять region_id и country_id, если нужно узнать из какой страны человек.
Второй момент - не полагайтесь сильно на AR.
Есть простые mysql запросы. Если нужно узнать, из какой страны человек, не нужно тянуть по связям регионы, города и адреса.
Пишете в Query нужные методы под конкретные задачи.
$model = Person::find()->withCountry()->one();
По поводу связей туда обратно немного не понял)) Вы имеете ввиду о таблице registrations? То что там ссылаюсь на event_id, когда его можно получить через таблицу event_periods... ?
Верно говорите по дублированию полей. Просто я думал, что лишняя таблица в запросе будет лишней связи. И поэтому ради оптимизации думал выносить эти id в другую связь. Логичнее же, конечно, что бы их не было. Чтобы с сохранениями не возиться сильно.
На AR особо не надеюсь. Я делаю везде свои repositories, которая служит обёрткой над AR. в любой момент можно будет написать туда хоть голый SQL. Так же в Query пишу методы типо active(), ->withCountry()... Это упрощает жизнь и косяки)
Меня именно больше интересует архитектура таблиц. Не будет ли проблем с запросами и получения связей. Так как не сильно разбираюсь в этом)) А судя по проекту таблиц будет много разбросанных. И к ним нужно будет обращаться большой связью)
-
- Сообщения: 977
- Зарегистрирован: 2014.08.27, 21:54
Re: Архитектура таблиц
У меня от страны до свойств обьекта с прайсами и ценами 9 таблиц. Все достаю одним запросом
https://www.gintur.com/service
Только то, что надо
https://www.gintur.com/service
Только то, что надо
Последний раз редактировалось Loveorigami 2019.06.10, 09:32, всего редактировалось 1 раз.
-
- Сообщения: 147
- Зарегистрирован: 2017.11.15, 23:54
Re: Архитектура таблиц
Ну значит я зря парюсь над этим)) И ничего страшного в этом нет, что будет много join запросов))Loveorigami писал(а): ↑2019.06.08, 23:17 У меня от страны до свойств обьекта с прайсами и ценами 9 таблиц. Все достаю одним запросом
https://www.gintur.com/service
Толко то, что надо
А вы AR используете в этих запросах? Если так, то наверное не очень красиво получается лазать из одной сущности в другие через другие сущности. Получается примерно такая цепочка $participant->request->period->event->name etc...
Как с таким бороться?
-
- Сообщения: 977
- Зарегистрирован: 2014.08.27, 21:54
Re: Архитектура таблиц
Нет. Все вытягивается одним запросом в select (country.name as countryName). В модель добавил трейт с этими ссвойствами. Обращаюь как $event->countryName
-
- Сообщения: 977
- Зарегистрирован: 2014.08.27, 21:54
Re: Архитектура таблиц
Или же сделайте хелпер и обрашайтесь как EventHelper::country($model), внутри которого будет вся ваша цепочка
-
- Сообщения: 147
- Зарегистрирован: 2017.11.15, 23:54
Re: Архитектура таблиц
Понял) Благодарю))Loveorigami писал(а): ↑2019.06.09, 09:22 Или же сделайте хелпер и обрашайтесь как EventHelper::country($model), внутри которого будет вся ваша цепочка
-
- Сообщения: 977
- Зарегистрирован: 2014.08.27, 21:54
Re: Архитектура таблиц
Нет, запрос должен быть один со многими join-связями .Ну значит я зря парюсь над этим)) И ничего страшного в этом нет, что будет много join запросов))
Например (в продолжении поста выше).
Код: Выделить всё
# Выборка цен по объектам
SELECT *
FROM (SELECT `minPrice`,
`objId`,
`roomId`,
`roomName`,
`roomOsn`,
`roomDop`,
`dateFrom`,
`dateTo`,
`mode`
FROM ((SELECT `q1`.*
FROM (SELECT 1 AS mode,
`d`.`data` AS `minPrice`,
`r`.`object_id` AS `objId`,
`r`.`id` AS `roomId`,
`r`.`name` AS `roomName`,
`r`.`osn` AS `roomOsn`,
`r`.`dop` AS `roomDop`,
`row`.`id` AS `rowId`,
`row`.`date_from` AS `dateFrom`,
`row`.`date_to` AS `dateTo`
FROM `price__tbl_data` `d`
INNER JOIN `price__tbl_col` `col` ON `d`.`col_id` = `col`.`id`
INNER JOIN `price__tbl_row` `row` ON `d`.`row_id` = `row`.`id`
INNER JOIN `price__tbl_item` `tbl` ON `row`.`tbl_id` = `tbl`.`id`
INNER JOIN `object__room` `r` ON `tbl`.`room_id` = `r`.`id`
WHERE (`d`.`data` > 0)
AND ((`row`.`date_from` <= '2018-12-05') AND (`row`.`date_to` >= '2018-12-05'))
AND ((`col`.`type_id` = 1) AND (`col`.`status` = TRUE))
AND ((`row`.`status` = TRUE) AND (`row`.`date_to` >= '2018-12-05') AND ((`tbl`.`status` = TRUE) AND (`r`.`status` = TRUE)))) `q1`
INNER JOIN (SELECT r.object_id, MIN(d.data) AS minData
FROM `price__tbl_data` `d`
INNER JOIN `price__tbl_col` `col` ON `d`.`col_id` = `col`.`id`
INNER JOIN `price__tbl_row` `row` ON `d`.`row_id` = `row`.`id`
INNER JOIN `price__tbl_item` `tbl` ON `row`.`tbl_id` = `tbl`.`id`
INNER JOIN `object__room` `r` ON `tbl`.`room_id` = `r`.`id`
WHERE (`d`.`data` > 0)
AND ((`row`.`date_from` <= '2018-12-05') AND (`row`.`date_to` >= '2018-12-05'))
AND ((`col`.`type_id` = 1) AND (`col`.`status` = TRUE))
AND ((`row`.`status` = TRUE) AND (`row`.`date_to` >= '2018-12-05') AND ((`tbl`.`status` = TRUE) AND (`r`.`status` = TRUE)))
GROUP BY `r`.`object_id`) `q2` ON q1.objId = q2.object_id AND q1.minPrice = q2.minData
ORDER BY `q1`.`minPrice`)
UNION ALL
(SELECT `q1`.*
FROM (SELECT 2 AS mode,
`d`.`data` AS `minPrice`,
`r`.`object_id` AS `objId`,
`r`.`id` AS `roomId`,
`r`.`name` AS `roomName`,
`r`.`osn` AS `roomOsn`,
`r`.`dop` AS `roomDop`,
`row`.`id` AS `rowId`,
`row`.`date_from` AS `dateFrom`,
`row`.`date_to` AS `dateTo`
FROM `price__tbl_data` `d`
INNER JOIN `price__tbl_col` `col` ON `d`.`col_id` = `col`.`id`
INNER JOIN `price__tbl_row` `row` ON `d`.`row_id` = `row`.`id`
INNER JOIN `price__tbl_item` `tbl` ON `row`.`tbl_id` = `tbl`.`id`
INNER JOIN `object__room` `r` ON `tbl`.`room_id` = `r`.`id`
WHERE (`d`.`data` > 0)
AND ((`col`.`type_id` = 1) AND (`col`.`status` = TRUE))
AND ((`row`.`status` = TRUE) AND (`row`.`date_to` >= '2018-12-05') AND ((`tbl`.`status` = TRUE) AND (`r`.`status` = TRUE)))) `q1`
INNER JOIN (SELECT r.object_id, MIN(d.data) AS minData
FROM `price__tbl_data` `d`
INNER JOIN `price__tbl_col` `col` ON `d`.`col_id` = `col`.`id`
INNER JOIN `price__tbl_row` `row` ON `d`.`row_id` = `row`.`id`
INNER JOIN `price__tbl_item` `tbl` ON `row`.`tbl_id` = `tbl`.`id`
INNER JOIN `object__room` `r` ON `tbl`.`room_id` = `r`.`id`
WHERE (`d`.`data` > 0)
AND ((`col`.`type_id` = 1) AND (`col`.`status` = TRUE))
AND ((`row`.`status` = TRUE) AND (`row`.`date_to` >= '2018-12-05') AND ((`tbl`.`status` = TRUE) AND (`r`.`status` = TRUE)))
GROUP BY `r`.`object_id`) `q2` ON q1.objId = q2.object_id AND q1.minPrice = q2.minData
ORDER BY `q1`.`minPrice`)) `uq`
ORDER BY `mode`, `minPrice`) `sq`
GROUP BY `objId`
Последний раз редактировалось Loveorigami 2019.06.10, 09:31, всего редактировалось 1 раз.
-
- Сообщения: 977
- Зарегистрирован: 2014.08.27, 21:54
Re: Архитектура таблиц
В AR примерно так (часть метода)
Код: Выделить всё
/**
* @var TblItemQuery $query
*/
$query = TblItem::find()
->alias('tbl')
->select([
'obj.id AS objId',
'obj.name AS objName',
'obj.slug AS objSlug',
'obj.text_price AS objTextPrice',
'town.slug AS townSlug',
'type.slug AS typeSlug',
'room.id AS roomId',
'room.name AS roomName',
'room.osn AS roomOsn',
'room.dop AS roomDop',
'roomcat.name AS roomCatName',
'tbl.id AS tblId',
'tbl.kf AS tblAdultKf', // коэффициент взрослых
'tbl.text AS tblTextPrice', // текст под таблицей
'txt.text AS tblTextTplPrice', // текст под таблицей
'l.name AS lechenieName',
'p.name AS pitanieName',
'spoS.spoStandartTurist',
'spoS.spoStandartAgent',
'spoS.spoStandartMalutki',
'spoA.spoAverageTurist',
'spoA.spoAverageAgent',
'spoA.spoAverageMalutki',
'spoA.spoDays',
'colAge.minAge',
'colAge.maxAgeFree',
'col.is_free AS isFree',
new Expression("$this->totalDays AS totalDays"),
new Expression("$kfSum AS kfSum"),
new Expression("$this->kidAge AS kidAge"),
new Expression('MIN(`row`.`date_from`) AS `date_min`'),
new Expression('MAX(`row`.`date_to`) AS `date_max`'),
$this->sumExpression('osnTblPriceSpo', $kfSum, $sum === self::AS_OSN),
$this->sumExpression('osnTblPriceNetto', $kfSum, $sum === self::AS_OSN),
$this->sumExpression('osnTblPriceFreeSpo', $kfSum, $sum === self::AS_FREE),
$this->sumExpression('osnTblPriceFreeNetto', $kfSum, $sum === self::AS_FREE),
$this->sumExpression('dopTblPriceSpo', $kfSum, $sum === self::AS_DOP),
$this->sumExpression('dopTblPriceNetto', $kfSum, $sum === self::AS_DOP),
$this->sumExpression('numTblPriceSpo', $kfSum, $sum === self::AS_MIX),
])
->innerJoinWith([
'rows row' => static function (TblRowQuery $query) {
$query->innerJoinWith([
'data d' => static function (TblDataQuery $query) {
$query->innerJoinWith([
'col col' => static function (TblColQuery $query) {
$query->innerJoinWith([
'type coltype',
])->published();
},
]);
},
])->published();
},
])
->innerJoinWith([
'room room' => static function ($query) {
/** @var ActiveQuery $query */
$query
->innerJoinWith([
'item obj' => static function ($query) {
/** @var ActiveQuery $query */
$query->innerJoinWith(['town town', 'type type']);
},
])
->innerJoinWith(['category roomcat'])
->published();
},
])
->joinWith(['lechenie l', 'pitanie p', 'textRelation txt'])
->leftJoin(['colAge' => $this->getMinChildAgeQuery()], 'colAge.tblId = tbl.id')
->leftJoin(['spoS' => $this->getSpoStandartQuery()], 'spoS.tblId = tbl.id')
->leftJoin(['spoA' => $this->getSpoAverageQuery()], 'spoA.tblId = tbl.id')
->where(['in', 'col.id', $colIds])
->andWhere(
[
'OR',
[
'AND',
['=', 'col.is_free', TblCol::IS_FREE],
['>=', 'd.data', 0],
],
['>', 'd.data', 0],
]
)
->andWhere(
[
'OR',
['tbl.kf' => $this->adults], // убираем таблицы с коэффициентом не для расчета
['tbl.kf' => null],
]
)
->andWhere(
[
'OR',
[
'AND',
['>=', 'row.date_to', $this->dateFrom],
['<=', 'row.date_from', $this->dateTo],
],
[
'AND',
['>=', 'row.date_from', $this->dateFrom],
['<=', 'row.date_to', $this->dateTo],
],
]
)
->andWhere(['obj.raschet_online' => 1])
->groupBy('row.tbl_id')
->having(['>=', 'date_max', $this->dateTo])
->andHaving(['<=', 'date_min', $this->dateFrom])
->andFilterWhere(['room.object_id' => $this->objId])
->andFilterWhere(['room.id' => $this->roomId])
->andFilterWhere(['tbl.id' => $this->tblId])
->byMinMaxPeriod($this->totalDays)
->published();
//print_r($query->createCommand()->rawSql);
$this->setUnion($query);
-
- Сообщения: 147
- Зарегистрирован: 2017.11.15, 23:54
Re: Архитектура таблиц
Вот это запросик ахахха)) Никогда такогого не встречал во фреймворке)) Буду изучать и делать подобные запросы))) Благодарю!