2009-02-08 4 views
9

В MS Sql Server легко создавать поля автоинкремента. В моих системах я остановился, чтобы использовать поля автоинкремента для первичных ключей, и теперь я использую Guid. Это было потрясающе, у меня есть много преимуществ с этими изменениями. Но в других непервичных ключевых областях мне действительно нужно было реализовать «мягкий автоинкремент». Это потому, что моя система независима от DB, поэтому я автоматически создаю значение autoinc в C#.Поля AutoIncrement в базах данных без поля автоинкремента

Я бы хотел, чтобы решения для полей автоинкремента в базах данных без автоинкремента, какое решение вы используете и почему? Есть какое-то утверждение Sql Ansi об этом? и генерирование непосредственно из моего C#, является лучшим решением?

PS: Я знаю, что выбрать максимум (ID) +1 из таблицы это не совсем одновременно дружеские ...

+0

Я бы предложил переписать заголовок на что-то более понятное, как «Создание полей автоинкремента с независимыми базами данных». Меня беспокоит то, что я не думаю, что найду твой титул, когда буду искать ту же тему. – Elijah

+0

Вам действительно нужно постоянно увеличивающееся число, или вам просто нужна уникальность? –

+0

Да, мне нужно «постоянно увеличивать число», для других ситуаций я использую гиды, CRC и т. Д. –

ответ

15

Механизм генерации уникальных идентификационных значений не должен быть подвержен изоляции транзакций. Это необходимо для того, чтобы база данных генерировала отдельное значение для каждого клиента, лучше, чем трюк SELECT MAX(id)+1 FROM table, что приводит к условию гонки, если два клиента пытаются одновременно присвоить новые значения id.

Вы не можете имитировать эту операцию, используя стандартные SQL-запросы (если вы не используете блокировки таблиц или serializable транзакций). Это должен быть механизм, встроенный в механизм базы данных.

ANSI SQL не описал операцию генерации уникальных значений для суррогатных ключей до SQL: 2003. До этого не было стандарта для автоинкрементных столбцов, поэтому почти каждая марка RDBMS предоставила некоторое частное решение. Естественно, они сильно различаются, и нет возможности использовать их простым, независимым от базы данных способом.

  • MySQL имеет возможность AUTO_INCREMENT столбца или SERIAL псевдо-типа данных, которое эквивалентно BIGINT UNSIGNED AUTO_INCREMENT;
  • Microsoft SQL Server имеет параметр столбца IDENTITY и NEWSEQUENTIALID(), который является чем-то вроде автоматического увеличения и GUID;
  • У Oracle есть объект SEQUENCE;
  • PostgreSQL имеет объект SEQUENCE или SERIAL псевдо-тип данных, который неявно создает объект последовательности в соответствии с соглашением об именах;
  • InterBase/Firebird имеет объект GENERATOR, который в значительной степени похож на SEQUENCE в Oracle; Жар-птица 2.1 поддерживает SEQUENCE;
  • SQLite рассматривает любое целое число, объявленное как ваш первичный ключ, как неявное автоинкремент;
  • DB2 UDB имеет практически все: SEQUENCE объектов, или вы можете объявить столбцы с опцией «GEN_ID».

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

+1

в Firebird 2.1: есть также SEQUENCE http://www.firebirdsql.org/rlsnotesh/rlsnotes210.html#rnfb20x-ddl-syntax-create-sequence –

+0

@Hugues: Спасибо за подсказку! Я добавил его в список. –

+0

SQL-сервер также имеет ** [NEWSEQUENTIALID()] (http://technet.microsoft.com/en-us/library/ms189786.aspx) **, что является чем-то вроде auto-incrementing и GUID. –

1

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

1

Традиционное решение должны иметь таблицу идентификаторов, которые выглядят как-то этот

CREATE TABLE ids (
    tablename VARCHAR(32) NOT NULL PRIMARY KEY, 
    nextid INTEGER 
) 

который ы заселенный одной строкой на таблицу при создании базы данных.

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

+2

Это решение зависит от условий гонки, и каждая основная СУБД имеет безопасную для транзакции альтернативу. –

2

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

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

сделано таким образом, ваши вставки может быть столь же просто, как:

INSERT INTO foo (id, name, rank, serial_number) 
VALUES (getNextFooId(), 'bar', 'fooRank', 123456); 

Затем определяют getNextFooId() в базе данных определенным образом, когда база данных инициализации.

0

Если вам нужно поле автоинкремента с непервичными ключами, очень приятное MySQL-решение для создания последовательностей арбитража - использовать относительно неизвестную функцию last_insert_id(expr).

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

(от http://dev.mysql.com/doc/refman/5.1/en/information-functions.html#function_last-insert-id)

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

CREATE TABLE `post` (
    `id` INT(10) UNSIGNED NOT NULL, 
    `title` VARCHAR(100) NOT NULL, 
    `comment_sequence` INT(10) UNSIGNED NOT NULL DEFAULT '0', 
    PRIMARY KEY (`id`) 
); 

CREATE TABLE `comment` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `post_id` INT(10) UNSIGNED NOT NULL, 
    `sequence` INT(10) UNSIGNED NOT NULL, 
    `content` TEXT NOT NULL, 
    PRIMARY KEY (`id`) 
); 

INSERT INTO post(id, title) VALUES(1, 'first post'); 
INSERT INTO post(id, title) VALUES(2, 'second post'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'blah'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'foo'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'bar'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=2; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(2, Last_insert_id(), 'lorem'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=2; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(2, Last_insert_id(), 'ipsum'); 

SELECT * FROM post; 
SELECT * FROM comment;