Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Если мне до сих пор приходится некоторым разработчиками объяснять почему обязательно нужно использовать автоинкрементное поле в таблице
Со временем любой стиль становится привычным.
Со временем любой стиль становится привычным
Все реализации orm тяготят своей сложностью
Всегда находился фатальный недостаток
Универсальных рецептов не существует.
Вам не приходилось сталкиваться с неожиданно отказывающим инструментом.
Понять как работает пятьдесят строк кода на порядок проще чем в миллионе строк, не говоря уже о быстродействии и пороге вхождения.
Название «универсальные» использовал по отношению к себе из за большого круга задач где это может применяться.
Если вспомнить закон Паретто и предположить что он работает и здесь
Считает что нет?
В помем случае 80 процентов кода охватывает всего одна функция. rb От этого и предположение.
rb и тех функций, которые она (и только она) вызывает. Что представляют собой 80% функциональности этой библиотеки? Каким количеством кода они реализованы?$sql = 'SELECT name, color, calories FROM fruit ORDER BY name';
foreach ($conn->query($sql) as $row) {
print $row['name'] . "\t";
print $row['color'] . "\t";
print $row['calories'] . "\n";
}<? foreach(rb("marks", "id", "id", rb("models", "marks_id")) as $marks): ?>
<h1><?=$marks['name']?></h1>
<? foreach(rb("models", "marks_id", "id", $marks['id']) as $models): ?>
<div><?=$models['name']?></div>
<? endforeach; ?>
<? endforeach; ?>Вывести марки только те, у которых есть хотя бы одна модель. И дальше список этих моделей.
foreach(var brand in ctx.Brands.Where(b => b.Models.Any()))
{
output(brand);
foreach(var model in brand.Models)
{
output(model);
}
}Библиотека сама догадывается как связаны модели с марками?
Если таких связей несколько по какому пути она пойдет?
brand.Models и brand.FlagshipModel — это две разные связи, и обращение к любой из них однозначно.Это из разряда магии?
Для меня это не так наглядно как для вас
Связей может быть даже не две а пять к примеру. Для каждой у вас реализована своя модель?
Каким образом вы определите по вторичному ключу вы обращяетесь от модули к марке, через промежуточную таблицу или по вторичному ключу у марки у модели?
Это и есть сложность
В моем случае это все те же две строки чуть длиннее в которых я через запятую перечисляю какие ключи использовать для связи
в вашем полный пересмотр всей концепции и правил приложения
В моем случае структура модели лишнее звено
Зачем это делать если можно не делать?
Причин определять модуль не могу для себя объяснить.
<? foreach(rb("marks", "id", "id", rb("modeling", "marks_id")) as $marks): ?>
<h1><?=$marks['name']?></h1>
<? foreach(rb("models", "id", "id", rb("modeling", "marks_id", "marks_id", $marks['id'])) as $models): ?>
<div><?=$models['name']?></div>
<? endforeach; ?>
<? endforeach; ?><? foreach(rb($tpl['marks'], "id", "id", rb($tpl['models'], "marks_id")) as $marks): ?>
<h1><?=$marks['name']?></h1>
<? foreach(rb($tpl['models'], "marks_id", "id", $marks['id']) as $models): ?>
<div><?=$models['name']?></div>
<? endforeach; ?>
<? endforeach; ?>В этом случае запроса будет всего два при неограниченном количестве их использования.
полное отсутствие каких то зависимостей.
rb — это не зависимость?Видно к какой таблице мы обращаемся, по каким полям идет выборка
При дальнейшей модификации связей в таблице (с одного ко многим до много ко многим) легким движением руки меняются структура выборки ничего не меняя в логике
Я хотел бы в языке видеть нативную функцию
До библиотеки это на мой взгляд не дотягивает.
Удобная функция обработки массива.
function query($table, $indexBy = null, $where = null)
{
$query = (new Query())->from($table);
if ($where) $query->where($where);
if ($indexBy) $query->indexBy($indexBy);
return $query->all();
}
function splitBy($array, $column)
{
$res = [];
foreach ($array as $row) {
$value = $row[$column];
$res[$value][] = $row;
}
return $res;
}
include 'mpfunc.php';
// --------
// пример с rb()
// SELECT * FROM models
// также происходит индексация массива по marks_id
// остается только последний элемент с таким ключом
// аналог неправильного запроса SELECT id FROM models GROUP BY marks_id
// SELECT * FROM marks WHERE `id` IN (1,2)
// foreach () {
// SELECT * FROM models WHERE `marks_id`=$marks['id']
// }
$res = rb("models", "marks_id");
foreach (rb("marks", "id", "id", $res) as $marks) {
echo '<h1>' . $marks['name'] . '</h1>';
foreach (rb("models", "marks_id", "id", $marks['id']) as $models) {
echo '<div>' . $models['name'] . '</div>';
}
}
// --------
// аналог с использованием query builder с вынесением повторяющихся операций в функцию
$models = query('models', 'marks_id');
foreach (query('marks', 'id', ['in', 'id', array_keys($models)]) as $mark) {
echo '<h1>' . $mark['name'] . '</h1>';
foreach(query('models', 'id', ['marks_id' => $mark['id']]) as $model) {
echo '<div>' . $model['name'] . '</div>';
}
}
// --------
// как надо сделать правильно
// (можно через DISTINCT или через EXISTS, не суть)
// SELECT * FROM `marks` WHERE `id` IN (SELECT DISTINCT `marks_id` FROM `models`)
// SELECT * FROM `models` WHERE `marks_id` IN ($filteredIDs)
// foreach () {
// [никаких запросов в цикле нет]
// }
$subQuery = (new Query())->select('marks_id')->distinct()->from('models');
$marks = query('marks', 'id', ['in', 'id', $subQuery]);
$filteredIDs = array_keys($marks);
$models = query('models', 'id', ['in', 'marks_id', $filteredIDs]);
$modelsByMark = splitBy($models, 'marks_id');
foreach ($marks as $mark) {
echo '<h1>' . $mark['name'] . '</h1>';
foreach($modelsByMark[$mark['id']] as $model) {
echo '<div>' . $model['name'] . '</div>';
}
}
// --------
// то же самое с использованием ActiveRecord
$subQuery = Model::find()->select('marks_id')->distinct();
$marksQuery = Mark::find()->where(['in', 'id', $subQuery]);
$marksQuery->with('models');
// [сюда можно добавить пагинацию и фильтрацию]
$marks = $marksQuery->all();
foreach ($marks as $mark) {
echo '<h1>' . $mark->name . '</h1>';
foreach($mark->models as $model) {
echo '<div>' . $model['name'] . '</div>';
}
}Удивлю, не все используют автоинкрементное поле в таблицах.
Как бы можно под дождем и без зонта ходить никто не растает. Но с зонтом удобнее.
Для меня автоинкрементное поле это прежде всего уникальная идентификация записи.
Когда такого поля нет привязаться не к чему.
.[1])Что 1?
Но в mysql такого нет. Тут и приходит на помощь автоинкремент.
Но надо как вы обеспечите уникальность?
Вся прелесть автоинкремента в качестве первичного ключа в том, что он избавляет от неминуемой борьбы со сложностями.
Вы не растаите бд не перестанет работать, но делать это станет сложнее
Данное поле должно обеспечивать уникальность в рамках таблицы.
придумывают костыли чтобы задавая по два или три первичных ключа
Все верно я о нем родимом.
В таблице BookAuthor все хорошо, пока вы не начали с ней работать. Как только потребуется добавить туда свойст каких то как вариант время добавления записи, кто сделал эту запись.
Дальнейший путь модификации может быть следующий это то что пришло в голову — у книги не один автор требуется добавлять несколько авторов.
Из разных источников данные могут разнится и следует хранить оба варианта
В этом случае ваши ключи теряют смысл.
Все возвращается к автоинкременту
У вас уже есть некий функционал, который выгребает значения используя два поля.
Используй вы сурогатный автоинкрементный ключ таких проблем не возникло бы.
функционал к примеру админской части модифицирующий или удаляющий записи
А есть еще пользовательские интерфейсы которые также используют два ключа.
Получается что вам легче все убить чем переделывать
У нас при удалении используется два поля
Удалить книгу 1 с автором 2
А дел то всего — не ставили в начале сурогатные ключи.
A surrogate key in a database is a unique identifier
Суррогатный ключ [...] дополнительное служебное поле, [...] единственное предназначение которого — служить первичным ключом.
Я могу сказать что он сурогатный?
С точки зрения названия он является сурогатным.
Вы пытаетесь одному определению дать два свойства которые не зависят друг от друга.
Посмотрите значение слова срогатный.
Частое употребление этого ключа в качестве первичного не накладывает обязанность ему быть уникальным.
Почему я не могу добавить два сурогатных поля в таблицу?
Вы сами себя вгоняете какие то рамки
существование сурогатных неуникальных ключей? Вы утверждаете что таких нет?
А два ключа созданных искусственно один из них первичный а второй нет. Какой из них сурогатный? Оба. Но уникальных не оба.
Сурогатный это противоположное значение естественному ключу. И никак не связано со требованиями к значениям данных в этом поле.
Не будет ли автоинкрементарное поле в данном случае повышать производительность вставки с учетом разнобоя BookId, AuthorId ?
Ваш вопрос содержал ответ. Зависит от ситуации.
Гипотетически в книжный магазин ежедневно заливается сотни тысяч новых книг и журналов / товаров поставщиков. А покупается / ищется 100-200.
Существуют определенные «негласные» правила проектирования.
Вы конечно можете этому не следовать, но тогда на практике у вас могут возникнуть разного рода трудности.
А какие трудности у вас возникнут если поля BookId и AuthorId назвать Field01 и Field02?
т.е. вы твердо убеждены, что ошибок при insert'е быть не может
А какие трудности возникнут, если большие куски sql-кода не заключать в хранимые процедуры а писать прямо в приложении?
А какие трудности возникнут, если не использовать вьюшки, а использовать вместо них просто вызовы select'а?
А никаких, кстати. Особенно если в приложении писать не SQL-код, а код, нативный для приложения, который транслируется в SQL-код.
Кроме неоходимости повторять код — никаких. Учитывая, что в большей части случаев я имею дело со сгенеренными кодом — и вовсе никаких.
Например, вам требуется вести логи изменений в тех или иных таблицах.
Заказчик, естественно, хочет в таблице логов видеть отсылку к элементу
Пояснять, почему в таблицу логов поле «elemid» лучше сделать числовым, пожалуй, не буду.
Например, у вас есть универсальная хранимая процедурка с каким-нибудь алгоритмом обработки.
Например, по сети лучше передавать имя хранимой с параметрами, чем длинный текст кода.
Или, например, порой требуется иметь возможность редактирования алгоритма без перекомпиляции приложения
Вы, видимо, просто нечасто пользуетесь данным инструментом, поэтому и не очень много знаете о возможностях, которые он предоставляет.
Логи изменений или логи состояний? Иными словами, дифы или снепшоты?
(Более того, когда у заказчика есть возможность редактировать хранимые процедуры, от которых зависит приложение — его поддержка превращается в ад. Жизненная история.)
Причем здесь дифы и снепшоты?
Т.е. грубо говоря, заказчик хочет зайти на сайт/в программу и увидеть, кто какие вносил изменения (например, кто поменял номер в накладной).
Хранимые и вьюшки являются своего рода интерфейсами подобно api в сайте.
Позволяют как менять структуру базы без необходимости ребилдов основного приложения (которое может быть установлено на непойми скольких компьютерах), так и запрещать доступ пользователям к бд напрямую.
Причем здесь дифы и снепшоты? Логи не на уровне БД, а на уровне приложения. Т.е. грубо говоря, заказчик хочет зайти на сайт/в программу и увидеть, кто какие вносил изменения (например, кто поменял номер в накладной).
$src = qn($sql = "SELECT * FROM `{$tab}`". ($where ? " WHERE ". implode(" AND ", $where) : ""). (($order = get($conf, 'settings', substr($src, strlen($conf['db']['prefix'])). "=>order") ?: "") ? " ORDER BY ". mpquot($order) : ""). " LIMIT ". (int)(array_key_exists('p', $_GET) ? $_GET['p']*$key : 0). ",". (int)$key,$IdName);
Универсальные многоразовые формы данных. Рецепты приготовления и сервировки