Данные в любом приложении являются ключевым фактором простоты их понимания. Со временем вырабатывается определенный подход к их выборке, обработке и обновлению. Если обрабатывая данные вы все еще задумываетесь над тем в какой форме хранить ваши данные, как из списка выбрать то что нужно — возможно данный подход упростит вашу жизнь. Основные требования к хранению данных могли бы звучать как то так:

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

Данные должны обеспечивать простоту доступа к основному элементу по идентификатору. Взаимосвязи со смежными элементами строятся на основе использования вторичного ключа в качестве указателя на основной элемент. Быстрый доступ будет плюсом.

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

Дополнительным бонусом будет использование «налету» что позволит обращаться к объектам через несколько уровней.

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

После многочисленных проб и ошибок нашел форму, которая меня устроила чуть меньше чем полностью. Условно давайте назовем ее «Основной формой». Наглядно данные основной формы можно представить массивом ключи которого будут значения автоинкрементного поля таблицы, а значения — список полей записи выбранной таблицы. Для выборки подобной структуры из базы данных будем использовать функцию условно названную rebuild (пересборка) rb():

pre(rb(«marks»));

Array(
    [1] => Array([id] => 1, [name] => Вольво)
    [2] => Array([id] => 2, [name] => Мерседес)
    [4] => Array([id] => 4, [name] => Ситроен)
)

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

Связанная таблица моделей могла бы выглядеть следующим образом:

pre(rb(«models»));

Array(
    [1] => Array([id] => 1, [marks_id] => 1, [name] => Volvo S60, [price] => 1644000)
    [2] => Array([id] => 2, [marks_id] => 1, [name] => Volvo V40, [price] => 1309000)
    [3] => Array([id] => 3, [marks_id] => 2, [name] => CLS Shooting Brake, [price] => 3520000)
)

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

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

<? 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; ?>

image

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

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

<? pre(rb(«models», «marks_id», «id», $_GET['marks_id'])) ?>

Array
(
    [1] => Array([id] => 1, [marks_id] => 1, [name] => Volvo S60, [price] => 1644000)
    [2] => Array([id] => 2, [marks_id] => 1, [name] => Volvo V40, [price] => 1309000)
)

Подобным образом можно выбирать и под другим полям. Выборка модели по ее id выглядит следующим образом:

<? pre(rb(«models», «id», $_GET['id'])) ?> где $_GET['id'] = 2;

Array
(
    [2] => Array([id] => 2, [marks_id] => 1, [name] => Volvo V40, [price] => 1309000)
)

Выбрать по одной подели каждой марки:

<? pre(rb(«models», «marks_id»)) ?>

Array
(
    [1] => Array([id] => 2, [marks_id] => 1, [name] => Volvo V40, [price] => 1309000)
    [2] => Array([id] => 3, [marks_id] => 2, [name] => CLS Shooting Brake, [price] => 3520000)
)

Подобным образом происходит обращение от модели к марке машины.

<? if($models = rb("models", "id", $_GET['id'])): ?>
    <? pre(rb("marks", "id", $models['marks_id'])); ?>
<? else: pre("Модель не найдена"); endif; ?>

Array(
    [1] => Array([id] => 1, [name] => Вольво)
)

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

Ну и собственно реализация данной функции.

