Назначения и позиции

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

Этот вопрос связан с предыдущим вопросом, который я отправил, но я воспроизвел релевантную информацию ниже: Проектирование баз данных - механизм приложения Google

В этом приложении существует понятие "Назначений" и "Позиций".

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

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

Name:                          Price: Commission: Time estimate   
Full Detail, Regular Size:        160       75       3.5 hours 
$10 Off Full Detail Coupon:       -10        0         0 hours 
Premium Detail:                   220      110       4.5 hours 
Derived totals(not a line item): $370     $185       8.0 hours

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

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

Appointment
 start_time
 etc...

Line_Item
 appointment_Key_List
 name
 price
 etc...

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

Более определенная проблема состоит в том, что я использую механизм приложения Google и если бы я хочу запросить для ряда назначений и их связанных позиций, я должен был бы сначала запросить для набора назначений и затем сделать второй запрос для позиций с помощью оператора IN, чтобы протестировать, если какой-либо из ключей назначения Line_Item попадает в набор ключей назначения, возвращенный из предыдущего запроса. Второй запрос перестанет работать, если у меня будет больше чем 30 ключей, требующих меня к черепку запрос. Я мог денормализовать данные для предотвращения этого сложного и обширного запроса чтения, и я должен буду, вероятно, денормализовать до некоторой степени так или иначе, но я избежал бы сложности в соответствующих случаях.

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

Спасибо!

5
задан Community 23 May 2017 в 10:32
поделиться

2 ответа

Предлагаемый вами подход будет работать нормально; вы можете смоделировать строку «assign_Key_list» как свойство списка, и оно будет работать так, как вы ожидаете. Вам не нужно использовать оператор IN - это для сопоставления одного значения в хранилище данных со списком имеющихся у вас ключей (например, "WHERE datastore_column IN ('a', 'b', 'c')), а вы делаете обратное - сопоставляете одно значение со списком в хранилище данных.

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

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

Краткое руководство по индексации хранилища данных App Engine

Хотя Python и Java предоставляют различные интерфейсы высокого уровня для хранилища данных, само хранилище данных использует абстракцию нижнего уровня, называемую сущностями. Сущность состоит из следующего:

  1. Уникальный первичный ключ
  2. Список пар (имя, значение)

Первичный ключ - это ключ хранилища данных, с которым вы уже знакомы. Список пар (имя, значение) - это представление App Engine для данных в вашей сущности. Пока все просто. Сущность со следующими значениями:

a_string = "Hello, world"
an_int = 123

будет сериализована во что-то вроде этого:

[('a_string', 'Hello, world'), ('an_int', 123)]

Но как это взаимодействует со списками? Что ж, списки рассматриваются как "многозначные" свойства. То есть список из n элементов сохраняется как n отдельных свойств. Пример, вероятно, проясняет это:

a_string = "Hello, world"
an_int = 123
a_list_of_ints = [42, 314, 9]

будет сериализован как:

[('a_string', 'Hello, world'), ('an_int', 123), ('a_list_of_ints', 42), ('a_list_of_ints', 314), ('a_list_of_ints', 9)]

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

Когда это становится важным, так это при взаимодействии с индексированием. Предположим, у вас есть указатель на «a_string» и «an_int». Когда вы вставляете или изменяете значение, App Engine создает для него набор записей указателя; для указанного выше индекса и указанной выше сущности он генерирует одну строку в индексе, которая выглядит примерно так:

('Hello, world', 123, a_key)

('a_key' здесь является заполнителем для ключа исходной сущности.) Когда вы выполняете запрос, который использует этого индекса, ему просто нужно выполнить поиск по индексу, чтобы найти строки с соответствующим префиксом (например, 'SELECT * FROM Kind WHERE a_string = "Hello, world" ORDER BY an_int').

Однако при индексировании списка App Engine вставляет несколько строк индекса. Индекс по 'an_int' и 'a_list_of_ints' сгенерирует эти строки для указанного выше объекта:

(123, 42, a_key)
(123, 314, a_key)
(123, 9, a_key)

Опять же, запросы работают так же, как и раньше - App Engine просто должен найти строку с правильным префиксом в индексе. Количество записей в списке не влияет на скорость выполнения запроса - только на то, сколько времени потребовалось для создания и записи записей индекса. Фактически, планировщик запросов не знает, что 'a_list_of_ints' является свойством с несколькими значениями - он просто обрабатывает его как любую другую запись индекса.

Итак, вкратце:

  1. Нет практической разницы между списком с одним элементом в нем и отдельным свойством в терминах индексирования и запроса
  2. Размер индексированного списка влияет на время и пространство, необходимое для индексации , но не для запросов.
  3. Вы можете выполнить запрос, который соответствует любому объекту с заданным значением в списке, используя простой фильтр равенства.
2
ответ дан 15 December 2019 в 06:14
поделиться

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

Ваша модель в нормализованной форме будет иметь третью таблицу со ссылками на строки Назначение и Line_Item :

Appointment
 start_time
 ...

Line_Item
 name
 price
 ...

Appointment_Line_Item
 appointment_key
 line_item_key

Однако есть проблема! Поскольку вы используете Google App Engine, а их хранилище данных весьма ограничено («GQL не может выполнять SQL-подобное JOIN») и в основном требует денормализации.

Вы предложили использовать поле в виде списка. Это возможно, но очень сложно проиндексировать. Поиск ключа ( assign_key ) в списке для каждой строки в базе данных на самом деле неэффективен. Я предлагаю две возможности:

  1. Дублировать Line_Item .

     Line_Item
    assign_key
    имя
    цена
    законченный
     ...

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

  2. Динамические поля для Line_Item :

     Line_Item
    duplicate_key
    assign_key
    имя
    цена
    законченный
     ...
    

    Создайте новое поле duplicate_key для Line_Item , которое указывает на другой Line_Item или на нуль (зарезервируйте этот ключ!). Нулевое значение означает, что Line_Item является исходным, любое другое значение означает, что этот Line_Item является дубликатом Line_Item , на который указывает поле. Все поля Line_Item , помеченные как повторяющиеся, наследуют поля исходного Line_Item , за исключением assign_key : поэтому он займет меньше места. Также это решение должно иметь индексированный ключ assign_key , чтобы ускорить поиск. Для этого требуется один дополнительный запрос на каждый дублированный Line_Item , что может быть проблемой.

Теперь это очевидный выбор: либо лучшая скорость, либо лучшее хранилище. Я бы выбрал первое, так как это снижает сложность вашей модели, а хранение данных никогда не является проблемой для современных систем.Меньшая сложность обычно означает меньше ошибок и меньше затрат на разработку / тестирование, что оправдывает стоимость требований к хранилищу.

1
ответ дан 15 December 2019 в 06:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: