Русское сообщество разработки на PHP-фреймворке Laravel.
Ты не вошёл. Вход тут.
Страницы 1
Здравствуйте!
В ДБ есть таблицы : 'tours' и 'tourists'
У 'tour' может быть МНОГО 'tourist'-ов, и 'tourist' может принимать участие в МНОГИХ 'tour'-ах. Я успешно связал их через 'belongsToMany" r (при добавлении строк в "tours' и 'tourists" создается строка и intermediate таблице 'tour_tourist' .
Структура "tour_tourist":
tour_id | tourist_id
1 | 37
1 | 38
1 | 39
(туристы с id = 37, 38 и 39 приписаны к туру с id = 1, но могут в будущем быть приписаны и к другим турам)
ПРОБЛЕМА: Один из 'tourists' (37/ 38 / 39 из примера выше) может быть 'buyer' (покупателем) тура . Отношения тура и покупателя 1-1. К тому же этот "покупатель" может ехать в тур с остальным, а может не ехать. Все данные передаются при отправке одной веб-формы.
Я хочу создать еще таблицу, с именем 'buyers':
tour_id | tourist_id | is_tourist
1 | 39 | 0
(1. одному 'tour_id' может соответствовать только 1 'tourist_id')
(2. 'tourist_id' должен быть из принадлежащих соотвествующему 'tour_id' в предыдущей таблице "tour_tourist" - в моем примере турист 39 принадлежит туру 1).
(3. 'is_tourist' это булево 1/0, едет/только платит - в моем примере 0 - только платит).
Мне интересно, как я могу создать 'buyer'-а через отношения (relationship) в Laravel'е (one-to-one? hasManyThrough?) .
Спасибо заранее!
Не в сети
Можно в той же контировочной (pivot) таблице tour_tourist и добавить нужные поля (для лучшей семантики лучше добавить 2 поля - is_tourist и is_buyer, потому как если buyer едет (=1), то его невозможно отличить от других туристов).
В итоге получается всего 3 таблицы - туры, туристы и расширенная pivot таблица.
Не в сети
Можно в той же контировочной (pivot) таблице tour_tourist и добавить нужные поля (для лучшей семантики лучше добавить 2 поля - is_tourist и is_buyer, потому как если buyer едет (=1), то его невозможно отличить от других туристов).
В итоге получается всего 3 таблицы - туры, туристы и расширенная pivot таблица.
соглсасен, это был мой первый вариант) но там много лишних единиц у is_tourist и много лишних нулей у buyer. Т.е. конечно, у меня не будет гигабайтов данных, но мне просто всегда не нравилась избыточность информации. Поэтому решил написать такой вопрос, думаю, это сложнее сделать, но в итоге будет "красивее":) Это, так сказать, Challenge для прокачки программерских скиллов для меня:)
Не в сети
Тогда можно добавить только одно поле - tourist_type:
0 - турист (значит едет)
1 - покупатель (тоже едет)
2 - спонсор (купил, но не едет)
Не в сети
Тогда можно добавить только одно поле - tourist_type:
0 - турист (значит едет)
1 - покупатель (тоже едет)
2 - спонсор (купил, но не едет)
да, но тогда будет много лишних нулей:) Я люблю математику, там повторяющуюся инфу выносят за скобки. 2aх+3bх лучше написать как x(2a+3b). Ну вот тут вынесение инфы о покупателя в отдельную таблицу - это как раз вынесение х за скобки, чтобы не было избыточной информации.
Как так сделать - вопрос:)
Не в сети
Ну если вы в одном поле нашли повторяющуюся информацию и считаете, что вместо него лучше создать таблицу buyers с 3 полями, которая практически полностью копирует таблицу tour_tourist, то мы с вами любим разные математики и я уж точно вам не помощник.
П.С. А ещё часто в таблицу статей добавляют поле deleted, в котором 1 значит "удалена", но в нём почти всегда нули. Это поле тоже может поломать математическое восприятие мира )
Не в сети
Ну если вы в одном поле нашли повторяющуюся информацию и считаете, что вместо него лучше создать таблицу buyers с 3 полями, которая практически полностью копирует таблицу tour_tourist, то мы с вами любим разные математики и я уж точно вам не помощник.
ну вы смотрите шире на вещи) У вас допустим в один тур поедет 1001 турист:). и только один из них - плательщик. Значит вам нужно создать в поле 'tourist_type' 1000 нулей (для туристов неплательщиков).
Вместо этого я создам одну строчку:
tour_id | tourist_id | is_tourist
1 | 1001 | 1
в ней всего 1 1001 1 = 6 знаков, вместо 1000 знаков у вас)
Можете поставить вместо туристов гены и ДНК, там счет пойдет на миллиарды уже нуликов.
Математики мы, возможно, действительно разные:)
Изменено Serge83 (23.06.2017 14:14:04)
Не в сети
ну вы смотрите шире на вещи) У вас допустим в один тур поедет 1001 турист:)
Как в анекдоте: "Пошлём X танков... Нет, икс мало. Пошлём Y танков".
Я-то представлял себе, что в тур едет, например, семья. Ну группа друзей. А тут оказывается 1 человек покупает тур сразу для 2 жд составов туристов или 6 самолётов или хз сколько автобусов. А если уж представить, что он оплачивает тур для 100500 миллионов туристов, то счет пойдёт на тысячи серверов.
Удачи!
Не в сети
Математика здесь ни при чём. В БД добавление лишнего поля во все строки — нормальная практика. Особенно если это поле с фиксированной длиной (число или строка типа CHAR/BINARY). В противоположность этому, использование новой таблицы, где «нулей не будет» — плохой подход.
sqlSELECT is_buyer FROM tourists WHERE tourist_id = ?
.sqlWHERE tourist_id = ?
— то она ищет это значение в индексе и если находит, то получает смещение в файле данных этой таблицы. Переход по смещению в файле чисто теоретически быстрее на маленьких таблицах при использовании HDD, но, во-первых, SSD постепенно занимают и серверный рынок, а, во-вторых, эта скорость ничтожна по сравнению со скоростью выполнения скриптов, накладных расходов на сеть и т.д.Соответственно, разницы между отдельной таблицей «без нулей» и таблицей пользователей с дополнительным полем в лучшем случае нет, поскольку лишнее поле влияет только на размер общей таблицы, а операции по поиску выше одинаковы для обеих таблиц.
Размер таблицы в вашем случае значения точно не имеет: если у вас 10000 пользователей, то это при длине строки в 200 байт — около 2 Мб. Минимальный размер числового поля — 1 байт, соответственно добавив 1 байт к длине строки в 200 байт вы увеличиваете размер такой таблицы на 10 Кб. Это мизер. MySQL, который любят ругать за неповоротливость, отлично работает с таблицами размером в гигабайты. Десятки гигабайт!
Почему выше я сказал «в лучшем случае нет» смысла в отдельной таблице? Потому что почти наверняка кроме простого определения «покупатель это или нет» (т.е.
sqlSELECT is_buyer FROM buyers_tourists WHERE tourist_id = ?
) вам потребуется получить информацию об этом пользователе. И вот здесь у вас простой запрос к таблице пользователей напрямую становится запросом с JOIN:
sqlSELECT t.* FROM buyers_tourists bt JOIN tourists t ON bt.tourist_id = t.tourist_id WHERE bt.tourist_id = ? AND is_buyer = 1
А запрос с JOIN это новый WHERE, новое сканирование индекса, новое чтение данных с диска. То есть сэкономив 10 Кб на диске вы увеличили ресурсозатратность запроса в 2 раза.
(Хотя справедливости ради надо сказать, что БД умеет бороться и с такими умными ходами, поэтому разница в реальности обычно меньше — маленькая таблица полностью загружается в память и обращения к диску не происходит.)
Советую вам придерживаться нескольких простых правил:
В контексте проблемы с покупателями, я бы вообще советовал отказаться от отдельного поля (и тем более таблицы), см. пп. 1 и 3. Кто такой покупатель? Это тот, у которого есть «заказы» (оплаченные туры). Заказы где хранятся? Очевидно, в той же БД, в другой таблице. Так зачем вам отдельное поле, если вы можете сделать запрос в таблицу с заказами и определить, покупатель он или нет?
Отдельное поле это дублирование данных. К примеру, пользователь оформил заказ, потом его по какой-то причине отменили и пользователь больше не есть «покупатель», а сбросить статус пользователя вы забыли. Тогда, к примеру, если у вас есть отчёт по среднему размеру заказов, то вместо учёта только пользователей с заказами вы учитываете и таких нулевых пользователей, то есть итоговое значение резко проседает. С другой стороны, удалить заказ из таблицы вы никак не можете забыть и эта проблема будет замечена при первом беглом тестировании.
Это только один из примеров, на практике баги вследствие дублирования данных встречаются самые неожиданные. Дублирование данных в 90% случаев это предоптимизация.
- В ДБ есть таблицы : ’tours’ и ’tourists’
«ДБ» это сокращение для «д…л», а не для «базы данных»
- Можно в той же контировочной (pivot) таблице
Pivot table = сводная таблица.
Не в сети
а я бы пивот не трогал, а добавил бы поле buyer_id на tours и сделал бы связь buyer с App\Tour на App\Tourist один-ко-многим естественно. а то что buyer – это один из tourists проверял бы уже в коде
Не в сети
Страницы 1