2009-02-08 19 views
9

En MS Sql Server es fácil crear campos de autoincrement. En mis sistemas me detuve para usar campos de autoincrement para claves primarias, y ahora uso Guid's. Fue increíble, tengo muchas ventajas con ese cambio. Pero en otros campos clave no primarios, realmente necesitaba implementar una "autoincrementación suave". Es porque mi sistema es independiente de DB, así que creo el valor autoinc programáticamente en C#.Campos de AutoIncrement en bases de datos sin campo de autoincrement

Me gustaría obtener soluciones para campos de autoincrement en bases de datos sin autoincrement, ¿cuál es la solución que su uso y por qué? Hay alguna declaración Sql Ansi sobre esto? y generar directamente desde mi C#, ¿es una mejor solución?

PD: Sé que select max (id) de la tabla 1 en realidad no es concurrente amigable ...

+0

Sugeriría reescribir el título para algo que sea un poco más claro como "Crear campos de autoincrementación independientes de la base de datos". Mi preocupación es que no creo que encontraría su título cuando buscaría el mismo tema. – Elijah

+0

¿Realmente necesita un número que se incrementa continuamente, o simplemente necesita exclusividad? –

+0

Sí Necesito "un número de incremento continuo", para otras situaciones uso guids, CRC, etc. ... –

Respuesta

15

El mecanismo para generar valores únicos de identificación no debe estar sujeto a aislamiento de transacción. Esto es necesario para que la base de datos genere un valor distinto para cada cliente, mejor que el truco de SELECT MAX(id)+1 FROM table, que da como resultado una condición de carrera si dos clientes intentan asignar nuevos valores id al mismo tiempo.

No puede simular esta operación utilizando consultas SQL estándar (a menos que use bloqueos de tabla o transacciones serializables). Tiene que ser un mecanismo integrado en el motor de la base de datos.

ANSI SQL no describió una operación para generar valores únicos para claves sustitutas hasta SQL: 2003. Antes de eso, no había un estándar para las columnas de incremento automático, por lo que casi todas las marcas de RDBMS proporcionaban alguna solución propietaria. Naturalmente, varían mucho y no hay forma de usarlos de una manera simple, independiente de la base de datos.

  • MySQL tiene la opción AUTO_INCREMENT columna, o SERIAL pseudo-tipo de datos que es equivalente a BIGINT UNSIGNED AUTO_INCREMENT;
  • Microsoft SQL Server tiene la opción de columna IDENTITY y NEWSEQUENTIALID() que es algo entre autoincremento y GUID;
  • Oracle tiene un objeto SEQUENCE;
  • PostgreSQL tiene un objeto SEQUENCE o SERIAL pseudo-tipo de datos que implícitamente crea un objeto de secuencia de acuerdo con una convención de nomenclatura;
  • InterBase/Firebird tiene un objeto GENERATOR que es muy parecido a SEQUENCE en Oracle; Firebird 2.1 admite SEQUENCE también;
  • SQLite trata cualquier entero declarado como clave principal como autoincrementado de forma implícita;
  • DB2 UDB tiene casi todo: SEQUENCE objetos, o puede declarar columnas con la opción "GEN_ID".

Todos estos mecanismos operan fuera del aislamiento de transacciones, asegurando que los clientes concurrentes obtengan valores únicos. También en todos los casos hay una forma de consultar el valor generado más recientemente para su sesión actual. Tiene que haberlo, así que puede usarlo para insertar filas en una tabla secundaria.

+1

en Firebird 2.1: también hay SECUENCIA http://www.firebirdsql.org/rlsnotesh/rlsnotes210.html#rnfb20x-ddl-syntax-create-sequence –

+0

@Hugues: ¡Gracias por la sugerencia! Lo he agregado a la lista. –

+0

SQL-Server también tiene el ** [NEWSEQUENTIALID()] (http://technet.microsoft.com/en-us/library/ms189786.aspx) ** que es algo entre autoincrementar y GUID. –

1

La mayoría de las bases de datos que no tienen campos autoincrement como SQL Server (estoy pensando en Oracle específicamente) tiene secuencias en las que pregunta a la secuencia por el próximo número. No importa cuántas personas soliciten números al mismo tiempo, todos obtienen un número único.

1

La solución tradicional es tener una tabla de identificadores que se ven algo como esto

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

que s poblada con una fila por cada mesa cuando se crea la base de datos.

Luego haga una selección para obtener la próxima identificación siguiente para la tabla en la que está insertando, increméntela y luego actualice la tabla con la nueva identificación. Obviamente, hay problemas de bloqueo aquí, pero para las bases de datos con tasas de inserción moderadas funciona bien. Y es completamente portátil.

+2

Esta solución está sujeta a condiciones de carrera, y todos los RDBMS principales tienen una alternativa de transacción segura. –

2

Creo que su pregunta es bastante buena. Sin embargo, es fácil perderse tratando de encontrar una solución solo de SQL. En realidad, querrá la optimización y la seguridad de las transacciones que ofrece el uso de las implementaciones de base de datos de los tipos de autoincrement.

Si necesita abstraer la implementación del operador de autoincrement, ¿por qué no crear un procedimiento almacenado para devolver el valor de autoincrement? La mayoría de los dialectos SQL acceden a los procedimientos almacenados de forma relativamente similar y deberían ser más portátiles. Luego puede crear una lógica de autoincrementación específica de la base de datos cuando crea el sproc, eliminando la necesidad de cambiar muchas declaraciones para que sean específicas del proveedor.

Hecho de esta manera, sus inserciones pueden ser tan simples como:

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

a continuación, definir getNextFooId() en una base de datos de manera específica cuando se inicializa la base de datos.

0

Si necesita un campo de autoincrement de clave no primaria, una muy buena solución de MySQL para crear secuencias de arbitraty es utilizar la función relativamente desconocida last_insert_id(expr).

Si expr se da como un argumento para LAST_INSERT_ID(), el valor del argumento es devuelto por la función y es recordado como el siguiente valor para ser devuelto por LAST_INSERT_ID(). Este puede ser utilizado para simular secuencias ...

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

Aquí es un ejemplo que muestra cómo una secuencia secundaria se puede mantener para la numeración de los comentarios para cada mensaje:

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; 
Cuestiones relacionadas