Использование базовой функции R aggregate
:
aggregate(value ~ name, dat1, I)
# name value.1 value.2 value.3 value.4
#1 firstName 0.4145 -0.4747 0.0659 -0.5024
#2 secondName -0.8259 0.1669 -0.8962 0.1681
Механизм MyISAM MySQL может это сделать. См. Их руководство в разделе Использование AUTO_INCREMENT :
Для таблиц MyISAM вы можете указать AUTO_INCREMENT на вторичном столбце в индексе с несколькими столбцами. В этом случае генерируемое значение для столбца AUTO_INCREMENT вычисляется как MAX (auto_increment_column) + 1 WHERE prefix = given-prefix. Это полезно, когда вы хотите поместить данные в упорядоченные группы.
blockquote>Документы продолжаются после этого параграфа, демонстрируя пример.
Механизм InnoDB в MySQL не поддерживайте эту функцию, что является неудачным, потому что лучше использовать InnoDB практически во всех случаях.
Вы не можете эмулировать это поведение с помощью триггеров (или любых операторов SQL, ограниченных областью транзакций) без блокировки таблиц в INSERT. Рассмотрим эту последовательность действий:
- Mario начинает транзакцию и вставляет новую строку для пользователя 4.
- Билл начинает транзакцию и вставляет новую строку для пользователя 4.
- Сеанс Марио запускает триггер для вычисления MAX (id) +1 для пользователя 4. Вы получаете 3.
- В сеансе Билла срабатывает триггер для вычисления MAX (id). Я получаю 3.
- Заседание Билла заканчивает его INSERT и совершает.
- Сеанс Марио пытается завершить свой INSERT, но теперь строка с (userid = 4, id = 3) существует, поэтому Марио получает конфликт с первичным ключом.
В общем, вы не можете контролировать порядок выполнения этих шагов без какой-либо синхронизации.
Решения для это либо:
- Получите эксклюзивную блокировку таблицы. Перед тем, как попробовать INSERT, заблокируйте таблицу. Это необходимо, чтобы предотвратить одновременные ВСТАВКИ от создания условия гонки , как в приведенном выше примере. Необходимо заблокировать всю таблицу, так как вы пытаетесь ограничить INSERT, нет конкретной строки для блокировки (если вы пытаетесь управлять доступом к данной строке с помощью UPDATE, вы можете заблокировать только определенную строку). Но блокировка таблицы приводит к тому, что доступ к таблице становится последовательным, что ограничивает вашу пропускную способность.
- Сделайте это за пределами транзакции. Создайте идентификационный номер таким образом, чтобы он не скрывался от двух параллельных транзакций. Кстати, это то, что делает AUTO_INCREMENT. Два параллельных сеанса будут получать уникальное значение id независимо от порядка выполнения или порядка фиксации. Но отслеживание последнего сгенерированного идентификатора для каждого пользователя требует доступа к базе данных или дублированного хранилища данных. Например, ключ memcached для каждого пользователя, который может быть инкрементирован атомарно .
Относительно легко убедиться, что вставки получают значения unique , Но трудно гарантировать, что они получат последовательные порядковые значения. Также рассмотрите:
- Что произойдет, если вы вСТАВИТЕСЬ в транзакции, а затем откат? Вы указали значение 3 в этой транзакции, а затем я выделил значение 4, поэтому, если вы откат и я зафиксирую, теперь есть пробел.
- Что произойдет, если INSERT завершится с ошибкой из-за других ограничений на таблица (например, другой столбец NOT NULL)? Вы также можете получить пробелы.
- Если вы когда-либо удаляете строку, вам нужно перенумеровать все следующие строки для одного и того же идентификатора пользователя? Что это делает для ваших записей memcached, если вы используете это решение?
SQL Server должен позволить вам сделать это. Если вы не можете реализовать это с помощью вычисленного столбца (возможно, нет - существуют некоторые ограничения), вы можете реализовать его в триггере .
MySQL также позволит вам реализовать это через триггеры.
В комментарии вы задаете вопрос об эффективности. Если вы не имеете дело с экстремальными объемами, сохранение 8-байтного DATETIME не является значительным накладным расходами по сравнению с использованием, например, 4-байтового INT.
Он также значительно упрощает ваши вставки данных, а также (g10)
Если вам это нужно, будьте осторожны с именами полей. Если в таблице есть uid
и id
, я ожидаю, что id
будет уникальным в этой таблице, а uid
- другим. Вместо этого используйте имена полей property_id
и amendment_id
.
В терминах реализации есть, как правило, два варианта.
1) , Триггер
Реализации различаются, но логика остается неизменной. Поскольку вы не указываете RDBMS (кроме NOT MS / Oracle), общая логика проста ...
MAX(amendment_id)
для вставки свойства_id MAX(amendment_id) + 1
Вещи, которые нужно знать, - это ... - вставляются одновременно несколько записей - вставляются записи с уже заполненным изменением_id - обновляются изменения существующих записей
2). Сохраненная процедура
Если вы используете хранимую процедуру для управления записью в таблицу, вы получаете намного больше контроля.
Я лично рекомендую маршрут хранимой процедуры, но триггеры работают.
Важно, чтобы ваши типы данных были правильными.
То, что вы описываете, является многочастным ключом. Поэтому используйте многочастный ключ. Не пытайтесь закодировать все в волшебное целое, вы будете отравлять остальную часть вашего кода.
Если запись идентифицирована (entity_id,version_number)
, тогда обнимайте это описание и используйте его напрямую, вместо того, чтобы изменять значение ваших ключей. Вам придется писать запросы, которые ограничивают номер версии, но это нормально. Базы данных хороши в этом.
version_number
может быть временной меткой, как предлагает a_horse_with_no_name. Это неплохая идея. Не существует значимого недостатка производительности для использования временных меток вместо простых целых чисел. Вы получаете значение , что более важно.
Вы могли бы поддерживать таблицу «последней версии», которая содержит для каждого entity_id
только запись с самым большим количеством файлов, недавний version_number
. Это будет больше для вас, так что сделайте это, только если вам действительно нужна производительность.