Куплю готовые умные зарплаты для сотрудников офиса,
а также документы, регламентирующие работу секретарей (офис-менеджеров), завхозов, курьеров и всех, кто обслуживает офис
На сайте ведутся работы
сегодня 10930 Подписчиков
Здравствуйте, Коллеги!
Большое спасибо за интересную статью. Появилось несколько вопросов.
1. В сычевской структуре данных появилась возможность добавить, например по ошибке, в один набор несколько свойств с одинаковым именем и разными значениями, чего в иных структурах в принципе не возможно (код не сможет даже по ошибке добавить еще один столбец в таблицу с уже существующим именем). Так?
И за этим нужно следить. Иначе функции ядра заглючат, когда получат из базы не одно, а несколько значений одного и того же свойства одного и того же набора. Так?
2. Неявно предполагается наличие свойства, уникального для данного типа набора. Так?
Например, свойство Фамилия маркирует набор типа Человек, благодяря чему можно сделать выборку всех людей. Однако, такого маркирующего свойства может не случиться. Или оно изменится в будущем, а мы не учли. Напрашивается простое решение: добавить в каждый набор обязательное свойство Тип (Человек, Документ, Пароход, ...) Но тогда как тень отца Гамлета снова появляются сущности или объекты, которые теперь именуются набором и имеют Тип. Причем, структура данных позволяет Тип не указывать, а если код рассчитан на то, что Тип существует - опять глюки. Для сравнения: если существует таблица Пароходы, то все, что в ней обязательно и только Пароходы. А если есть столбец с уникальным id, то каждый Пароход учтен и не потеряется.
И еще пример. Если мне звонит верстальщик, и жалуется, что у него с газетной полосы пропала статья, я смотрю в таблицу Статьи, нахожу по значениям свойств потерявшуюся (у нее, например, слетел линк на полосу) и быстро восстанавливаю. В сычевской структуре, так просто, видимо, не получится?
Уважаемый Всеволод!
... появилась возможность добавить, например по ошибке, в один набор несколько свойств с одинаковым именем и разными значениями... Так?
Если Вы имеете в виду ситуацию, показанную ниже синим (один набор, одинаковое имя, разные значения), то это может быть и не ошибка. Это кому-то зачем-то потребовалось "кантри" и "рок-н-ролл" получить/обработать общим набором. Если это позволить сделать, у него получится.
Set_id | Name_id | Value_id | ___________ |
---|---|---|---|
1 | 3 | 4 | Все записи Стинга |
2 | 2 | 2 | Весь рок-н-ролл |
2 | 2 | 5 | Все записи кантри |
3 | 1 | 1 | Все рыжие исполнители |
... | ... | ... |
Values
Value_Id | Values |
---|---|
1 | Рыжие |
2 | Рок-н-ролл |
3 | Блондины |
4 | Стинг |
5 | Кантри |
6 | Брюнеты |
7 | ... и т.д. ... |
Неявно предполагается наличие свойства, уникального для данного типа набора.... Однако, такого маркирующего свойства может не случиться. Или оно изменится в будущем, а мы не учли. ........ добавить в каждый набор обязательное свойство Тип... Но тогда как тень отца Гамлета снова появляются сущности или объекты, которые теперь именуются набором и имеют Тип. Причем, структура данных позволяет Тип не указывать, а если код рассчитан на то, что Тип существует - опять глюки. Для сравнения: если существует таблица Пароходы, то все, что в ней обязательно и только Пароходы. А если есть столбец с уникальным id, то каждый Пароход учтен и не потеряется.
Это не "тип". Это любой "маркер", который Вы можете завести, например, в список констант (Names). Но это не тип, также как ID - это не "тип", верно ведь? Вы можете его использовать, можете не использовать, можете изменять и т.д. При этом, код вовсе не рассчитан на то, что "какой-то тип существует": как были неизменные идеальные функции вида:
В общем, нет никакой необходимости заводить таблицу "Пароходы" :)
И еще пример. Если мне звонит верстальщик, и жалуется, что у него с газетной полосы пропала статья, я смотрю в таблицу Статьи, нахожу по значениям свойств потерявшуюся (у нее, например, слетел линк на полосу) и быстро восстанавливаю. В сычевской структуре, так просто, видимо, не получится?
Получится даже очень просто: одним get'ом. Если Вы знаете заголовок статьи, можете сделать запрос, который вернёт (например) id статьи, саму статью и/или линк на неё.
И фраза "я смотрю в таблицу" ложная. Компьютер должен осуществить поиск в базе, а не Ваши глаза. Например, на этом Форуме более 20 тысяч разных обсуждений и вот попробуйте просмотреть глазами предполагаемую таблицу "Обсуждения". А есть проекты, как Вы понимаете, и гораздо крупнее.
В целом, частый аргумент: "Я зато быстро посмотрю в конкретной таблице "Статьи (Пароходы и т.д.)" ошибочен. Он может работать короткое время, когда у Вас мало записей. А Вы представьте мысленно, что их сотни тысяч и многие внешне похожи (например, разных записей, начинающихся со слова "Барби" в номенклатурном справочнике могут быть тысячи; записей, которые трудно прочесть глазами - например, "артикулов поставщика (рандомных)" - десятки тысяч и т.п.) - глядеть - не наглядеться.
Этот аргумент, по сути, означает: "Мне будет легче самому поработать компьютером". Судите сами, в каких случаях это может быть верным.
И, конечно, когда в базе "завелись" "Статьи" и "Пароходы", стоит перечитать первые 2 способа построения структуры данных, описанные в начале статьи - будут те же проблемы.
Большое Спасибо за Ваши вопросы, спрашивайте ещё :)
Здравствуйте, Коллеги!
Еще ремарка. Часто заказчик хочет (хотя и не говорит этого в слух, потому что считает, что это не возможно или труднореализуемо) хранить историю изменения свойств. Т.е. при изменении свойства, не затирать предыдущее, а добавить еще одно значение и time stamp изменения. При этом актуальным будет только одно значение - последнее. А остальные нужны для особых случаев (отчетов, поисков и т.п.)
А еще лучше хранить и id пользователя, который изменил свойство (его ip, фото, отпечатки пальцев...) В структуре (1) мы просто добавим соответствующие столбцы.
Реализуемо ли это в EAV по Сычевски?
Да. Гибкость большая. Но, я предполагаю, что придется тщательнее следить за целостностью данных. В случае когда, например, нельзя допускать несколько одноименных значений в наборе.
Когда нельзя допускать нескольких одноимённых значений в наборе, сделаем реализацию, когда подобное просто невозможно.
В функции Add ядра (то есть, уже на уровне запроса к базе) используем проверку.
Например, вот такая конструкция (функция Add создаст её автоматически):
UPDATE OR INSERT INTO SETS (SET_ID, NAME_ID, VALUE_ID) VALUES (1, 1, 12) matching (SET_ID, NAME_ID);
сама по себе уже не позволит нам вольности, подобной той, что приведена теперь красным, и результат гарантированно будет таким как приведено ниже синим:
Set_id | Name_id | Value_id | ___________ |
---|---|---|---|
1 | 3 | 4 | Все записи Стинга |
2 | 2 | 2 | Весь рок-н-ролл |
2 | 2 | 5 | Все записи кантри |
3 | 1 | 1 | Все рыжие исполнители |
... | ... | ... | |
Sets
Set_id | Name_id | Value_id | ___________ |
---|---|---|---|
1 | 3 | 4 | Все записи Стинга |
2 | 2 | 5 | Все записи кантри |
3 | 1 | 1 | Все рыжие исполнители |
... | ... | ... | |
У себя для основных свойств мы используем структуру 1. Она названа “слабой” несправедливо. У нее есть сильные стороны: её трудно испортить (изменить). А нам это и надо.
Это только кажется. Прочитайте, например, материал "Освобождение узников оператора IF", начиная прямо с первой истории.
Мы знаем крупного европейского разработчика, который тоже использует структуру 1. При этом в каждой таблице сущностей оставляют несколько пустых полей на всякий случай. Если что-то потом понадобится докрутить. Такая структура (1) понятна заказчику, под нее он соглашается выделять бюджет и/или ждать, когда перепишут программу под его новые требования. Интересно, изменится ли такая ситуация?
Нередко подобное происходит потому, что аутсорсинговая компания сдаёт в аренду "людей х часы". Поэтому мотивация её понятна. А при сычёвских методах разводить опытного Заказчика на лишнюю работу уже не получится.
Более того, описываемая метода и создавалась от имени Заказчиков, которым надоело отдавать большие доли бюджетов за фиксы и просрочки, и надоело нанимать лишних людей. Посмотрите в конце статьи требования к идеальной программе:
1) Идеально - это когда программу сможет в отсутствие Автора не только сопровождать, но и развивать программист с меньшей квалификацией, чем Автор программы.
2) Идеально - когда добавление новой функциональности в программу не потребует внесения изменений и/или добавлений в программный код (более того, в совершенно идеальной программе увеличение функциональности приводит к сокращению кода); причём всё это без ущерба для любых параметров (например, быстродействия и т.д.).
Ведь понятно, что какими бы там не были новые требования, их реализация в описанной здесь модели занимает на порядки меньше времени, чем в структуре 1.
Также понятно, что когда Заказчик платит и не спорит, то разработчик становится «крупным и европейским», но это как русский ремонт квартиры - прораб надувает смету и сроки, а Клиент страдает и беднеет.
Чтобы такого в мире было поменьше и написана наша статья.
Большое Спасибо за Ваши вопросы,
Уважаемый Всеволод!
Также мы используем структуру EAV обычную для второстепенных свойств. По ходу дела туда накидывают что угодно. Теперь там бардак; таблица разрослась и стала тормозить. Но с этим справились кешированием. Тормозила, ибо вся из стрингов.
Наверное, не надо так делать. То есть, не надо делить свойства на «первостепенные» и «второстепенные» - это не только потребует создания кода, который будет их «различать и взвешивать», но и потребует бизнес-процесса по созданию поддержи всей этой функциональности. А есть риск, что за этим просто перестанут следить.
Тем более, не надо делать одну базу для «первостепенных» свойств, другую - для «второстепенных», даже ещё столь разных архитектур, что поддержка каждой влечёт за собой необходимость создания и поддержки разного кода, увеличение документации и необходимость слежения за ней (документацию тоже надо обновлять). Делайте 1 базу как здесь :).
Сущность описывается не целочисленным id, а строкой “id+тип”. От типа не отвязались, чтобы определять чьи же это свойства. Вдобавок там остается мусор из свойств удаленных объектов. Чистить пока некому.
Этим опять-таки должно заниматься ядро, а не человек, что оно - в нашем случае - и делает (это видно даже и в статье - в разделе «про код» посмотрите комментарии к реализациям функций Add и Delete)
Удаляя набор, ядро "подчищает" базу, удаляя из неё соответствующие значения Values, если они не встречались ни в каком другом наборе.
Поскольку в нашем случае Values вынесены в отдельную таблицу, для определения этого, достаточно просмотреть столбец из численных идентификаторов в таблице Sets, а не проводить сравнение строк (каковое сравнение строк и тормозит разные другие реализации EAV).
Что касается EAV по Сычёвски, то, видимо, понадобится генерация уникального id для таблицы наборов, в которой столбец Set_id будет внешним ключом. Не знаю, понадобится ли еще один столбец с уникальным id для таблицы наборов? По умолчанию, таковой мы везде вставляем. В случае шардинга таблицы Values, соответствующий столбец в таблице наборов перестанет быть внешним ключом. Так?
Дополнительный столбец с уникальными Id может потребоваться лишь в редких случаях шардинга таблицы Sets. Нужен он будет только для определения ядром, в какой из таблиц Sets находится необходимая запись. На уровне работы с данными это поле не будет использоваться вовсе, поэтому его появление никак не скажется на быстродействии.
Шардинг таблицы Sets назван редким случаем потому, что эта таблица состоит из полей, в которых нет ничего, кроме Id. И поиск по этой таблице - самый быстрый из возможных. Только, когда проект становится действительно громадным, эту таблицу можно порезать.
В случае с шардингом таблицы Values (когда она очень длинная и строковая) соответствующее поле в таблице Sets действительно перестает быть внешним ключом, но все функции слежения за целостностью данных в этом случае мы поручаем ядру. Так, например, эту проверку делают функции Add и Delete.
Спасибо,
Здравствуйте, Коллеги! Спасибо за пояснения!
Все же, вопросы остаются:
1. Зачем, например, в функции Add обновлять значение, которое и так есть?
add (null, $login, "admin3") - сама создаст и выполнит:
SELECT VALUE_ID FROM VALUES_STRING WHERE VAL = 'admin3';
UPDATE OR INSERT INTO VALUES_STRING (VALUE_ID, VAL) VALUES (1, 'admin3') matching (VALUE_ID);
INSERT INTO SETS (SET_ID, NAME_ID, VALUE_ID) VALUES (1, 0, 1);
Я бы так сделал:
-----------------
DECLARE @pID INT;
begin tran
SELECT @pID = VALUE_ID FROM VALUES_STRING WHERE VAL = 'admin3';
if @@rowcount = 0
begin
INSERT INTO VALUES_STRING (VAL) VALUES ('admin3');
SELECT @pID = @@IDENTITY;
end
commit tran;
INSERT INTO SETS (SET_ID, NAME_ID, VALUE_ID) VALUES (1, 0, @pID);
2. Непонятно как функция Add чистит базу. Вы пишете в разделе про быстродействие:
... функция add проверяет (поисковая операция), есть ли в таблице Values значение Value. Если нет, то создает (операция добавления).
... Также проводится проверка, есть ли другие наборы с таким же свойством (с тем же самым Name и тем же самым Value_id) - это быстрая поисковая операция.
Если других наборов нет, удаляет значение Value из таблицы Values.
Но мы же это значение только что добавили?! А если оно было, то оно и нужно.
Другое дело, если функция add($SetId, $Name, $Value) не нашла ни одного набора и собирается сообщить об ошибке добавления. Тогда можно заодно проверить, а другие наборы есть с такими $Name и $Value и, если нет, значение удалить. Но как мы попадем на эту ветку алгоритма? (см. 2а)
2а. Будет ли идентификатор набора нужен за пределами базы? Я предполагаю, что программа-клиент будет запрашивать что вроде этого:
В набор, у которого свойство guid = '4cfc0d4f-77c5-42dd-9dc8-7f5146f34809', добавить свойство filename со значением 'myPhoto.jpg'. А если набора с таким свойством (guid) нет, ничего не делать. И значения свойства filename не проверяются на актуальность.
Здравствуйте, Александр! Примеры кода в Вашей статье - это просто иллюстрация или код, который можно читать и понимать? Извините, я его не понимаю.
2) Add ( $Set_id, $Name, $Value) // Добавляет набору (Set) с идентификатором id свойство Name со значением Value или обновляет его, если такое свойство уже есть.
add (null, $password,"123456")- сама создаст и выполнит:
Почему первый параметр null, а не конкретное значение? Можно было бы понимать, что нужно обновить свойства Name у ВСЕХ наборов, но тогда как различить этот случай с добавлением 1, когда просто добавляется новый набор?
Далее (допустим была опечатка, и вместо null стоит на самом деле 1):
1) SELECT VALUE_ID FROM VALUES_STRING WHERE VAL = '777';
2) UPDATE OR INSERT INTO VALUES_STRING (VALUE_ID, VAL) VALUES (12, '777') matching (VALUE_ID);
3) SELECT VALUE_ID FROM SETS WHERE SET_ID = 1 AND NAME_ID = 1;
4) SELECT SET_ID FROM SETS WHERE VALUE_ID = 2;
5) UPDATE OR INSERT INTO SETS (SET_ID, NAME_ID, VALUE_ID) VALUES (1, 1, 12) matching (SET_ID, NAME_ID);
Для чего нужны строки 3) и 4) ?
По строке 5): Если указанный набор не найден и SET_ID внешний ключ от автоинкрементного первичного ключа (есть таблица из одного столбца, которая раздает новые идентификаторы), то INSERT с заданным id не пройдет.
Вариант с зачисткой:
Возможна ситуация:
1)SELECT VALUE_ID FROM VALUES_STRING WHERE VAL = 'admin6';
2)UPDATE OR INSERT INTO VALUES_STRING (VALUE_ID, VAL) VALUES (11, 'admin6') matching (VALUE_ID);
3)SELECT VALUE_ID FROM SETS WHERE SET_ID = 1 AND NAME_ID = 0;
4)SELECT SET_ID FROM SETS WHERE VALUE_ID = 1;
5)DELETE FROM VALUES_STRING WHERE VALUE_ID = 1;
6)UPDATE OR INSERT INTO SETS (SET_ID, NAME_ID, VALUE_ID) VALUES (1, 0, 11) matching (SET_ID, NAME_ID);
3) берем предыдущее значение свойства заданного набора
4)берем множество наборов с этим же значением
где проверка, что можно удалять?
5) удаляем устаревшее значение
Добрый день.
add (null, $password,"123456")- сама создаст и выполнит:
Почему первый параметр null, а не конкретное значение?
Здесь создается новый набор, которому добавляется свойство $password со значением "123456".
Когда мы передаем функции Add null вместо конкретного SetId , она создает новый.
Об этом сказано в статье:
Так что, никакой опечатки.
Для чего нужны строки 3) и 4) ?
Эти строки получают информацию для проверки: можно или нельзя значение удалить.
Sql запросы генерируются функциями ядра. Их результаты обрабатываются функциями на языке php, и, в зависимости от них, принимается решение об удалении записи из таблицы Values.
Где проверка, что можно удалять?
Сама проверка (функция ядра) выглядит так:
"(...) С учётом описанной выше идеализации кода, реализуем "общение" программы с базой данных через универсальный интерфейс ("ядро") - так, чтобы программа (как и положено по всем канонам) не знала ничего об устройстве базы данных, а базе данных было всё равно, какая там программа. Здесь назовём лишь ключевые "ядерные функции" (статья есть статья), а если будут многочисленные запросы на публикацию ядра целиком, то опубликуем отдельно".
Но если кратко пояснить, здесь происходит:
get_brick – генерирует запрос на получение записи, в данном случае запрос:
SELECT SET_ID FROM SETS WHERE VALUE_ID = 1;Находит, наборы, которые содержат ссылку на запись в таблице Values с идентификатором 1.
Если таковых 1 или меньше запускаем delete_brick который создаст запрос:
DELETE FROM VALUES_STRING WHERE VALUE_ID = 1;который удалит более ненужное значение.
Это и является ответом на Ваш следующий вопрос.
C Уважением,
Здравствуйте, Александр!
Понятно Ваше стремление максимально упростить базу. Однако, у меня складывается впечатление, что сложность не исчезла, а перешла в ядро и спряталась там.
Поясню. Если кто-то по каким-либо причинам поменяет тип у свойства, т.е. перепишет диспетчер или потеряет его (это же обычный текстовый файл), то вся база "нажитая непосильным трудом" превратится в хаос. Также и коды имен свойств придется охранять от порчи как зеницу ока, а они в обычном текстовом файле находятся. Для сопровождения это совсем не подарок, особенно если сопровождением начинающий программист занимается в удаленном офисе.
Напрашивается простое решение: положить коды имен и диспетчер в ту же базу в отдельные таблицы. Но это шаг назад к усложнению базы.
2-й вопрос: почему Вы не используете хранимые процедуры? Они избавят от генерации sql кода, его передачи и компиляции. А ядро будет просто выбирать нужную процедуру и отправлять параметры. Наверное, не используете, потому что диспетчер в ядре и поэтому нужно запросы ядром создавать. Если же диспетчер будет в базе, то это даст и надежность хранения и возможность использовать хранимые процедуры.
Я думаю, Вы хотите иметь возможность переписывать диспетчер в ядре. Но если база уже наполнена, этого сделать нельзя по смыслу. Диспетчер и база станут нераздельны. И новому диспетчеру понадобится новая база. Так может им и быть вместе?
С уважением,
Уважаемый Всеволод!
Если кто-то по каким-либо причинам поменяет тип у свойства, т.е. перепишет диспетчер или потеряет его (это же обычный текстовый файл), то вся база "нажитая непосильным трудом" превратится в хаос.
Да, нет. Список имен (не типов) имеет достаточно простую структуру, и работа с ним вполне автоматизирована (в т.ч., "защита от дурака есть", да и бэкапится). Похожую систему использует операционная система "Андроид". Там каждое приложение имеет такой справочник, который называется R.java. В нем хранится соответствие читаемых для человека идентификаторов ресурсов (текстов, изображений и т.д.) и чисел, которые использует сама ОС для работы с приложением. Помех работе системы наличие такого файла не создает. Хранение и сопровождение его требует гораздо меньших затрат нежели хранение любого другого скрипта системы.
Я думаю, Вы хотите иметь возможность переписывать диспетчер в ядре
Конечно. Ведь по мере развития любого проекта добавляется новая функциональность, новые данные и т.д. И, по условию задачи, мы не знаем какие новые потребности возникнут у Клиента в будущем (да, и он не знает). Поэтому должна быть возможность при минимуме усилий добавить в базу новую структуру, не добавляя её (если выражаться по тризовски). В нашем случае, это будет лишь тупая таблица Values. Добавить её несложно.
Если нам потребуется добавить в базу особый тип данных, не меняя структуру хранения уже внесенных, то достаточно добавить соответствующую таблицу Values, и записать её название в список констант описанный в ядре.
При добавлении нового типа Values, нужно внести всего одну строку в Диспетчер и всего одно слово в список констант. Никакого транспорта при этом не требуется. Но самое главное, что, как бы мы не меняли структуру хранения значений, ядру не потребуется никаких новых функций.
Если же у Вас вдруг поменялась сама схема шардинга таблиц Values, то при такой простой структуре написать транспорт данных для полностью нового Диспетчера не составляет труда. И процесс транспорта займёт гораздо меньшее время, чем в случае с "обычной структурой", даже вполне себе нормализованной.
Почему Вы не используете хранимые процедуры?
Тогда при работе с разными СУБД нам придется учитывать их специфику и "подгонять" процедуры под них. У Клиентов могут работать: а) разные реляционные БД (SQL-сервер, Firebird, Sqlite, Oracle, PostgreSQL, MySQL и др.); б) NoSQL , файловые БД - тоже разные. Более того, вдруг проект у Клиента "переедет" на другую СУБД, а диалекты процедурных языков разных СУБД могут быть несовместимы. См., например.
Даже, если и насобирать заранее "заготовок" под каждую, что мы будем делать, если понадобится обновить Составитель Запросов или Диспетчер? А зачем нам это всё (при "сычёвской-то структуре", которой вообще всё равно какая там база). Если можно об этом не думать, то лучше и не думать.
С другой стороны, и сам проект (Owl's) тоже будет развиваться. Получается, если нужно будет сделать обновление у Клиента, то надо либо писать скрипты, либо связываться с Клиентом и объяснять его админу, что и как сделать. Скорее всего, никто ничего делать не будет.
Если же Составитель запросов и Диспетчер изолированы от базы, то они будут работать независимо от её типа. А обновление ядра будет заключаться в замене файла на новую версию.
«Еще момент: если уже есть 1000 имен свойств в текстовом файле и Вам говорят завести еще 10 новых, то нужно открыть в редакторе длинный список, придумать 10 новых уникальных имен (!!!) и аккуратно вписать, ничего не испортив. А потом еще в диспетчер добавить, который тоже будет иметь много строк. Чтобы исключить ошибки, понадобятся специальные сервисы для обслуживания длинных списков, которые уже есть в СУБД.»
Давайте сравним "обычную" структуру данных с рассмотренной в статье и посмотрим настолько ли это большая проблема если действовать “as Owls”.
А. Предположим, имеется "обычная" вполне нормализованная структура.
У нас уже есть 1000 различных свойств разбросанных по разным таблицам (!!!). Нам нужно добавить 10 новых.
1. Если повторение названий столбцов в разных таблицах не критично, то проблема с выбором имени стоит не так остро.
Если же регламент требует уникальности названий (что бывает достаточно часто), нам придется проверить все названия столбцов во всех таблицах (при тысяче свойств их возможно будет очень много), и убедиться, что новое имя не занято.
2. Далее надо определить в какую конкретно таблицу добавить новый столбец (при тысяче свойств их возможно будет очень много).
3. После чего изменить структуру таблицы, добавив новый столбец (а, возможно, новую таблицу).
4. При этом, будем помнить о том, что, возможно, потребуется добавить интерфейсные таблицы для работы с новым свойством.
и
5. Повторить 10 раз.
B. «As Owls»
У нас уже есть тысяча различных свойств которые описаны в одном подключаемом файле. Нам нужно добавить 10 новых.
1. Регламент всегда требует уникальности имён свойств. Автоматизировать проверку занято ли читаемое для человека имя свойства (например, само слово «email» в строке $email = 8) достаточно легко (если не хочется использовать сервис Ctrl+F который уже встроен в текстовый редактор).
Уникальность чисел, соответствующих читаемому для человека названию (например, само число 8 в строке $email = 8), можно обеспечить простой нумерацией новых свойств подряд. То есть, новому свойству всегда ставится в соответствие число на единицу больше последнего.
2. Повторить 10 раз.
Как видно, сложностей, при решении такой задачи не возникает. Более того, в общем случае, для нормализованной базы данных, её решение будет намного сложнее (уже и при 60 свойствах это может быть крайне неприятно).
Если же действовать «по Сычёвски», то, и при 60-ти, и при 1000 свойств, задача совершенно не сложная. И, как уже было описано ранее, описанный процесс можно полностью автоматизировать.
Спасибо,
Здравствуйте, Александр!
Если нам потребуется добавить в базу особый тип данных
Да. Например, DateTime. Его можно хранить и как int, но у этого типа есть особенность: мало повторов, много уникальных значений; каждую секунду, а то и чаще, новое значение появляется. Поэтому есть соблазн DateTime хранить сразу в таблице наборов в столбце VALUE_ID как значение int, а не ссылку. Но это нарушает единство модели. Там ссылка, а здесь оно выступает как значение. И еще, для типа DateTime, как правило, существует много сервисов, которые хочется иметь, а конвертировать туда-сюда не хочется. Вы как с датами обходитесь?
Тогда (в случае с хранимыми процедурами) при работе с разными СУБД нам придется учитывать их специфику и "подгонять" процедуры под них
Да. Но и сам генератор sql-запросов тоже должен учитывать специфику разных СУБД. В MS SQL, например, нет конструкции
UPDATE OR INSERT INTO... , а реализация с помощью обычных UPDATE и INSERT вырастает в длинную строку запросов.
С уважением,
Здравствуйте, Александр!
Я посчитал количество запросов. Оно ужасно. Или это так кажется?
Поясню.
Допустим в таблице (по структуре 1) лежат документы, имеющие 10 свойств, и требуется получить все документы, где свойство act = 777.
Обойдемся ОДНИМ запросом:
SELECT * FROM docs WHERE act = 777
И получим, допустим, 20 документов, которые и отобразим.
Теперь рассмотрим Сычевскую структуру.
set_ids = get(null, act, 777);//получим 20 чисел
для каждого числа сделаем:
get(set_id, null, null);//получим 10 пар чисел, это имена и id свойств
и еще нужно взять сами значения свойств:
SELECT VAL FROM нужная_таблица WHERE VALUE_ID = n;
Итого запросов: 10 * 20 + 20 + 1 = 221 селект. Вместо одного.
Вопрос: Вы как-то боретесь с ростом запросов? Начальство, да и клиент, если ему рассказать, не пропустит такую структуру в разработку.
Думают так: пусть лучше программист один раз перепишет (для того его и держим), и прорамма будет потом нормально работать. И не надо будет переживать,
что сервер не выдержит, и надо будет объяснять клиенту, что нужно покупать новый.
Еще раз напишу, что на мой взгляд сложность никуда не делась. Она приобрела другую форму.
С уважением,
Здравствуйте, Александр!
Спасибо за пояснения. В последнем запросе, наверное, имелось ввиду:
SELECT VALUE_ID, VAL FROM VALUES_STRING WHERE VALUE_ID IN (2,3,4,5,44,51)
иначе не разберемся со значениями; к чему они относятся. И таких запросов будет столько, сколько понадобится таблиц со значениями:
SELECT VALUE_ID, VAL FROM VALUES_DATE_TIME WHERE VALUE_ID IN (2002,2003,2004,2005,2044,2051) и т.д.
Еще момент:
Функцию Add ( $Set_id, $Name, $Value) можно понимать двояко:
1. Добавляет набору (Set) с идентификатором id свойство Name со значением Value или обновляет его, если такое свойство уже есть. Как указано в статье.
2. Даже если такое свойство уже есть, добавим второй экземпляр (см. обсуждение выше):
Set_id Name_id Value_id
2 2 2 (Весь рок-н-ролл)
2 2 5 (Все записи кантри)
где константы ("Names"): $Музыкальные жанры = 2
В "печально знаменитой" структуре 1 в этой ситуации было бы так:
Музыкальные жанры
2, 5
или
Музыкальные жанры
рок-н-ролл, кантри
Не знаю, нужно ли исключать полные дубли (2 2 2) и (2 2 2)? Возможно, и они в ряде ситуаций понадобятся. Можно же записать:
Музыкальные жанры
рок-н-ролл, рок-н-ролл
Я думаю, в связи с вышесказанным будет актуальной функция:
Update($Set_id, $Name, $Value), которая обновляет свойство или добавляет, если его нет.
А функция Add ($Set_id, $Name, $Value) - всегда добавляет, даже если уже есть. Два будет; значит, так надо.
Про имя и т.п. написал в почту.
С уважением,
Sets
Set_id | Name_id | Value_id | ___________ |
---|---|---|---|
1 | 4 | 4 | Все записи Стинга |
2 | 2 | 5 | Все записи кантри |
3 | 1 | 1 | Все рыжие исполнители |
4 | 3 | 7 | Кантри, рок-н-ролл |
... | ... | ... | |
Values
Value_Id | Values |
---|---|
1 | Рыжие |
2 | Рок-н-ролл |
3 | Блондины |
4 | Стинг |
5 | Кантри |
6 | Брюнеты |
7 | Кантри, рок-н-ролл |
8 | Кантри, рок-н-ролл, блюз |
... и т.д. ... |
Здравствуйте, Александр!
Похоже, наметились такие тенденции:
1. Если значений свойства м.б. несколько, перечислить их через запятую в одной строке. Это позволит не изменять структуру таблиц и функции. Но это хорошо, если значение нужно только для вывода на экран. Мы так делали, пользователь смотрел на данные, а потом захотел их фильтровать. Когда понадобится анализ, например, фильтрация, без id не обойдемся.
Пример1: 'рок, рок Live' Нужно вывести только рок. Поиск по подстроке не сработает.
Пример2: есть значения 'Классическая', 'Опера'. При фильтрации по 'Классическая' значение 'Опера' тоже должно быть.
Есть таблица о вхождении значения 'Опера' в 'Классическая', а там id. Значит нужно парсить строку, получать id, возносить молитвы, чтобы не оказалось одинаковых строк с разными id (за 2 года могли навводить что угодно), объяснять руководству почему не передали id сразу, ведь это очевидно (теперь очевидно; раньше нет).
Хорошо бы иметь средство простого (мгновенного) перехода от перечисления через запятую в виде строки к массиву id на
всех уровнях программы. Сейчас с этим борются нормализацией, чтобы везде были только id.
2. В долгоживущих программах у функций отрастает последний параметр: bool trigger. В одном случае так, в другом иначе. Не добавлять ли его сразу при разработке? wsdl-сервис генерит 2 варианта одной функции: ту, которую программист написал, и вторую такую же + хвост (object userParam), где object - ссылка на что угодно. Если в хвост передать массив object[], то покроем все возможные в будущем изменения. Но программист будет вынужден написать разбор 'хвоста'. Передан ли массив? Какова его длина? Соответствуют ли элементы массива тому, что ожидалось? Если нет, сообщить об ошибке (по каждому варианту). Код станет длинным, разветвится за счет сервиса обслуживания самой функции, а не полезной работы.
Что лучше: иметь одну, но сложную функцию или несколько, но простых?
Еще момент про количество вызовов: 1 или 4. Если вызовы асинхронны, т.е. ответ приходит как событие, то придется вместо одного обработчика события написать 4 и соотнести их между собой; это же одна операция. А если цикл или два цикла
Сложность программы сильно возрастет. Кто ловил неконтролируемо размножившиеся обработчики и перепутавшиеся события, тот поймет. Для сложных случаев нам пришлось написать свою собственную систему генерации событий, чтобы полностью её контролировать в дополнение к стандартной. Для нас, если в одной операции более двух обработчиков событий, это уже сигнал тревоги: потенциальные глюки.
С уважением,
1. Если значений свойства м.б. несколько, перечислить их через запятую в одной строке (...) Сейчас с этим борются нормализацией, чтобы везде были только id.
В этом случае можно хранить подобное поле например в виде свойства $genre_list со значением «;4;5;» где 4 и 5 идентификаторы наборов, каждый из которых представляет из себя отдельный жанр.
set_id |
name_id |
value_id |
______________ |
4 |
9 |
32 |
Rock |
5 |
9 |
36 |
Folk |
7 |
11 |
54 |
;4;5; |
value_id |
val |
32 |
Rock |
36 |
Folk |
54 |
;4;5; |
for//(цикл по всем диапазонам)
{
core.GetDocsAsync(from, to, between_or_not_between); //собственно вызов
}
//--
void core_GetDocsCompleted(object sender, GetDocsCompletedEventArgs e)
{
//--
Show(e.Result,table);
//вe.Resultпришли наши данные, которые и покажем; данные появляются порциями по мере вызова обработчика от каждого диапазона
//--
}
Если процесс получения данных разбить на несколько этапов, то после прихода каждого обработчика придется делать следующий вызов, на который навешан свой очередной обработчик, а в конце концов все собрать в таблицу. Код сильно усложнится.SNV[]GetNameValByProperty(intSet_id,int Name,object Value);
Где:
public class SNV
{
public int Set_id;
public int Name;
public object Value;
Так GetNameValByProperty(null, act, 777) вернет все наборы c именами и значениями,у которых свойство act=777.
В статье речь шла только о проверке равенства параметров. Что делать с остальными операциями, особенно between и not between,где два операнда?
Можно было бы в методе GetNameValByProperty(int Set_id,int Name, object Value) первым параметром передавать код операции =,!=,>,<,>=,<=
Но у between два операнда. (Разбить на 2 части не получится. Все равно придется передавать второй параметр: and или or). Опять хвост вырос:
GetNameValByProperty(between, date, ’2015-01-01’ ’2016-01-01’);
//все наборы c именами и значениями, у которых свойство date между ‘2015-01-01’и‘2016-01-01’
}
С уважением,Здравствуйте, Коллеги!
Не сталкивались ли Вы с ситуацией, когда ядро, реализующее SQL-запросы к Сычевской базе данных, и нормально работающее в обычных проектах, приводит к дедлокам (deadlock) в проектах нагруженных? Иначе говоря, прибежало множество пользователей, стало что-то активно делать (что именно, сказать затруднительно, т.к. запросы единообразны), и база (MS SQL) стала отклонять часть запросов с ошибкой: "Transaction (Process ID 60) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim."
У нас это впервые; Структуры 1 с этим справляются.
Объединяете ли Вы серию простых SQL-запросов в одну транзакцию? Например, так:
begin tran
... наши запросы, реализующие, например, обновление свойства...
end commit tran
Мы объединяем.
P.S. Некоторое время назад с помощью оптимизации ядра мы уже полностью избавлялись от дедлоков. Затем нагрузка возросла, и дедлоки снова появились, хоть и в меньшем количестве.
С уважением,
Здравствуйте, Александр!
Мы используем Сычевскую структуру в экспериментальном режиме для формирования отчетов о времени работы над документами. Документов 2 типа: "статья" и "часть статьи" или "блок". Монопольный доступ существует только на блок. Одну статью могут править несколько пользователей: корректор - текст, художник - картинку, креативщик - заголовок. Мультидоступ к статье - одна из обязательных фич. В реальной работе мультидоступ практикуют редко. Однако, не удалось получить из лога очевидных свидетельств о конкретных причинах дедлока. Может быть, данные пользователи его практикуют часто. А возможны и другие причины.
Когда документ открывается, в базу пишется набор со свойствами этого документа, а также время открытия и время закрытия; последнее пока не известно и берется равным 01.01.1900г. Эта дата является маркером случая, когда документ не был закрыт корректно, а повис/слетел/что-то еще.
Реальное время закрытия добавляется в набор позже, при корректном закрытии документа. По нашему опыту, дедлоки случаются чаще всего при обработке дат. Видимо, даты обрабатываются медленно.
Блок может занимать пользователь или программная функция. В последнем случае время обработки документа (блока) менее секунды. Такие наборы не интересны и удаляются при закрытии документа. Теоретически удаление набора может начаться до того как предыдущая транзакция добавления данного набора завершилась. Это вторая возможная причина. А есть записи о дедлоках, которые под упомянутые две причины не подходят, но там тоже участвуют даты.
Дата пишется с точностью до миллисекунды. Каждое значение получается уникальным и таблица значений дат (values_dt) быстро растет. (Note: Сходная проблема, скорее всего, появится для чисел с плавающей точкой. Там еще операция точного равенства "поплывет". Понадобится заменить "равно" на "равно приблизительно").
Записей в таблице наборов: 1 767 635.
Количество значений: целых чисел - 22 635, строк - 6 024, дат - 328 542
Пример отклоненного запроса по добавлению/изменению значения в наборе:
DECLARE @pID INT;
SELECT @pID = VALUE_ID FROM values_dt WHERE VAL = @pVal; -- есть ли такое значение
if @@rowcount = 0
begin
INSERT INTO val_id DEFAULT VALUES; -- сгенерить id значения
SELECT @pID = @@IDENTITY;
INSERT INTO values_dt (Value_id, VAL) VALUES (@pID, @pVal); -- новая дата
end
DECLARE @pPrevVal INT;
DECLARE @pCnt INT;
begin tran -- начало танзакции
SELECT @pPrevVal = VALUE_ID FROM sets WHERE set_id = @pSet and Name_id = @pName;
SELECT @pCnt = COUNT(Set_id) FROM sets WHERE Value_id = @pPrevVal;
if @pCnt = 1
begin DELETE FROM values_dt WHERE VALUE_ID = @pPrevVal;
end
UPDATE sets SET VALUE_ID = @pID WHERE Set_id = @pSet and Name_id = @pName;
if @@rowcount = 0
begin INSERT INTO sets (SET_ID, NAME_ID, VALUE_ID) VALUES (@pSet, @pName, @pID);
end
commit tran -- конец транзакции
Здравствуйте, Александр!
Почему мы ввели транзакции? Воспользуюсь Вашим примером:
delete (1,$login)- сама создаст и выполнит:
SELECT NAME_ID,VALUE_ID FROM SETS WHERE SET_ID = 1 AND NAME_ID = 0;
SELECT SET_ID FROM SETS WHERE VALUE_ID = 11;
DELETE FROM VALUES_STRING WHERE VALUE_ID = 11; (здесь функция Delete "подчищает" базу) Если после того как определено, что надо подчистить, но до того как реально подчищено другой пользователь в другом наборе добавил ссылку на тоже значение, а потом сработает зачистка, то будет повисшая ссылка (VALUE_ID без VAL). То же в добавлении.
DELETE FROM SETS WHERE SET_ID = 1 AND NAME_ID = 0;
Поэтому зачистку и удаление/добавление мы объединили в транзакцию.
Вторая реализация зачистки без явной транзакции одним запросом (в нем будет скрытая транзакция):
DELETE FROM values_dt WHERE Value_id IN
(SELECT Value_id FROM sets WHERE Value_id IN
(SELECT Value_id FROM sets WHERE SET_ID = @pSet_id AND NAME_ID = @pName)
GROUP BY Value_id HAVING COUNT(Set_id)<2); -- найти множество Value_id для зачистки и зачистить
DELETE FROM sets WHERE SET_ID = @pSet_id AND NAME_ID = @pName; -- удалить свойство набора
Запрос сложный, поэтому есть тенденция оформить его как хранимую процедуру (чтобы не передавать длинную строку в базу и не компилировать каждый раз запрос), осознавая недостатки хранимой процедуры.
Точность дат нужна посекундная. Исходя из реальных данных посекундная точность уменьшит количество значений примерно на 15%. (Неиспользуемые даты удаляются.)
Если "короткие" наборы (документ был открыт менее 3-х секунд) не записывать сразу в базу, то придется его записать в базу_2 (специально созданный для этого кеш) пока не выяснилось, какой он. Кеша к Сычевской структуре пока не сделали. А он, наверное, понадобится не только для данной задачи?
С уважением,
Уважаемый, Александр!
мы используем функции begin_transaction() и commit_transaction($yes_or_no)
Значит, Вы сами, на ядре, ведете журнал транзакций и организуете откат? Используете ли Вы "свои" индексы?
В нашем ядре каждый запрос исполняется независимо от остальных - в отдельной транзакции
Зачем тогда булевское поле locked в таблице Values? Да и как оно поможет? Допустим, функция Add1 начала работу и заблокировала значение. В это время вторая функция Add2 видит, что значение существует, хоть и заблокировано и добавляет на него ссылку. После этого Add1 удаляет значение и возникает повисшая ссылка.
Если же, Add2 не допускается к чтению заблокированного значения, то она может добавить то же значение с новым id. Если Add1 не понадобится удалять значение, то появится дубль.
Поле locked добавляет вероятность появления ложно заблокированных значений. Функция заблокировала и, не успев освободить, сбойнула. Значит нужна еще и транзакция на все операции от блокировки до освобождения, чтобы откатить в случае чего. У нас есть поле locked в других таблицах по Структуре1, но без транзакций (они замедляют работу). Админу приходится специальным инструментом регулярно, хоть и не часто, разблокировать ложные блокировки.
С уважением,
Здравствуйте, Алексей!
Подскажите, пожалуйста, удалось ли вам решить проблему дедлоками?
Дедлоки порешали. Пока. По нашему опыту, решение проблемы с производительностью приводит к тому, что программу начинают активно использовать (или приходит новый клиент, которому все надо быстрее-больше-за_те_же_деньги) и через некоторое время снова что-нибудь отстает.
С уважением,
Здравствуйте, Александр!
Кроме того, несмотря на то, что Клиентская часть "не знает" о структуре и т.п., количество программного кода, которое будет на стороне Клиента (если будет "классический подход" ) также будет расти лавинообразно - могу пояснить почему.
Поясните, пожалуйста, если есть время.
Более того, существует уже и "ядро-2", которое сразу создаёт действующие пользовательские интерфейсы. Ну, то есть, получает на вход "anything from anywhere" и создаёт Вам, например, сразу специфическое действующее мобильное приложение на Java с пользовательским интерфейсом...
Мы у себя реализовали следующее: ядро возвращает данные на клиент не в виде привычных "программистских" структур данных (массивы, переменные), а уже "упакованными" в интерфейс пользователя (u-iface). Для этого u-iface представляется в виде простенького языка разметки (у нас своя разметка, но поскольку требования к u-iface постоянно растут, то стало ясно, что сразу надо было брать html). Если появится новый клиент, например, мобильный, для него потребуется написать только парсер разметки u-iface, чтобы он начал отображать формы с данными.
У Вас по другому?
P.S. Начальство, однако, по-прежнему предпочитает hand-made формы, которые дизайнер отрисовывал по-пиксельно.
P.P.S. Программисты уже пишут не программы, а программы, которые пишут программы. Не пора ли выбирать новую профессию? =))
Здравствуйте!
вообще не нужно html-разметку передавать, достаточно мета-структуру данных и способ построения формы
С ростом требований способ построения формы будет все более походить на html - разметку. Или иной способ построения любых форм. Что Вы подразумеваете под движком? Парсер разметки на клиенте остался. Генерируется форма автоматически на сервере для того, чтобы программист не занимался рисованием [формы] в рабочее время =) А затем наполнением "рисунка" данными (binding). А затем перерисовкой, если что-то поменялось.
С уважением,
Уважаемый Евгений!
Я про IN и говорю. Что специфика проекта же не заканчивается на выборке только одних данных, и что этих IN-ов может быть сотни, которые и увязываются между собою в каждом конкретном проекте (специфика). И только потом уже уровень приложения основывается на этом промежуточном уровне бизнес-логики.
Число IN-ов, на самом деле, не зависит от специфики. Давайте рассмотрим, в каких случаях в принципе могут возникнуть "сотни" IN-ов.
Ситуация первая: Нам нужно выбрать сотню свойств у объекта. Эту ситуацию мы уже рассмотрели выше.
Ситуация вторая: Нам нужно выбрать свойства у сотни объектов. Напомним что функция Get принимает на вход массивы из идентификаторов, имен и свойств, то есть она умеет работать с множеством объектов сразу.
А значит, чтобы выбрать данные для сотни объектов нам не обязательно выполнять сотню Get-ов. Нам нужно выполнить ОДИН Get для сотни объектов.
Мы просто положим их в массив $ids и вызовем, например, Get ($ids, $names, null);
Множество идентификаторов здесь свернутся в один IN таким же образом, как сворачиваются $names.
Важно помнить, что, поскольку в нашей базе все объекты хранятся в одинаковом формате, мы можем в массив $ids передать идентификаторы сразу нескольких разных по типу объектов. И запрос Get ($ids,null,null); все равно отработает.
На остальные вопросы - в следующем сообщении.
Cпасибо,
P.S. Письмо получили - в понедельник постучимся в скайп.
Здравствуйте, Александр!
"Мы сейчас ведём одновременно сразу 8 разных проектов на базе "EAV as Owl's" - так что, сквозному примеру есть откуда взяться"
Имеется ли в виду, что 8 разных проектов используют одну таблицу SETS? Как Вы считаете, целесообразно ли проектировать ситуацию, когда одно ядро будет иметь несколько таблиц SETS? С одной стороны, можно все хранить в одной таблице и при необходимости ее шардить. С другой, клиент может потребовать на всякий случай отделить его данные по одному проекту от данных по другим проектам.
Применялась ли структура "EAV as Owl`s" для хранения бинарных массивов? Есть задача оперировать большим количеством превьюшек. Размеры: от нескольких Кб до 1 Гб. Традиционно они хранились как файлы на диске. Появилось желание засунуть их в базу для упрощения способа доступа к ним. Хотя клиент скорее всего возмутится исчезновением привычных файлов, которые можно открыть "вручную".
С уважением,
Добрый день, Всеволод.
Имеется ли в виду, что 8 разных проектов используют одну таблицу SETS?
Нет, одно ядро обслуживает 8 разных проектов (в принципе, сколько угодно может обслуживать).
Несколько проектов, конечно, вполне могут использовать одну таблицу SETS, но не думаю, что это целесообразно. И Клиенты это не одобрят, и технически все эти разные сущности с нескольких разных проектов будут перебираться каждый раз когда Вы будете проводить поиск для одного конкретного проекта.
Более того, поскольку, как сказано выше, ядро может обслуживать сколько угодно баз (и проектов) одновременно, можно разделить данные по одному большому проекту на несколько разных баз.
Как Вы считаете, целесообразно ли проектировать ситуацию, когда одно ядро будет иметь несколько таблиц SETS?
Да, целесообразно. И, если Вы выберите правильное основание для такого шардинга, то вы получите значительный выигрыш в быстродействии.
Применялась ли структура "EAV as Owl′s" для хранения бинарных массивов? Есть задача оперировать большим количеством превьюшек. Размеры: от нескольких Кб до 1 Гб.
Применяется чаще для хранения ссылок на них. Исключение - тексты статей и документов. Что касается изображений, видео и прочей музыки :), то на подобные объекты в базе хранятся ссылки.
С Уважением,
"Мы у себя реализовали следующее: ядро возвращает данные на клиент не в виде привычных "программистских" структур данных (массивы, переменные), а уже "упакованными" в интерфейс пользователя (u-iface). Для этого u-iface представляется в виде простенького языка разметки (у нас своя разметка, но поскольку требования к u-iface постоянно растут, то стало ясно, что сразу надо было брать html). Если появится новый клиент, например, мобильный, для него потребуется написать только парсер разметки u-iface, чтобы он начал отображать формы с данными.
У Вас по другому?
У нас это разделено на 2 ядра. Ядро, которое вычитывает данные из базы, и ядро, которое формирует разметку. Оба независимы друг от друга, поэтому легко могут изменяться и подстраиваться под изменяющиеся требования.
P.S. Начальство, однако, по-прежнему, предпочитает hand-made формы, которые дизайнер отрисовывал по-пиксельно.
Не скажу Вам пока за веб-страницы, но андроидные мобильные приложения у нас программно (то есть, автоматически) сразу получаются лучше, чем дизайнер с программистом в 4-е руки cделают. И уж точно быстрее. Программа и лучше прорисует, и java-код приложения создаст сразу и компактнее, чем программист; и стандарт "Google Material Design" формализуется достаточно хорошо. И программу можно "учить".
P.P.S. Программисты уже пишут не программы, а программы, которые пишут программы. Не пора ли выбирать новую профессию? =))
Вечная профессия - это изобретатель. Остальные краткосрочны. :)
C Уважением,
Здравствуйте, Александр!
Большое спасибо за Ваши ответы! В нашей практике мы каждому клиенту по серверу ставим. Соответственно, своя копия софта и своя база на проект (на клиента).
если Вы выберите правильное основание для такого шардинга, то вы получите значительный выигрыш в быстродействии.
Получается, что может быть несколько оснований для шардинга. Традиционное: для хранения большого размера данных. В этом случае ядро может "само шардить"; внешним программам все равно, что внутри ядра, лишь бы было быстро и красиво.
И логическое: лог проекта хотим хранить в SETS_LOG, а документы проекта в SETS_DOC и т.п. И придется сообщать ядру о таком нашем желании; само оно не догадается. Появляется индекс для таблицы SETS, который надо передавать в функции Add, Del, Get. Надо так надо, но не нарушит ли это исходные принципы простоты?
В будущем, наверное, будут полезными стандарты использования структуры "EAV as Owl's" подобные тем, что когда-то выпускала Sun для java-разработчиков.
С уважением,
Уважаемый Всеволод!
... лог проекта хотим хранить в SETS_LOG, а документы проекта в SETS_DOC и т.п. ...
Обратите внимание на более общий аспект. Если Вы будете делать таблицы Sets "по именам" (Names) (если я верно понял Ваш пример), Вы можете отказаться от поля NAME_ID в этой таблице, что увеличит и быстродействие, и простоту "шардинга".
... И придется сообщать ядру о таком нашем желании; само оно не догадается...
Ну, последняя версия догадывается :) ...
В будущем, наверное, будут полезными стандарты использования структуры "EAV as Owl's"подобные тем, что когда-то выпускала Sun для java-разработчиков.
Скорее всего, да.
Спасибо,
Здравствуйте, Александр!
Таблицы SETS делаем по задачам. Допустим, какой-то модуль программы породил много своих данных (что-то вроде лога). Данные других модулей решили не смешивать с той таблицей. У разных модулей разная интенсивность использования, разное сопровождение, разная "программная жизнь", в зависимости от выполняемой пользовательской функции.
Имена в разных SETS могут пересекаться.
Я думаю, что для каждой SETS лучше иметь свои наборы таблиц значений VALUES_INT, VALUES_STRING и т.д. Потому что: а) значения не смешиваются в одной таблице "из общих соображений", б) чтобы проверить используется ли данное значение или его уже можно удалить, не надо проверять все SETS, а только "свою".
Однако, если сделать для значений единую индексацию, и завести поле с количеством ссылок на данное значение из различных SETS (добавили +1, удалили -1), то и проверять придется только этот счетчик. Тогда можно будет обойтись одним набором значений для всех SETS. Не знаю, какой вариант предпочтительней?
Если сделать единую индексацию для наборов во всех SETS, то в момент генерации нового Set_ID, можно записать индекс таблицы SETS, в которой набор пропишется. Так мы получим "адресную книгу" всех наборов по их индексам и можно не указывать при вызове функций Get, Del, Add (как Update) таблицу SETS. Но тут уже появляются дополнительные операции по запросу "адреса" набора, который в нашем случае и так известен.
С уважением,
Здравствуйте, Александр!
Спасибо за Ваш ответ. Очень интересное решение с уникальностью имен. Оно как бы давно напрашивалось. Например, мы приняли "не гласное соглашение", что имена от 0 до 1000 - строки, от 1001 до 2000 - даты и т.д. для удобства определения таблицы значений. Также уже "всплывало" свойство-маркер, по которому определялся тип сущности. Например, сущность документ_в_годовом_отчете имела свойство id_year, а документ_в_месячном_отчете - id_month. А по сути это один и тот же id документа, который раздвоился и стал маркером.
В будущем (не знаю, в ближайшем или нет) мы можем столкнуться с необходимостью переписать толстый клиент под HTML5. Если у Вас продается какой-либо робот для этой задачи, можно списаться по почте.
С уважением,
Здравствуйте, Александр!
Приходилось ли Вам блокировать свойство в наборе? Например, если нужно сделать следующее:
int Set_id = Get(null, id, 777);
if (Set_id > 0) Add(Set_id, name, "Иван");
else
Add(null, new int[]{id, name}, new object[]{777, "Иван"});
Между Get и Add кто-то может этот набор (если он существует) удалить. Для предотвращения этого можно добавить в таблицу SETS поле locked и сделать Get с блокировкой свойства id у существующего набора.
int Set_id = Lock(null, id, 777);
Функция Lock заблокирует свойство id и вернет Set_id или создаст новый набор с id=777, заблокирует и вернет Set_id.
Соответственно, Del проверяет locked.
2-й вариант на уровне кода не лучше ли? В набор добавляем свойство locked.
int Set_id = Get(null, id, 777);
if (Set_id > 0)
{
int result = Add(Set_id, locked, 1);
if (result >= 0) // успешная блокировка
{
Add(Set_id, name, "Иван");
Add(Set_id, locked, 0);
}
else // набор успели удалить
{
Set_id = Add(null, new int[]{id, locked}, new object[]{777, 1});
Add(Set_id, name, "Иван");
Add(Set_id, locked, 0);
}
}
С уважением,
Александр, ранее речь шла о блокировке значений. Последний вопрос о возможной блокировке наборов в таблицах SETS. На почту отослал.
Здравствуйте, коллеги!
У моего руководства возник вопрос об особенностях эксплуатации структуры EAV by Owl's (в нашей версии функций ядра). Версия работает в пилотном режиме для второстепенных задач, поскольку мои коллеги не в курсе ее устройства.
Случилось так, что пользователи ввели большое количество ценных строковых данных (большое - с их точки зрения, с технической - маленькое), которые легли в справочник строковых значений вперемежку между собой и между другими данными. Иначе говоря, возникло беспокойство у руководства, если с функциями ядра что-то случится, нельзя будет как раньше руками залезть в базу, найти плоскую таблицу и получить те самые данные.
Со своей стороны я пока предложил лишь конвертер, который выкачивает резервные копии в виде плоской таблицы. Не сталкивались ли Вы с ситуацией, когда клиенты по прежнему хотят читать сырые данные из базы без приложения?
Пример:
Данные о заказах в Структуре 1:
...
Иванов Иван Иванович Сиреневая ул, д.8, к2 Аккумуляторы 10 000 000 руб
...
Иванов Иван Иванович Сиреневая ул, д.8, к2 Пицца 300 руб
...
Допустим, случился сбой и фамилия 'Иванов' была некорректно изменена или удалена в строке с аккумуляторами. Используя избыточность данных, ее можно будет восстановить из строки с пиццей. Это сделает вручную в базе сотрудник техподдержки. (Понятно, что бывают случаи, когда это не получится.)
В структуре EAV by Owl`s значение 'Иванов' хранится в единственном экземпляре в справочнике строковых значений, и если с ним что-то случается, то:
1. Это отразится сразу во всех местах, где это значение используется.
2. Сотрудник техподдержки точно не восстановит фамилию. (Понятно, что бывают случаи, когда есть корректный бекап.)
Мы из своего опыта подтверждаем, что база в структуре EAV by Owl`s более компактна и быстрее обрабатывается. Интересно обсудить вопрос ее устойчивости к ошибкам. В своей версии главных функций я сталкивался с ситуацией, когда ошибочно записанный набор ставит техподдержку в тупик.
С уважением,
Уважаемый Михаил!
1. Бэкап всегда нужен. Лучше его иметь, чем усложнять систему.
2. Получайте "ядра" от Производителя, который Вам всегда предоставит актуальную версию и техподдержку. Сейчас уже разрабатываются "ядра" версий: 6.Cloud (которая будет всегда доступна из "облака", а база и весь Ваш проект может быть у Вас) и 6.Grain (которая написана на python’е и работает на сервере в скомпилированном виде, так что её можно будет поставить на свою машину). Версия номер 5 будет показана на вебинаре, на который Вы записались.
Также у тех свойств, для которых может потребоваться восстановить старое значение, последняя версия ядра позволяет сохранять историю.
3. Тем не менее, рассмотрим Ваш пример (хотя, в скобках заметим, что и в "правильно нормализованной структуре" каждая фамилия должна храниться в единственном экземпляре).
3.1. Представим модельно: Ваш случай + "ядро" исчезло, а данные все ещё надо получить. Но Вы же знаете как работает get (равно, add и delete) и как, и какие он генерирует SQL-запросы. Используя такие же SQL-запросы, Вы можете получить желаемые данные с помощью любой программы или утилиты для работы с базой данных.
3.2. Сам ID в таблице Values недоступен для редактирования обыкновенным Пользователям. Так что, они никак не смогут его испортить. А вот программист, который полез в базу руками и случайно подправил что-то не там - как раз может что-нибудь испортить.
3.3. Вы пишете про случайное изменение или удаление фамилии "Иванов". Но, если какой-либо Пользователь поменяет свою фамилию, то у его и только его набора изменится ссылка на значение свойства. У остальных наборов ссылающихся на прежнее значение (если таковые есть), ссылка останется прежней, а само значение "Иванов" затронуто не будет. Просто меньшее число наборов будет на него ссылаться.
Поэтому при корректно написанном ядре трудно представить себе ситуацию, в которой фамилия "Иванов" будет "испорчена". И, как сказано выше, для свойств можно хранить историю.
3.4. В случае нечаянно испорченной фамилии даже Google Support не будет прибегать к сложным техническим решения, а просто попросит Вас заново ввести фамилию :)
4. Это неправильно - смотреть на таблицу. Это работа для компьютера, а не человека. При любой структуре и достаточно большой базе человек точно ошибётся. И не надо беспокоиться относительно "утраты" возможности смотреть на таблицы и править их руками - это такое же беспокойство, как беспокойство относительно утраты возможности перепаять микропроцессор :).
Спасибо Вам за Ваши вопросы и за то, что используете нашу базу - пусть и для вспомогательных целей. Первые паровые машины, как известно, ставили на корабли тоже для вспомогательных целей (преодолеть полосу штиля). А потом получилось кое-что иное.
Спрашивайте ещё,
Эта парадигма создавалась из утилитарного стремления к идеалу, как в управленческом, так и в тризовском смысле. Формулировки идеальности (которые соответствуют по духу и замыслу тем, что сформулировал Генрих Альтшуллер, создавая ТРИЗ, и которые продвигали эту разработку) следующие:
1) Идеально - это когда программу сможет в отсутствие Автора не только сопровождать, но и развивать программист с меньшей квалификацией, чем Автор программы.
2) Идеально - когда добавление новой функциональности в программу не потребует внесения изменений и/или добавлений в программный код (более того, в совершенно идеальной программе увеличение функциональности приводит к сокращению кода); причём всё это без ущерба для любых параметров (например, быстродействия и т.д.).
И чтобы программы писались настолько быстро и сопровождались настолько просто, что их создание и сопровождение можно было бы поручить детям. А взрослые и умные специалисты пусть бы занялись чем-нибудь великим.
Сразу начнем с сути дела. Существует некий язык записи сути задачи. Если его применять, то сложные (креативные) задачи решать становится удобнее.
Как и любая модель, эта модель тоже жизнь собой не заменяет, однако авторы статьи попробовали и убедились в том, что действительно удобно. Хотя поначалу кажется непривычным… Впрочем, авторы, уважая время Коллег, постеснялись бы писать о привычном, знакомом, общеизвестном.
Рассмотрим серию примеров. Пока намеренно несложных. (Более сложные мы в дальнейшем тоже рассмотрим).
Действительно, создать качественную программу "из объектов", двигаясь "снизу вверх" (от подсистем к системе; от реализации к проекту), можно только при условии предельной простоты такой программы.
Однако, похоже, существует инерция мышления, оставшаяся с тех времен, когда все писали на низком уровне, и мысли о реализации неизбежно занимали наибольший промежуток времени. И многие программисты (конечно, не все) часто думают "снизу вверх". Больше над реализацией, чем над проектом.
Но, например, хороший архитектор никогда не проектирует дом "из квартир", а думает о нем сразу "в целом", "вписывает" в контекст окружающей среды, пользуется знаниями о готовых "стилях" (или создает свой стиль, зная о других). Хороший авиаконструктор не проектирует новый самолет "из его элементов", но пытается понять, как машина "летает в целом", или отталкивается от "принципов полета этого класса машин" и т.д., и т.п.
Мышление "снизу вверх" сродни попытке сложить организм из атомов углерода и водорода. Теоретически так сделать можно, но очень уж "многофакторно" и затратно как по времени, так и по ресурсам. И все равно, несмотря на затраты и при квалифицированной работе, получится урод + "теория неизбежности ошибок" вместо качественного продукта. За исключением, может быть, тех случаев, когда "организмом" (перепутав термины) назвали что-то предельно простое (например, 1 звено СН).
Для решения некоторых задач программирования в качестве "инструмента", облегчающего разработчику абстрагирование от конкретики, предлагается использовать "абсолютно тупого героя", которому поручается некая деятельность. Поскольку этот герой непроходимо туп (мы даже пишем его с маленькой буквы, чтобы и тени величия в нем не наблюдалось), то задача поручить ему деятельность, которая, тем не менее, должна быть выполнена — нетривиальна.
Тем не менее, надо описать решение так, чтобы тупой (несколько тупых) смогли качественно и незатратно порученную задачу выполнить.
Остальное понятно из контекста разбираемых примеров:
Два великих человека радикально высказались об оптимизации: "Единственный способ оптимизировать затратный проект – это его закрыть" (Питер Друкер) и "Идеальная система - та, которой нет, а функции ее выполняются" (Генрих Альтшуллер).
Этот материал посвящен (основанным на ТРИЗ) приемам сокращения работ при сохранении и улучшении их результатов. В этой части работы вслед за классиками сформулируем: "Лучшая работа – та, которую делать не надо, но результат ее получается".
Один раз решили научить тупого молоть уголь и рисовать угольной крошкой по трафаретам на поверхностях и чуть не наломали дров. Сначала научили тупого рисовать углем "бабушку" на "окошке". Для этого, велели ему:
Кто придумал такую тупую инструкцию вспомнить уже невозможно — так, что материться глупо — надо работать. Поэтому с помощью воплей, дубины, «соцпакета» и 10-ти тупых администраторов кое-как упомянутый художественный результат достигался.
Причем 10 тупых администраторов периодически посещали семинары по мотивации тупых, где их учили более правильным "соцпакетам", в результате применения которых тупой должен был бы "раскрыться" и стать Личностью. А уж Личность смогла бы рисовать не только бабушек, но и птичек.
Однако, в связи с развитием рыночных отношений, задание усложнилось.
Уровень зарплат на рынке и размер конкретного бизнеса не связаны друг с другом.
Есть определенные профессии, и есть их цена на рынке труда. Есть задания, которые поручены, и есть стоимость их выполнения. А также есть вопрос, который задают все: "Сколько платят такому специалисту в нашем городе?"
Конечно, хороший работник может и должен получать больше, а плохой — меньше. Однако, зарплата аналогичных работников в разных фирмах, в принципе, не может отличаться на порядки. Она может отличаться на 20–30%, но не в разы, тем более, не в десятки раз. Хотя выручка в крупном супермаркете и в "магазине на углу" может отличаться в сотни раз.
Трудности при определении эталонных планов продаж в разных магазинах одной торговой сети могут, кроме прочего, возникать из-за различной проходимости...
Эталоны, соответственно, установили не для каждого конкретного магазина, а для каждого типа магазина - то есть эталон будет одинаковым для всех магазинов попадающих в определенную группу и правила задания эталона определяются для группы. Казалось бы разница несущественна, но это только на первый взгляд...
Выступление Сергея Сычёва на конференции "Как навести порядок в бизнесе",
Москва, январь 2011 г.
Данная закономерность была сформулирована автором в 1993 году в Иркутске. Ряд фактов уже тогда давали возможность построить модель, которую, тем не менее, нужно было проверять в течение долгого времени. Последующие 13 лет позволили сделать ряд обобщений, подтвердивших изначальную гипотезу. И в 2006 году автор счёл возможным первую версию закономерности опубликовать открыто.
Летом 2013 года публикуется версия 2.0 - более полная, развернутая и содержащая одно существенное методическое отличие от первой версии.
Автор будет признателен Коллегам, которые найдут "узкие места" данной работы и направят автору критические замечания и/или примеры, как подтверждающие, так и опровергающие положения изложенные в материале.
Мы сделали для рабочих больше, чем профсоюзы. Мы не только дали им зарплату большую, чем требовали профсоюзы на своих стачках, но и повысили выработку, сократив затраты труда и существенно снизив эксплуатацию. Рабочие в США с момента внедрения системы научного менеджмента перестали называться "пролетариями", они стали делать сбережения, покупать жилье и платить за хорошее обучение своих детей.
Таким образом, вопреки марксизму было показано, что повышение производительности (в том числе, скачкообразное) при капитализме не приводит к обнищанию рабочих. Наоборот, приводит к их обогащению".
Азбука консалтинга гласит: "Область постановки задач" (то есть, как они формулируются) достаточно часто не совпадает с "областью их действительного решения" (то есть, что случилось на самом деле). Например, желание "поднять командный дух", "сплотить коллектив", "ввести мотивацию от результатов всей компании" и т.п. – это нередко борьба со следствиями, а не с причинами.
Недавно наш знакомый консультант Mister Any получил следующий запрос: "Для подкрепления командного духа мне хочется ввести в отделе продаж доплату за выполнение плана всего отдела. Какой размер оптимален по отношению к общей зарплате сотрудника и к чему одна должна быть привязана? Рассчитываю на Вашу помощь". Подпись: Mr. Heart".
24 сентября 1998 г. умер великий человек. Генрих Саулович Альтшуллер, создатель и первый разработчик ТРИЗ - теории решения изобретательских задач, приемов развития творческого воображения, ЖСТЛ - жизненной стратегии творческой личности.
Уже сейчас ясно: это наука следующего тысячелетия. И нам будет трудно объяснить внукам, почему власть и электорат, начальники и ученые, спец. органы и педагоги так боялись новой науки в веке уходящем... Наверное потому, что ТРИЗ и ЖСТЛ меняют мышление, а значит и жизнь. Генрих Саулович считал, что человек должен прожить ее Достойно - на пределе своих возможностей. И он позволил себе так жить. Возможно, наши внуки окажутся сильнее нас...
И.Л. Викентьев
Недостаток 1 (из 9-ти).
Неучет того факта, что сумма сделки и трудоемкость выполнения работ не связаны между собой. Соответственно,
1.1. Крупный разовый (иногда нежданный) "оборотистый" заказ не влечет за собой дополнительной трудоемкости, а зарплату увеличивает и в результате расслабляет сотрудников.
И наоборот. Когда основная работа по обслуживанию долгожданного Клиента только начинается, а новых поступлений не предвидится, сотрудники "правомерно" интересуются: почему при больших стараниях и трудозатратах, чем в прошлый "прибыльный" месяц, их зарплата "падает".
1.2. У сотрудников невольно вырабатывается неприязнь к дешевым товарам и услугам, стремление избегать работы с ними и соответствующее отношение к Клиентам, которые их покупают. Так, в иных магазинах у стенда с кофеварками/кофемолками (условно) продавца можно и не дождаться (в отличие, например, от стенда с проекционными ТВ).
Как результат, премия "непрозрачна". Связь оплаты с результатами труда пропадает...
Товаров все больше и больше, поэтому непонятно, что рекламировать из имеющегося ассортимента, тем более что макет газетной полосы не резиновый, а кегль шрифта тоже имеет нижнюю границу. Руки опускаются, а в голову приходят уж очень общие идеи типа: "Все для всех всегда". А реклама магазина все больше и больше становится похожа на рекламу банка: там тоже "Для всех и повсеместно". Чтобы упомнить все товары, нужно долго обучаться мнемотехнике.
Создавшаяся ситуация неопределенности усложняет и работу отдела рекламы, и контроль над ним. Товар - с колес, макет - в пожаре... Все заняты текучкой. Некогда планировать, некогда искать идеи, некогда отслеживать эффективность. Надо продавать, изготавливать, размещать: Стоп! Очень уж удобная позиция. Остановимся на несколько часов и посмотрим на десятки тысяч, нет, сотни тысяч товаров и услуг иначе...
Есть устойчивые вещи, которые редко подвергаются анализу (типа: "А как же иначе?). Отсюда такие характерные ошибки мотивации сотрудников, как:
"Больная" для многих предпринимателей тема - участие работника в прибылях.
К счастью, есть решения, позволяющие не допустить связанных с этим ошибок в бизнесе.
Фрагмент выступления Сергея Сычёва, скрытно снятый слушателем на мобильный телефон.
В объявлениях о приёме на работу нередко пишут, что нужны те, кто "умеет работать в команде", как будто это дефицит. Пожалуй, следует писать иное: "Требуется эгоист, способный растолкать локтями тех, кто мешает ему заработать".
Но перейдём от сравнений к методике.
Когда мы измеряем результат работы сразу группы людей, не измеряя результатов каждого участника группы, то такой учёт результатов назовём "бригадным". Или командным результатом.
"Бригадный" учёт результатов труда - вредный, иногда вынужденный и может быть оправдан лишь в некоторых ситуациях. Рассмотрим подробнее....
Сергей Сычёв, "Предпринимательская этика", фрагмент части I "Этика и бизнес-процессы", апрель 2014., г. Ростов-на-Дону
- Да, так мы устроили этот мир. Там, где одни видят социализацию, другие видят капитализацию. И даже повышают одно за счет другого. Учись, консультант, пока жив. Ты, а не я.
...А ты что предлагаешь нам? Оптимизировать? Т.е. уменьшить активы, и реальный и регулярно оплачиваемый нам убыток заменить на непрогнозируемый, мелковероятностный доход? Это, по-твоему, консультация?..
...Вот и вся суть. Мертвые юридические души мы делим на части, даем им источник финансирования, социальную миссию и систему работы, при которой, торгуя всего-навсего друг с другом, эти социальные зомби превращают убытки в живой кэш...
Задача
В магазине несколько разных отделов/секций. В каждом работает по несколько продавцов-консультантов. При этом зачастую возникает необходимость замещать Коллег то в одной секции, то в другой. Как при этом устанавливать план продаж? Ведь когда продавец работает в другой секции, продажи его отдела явно не увеличиваются.
Решение...
…Если компания спроектирована плохо, то она будет плохо работать. Часто она бывает вообще не спроектирована, а работает так, как "исторически сложилось". И знаете, как называется самая популярная ошибка? Она называется: "Когда я стану большим".
…Неприятная мысль заключается в том, если Ваш бизнес малый, то, скорее всего, он таковым и останется. Принтер, рассчитанный на 1 тысячу копий, не сделает больше 1 тысячи копий, а компания, которая организована определенным образом (сделана, как маленькая), таковой и останется, потому что она такая…" – утверждает Автор в интервью украинскому изданию "Art of Sales" ("Искусство продаж").
В материале приведен список основной литературы по ТРИЗ.
В разные годы издавались и другие книги (как правило, меньшим тиражом) других авторов. Но мы рекомендуем начинать знакомство именно с классики ТРИЗ - книг Г.С. Альтшуллера. Как правило, найти эти книги можно в крупных библиотеках.
Данная закономерность была сформулирована автором в 1993 году в Иркутске. Ряд фактов уже тогда позволил построить модель, которую, тем не менее, нужно было проверить… Последующие 13 лет позволили сделать ряд обобщений, подтвердивших изначальную гипотезу.
Материал публикуется с некоторыми сокращениями и в то же время – с небольшими дополнениями.
Фрагмент выступления 08 апреля 2014 года на конференции "Открытые бизнес-методики и технологии", г. Ростов-на-Дону
Наиболее частая управленческая ошибка, вызывающая "командную болезнь", называется "объектное структурирование", когда в рамках отдела продаж неверно распределены зоны ответственности менеджеров (они закреплены за брэндами, товарными группами и т.д.). Иногда продажи вообще не выделены в отдельное подразделение. Т.е. сотрудники отделов, которые оказывают ту или иную услугу Клиентам (консалтинг, сервис и т.д.), сами же эту услугу и продают.
Рассмотрим на примерах...
Фрагмент выступления Сергея Сычёва на ежегодном TRIZ-RI ФОРУМе
"Открытые бизнес-методики и технологии",
Москва, 27 сентября 2011 г.
В настоящем материале Авторами приведена не "единственно верная", а одна из возможных систем мотивации рекламиста. В ней отдел рекламы "сам себя измеряет".
Нахождение простой идеи измерения результата требует не меньшей, а порой и большей креативности, чем нахождение идеи рекламной кампании. Это тоже входит в профессию. Поэтому в тех случаях, когда итоги рекламной акции численно определить невозможно, разговор о премии не ведется. Заработная плата рекламиста ограничивается только постоянной частью. Премия (сверхзаработок) выплачивается за измеряемый результат.
"Привязку" Клиентов к конкретным менеджерам обычно оправдывают долгой историей их общения в процессе сотрудничества, формирующей, помимо деловых, и личные отношения. А значит, формирующей и более глубокое понимание конкретным менеджером специфики работы с данной компанией.
Однако такая "привязка", вопреки стереотипу о комфортной работе со старым знакомым, неудобна для Клиента. Клиенту приходится ждать "своего сотрудника" и испытывать массу накладок, когда "старого знакомого" нет, хотя любой мог бы его обслужить. А если Клиент действительно полюбил работать с конкретным менеджером, то следует задать себе вопрос: "Почему Клиент хочет "личных отношений" с конкретным хорошим менеджером, даже в ущерб своим удобствам (приходится ожидать, перезванивать, просить, "чтобы передали" и т.п.)?"
Нередко, считая оборачиваемость средств в товарах, затрудняются определить точную сумму, связанную в складских остатках, за тот или иной период (знаменатель формулы). Ибо остатки товара меняются каждый день. И тогда берут ее среднее значение.
Эта классическая ошибка связана с инерцией мышления, когда средства в остатках почему-то отождествляются с потоком поступлений. Однако, даже через руки бедняка в течение всей жизни мог пройти миллион, при том что в каждый конкретный месяц он еле-еле сводил концы с концами и всегда оперировал мелкими суммами.
В статье – на наглядных примерах – показывается, где кроется неочевидная ошибка, и как ее избежать. Приводится корректная формула оборачиваемости денежных средств, связанных в товаре.
Мысль 1. Никогда не создавайте нескольких расценок за одну и ту же работу (равно и разных правил оценки одной и той же работы): ни во времени, ни в пространстве, ни между людьми.
Если Вы создадите их во времени (например, как описано в статье: за работу в будни - одни расценки, за работу в выходные дни - другие или иные вариации), работники сократят производительность в обычное время и перебросят объёмы на выходные. И Ваш бизнес-процесс будет дестабилизирован. Причём во множестве случаев работники смогут это сделать настолько "плавно", что заметить не удастся.
Мысль 2 (более глубокая)...
Рекомендация 2.2 (из 10-ти).
Молодая фирма, стартуя, имеет бизнес-план, как минимум, на первые три года жизни (по крайней мере, иметь его чрезвычайно полезно). Результаты, на которые надо выйти за эти три года, и будем считать "эталонными". Кроме "эталонных", зададим промежуточные результаты — те, на которые надо выйти к заданным срокам. Фактические результаты в стартовый период" будем сравнивать не с эталонными, а с промежуточными результатами.
Важный момент: промежуточные результаты задаются на старте, а не по "факту" (от достигнутого).
Сравните:
а) план вырос потому, что подошел срок, когда пора выйти на показатели, намеченные в бизнес-плане;
б) план вырос потому, что достигли хороших результатов, а всегда хочется больше.
Пункт а), в отличие от пункта б), протеста не вызывает.
Задача из раздела "Оценка эффективности рекламных решений".
Для продвижения светильников, продающихся в соответствующем городском магазине, рекламист предложил провести акцию "Светлая неделя". В рамках акции – несколько решений:
Заказчик работу рекламиста принял и оплатил, но, конечно, решил оценить результативность. Но как?
Вы не поверите, но цель настоящей статьи – совсем не критика, ведь устранить несуразности, описанные ниже, не составит труда, а затраты копеечные. Авторов материала по должности нередко приглашают вычитывать разнообразные "концепции заведений", "системы менеджмента качества", "технологии клиентоориентированного обслуживания" и т.п. Но иногда приходится говорить: "Протрите, пожалуйста, столик, а только потом положите концепцию".
В статье приводятся, прежде всего, ошибки коммуникации с Гостем и простые решения, их устраняющие. Мы намеренно опускаем ошибки сервировки, подачи на стол и нарушение застольного этикета со стороны официантов.
Конечно, есть и традиционные заклинания о том, что "реклама не измеряема". Есть "и тому подобное".
Мы попробуем написать о том, чего пока нет.
Эта статья является "пояснительной запиской" к той части эл. кейса "Алгоритм решения рекламных и маркетинговых задач "Рекламное Измерение" (Алгоритм Сычева С.В., Система "ТРИЗ-ШАНС"), которая посвящена прогнозу результативности рекламных решений, по возможности, простыми и незатратными способами.
В помощь тем, кто уже работал с Алгоритмом "РИ", и тем, кто с ним еще не знаком…
О Ф.У. Тейлоре написано много. Между тем некорректная работа современных "писателей" (и не только российских) с первоисточниками, а также порой их обычное невежество превысили границы любых приличий. Ранее в своей работе "Мифы о системе научного менеджмента Ф.У. Тейлора" на этот факт уже обратили внимание специалисты А. Демьяненко и Л. Дятлова.
Ладно бы слабые люди просто хотели оттопырить свой "вклад в теорию менеджмента" и кидались бы на "слонов" - о том классиками давно написаны басни. Но приходится сталкиваться (и нередко) с ситуациями, когда недобросовестный критик мало того, что исказит первоисточник, так еще, по точному выражению И.Л. Викентьева, "вступит в полемику и "в пылу спора" припишет оппоненту глупость, которую сам придумал, а затем "разоблачит" его".
Мы хотим, по мере сил, этому препятствовать. Поэтому в Части I. "Предисловие к интервью" рассмотрены несколько частых, но ложных утверждений о научном менеджменте и о системе Ф.У.Тейлора, а также их опровержения.
Бывает так, что рабочие процессы не отлажены, Клиенты недовольны работой, сотрудники пеняют друг на друга, а некоторые руководители находят проблему не в себе, а в отсутствии "командного духа" и недостаточном стимулировании за "общие результаты". Обычно никто с этим не спорит, к тому же это модно.
А если технологии просто нет и работники делают все "как умеют", так "на то ведь "профессионалов" и брали".
Проблема, конечно, часто сидит глубже...
Если Вы бываете в Европе по делам бизнеса или с частными целями, найдите время заехать к нам в Прагу. На консультацию, на стажировку, на деловой завтрак. За свежими идеями, за новыми методиками, за иной обстановкой и за иными возможностями.
В Праге Вас встретят наши лучшие эксперты. Мы предложим Вам качественные программы бизнес-обучения по-европейски. В том числе, сделанные "под Вас". В том формате, в каком удобно Вам.
Часто бывает так: людей на работу взяли, а дело не движется. Звонков мало, заявок еще меньше, клиентская база тает… И не только в отделе продаж.
А Вы возьмите к себе "на работу" наших специалистов.
Каждый из нас имеет более чем 20-летний опыт практического внедрения системы управления предприятием, администрирования бизнес-процессов в отделах: активных продаж, закупки (снабжения), продвижения, складском хозяйстве, бухгалтерии, IT и пр.
С нашим приходом на предприятие уже через короткое время процессы управления организацией начинают работать как хорошо отлаженный механизм. А штатные сотрудники привыкают выполнять свои функции.
"ТРИЗ" - Теория Решения Изобретательских Задач – самая сильная, на сегодняшний день, система создания новых идей и изобретений известна во многих странах: Германии, Великобритании, США, Швеции, Франции, Японии, Корее, Израиле, Вьетнаме, Испании, Финляндии, Канаде и др.
Книги автора ТРИЗ Генриха Альтшуллера [15.10.1926 - 24.09.1998] переведены на десятки иностранных языков. Большинство успешных компаний активно используют её для совершенствования своих товаров и услуг.
По мере развития компании обостряется необходимость умного сокращения усилий и затрат для достижения той же результативности (или даже повышения результативности при сокращении усилий и затрат)...
Когда зарплата "работает", это чувствуют и Клиент с порога заведения, и сотрудник, только, что принятый в Компанию. Это ощущается и по:
В фирму с хорошей технологией и правильной зарплатой приятно попадать. На наших обучающих мероприятиях Вы узнаете, как выстроить и внедрить такую систему мотивации...
Речь вовсе НЕ о "должностных обязанностях" или "должностных инструкциях", которых и без того великое множество в Интернете. Речь о предельно конкретных (а потому легко проверяемых), действиях сотрудников с оптимальным разделением функций между ними.
Иногда кажется, что "мы и сами пропишем" внутри компании своими силами…. Но тот, кому делегируется эта работа, просто "ходит" за каждым сотрудником и фиксирует всё, что делается в течение дня, недели, месяца. А потом устает и бросает...
Поэтому лучше поручить эту работу нам - в силу почти 20-летнего опыта мы делаем это быстро, прозрачно и очень качественно.
Подобно велосипеду, профессионально сделанная и заложенная в основу корпоративной культуры система "фирменных стандартов" изобретается один раз и надолго.