Чтобы использовать внешнюю реляционную таблицу для записи, необходимо при объявлении таблицы, определить ее статус чтения / записи как OUT. Общая форма такой декларации имеет следующий вид:
Оператор: OUT
table table-name OUT string-list: key-spec, data-spec, data-spec, ...;
где необязательный string-list зависит от типа базы данных и используемого метода доступа. Большинство последующих примеров не включают в себя string-list.
write table
Значения выражения AMPL впоследствии записываются в таблицу командой:
write table table-name;
write table table-name использует объявленное имя таблицы, чтобы записать данные. Объявление table... указывает внешний файл и, необязательно, явно в string-list или неявно (по умолчанию), реляционную таблицу в этом файле. Обычно именованный внешний файл или таблица создаются, если они не существуют, и перезаписываются если существуют. Объявление таблицы должно включать одну или несколько спецификаций данных, указывающих, что определенные столбцы необходимо заменить либо добавить в таблицу. При этом, значение статуса чтения / записи необходимо установить в IN или INOUT. Обработчик таблиц может иметь свои собственные более подробные правила для определения условий модификации или создания файлов и таблиц, которые описаны в документации обработчика.
Объявление key-specs и data-specs в декларации table, для записи внешних таблиц, внешне похожи на объявления для чтения. Однако диапазон возможных выражений AMPL при записи, значительно шире. Он может включать все выражения с множественными и числовыми значениями. Кроме того, если строки таблицы, которые должны быть прочитаны, являются строками некоторой существующей таблицы, то строки, которые необходимо записать, должны быть определены из выражений AMPL при объявлении таблицы. В частности, записываемые строки могут быть выведены либо из data-specs, используя те же соглашения, что и в команде display, либо из key-spec.
Запись строк на основании данных
Если key-spec является просто заключенным в квадратные скобки списком имен ключевых столбцов,
[key-col-name, key-col-name, ...]
тогда объявление table работает так же, как команда display. Она определяет записи строк внешней таблицы, принимая объединение наборов индексации, указанных или подразумеваемых в data-specs. Формат списка data-specs такой же, как display, за исключением того, что все перечисленные элементы должны иметь одинаковое измерение.
В простейшем случае data-specs - это имена компонентов модели, индексированные по одному и тому же набору:
table Foods OUT: [FoodName], f_min, Buy, f_max;
Когда выполняется команда write table Foods, создается ключевой столбец FoodName и столбцы данных f_min, Buy и f_max. Поскольку все компоненты AMPL, соответствующие столбцам данных, проиндексированы по набору FOOD, для каждого элемента FOOD создается одна строка. В текущей строке элемент FOOD записывается в ключевой столбец FoodName, а значения f_min, Buy и f_max, индексированные по FOOD, записываются в одноименные столбцы данных. Для данных в примере диета, итоговая реляционная таблица имеет следующий вид:
FoodName f_min Buy f_max BEEF 2 5.36061 10 CHK 2 2 10 FISH 2 2 10 HAM 2 10 10 MCH 2 10 10 MTL 2 10 10 SPG 2 9.30605 10 TUR 2 2 10
Таблицы, соответствующие многомерным наборам, обрабатываются аналогично. Причем, количество имен столбцов в квадратных скобках, перечисленных в key-spec должно соответствовать количеству элементов в data-spec. Таким образом, таблица, содержащая результаты из steelT.mod, может быть определена как:
table SteelProd OUT: [PROD, TIME], Make, Sell, Inv;
Поскольку Make и Sell индексируются по {PROD, 1..T}, а Inv индексируется по {PROD, 0..T}. Команда записи таблицы SteelProd создаст таблицу с одной строкой для каждого элемента объединенного набора:
PROD TIME Make Sell Inv bands 0 . . 10 bands 1 5990 6000 0 bands 2 6000 6000 0 bands 3 1400 1400 0 bands 4 2000 2000 0 coils 0 . . 0 coils 1 1407 307 1100 coils 2 1400 2500 0 coils 3 3500 3500 0 coils 4 4200 4200 0
Две строки имеют пустые записи в столбцах Make и Sell, потому что (bands, 0) и (coils, 0) не являются элементами наборов Make и Sell. Мы используем «.», чтобы указать пустые записи таблицы. Внешний вид и способ обработки пустых записей различаются в зависимости от используемого программного обеспечения базы данных. Эта форма записи применяется и для записи суффиксных имен переменных и ограничений, в т.ч. dual и slack, связанных с ограничением Diet:
table Nutrs OUT: [Nutrient], Diet.lslack, Diet.ldual, Diet.uslack, Diet.udual; # ERROR
Команда write table Nutrs, скорее всего, будет отклонена, поскольку в большинстве программ для баз данных имена с «точкой» не допускаются в качестве имен столбцов:
write table Nutrs; Error executing "write table" command: Error writing table Nutrs with table handler ampl.odbc: Column 2’s name "Diet.lslack" contains non-alphanumeric character ’.’.
В этой ситуации требуется, чтобы за каждым выражением AMPL следовал оператор «~» и соответствующее имя столбца, допустимое для использования в реляционной таблице:
table Nutrs OUT: [Nutrient], Diet.lslack ˜ lb_slack, Diet.ldual ˜ lb_dual, Diet.uslack ˜ ub_slack, Diet.udual ˜ ub_dual;
Эта запись говорит о том, что значения, представленные Diet.lslack, должны быть помещены в столбец с именем lb_slack. Значения, представленные Diet.ldual, должны быть помещены в столбец с именем lb_dual и так далее. Команда write table Nutrs создает реляционную таблицу:
Nutrient lb_slack lb_dual ub_slack ub_dual A 1256.29 0 18043.7 0 B1 336.257 0 18963.7 0 B2 0 0.404585 19300 0 C 982.515 0 18317.5 0 CAL 3794.62 0 4205.38 0 NA 50000 0 0 -0.00306905
Оператор «~» также может использоваться с именами без индексов, если необходимо присвоить столбцу базы данных, имя, отличное от имени объекта AMPL.
Более общие выражения для значений в столбцах данных требуют использования фиктивных индексов, так же, как они используются в списке данных команды display.
Поскольку индексированные выражения AMPL редко являются допустимыми именами столбцов для базы данных, они должны сопровождаться ~data-col-name, чтобы обеспечить согласованность имен.
Например, для записи столбца servings, содержащего количество порций каждого закупаемого продукта и столбца с указанием количества покупок в процентах от максимально допустимого, объявление таблицы можно представить как:
table Purchases OUT: [FoodName], Buy ˜ servings, {j in FOOD} 100*Buy[j]/f_max[j] ˜ percent;
или
table Purchases OUT: [FoodName],{j in FOOD} (Buy[j] ˜ servings, 100*Buy[j]/f_max[j] ˜ percent);
В любом случае, поскольку обе data-specs дают выражения, индексированные по FOOD для набора AMPL, результирующая таблица имеет одну строку для каждого элемента этого набора:
FoodName servings percent BEEF 5.36061 53.6061 CHK 2 20 FISH 2 20 HAM 10 100 MCH 10 100 MTL 10 100 SPG 9.30605 93.0605 TUR 2 20
Выражение data-specs также может использовать операторы типа sum, которые определяют свои собственные фиктивные индексы. Таким образом, таблица общего производства и продаж за период для steelT.mod может быть объявлена как:
table SteelTotal OUT: [TIME], {t in 1..T} (sum {p in PROD} Make[p,t] ˜ Made, sum {p in PROD} Sell[p,t] ˜ Sold);
В качестве двумерного примера, рассмотрим таблицу количества продаж и доли спроса:
table SteelSales OUT: [PROD, TIME], Sell, {p in PROD, t in 1..T} Sell[p,t]/market[p,t] ˜ FracDemand;
Результирующая внешняя таблица будет иметь ключевые столбцы PROD и TIME, а также столбцы данных Sell и FracDemand.
Запись строк, на основании явного объявления индексов
Альтернативная форма объявления table указывает, что одна строка таблицы должна быть записана для каждого элемента явно заданного набора AMPL. Чтобы объявление работало таким образом, key-spec должна быть записана как:
set-spec -> [key-col-spec, key-col-spec, ...]
В отличие от стрелки <-, которая указывает значения, которые должны быть считаны в набор, форма -> обозначает, что информация должна быть записана из набора в ключевые столбцы.
Явное выражение для набора индексов строк задается в set-spec, которая может быть именем набора AMPL или любым выражением набора AMPL, заключенным в фигурные скобки {}. Спецификации key-col-spec задают имена соответствующих ключевых столбцов в базе данных. Фиктивные индексы, если необходимо, могут появляться либо в set-spec, либо в key-col-specs. Простейший случай этой формы включает запись столбцов базы данных для компонентов модели, индексированных по одномерному набору. Например, для diet.mod:
table Foods OUT: FOOD-> [FoodName], f_min, Buy, f_max;
Когда выполняется write table Foods, для каждого члена набора AMPL FOOD создается строка таблицы. В этой строке элемент набора записывается в ключевой столбец FoodName, а значения f_min, Buy и f_max, проиндексированные по элементам набора, записываются в одноименные столбцы данных. (Для данных, используемых в нашем примере диеты, результирующая таблица будет такой же, как и для таблицы FoodName, приведенной ранее в этом разделе). Если ключевой столбец имеет то же имя FOOD, что и набор AMPL, соответствующее объявление таблицы имеет вид:
table Foods OUT: FOOD -> [FOOD], f_min, Buy, f_max;
Только в этом особом случае, key-spec, может быть записан в сокращенной форме [FOOD] OUT.
Использование «~» с именами AMPL и суфиксными именами регулируется вышеизложенными соображениями, так, что пример диеты и двойных значений может быть записан:
table Nutrs OUT: NUTR-> [Nutrient], Diet.lslack ˜ lb_slack, Diet.ldual˜ lb_dual, Diet.uslack ˜ ub_slack, Diet.udual ˜ ub_dual;
Команда write table Nutrs запишет ту же таблицу, как было показано ранее.
Более общие выражения для значений в столбцах данных, требуют использования фиктивных индексов. Поскольку строки, которые должны быть записаны, определяются через key-spec, здесь же определяются и фиктивные элементы, (а не в data-specs, как в альтернативной форме выше). Например, чтобы указать столбец, содержащий количество продуктов, купленных в процентах от максимально допустимого, необходимо написать 100 * Buy [j] / f_max [j]. Это в свою очередь, требует определения фиктивного индекса j. Определение может появиться либо в set-spec в виде формы {index-list in set-expr}:
table Purchases OUT: {j in FOOD} -> [FoodName], Buy[j] ˜servings, 100*Buy[j]/f_max[j] ˜ percent; или в key-col-spec в виде ˜ key-col-name: table Purchases OUT: FOOD -> [j ˜ FoodName], Buy[j] ˜ servings, 100*Buy[j]/f_max[j] ˜ percent;
Представленные две формы эквивалентны. В любом случае, когда записывается каждая строка, индекс j принимает значение ключевого столбца, которое используется при интерпретации выражений, которые дают значения для столбцов данных. Для нашего примера результирующая таблица, с ключевым столбцом FoodName и столбцами данных servings и percent, выглядит также, как показано ранее. Аналогично, предыдущий пример таблицы SteelTotal можно записать как:
table SteelTotal OUT: {t in 1..T} -> [TIME], sum{p in PROD} Make[p,t] ˜ Made, sum {p in PROD} Sell[p,t] ˜ Sold;
или
table SteelTotal OUT: {1..T} -> [t ˜ TIME], sum {p in PROD} Make[p,t] ~ Made, sum {p in PROD} Sell[p,t] ~Sold;
Результат будет иметь ключевой столбец TIME, содержащий целые числа от 1 до T, и столбцы данных Made и Sold. (Обратите внимание, так как 1..T является выражением набора, а не именем набора, он должен быть включен в фигурные скобки для использования в качестве спецификации набора.)
Таблицы, соответствующие многомерным наборам, обрабатываются аналогично. Основным требованием является соответствие размерности key-col-specs и set-spec.
Таким образом, таблица, содержащая результаты из steelT.mod, может быть определена в следующем виде:
table SteelProd OUT: {PROD, 1..T} -> [PROD, TIME], Make, Sell, Inv;
Последующее объявление write table SteelProd создает таблицу вида:
PROD TIME Make Sell Inv bands 1 5990 6000 0 bands 2 6000 6000 0 bands 3 1400 1400 0 bands 4 2000 2000 0 coils 1 1407 307 1100 coils 2 1400 2500 0 coils 3 3500 3500 0 coils 4 4200 4200 0
Этот результат не совсем совпадает с таблицей, созданной в предыдущем примере SteelProd, поскольку записываемые здесь строки точно соответствуют элементам набора {PROD, 1..T}, а не выводятся из наборов индексации Make, Sell, и Inv. В частности, значения Inv ["band",0] и Inv ["coils",0] не отображаются в этой таблице.
Условия применения фиктивных индексов для более высоких измерений такие же, как и для одного измерения. Таким образом, пример SteelSales может быть записан с использованием фиктивных индексов, определенных в set-spec:
table SteelSales OUT: {p in PROD, t in 1..T} -> [PROD, TIME], Sell[p,t] ˜ sold, Sell[p,t]/market[p,t] ˜ met;
Или с фиктивными индексами, добавленными в key-col-specs:
table SteelSales OUT: {PROD,1..T} -> [p ˜ PROD, t ˜ TIME], Sell[p,t]˜ sold, Sell[p,t]/market[p,t] ˜ met;
Если фиктивные индексы появляются одновременно в set-spec, и в key-col-specs, тогда индексы key-col-specs имеют приоритет.