Комментарии (118)


  1. Graid
    11.03.2016 11:22
    +5

    Чуть не всплакнул от умиления, передо мной пронесся 2005 год.


    1. mpakep
      11.03.2016 13:26
      -3

      Тот мир в котором мы находимся неминуемо идет к своему краху из за сложности инструментов и концепций. Это не мое мнение, но я с ним полностью согласен. Найти для себя как можно более простые инструменты отвечающие как можно более широкому кругу задач — бесценно. Для себя я нашел такой. Моим задачам он отвечает на 95% лишь в каждом двадцатом шаблоне или странице приходится использовать что то толичное от вышеописанного.


      1. Graid
        11.03.2016 14:03

        Поймите, ваш кода на самом деле намного сложнее нежели вы думаете.
        Посмотрите на код современных фреймворков, возможно он покажется чрезмерно "раздутым", но легок для чтения и восприятия.


        1. mpakep
          11.03.2016 14:29
          +1

          сложен не код. Сложна концепция. Если мне до сих пор приходится некоторым разработчиками объяснять почему обязательно нужно использовать автоинкрементное поле в таблице — другие концепции также натыкаются на сложность восприятия и особенности их понимания конкретным человеком. В данном случае предполагается что человек для себя уже ответил на вопрос зачем ему это а на вопрос как это сделать он найдет в примерах. А легкость восприятия это вопрос привычки. Легче всего воспринимается код написанный в привычном стиле написания кода. Со временем любой стиль становится привычным.


          1. lair
            11.03.2016 14:51

            Если мне до сих пор приходится некоторым разработчиками объяснять почему обязательно нужно использовать автоинкрементное поле в таблице

            А что, правда обязательно нужно?

            Со временем любой стиль становится привычным.

            Нет. К счастью.


          1. Graid
            11.03.2016 14:52
            +1

            https://github.com/mpak2/mpak.su/blob/master/modules/gbook/index.php
            Каптча по ссылке выше обходится на раз, а обилие для возможных инъекций и других уязвимостей просто удручает.
            Вам, мне кажется просто надо посмотреть существующие подходы и только тогда искать собственный путь.


          1. michael_vostrikov
            11.03.2016 21:21
            +2

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

            Со временем любой стиль становится привычным

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


  1. michael_vostrikov
    11.03.2016 12:01

    А чем отличается ваш подход от других, скажем так, "форм" хранения и отображения данных?


    1. mpakep
      11.03.2016 13:21

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


      1. michael_vostrikov
        11.03.2016 14:15
        +1

        Приведите пожалуйста пример из тех, которые вы встречали, и опишите, в чем отличие вашего подхода от него.


  1. lair
    11.03.2016 12:22
    +3

    Эээ… ORM, не?


    1. mpakep
      11.03.2016 13:30

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


      1. lair
        11.03.2016 13:41
        +3

        Все реализации orm тяготят своей сложностью

        А какое вам дело до сложности, если она корректно инкапсулирована?

        Всегда находился фатальный недостаток

        Например?

        Универсальных рецептов не существует.

        То-то у вас в заголовке написано "универсальные".


        1. mpakep
          11.03.2016 13:48

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


          1. lair
            11.03.2016 13:51
            +1

            Вам не приходилось сталкиваться с неожиданно отказывающим инструментом.

            Приходилось. Поэтому лучше выбирать такие инструменты, которые отказывают реже.

            Понять как работает пятьдесят строк кода на порядок проще чем в миллионе строк, не говоря уже о быстродействии и пороге вхождения.

            Вопрос в том, делают ли эти пятьдесят строк кода то же самое, что тот миллион. Что-то я сомневаюсь.

            Название «универсальные» использовал по отношению к себе из за большого круга задач где это может применяться.

            Большого круга ваших задач (которые вы, кстати, не озвучили).

            PS Но вообще, конечно, ваша аргументация — это типичный NIH.


            1. mpakep
              11.03.2016 14:02

              Если вспомнить закон Паретто и предположить что он работает и здесь то 20% кода в любой библиотеке будут охватывать 80% ее фукнционала. Остальные 80% будут работать лишь на 20% задач. Тут вопрос в том какого уровня функционал человека устраивает. Мне хватает 10% — вам возможно будет мало 50% от сюда и объем кода который будет по тому же закону разниться на порядки.


              1. lair
                11.03.2016 14:03

                Если вспомнить закон Паретто и предположить что он работает и здесь

                И на чем основано это предположение?


                1. mpakep
                  11.03.2016 14:08

                  Собственно на предположении и основывается. Если почитать сферы деятельности в котором находит применение данный закон можно предположить что он работает и тут. Считает что нет?


                  1. lair
                    11.03.2016 14:15

                    Считает что нет?

                    В формулировке "20% кода в любой библиотеке будут охватывать 80% ее фукнционала" — точно нет. Я знаю библиотеки, в которых это не так.

                    В помем случае 80 процентов кода охватывает всего одна функция. rb От этого и предположение.

                    Простите, что она делает?


                    1. mpakep
                      11.03.2016 14:22

                      Выборку данных по нужному условию из смежных связанных таблиц. Если не считать конструкций языка if и foreach которых будет тоже не мало это одна из самых частоупотребимых функций.


                      1. lair
                        11.03.2016 14:23

                        Вы не поняли вопроса. Вы пишете, что функция "охватывает 80 процентов кода". Что вы имеете в виду?


                        1. mpakep
                          11.03.2016 14:34

                          Если внутреннее ветвление кода условно назвать путями следования в коде то большая часть будет проходить одними и теми же путями. У каждого человека есть часто употребимые конструкции и методы. Такие же есть и у языков. К примеру foreach применяется чаще чем do while во всяком случае для меня. От этого и утверждение о неравномерности таких путей в алгоритме. Большая их часть будет ходить по одним и обходить другие.


                          1. lair
                            11.03.2016 14:51

                            Поскольку вы опять не ответили на мой вопрос, придется задать альтернативный.

                            Вот есть ваша "библиотека", состоящая из функции rb и тех функций, которые она (и только она) вызывает. Что представляют собой 80% функциональности этой библиотеки? Каким количеством кода они реализованы?


                            1. mpakep
                              11.03.2016 15:42

                              80% это сама функция а оставшиеся 20 это другие функции малоиспользуемые в коде.


                              1. lair
                                11.03.2016 15:42

                                80% кода или 80% функциональности?


                                1. mpakep
                                  11.03.2016 16:11

                                  Это игра словами. Я думаю вы понимаете что я имел ввиду.


                                  1. lair
                                    11.03.2016 16:12

                                    Это не игра словами, это вполне осмысленный вопрос.

                                    Иными словами, подтверждения вашему утверждению о применимости закона Парето к библиотечному коду пока не найдено. Предсказуемо.


                1. mpakep
                  11.03.2016 14:12

                  В помем случае 80 процентов кода охватывает всего одна функция. rb От этого и предположение.


  1. psrafo
    11.03.2016 12:37
    -1

    Не, просто ничего особенного, этот подход, или что-то схожее идет как бы по дефолту.


    1. mpakep
      11.03.2016 13:33

      По дефолту где? Хорошо бы подобный подход бул реализован в php по дефолту. Но я встретил только две функции которые полноценно работают с подобными структурами array_column и array_map сторонние решения тяготят своей ООП концепцией, избыточным функционалом и часто быстродействием. Нативные функции реализованные из коробки были бы очень востребованы.


      1. michael_vostrikov
        11.03.2016 14:36
        +1

        Ну вот, например, пример из документации PDO (который, как я понял, вы используете для работы с БД):

        $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";
        }

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


        1. mpakep
          11.03.2016 14:40

          Возвратить ассоциативный массив часто задач. Попробуйте реализовать данный пример.

          <? 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; ?>

          Вывести марки только те, у которых есть хотя бы одна модель. И дальше список этих моделей.


          1. lair
            11.03.2016 14:55

            Вывести марки только те, у которых есть хотя бы одна модель. И дальше список этих моделей.

            Вы так говорите, как будто это проблема какая-то

            foreach(var brand in ctx.Brands.Where(b => b.Models.Any()))
            {
              output(brand);
              foreach(var model in brand.Models)
              {
                output(model);
              }
            }

            (понятное дело, что я на SoC положил здесь с прибором ради наглядности)

            Ну и да, никаких магических строк, стопроцентная статическая типизация, все плюшки.


            1. mpakep
              11.03.2016 15:07

              Давайте усложним задачу. Как поменяется ваш код если связь марки с моделью поменяется до много ко многому, через промежуточную таблицу Модульный ряд modeling (id, marks_id, models_id)


              1. lair
                11.03.2016 15:09

                Никак. Но в выводе появятся дубликаты. Если их надо убирать — тогда другой разговор.


                1. mpakep
                  11.03.2016 15:11

                  Библиотека сама догадывается как связаны модели с марками? Если таких связей несколько по какому пути она пойдет?


                  1. lair
                    11.03.2016 15:13

                    Библиотека сама догадывается как связаны модели с марками?

                    Конечно, нет. Связи описаны в модели.

                    Если таких связей несколько по какому пути она пойдет?

                    Так в коде же написано: foreach(var model in brand.Models). Это точное указание на связь.


                    1. mpakep
                      11.03.2016 15:16

                      Таблицы модель и марка связаны через третью таблицу модельный ряд (много ко многому) если допустить что у марки есть флагманская модель и ее вторичный ключ есть в таблице марки А у модели по каким то причинам потребовалось добавить ключ марки то связь уже не такая явная.


                      1. lair
                        11.03.2016 15:22

                        (Вы, похоже, просто никогда не работали с нормальными ORM с нормальной же моделью.)

                        brand.Models и brand.FlagshipModel — это две разные связи, и обращение к любой из них однозначно.


                        1. mpakep
                          11.03.2016 15:36

                          Это из разряда магии? Для меня это не так наглядно как для вас. Связей может быть даже не две а пять к примеру. Для каждой у вас реализована своя модель? Каким образом вы определите по вторичному ключу вы обращяетесь от модули к марке, через промежуточную таблицу или по вторичному ключу от марки к модели если каждый ключь реализует свою логику и узкоориентированную связь в бизнес логике.


                          1. lair
                            11.03.2016 15:38

                            Это из разряда магии?

                            Нет, это из разряда корректно обрабатываемых метаданных.

                            Для меня это не так наглядно как для вас

                            … хотя казалось бы, все явно написано в коде.

                            Связей может быть даже не две а пять к примеру. Для каждой у вас реализована своя модель?

                            У меня реализована одна модель, описывающая все связи (если, конечно, они все в одном bounded context).

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

                            В описании связи явно указано, через что она сделана — через обратный вторичный ключ или через промежуточную таблицу.


                            1. mpakep
                              11.03.2016 15:45

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


                              1. lair
                                11.03.2016 15:46

                                Это и есть сложность

                                Сложность ли?

                                В моем случае это все те же две строки чуть длиннее в которых я через запятую перечисляю какие ключи использовать для связи

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

                                в вашем полный пересмотр всей концепции и правил приложения

                                Кто вам это сказал? Если меняется структура модели, понадобится только переопределить модель.


                                1. mpakep
                                  11.03.2016 15:55

                                  Бритва_Оккама В моем случае структура модели лишнее звено. Предпочитаю не плодить сущности и если есть возможность обойтись без них то так и будет сделано. Причин определять модуль не могу для себя объяснить. Зачем это делать если можно не делать? К тому же не исклчается ситуация когда модель будет описана, но по какой то причине не использоваться в коде. Куски время от времени выкидываются из приложения. В этом случае за собой тянутся зависимости кода друг от друга и неиспользуемые его части.


                                  1. lair
                                    11.03.2016 15:57

                                    В моем случае структура модели лишнее звено

                                    То есть вы считаете нормальным повторять один и тот же код во всех местах, где вам нужно вывести данные по одной и той же связи?

                                    Зачем это делать если можно не делать?

                                    А зачем вообще работать, если можно не работать?

                                    Причин определять модуль не могу для себя объяснить.

                                    Формальное определение домена, возможность статического анализа кода — это первое, что в голову пришло.


              1. Graid
                11.03.2016 15:12

                ActiveRecord вам в руки.


            1. mpakep
              11.03.2016 15:10

              <? 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; ?>


              1. Graid
                11.03.2016 15:14

                Как думаете сколько запросов сгенерирует ваш скрипт при нахождении 50 марок?


                1. mpakep
                  11.03.2016 15:23
                  +1

                  4 запроса. Но практика показывает, что наглядность дороже оптимизации. Правильно построенные ключи сводят к минимуму накладные расходы. Это согласуется с мнением что базам удобнее работать с двумя простыми запросами чем с одним сложным. По своей практике могу сказать что это действительно так. Любой самый сложный скрипт занимает доли секунд. Хотя и тут остаются маневры для оптимизации если интересно могу предложить один. Функция пересборки названа не зря. Один раз выгрузив массив мы можем его пересобирать многократно

                  $tpl['models'] = rb("models");
                  $tpl['marks'] = rb("marks"):

                  <? 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; ?>

                  В этом случае запроса будет всего два при неограниченном количестве их использования.


                  1. lair
                    11.03.2016 15:26

                    В этом случае запроса будет всего два при неограниченном количестве их использования.

                    … и вы выберете из БД все марки, включая те, у которых нет моделей. Клевая оптимизация.


                    1. mpakep
                      11.03.2016 16:05

                      Есть функция array_diff_key которая поможет. Никакого дополнительного функционала для этого не потребуется.


                      1. lair
                        11.03.2016 16:07

                        Есть функция array_diff_key которая поможет

                        И как же она поможет выбрать из БД только те марки, у которых есть модели?


                        1. mpakep
                          11.03.2016 16:16

                          Как то так.

                          Марки не имеющие моделей = array_diff_key(rb('marks']), rb('marks', «id», «id», rb('models', «marks_id»)))

                          Исключаем из списка марк те, которые имеют модели


                          1. lair
                            11.03.2016 16:17

                            rb('marks'])

                            Этот запрос разве не заберет из базы все марки?


          1. mpakep
            11.03.2016 15:03
            +1

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


            1. lair
              11.03.2016 15:09

              полное отсутствие каких то зависимостей.

              А функция rb — это не зависимость?

              Видно к какой таблице мы обращаемся, по каким полям идет выборка

              Это, типа, достоинство? А инкапсуляция от БД вам не нужна?

              При дальнейшей модификации связей в таблице (с одного ко многим до много ко многим) легким движением руки меняются структура выборки ничего не меняя в логике

              Если у вас при изменении арности модели от 1-N на N-N не меняется логика — то у вас что-то не так в бизнесе.


              1. mpakep
                11.03.2016 15:14

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


                1. lair
                  11.03.2016 15:15

                  Я хотел бы в языке видеть нативную функцию

                  Зачем? Зачем раздувать язык функциональностью библиотеки?


                  1. mpakep
                    11.03.2016 15:26

                    До библиотеки это на мой взгляд не дотягивает. Удобная функция обработки массива. Его выборки по ключу.
                    С утверждением про библиотеку соглашусь не надо. А от одной крайне важной я бы не отказался.


                    1. lair
                      11.03.2016 15:28

                      До библиотеки это на мой взгляд не дотягивает.

                      Из любой функции можно сделать библиотеку, и никто от этого не пострадает.

                      Удобная функция обработки массива.

                      "Удобные функции обработки массива" — это стандартные функциональные map/flatMap/filter/reduce и так далее. А то, что у вас написано — это что-то узкоспецифичное и приятное лично вам.


                      1. mpakep
                        11.03.2016 15:30

                        Все верно. Это из разряда на любителя.


                        1. lair
                          11.03.2016 15:35

                          Вот поэтому и не надо это вносить в язык.


              1. mpakep
                11.03.2016 15:32

                Бизнес правила меняются на ходу. В своей практике нам приходится только подстраиваться под тех, кто оплачивает всю вечеринку. И если будут варианты из упереться и не делать так как что то не так в бизнесе или быстро под него подстроиться я предпочту последнее.


          1. michael_vostrikov
            11.03.2016 21:06
            +2

            Вот вам несколько примеров. Обратите внимание на порядок запросов, а также на возможность пагинации и фильтрации.

                    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. psrafo
        11.03.2016 14:38

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


        1. mpakep
          11.03.2016 15:50

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


          1. lair
            11.03.2016 15:58

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

            Я еще раз спрошу, мне не лень: а что, надо? Я не использую практически никогда, ЧЯДНТ?


            1. mpakep
              11.03.2016 16:21
              +1

              Это из разряда не обязательных, но удобных вещей. Как бы можно под дождем и без зонта ходить никто не растает. Но с зонтом сухо и приятно. Для меня автоинкрементное поле это прежде всего уникальная идентификация записи. Возможность в любом месте использовать независим от структуры самой таблицы. Когда такого поля нет привязаться не к чему. Опять же вы всегда можете добавить два ключ по двум полям с добавлением их уникальности, но это превратит в ад вашу жизнь в случае изменения бизнес логики которая как озвучили выше меняется часто. Каждое такое но, это потенциальный сбой на поздних этапах работы приложения и чем их больше, тем они вероятнее. Автоинкрементное поле однозначно избавляет от озвученных ситуаций. Если вы для себя не объяснили почему вам нужно автоинкрементное поле, я вам не смогу привести убедительные аргументы.


              1. lair
                11.03.2016 16:24
                -1

                Как бы можно под дождем и без зонта ходить никто не растает. Но с зонтом удобнее.

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

                Для меня автоинкрементное поле это прежде всего уникальная идентификация записи.

                Других способов уникальной идентификации записи вы не знаете?

                Когда такого поля нет привязаться не к чему.

                Да ладно. Первичные ключи отменили, что ли?


                1. mpakep
                  11.03.2016 16:27

                  Таблица(
                  «name»=>«Предмет», «Свойство»=>«Много»
                  «name»=>«Предмет», «Свойство»=>«Много»
                  )

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


                  1. lair
                    11.03.2016 16:28

                    В этой "таблице" (которая, на самом деле, не таблица) нет первичного ключа.

                    (но вообще, конечно, .[1])


                    1. mpakep
                      11.03.2016 16:32

                      Что 1? Отсортировать мы не можем так как нет различающихся данных. В любом случае порядок вывода записей в выборке не будет определен. При двух запросах первым может быть второй, а во втором случае подругому. В базах данных поумнее у нас есть gid однозначно определяющий в бд запись. Но в mysql такого нет. Тут и приходит на помощь автоинкремент.


                      1. lair
                        11.03.2016 16:35

                        Что 1?

                        Вы попросили однозначно определить вторую запись. Я это и сделал.

                        Но в mysql такого нет. Тут и приходит на помощь автоинкремент.

                        Вы хотите сказать, что в mysql первичный ключ можно сделать только по автоинкременту? Можно, я вам не поверю?


                        1. mpakep
                          11.03.2016 16:43
                          +1

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


                          1. lair
                            11.03.2016 16:47

                            Но надо как вы обеспечите уникальность?

                            Ограничением на уровне БД, вестимо. В большей части БД (если не во всех) признак primary key автоматически означает требование уникальности, в остальных (если они еще остались) можно поставить это требование дополнительно.

                            Вся прелесть автоинкремента в качестве первичного ключа в том, что он избавляет от неминуемой борьбы со сложностями.

                            И добавляет других сложностей, ага.

                            Вы не растаите бд не перестанет работать, но делать это станет сложнее

                            И что же именно сложнее? Вот конкретно, по пунктам?


                    1. mpakep
                      11.03.2016 16:38

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


                      1. lair
                        11.03.2016 16:39

                        Данное поле должно обеспечивать уникальность в рамках таблицы.

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

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

                        В таблице может быть только один первичный ключ. Он, собственно, потому и первичный.


                        1. mpakep
                          11.03.2016 16:48

                          Есть еще Составные первичные ключи https://habrahabr.ru/post/193284/ ключ не обязательно один их может быть хоть три. Это извращение, к которому приходится прибегать чтобы не использовать автоинкремент а однозначно уникальных полей нет.


                          1. lair
                            11.03.2016 16:50

                            Составной первичный ключ — это один первичный ключ (из какого количества колонок он собран, не имеет значения). И да, зачастую (особенно в тех самых таблицах связей) их применение полностью оправдано.

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


                            1. mpakep
                              11.03.2016 16:55

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


                              1. lair
                                11.03.2016 16:58

                                … все, что вы описываете, решается введением суррогатного идентификатора. Быть автоинкрементом ему не обязательно.

                                (дискуссия "во всякой ли таблице нужен суррогатный идентификатор" — это отдельный вопрос. В частности, зачем суррогатный ключ в таблице связей BookAuthor, все данные в которой состоят из BookId, AuthorId, и эта комбинация обязана быть уникальной?)


                                1. mpakep
                                  11.03.2016 17:05

                                  Все верно я о нем родимом. Даже не представляю как можно работать без «сурогатного идентификатора» удивляюсь, когда на моем пути встречаюся люди его не использующие. В таблице BookAuthor все хорошо, пока вы не начали с ней работать. Как только потребуется добавить туда свойст каких то как вариант время добавления записи, кто сделал эту запись. Дальнейший путь модификации может быть следующий это то что пришло в голову — у книги не один автор требуется добавлять несколько авторов. Или к примеру такая ситуация. Из разных источников данные могут разнится и следует хранить оба варианта В этом случае ваши ключи теряют смысл. Все возвращается к автоинкременту ну или к жуткой неразберихе с кодами. Для полной картины требуется добавить вторичный ключ источника откуда она получина. И первые два ключа уже не могут обеспечить уникальность. Изменение одного бизнес правила, и вся логика летит к чертям.


                                  1. lair
                                    11.03.2016 17:08

                                    Все верно я о нем родимом.

                                    Так вы определитесь, вы о суррогатном идентификаторе или об автоинкременте?

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

                                    И в чем проблема? Пошли и добавили.

                                    Дальнейший путь модификации может быть следующий это то что пришло в голову — у книги не один автор требуется добавлять несколько авторов.

                                    Вообще-то ровно для этого эта таблица и создана — это типичное многое-ко-многим между авторами и книками.

                                    Из разных источников данные могут разнится и следует хранить оба варианта

                                    Добавляете идентификатор источника, тем самым гарантируя уникальность теперь уже на уровне триады.

                                    В этом случае ваши ключи теряют смысл.

                                    Никоим образом.

                                    Все возвращается к автоинкременту

                                    Отнюдь. В худшем случае все возвращается к суррогатному ключу.


                                    1. mpakep
                                      11.03.2016 17:11

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


                                      1. lair
                                        11.03.2016 17:15

                                        У вас уже есть некий функционал, который выгребает значения используя два поля.

                                        Это какой?

                                        Используй вы сурогатный автоинкрементный ключ таких проблем не возникло бы.

                                        Во-первых, используй я суррогатный идентификатор — совершенно не важно, автоинкрементый он или нет.

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

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


                                        1. mpakep
                                          11.03.2016 17:19

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


                                          1. lair
                                            11.03.2016 17:25

                                            функционал к примеру админской части модифицирующий или удаляющий записи

                                            А что там модифицировать по двум ключам? А к удалению мы сейчас вернемся.

                                            А есть еще пользовательские интерфейсы которые также используют два ключа.

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

                                            Получается что вам легче все убить чем переделывать

                                            С чего бы?

                                            Так вот, про удаление.

                                            Рассмотрим следующий сценарий:

                                            1. администратор открывает карточку книги
                                            2. администратор видит список авторов (А, Б, В) книги
                                            3. администратор удаляет автора (В) из книги.

                                            Предположим, что используются суррогатные ключи. Несколько вопросов:
                                            (а) какой конкретно будет запрос и план запроса на шаге (2)?
                                            (б) какой конкретно будет запрос и план запроса на шаге (3)?

                                            А теперь маленькое дополнение: между шагами (2) и (3) второй администратор заменяет автора (В) на автора (Г). Что произойдет?


                                            1. mpakep
                                              11.03.2016 17:32

                                              Ситуация такая. У нас разные источники информации. Один из воспоминаний автора а второй по информации от соседей. Оба этих источника говорят что книгу 1 наипсал автор 2 источники разные

                                              Итого получаем

                                              Книга Автор Источник
                                              1 2 5
                                              1 2 6

                                              У нас при удалении используется два поля

                                              Удалить книгу 1 с автором 2

                                              В этом случае удалятся две записи. Удаление по двум ключам нас после добавления источника уже не устроит. Такая же ситуация и с модификацией.


                                              1. lair
                                                11.03.2016 17:35

                                                А вы не могли бы все-таки ответить на конкретные вопросы из моего комментария?

                                                У нас при удалении используется два поля
                                                Удалить книгу 1 с автором 2

                                                Как мило, да. У нас добавилось новое ключевое поле, но мы его не учли в системе. Вам не приходит в голову, что в этом случае во всех интерфейсах пользователь увидит дубликаты записей без объяснения причин?

                                                И да, то, что в этом случае удалятся две записи — это вполне может быть правильное поведение системы.


                                        1. mpakep
                                          11.03.2016 17:25

                                          Так и это еще не все. Как правило подобных мелких задач десятки по всему проекту и они накапливаются лавинообразно. После какого то критического их количества уже нет человека который может сказать что вообще происходит. А дел то всего — не ставили в начале сурогатные (автоинкрементные) ключи. Сурогатный — искусственно введенный но не уникальный. Автоинкрементный — уникальный.


                                          1. lair
                                            11.03.2016 17:26

                                            А дел то всего — не ставили в начале сурогатные ключи.

                                            Ну то есть вы уже не настаиваете на автоинкрементах, только на суррогатных ключах?


                                            1. mpakep
                                              11.03.2016 17:44

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

                                              Грубо говоря к любой записи добавляя поле с единицей я могу сказать что оно сурогатное (но не уникальное) я каждый раз говорю именно об автоинкрементных полях


                                              1. lair
                                                11.03.2016 18:06

                                                Ох. Не бывает суррогатных полей, бывают суррогатные ключи. И суррогатный ключ по определению уникален.


                                                1. mpakep
                                                  11.03.2016 18:19

                                                  Заблуждаетесь. Слово сурогайтный обозначает только его искусственное а не естественное происхождение. Текст из википедии:

                                                  Как правило, суррогатный ключ — это просто числовое поле, в которое заносятся значения из возрастающей числовой последовательности.

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


                                                  1. lair
                                                    11.03.2016 18:25

                                                    Читайте википедию внимательно:

                                                    A surrogate key in a database is a unique identifier

                                                    Впрочем, в русской аналогично:

                                                    Суррогатный ключ [...] дополнительное служебное поле, [...] единственное предназначение которого — служить первичным ключом.

                                                    (Первичные ключи всегда уникальны)

                                                    Так что нет, я не заблуждаюсь.

                                                    И, кстати, нет, автоинкремент — не единственный способ обеспечения уникальности.


                                                    1. mpakep
                                                      11.03.2016 18:34

                                                      Если я создам искусственный ключ. Все его значения будут одинаковые к примеру нулевые. Я могу сказать что он сурогатный? Неуникальный сурогатный ключ, который не служит первичным. С точки зрения названия он является сурогатным. Но не является уникальным и первичным. Вы приписываете термину свойства которых у него нет. Сурогатный обозначет лишь одно — искусственно добавленый. Все остальное это частный случай и устоявшаяся практика.


                                                      1. lair
                                                        11.03.2016 18:36

                                                        Я могу сказать что он сурогатный?

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

                                                        С точки зрения названия он является сурогатным.

                                                        Нет, не является. Определения — штука вполне строгая.


                                                        1. mpakep
                                                          11.03.2016 18:41

                                                          Вы пытаетесь одному определению дать два свойства которые не зависят друг от друга. Посмотрите значение слова срогатный. Частое употребление этого ключа в качестве первичного не накладывает обязанность ему быть уникальным. Почему я не могу добавить два сурогатных поля в таблицу? Какое из них будет первичным и обязаны ли боа быть уникальным. Во избежании недопонимания уточняйте каждое свойство. Уникальности отдельно а искусственное происхождение отдельно. Потому что свойства никак между собой не пересекаются. Есть искусственные есть естественные. Есть уникальные и есть неуникальные. Может быть сколько угодно искусственных и сколько угодно уникальных. Никаких ограничений на их значния это не накладывает.

                                                          Почувствуйте разницу в определении «как правило» что сказано в википедии с «обязано быть» в вашем предыдущем комментарии. В описании я не встретил что оно обязанно быть уникальным. Часто бывает уникальным — да. Как правило используется в качестве уникального — да. Но не обязано.


                                                          1. lair
                                                            11.03.2016 18:48

                                                            Вы пытаетесь одному определению дать два свойства которые не зависят друг от друга.

                                                            Я ничего не пытаюсь — это устоявшаяся терминология.

                                                            Посмотрите значение слова срогатный.

                                                            Значение слова "суррогатный" и значение термина "суррогатный ключ" не обязаны совпадать. Впрочем, значение термина "суррогатный ключ" является практически точным пересечением значений слова "суррогатный" и термина "потенциальный ключ", так что все логично.

                                                            Частое употребление этого ключа в качестве первичного не накладывает обязанность ему быть уникальным.

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

                                                            Почему я не могу добавить два сурогатных поля в таблицу?

                                                            Я не знаю, что такое суррогатное поле, и не буду это обсуждать. Я говорю только о суррогатных ключах.


                                                            1. mpakep
                                                              11.03.2016 19:06

                                                              Вы сами себя вгоняете какие то рамки исключая существование сурогатных неуникальных ключей? Вы утверждаете что таких нет? А два ключа созданных искусственно один из них первичный а второй нет. Какой из них сурогатный? Оба. Но уникальных не оба.

                                                              Вы утверждаете что теплый это всегда мягкий. И это не обязательно.
                                                              Исключаете мягкий но холодный или теплый но жесткий. Как правило мягкий является теплым. Но не обязательно.

                                                              Также и с сурогатный уникальный.

                                                              Сурогатный это противоположное значение естественному ключу. И никак не связано со требованиями к значениям данных в этом поле.


                                                              1. lair
                                                                12.03.2016 00:14

                                                                Вы сами себя вгоняете какие то рамки

                                                                Это не я себя вгоняю, это определения такие. Сложившаяся система терминов.

                                                                существование сурогатных неуникальных ключей? Вы утверждаете что таких нет?

                                                                Да, именно это я и утверждаю.

                                                                А два ключа созданных искусственно один из них первичный а второй нет. Какой из них сурогатный? Оба. Но уникальных не оба.

                                                                Если это именно ключи, то оба обязаны быть уникальными.

                                                                Сурогатный это противоположное значение естественному ключу. И никак не связано со требованиями к значениям данных в этом поле.

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

                                                                Неудивительно, при таком подходе, что вы не можете никому ничего объяснить.


                                1. coh
                                  11.03.2016 17:25

                                  Говоря, к примеру, о MySQL и InnoDВ, помня про clustered index. Монотонно возрастающий ключ приводит к вставке строки в конец, иначе сортировке индекса.
                                  Не будет ли автоинкрементарное поле в данном случае повышать производительность вставки с учетом разнобоя BookId, AuthorId? http://dev.mysql.com/doc/refman/5.7/en/innodb-index-types.html


                                  1. lair
                                    11.03.2016 17:27

                                    Не будет ли автоинкрементарное поле в данном случае повышать производительность вставки с учетом разнобоя BookId, AuthorId ?

                                    Какая операция происходит чаще — выбор конкретной записи из таблицы связей, или выбор всех авторов для книги/всех книг для автора?


                                    1. coh
                                      11.03.2016 17:35

                                      Ваш вопрос содержал ответ. Зависит от ситуации.
                                      Гипотетически в книжный магазин ежедневно заливается сотни тысяч новых книг и журналов / товаров поставщиков. А покупается / ищется 100-200.


                                      1. lair
                                        11.03.2016 17:42

                                        Ваш вопрос содержал ответ. Зависит от ситуации.

                                        Именно.

                                        Гипотетически в книжный магазин ежедневно заливается сотни тысяч новых книг и журналов / товаров поставщиков. А покупается / ищется 100-200.

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


            1. kroshanin
              11.03.2016 21:16
              +1

              Я немного вклинюсь в вашу беседу.
              Существуют определенные "негласные" правила проектирования. Так вот то, что в таблице должно быть поле под названием "id" и быть оно должно автоинкрементным — это как раз и считается "правилом хорошего тона". Вы конечно можете этому не следовать, но тогда на практике у вас могут возникнуть разного рода трудности.


              1. lair
                12.03.2016 00:17

                Существуют определенные «негласные» правила проектирования.

                Зачем следовать негласным, когда есть реляционная теория с вполне гласными правилами?

                Вы конечно можете этому не следовать, но тогда на практике у вас могут возникнуть разного рода трудности.

                И какие же трудности у меня возникнут, если в тривиальной таблице связей (BookId, AuthorId) у меня не будет поля Id?
                Какие трудности у меня возникнут, если поле Id в первичной таблице сущности будет не автоинкрементным, или, упаси боже, и вовсе не целочисленного типа?
                Какие трудности у меня возникнут, если идентификационное поле в "хвостовой" таблице при TPT-стратегии наследования будет называться не Id, а BookId?


                1. boodda
                  12.03.2016 00:51

                  Более того в этой же таблице может быть еще колонка ссылка на третью таблицу, к примеру с языком книги, language_id, а так же например количество на складе count, и не нужен тут ID, он не даёт тут ровным счетом ничего


                1. kroshanin
                  12.03.2016 10:29

                  А какие трудности у вас возникнут если поля BookId и AuthorId назвать Field01 и Field02? Это же не запрещено?
                  А какие трудности возникнут, если при добавлении записей одновременно в две простые таблицы (т.е. вы твердо убеждены, что ошибок при insert'е быть не может) их не оборачивать в транзакцию?
                  А какие трудности возникнут, если большие куски sql-кода не заключать в хранимые процедуры а писать прямо в приложении?
                  А какие трудности возникнут, если не использовать вьюшки, а использовать вместо них просто вызовы select'а?
                  Какие вообще могут быть трудности, если не следовать общепринятым правилам?


                  1. lair
                    12.03.2016 10:34

                    То есть ответить на мои вопросы вы не можете, я правильно понимаю? И описанное вами правило принимаете на веру?

                    PS.

                    А какие трудности у вас возникнут если поля BookId и AuthorId назвать Field01 и Field02?

                    Нельзя будет понять их семантику при взгляде на таблицу.

                    т.е. вы твердо убеждены, что ошибок при insert'е быть не может

                    Я никогда в этом не убежден.

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

                    А никаких, кстати. Особенно если в приложении писать не SQL-код, а код, нативный для приложения, который транслируется в SQL-код.

                    А какие трудности возникнут, если не использовать вьюшки, а использовать вместо них просто вызовы select'а?

                    Кроме неоходимости повторять код — никаких. Учитывая, что в большей части случаев я имею дело со сгенеренными кодом — и вовсе никаких.


                    1. kroshanin
                      12.03.2016 11:59

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

                      Например, вам требуется вести логи изменений в тех или иных таблицах. Заказчик, естественно, хочет в таблице логов видеть отсылку к элементу (возможность "открыть" страницу/форму элемента). Для этого в таблицу логов просто добавляется поле "elemid". Это к вопросу, что id лучше делать числовым. Пояснять, почему в таблицу логов поле "elemid" лучше сделать числовым, пожалуй, не буду.

                      Второй пример к вопросы, почему желательно называть "id", а не "BookId", "AuthorId". Например, у вас есть универсальная хранимая процедурка с каким-нибудь алгоритмом обработки. На вход поступает список имен таблиц, внутри формируется текст запроса и вызывается через bulk_cmd, результаты фиксируются в какой-либо таблицы (мол, элемент такой-то обработан). Если у разных таблиц будут по разному названы ключевые поля, то придется их передавать еще одним параметром.

                      PS.

                      А никаких, кстати. Особенно если в приложении писать не SQL-код, а код, нативный для приложения, который транслируется в SQL-код.

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

                      Кроме неоходимости повторять код — никаких. Учитывая, что в большей части случаев я имею дело со сгенеренными кодом — и вовсе никаких.

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


                      1. lair
                        12.03.2016 12:06

                        Например, вам требуется вести логи изменений в тех или иных таблицах.

                        Логи изменений или логи состояний? Иными словами, дифы или снепшоты?

                        Заказчик, естественно, хочет в таблице логов видеть отсылку к элементу

                        Какому элементу?

                        Пояснять, почему в таблицу логов поле «elemid» лучше сделать числовым, пожалуй, не буду.

                        Поясните, пожалуйста.

                        Например, у вас есть универсальная хранимая процедурка с каким-нибудь алгоритмом обработки.

                        Плохой пример. У меня нет (и, к счастью, не бывает) универсальных хранимых процедур.

                        Например, по сети лучше передавать имя хранимой с параметрами, чем длинный текст кода.

                        Это пренебрежимо в нынешних реалиях.

                        Вот если следующий шаг зависит от предыдущего — тогда начинаются нюансы: нам надо либо встраивать эту логику в собираемый и посылаемый SQL-код, либо делать больше одного раунтрипа.

                        Или, например, порой требуется иметь возможность редактирования алгоритма без перекомпиляции приложения

                        Для этого есть и другие способы, причем совместимые с нормальной доменной моделью.

                        (Более того, когда у заказчика есть возможность редактировать хранимые процедуры, от которых зависит приложение — его поддержка превращается в ад. Жизненная история.)

                        При этом у того, чтобы не писать хранимые процедуры, есть куча очевидных и не очень преимуществ.

                        Вы, видимо, просто нечасто пользуетесь данным инструментом, поэтому и не очень много знаете о возможностях, которые он предоставляет.

                        Например?


                        1. kroshanin
                          12.03.2016 14:36

                          Логи изменений или логи состояний? Иными словами, дифы или снепшоты?

                          Причем здесь дифы и снепшоты? Логи не на уровне БД, а на уровне приложения. Т.е. грубо говоря, заказчик хочет зайти на сайт/в программу и увидеть, кто какие вносил изменения (например, кто поменял номер в накладной).

                          (Более того, когда у заказчика есть возможность редактировать хранимые процедуры, от которых зависит приложение — его поддержка превращается в ад. Жизненная история.)

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

                          На остальное даже отвечать желания нету..


                          1. lair
                            12.03.2016 14:44

                            Причем здесь дифы и снепшоты?

                            Ну так, это влияет на реализацию.

                            Т.е. грубо говоря, заказчик хочет зайти на сайт/в программу и увидеть, кто какие вносил изменения (например, кто поменял номер в накладной).

                            Иии? Это же типовая задача, типовым же способом и решается. Зачем для нее автоинкременты или поле id в каждой таблице — нифига не понятно.

                            Хранимые и вьюшки являются своего рода интерфейсами подобно api в сайте.

                            Могут являться. Но это совершенно не обязательно (и без этого прекрасно можно жить).

                            Позволяют как менять структуру базы без необходимости ребилдов основного приложения (которое может быть установлено на непойми скольких компьютерах), так и запрещать доступ пользователям к бд напрямую.

                            Вы про сервера приложений не слышали никогда? Или про сервисную архитектуру?


                          1. boodda
                            12.03.2016 20:04
                            +1

                            Причем здесь дифы и снепшоты? Логи не на уровне БД, а на уровне приложения. Т.е. грубо говоря, заказчик хочет зайти на сайт/в программу и увидеть, кто какие вносил изменения (например, кто поменял номер в накладной).

                            Ну так именно поэтому стоило бы и логику ведения логов делать внутри приложения, а не внутри БД

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

                            Поэтому в вебе любую БД лучше использовать только как хранилище данных, без каких либо внутренних обработок.


              1. VolCh
                13.03.2016 10:41

                1. При проектировании таблиц связи id только мешает


  1. boodda
    12.03.2016 00:45
    +5

    $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);

    Здесь вы просто смешали все что можно было смешать:

    • куча(просто гора) переменных с неясным назначением
    • 4 (4 КАРЛ!) тернарных оператора в одной строке, вы хотели что бы никто не понял? У вас получилось
    • использование глобальных переменных $conf['db']..
    • использование суперглобальных переменных? С чего вы вообще взяли что массив $_GET существует, и почему не POST или еще что то ?
    • переменная tab подставляется в функции без фильтрации -> SQL инъекция

    Этот код ужасен:

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

    Я так полагаю, что на этот код потрачены месяцы жизни и поэтому вы считаете это отличной работой, но нет. Это возможно работает, но нет. Откройте уже наконец для себя мир ORM(Eloquent, Doctrine). Но начинать стоит с теории реляционных БД.
    У вас очевидно невысокий уровень понимания устройства и работы БД. Надо его подтянуть.

    Почитать про паттерны проектирования. Понять их суть, предназначение.
    строки 249-273. Этого бы не случилось никогда.

    И вот вам вопрос на засыпку. Как вы думаете, существует ли человек, который хотя бы за 20 минут сможет ответить, что происходит в функции "bcont" строка 297. Ну не просто что, а как конкретно это происходит.


    1. mpakep
      13.03.2016 16:51
      -1

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