2009-03-19 12 views
14

En Oracle hay un mecanismo para generar números de secuencia, por ejemplo;Secuencia de Oracle pero luego en MS SQL Server

CREATE SEQUENCE supplier_seq 

    MINVALUE 1 
    MAXVALUE 999999999999999999999999999 
    START WITH 1 
    INCREMENT BY 1 
    CACHE 20; 

y luego ejecutar la declaración

supplier_seq.nextval 

para recuperar el siguiente número de secuencia.

¿Cómo crearía la misma funcionalidad en MS SQL Server?

Editar: No estoy buscando formas de generar automáticamente claves para los registros de la tabla. Necesito generar un valor único que pueda usar como ID (lógico) para un proceso. Entonces necesito la funcionalidad exacta que proporciona Oracle.

Respuesta

15

No hay coincidencia exacta.

El equivalente es la IDENTIDAD que puede establecer como un tipo de datos al crear una tabla. SQLSERVER creará automáticamente un número de secuencia en ejecución durante la inserción. El último valor insertado puede ser obtenida llamando SCOPE_IDENTITY() o mediante la consulta de la variable de sistema @@ IDENTITY (como se ha señalado por Frans)

Si necesita el equivalente exacto, lo que se necesita para crear una tabla y luego escribir un procedimiento para devolver el siguiente valor y otras operaciones. Ver la respuesta de Marks sobre las trampas en esto.

Editar:
SQL Server ha implementado la secuencia similar a la de Oracle. Por favor, consulte esta pregunta para más detalles.

How would you implement sequences in Microsoft SQL Server?

+0

necesito el equivalente exacto, y el uso de un procedimiento fue la primera idea también. Me preguntaba si había una solución más 'natural' para esto. – Raymond

+0

Aquí no tienes suerte. – Dheer

+1

Parece que SQL Server 2012 introdujo objetos SEQUENCE por http://stackoverflow.com/questions/282943/how-would-you-implement-sequences-in-microsoft-sql-server y http://msdn.microsoft.com/ es-us/library/ff878091.aspx – Loudenvier

5

hacen del campo un campo de identidad. El campo obtendrá su valor automáticamente. Puede obtener el último valor insertado llamando a SCOPE_IDENTITY() o consultando la variable del sistema @@ IDENTITY

Se prefiere la función SCOPE_IDENTITY().

3

Como DHeer dijo que no hay absolutamente ninguna coincidencia exacta. Si intenta crear su propio procedimiento para hacer esto, siempre evitará que su aplicación se escale.

Las secuencias de Oracle son altamente escalables.

Bien, lo llevo un poco atrás. Si realmente está dispuesto a concentrarse en la concurrencia y está dispuesto a tomar los números fuera de servicio como es posible con una secuencia, tiene una oportunidad. Pero como parece que no está familiarizado con t-sql, comenzaría a buscar otras opciones (portar una aplicación de Oracle a MSSS, es lo que está haciendo)

Por ejemplo, solo genere un GUID en la función "nextval". Eso escalaría.

Ah, y NO use una tabla para todos los valores, solo para mantener el valor máximo en la caché. Tendría que bloquearlo para asegurarse de dar valores únicos y aquí es donde dejará de escalar. Tendrá que averiguar si hay una manera de almacenar en caché los valores en la memoria y el acceso programático a algún tipo de bloqueos ligeros: bloqueos de memoria, no bloqueos de tabla.

+0

Puede haber lagunas en una secuencia de Oracle pero fuera de servicio? – tuinstoel

+0

Está justo en la secuencia DDL. ORDER Especifique ORDER para garantizar que los números de secuencia se generen por orden de solicitud. Esta cláusula es útil si está utilizando los números de secuencia como marcas de tiempo. Garantizar el orden generalmente no es importante para las secuencias utilizadas para generar claves primarias. –

1

Desearía que SQL Server tuviera esta característica. Haría tantas cosas más fáciles.

Así es como he entendido esto.

Crea una tabla llamada tblIdentities. En esta tabla, ponga una fila con sus valores mínimos y máximos y con qué frecuencia debe reiniciarse el número de Secuencia. También ponga el nombre de una nueva tabla (llámelo tblMySeqNum). Hacer esto hace que agregar más generadores de número de secuencia más tarde sea bastante fácil.

tblMySeqNum tiene dos columnas. ID (que es una identidad int) e InsertDate (que es una columna de fecha y hora con un valor predeterminado de GetDate()).

Cuando necesite un nuevo número seq, llame a un sproc que se inserta en esta tabla y use SCOPE_IDENTITY() para crear la identidad. Asegúrese de no haber excedido el máximo en tblIdentities. Si tiene, entonces devuelve un error. Si no, devuelve tu número de secuencia.

Ahora, para reiniciar y limpiar. Tenga un trabajo que se ejecute con la periodicidad necesaria y que verifique todas las tablas enumeradas en tblIdentites (solo una por ahora) para ver si necesitan restablecerse. Si han pulsado el valor o la hora de reinicio, llame a DBCC IDENT RESEED en el nombre de la tabla enumerada en la fila (tblMySeqNum en este ejemplo). Este también es un buen momento para borrar nuestras filas adicionales que realmente no necesita en esa tabla.

NO haga la limpieza o la resiembra en su sproc que obtiene la identidad. Si lo hace, entonces su generador de números de secuencia no se escalará en absoluto.

Como he dicho, sería mucho más fácil que esta característica estuviera en SQL Server, pero he descubierto que este funcionamiento funciona bastante bien.

Vaccano

+0

Me encanta cuando recibo un voto negativo, pero el votante es demasiado cobarde para decir por qué están bajando la votación ... – Vaccano

1

Esto podría ya han sido contestadas hace mucho tiempo ... pero desde SQL 2005 en adelante se puede utilizar la función ROW_NUMBER ... un ejemplo sería:

select ROW_NUMBER() OVER (ORDER BY productID) as DynamicRowNumber, xxxxxx,xxxxx 

El OVER declaración utiliza el ORDER BY para la clave primaria única en mi caso ...

Espero que esto ayude ... ¡¡¡no más tablas temporales, o combinaciones extrañas !!

6

La identidad es la mejor y más escalable solución, PERO, si necesita una secuencia que no es un int creciente, como 00A, 00B, 00C o alguna secuencia especial, existe un segundo método mejor. Si se implementa correctamente, se escala correctamente, pero si se implementa mal, escalará mal. Dudo en recomendarlo, pero lo que usted hace es:

  1. Debe almacenar el "próximo valor" en una tabla. La tabla puede ser simple, de una sola fila y de una sola columna con ese valor. Si tiene varias secuencias, pueden compartir la tabla, pero puede obtener menos contención si tiene tablas separadas para cada una.
  2. Necesita escribir una sola declaración de actualización que incrementará ese valor en 1 intervalo. Puede poner la actualización en un proceso almacenado para que sea fácil de usar y evitar que se repita en el código en diferentes lugares.
  3. Usando la secuencia correctamente, para que se escale razonablemente (no, no tan bien como Identitiy :-) requiere dos cosas: a. la declaración de actualización tiene una sintaxis especial hecha para este problema exacto que incrementará y devolverá el valor en una sola declaración; segundo. debe obtener el valor de la secuencia personalizada ANTES del inicio de una transacción y fuera del alcance de la transacción. Esa es una razón por la cual la identidad escala - devuelve un nuevo valor independientemente del alcance de la transacción, para cualquier inserción intentado, pero no retrocede en la falla. Eso significa que no bloqueará, y también significa que tendrá vacíos para las transacciones fallidas.

La sintaxis de actualización especial varía un poco por versión, pero la esencia es que usted hace una asignación a una variable y la actualización en la misma instrucción. Para el año 2008, Itzik Ben-Gan tiene esta solución clara y simple: http://www.sqlmag.com/Articles/ArticleID/101339/101339.html?Ad=1

La vieja escuela de 2000 y el método más tarde se ve así:

ACTUALIZACIÓN secuenciaNavegación SET @localVar = Valor = valor + 5 - cambiar la cola finalización de su lógica de incremento

Esto aumentará y le devolverá el siguiente valor.

Si no puede tener lagunas (resista ese requisito :-) entonces es técnicamente posible poner esa actualización o proceso en el resto de su transacción, pero recibe un gran golpe de concurrencia ya que cada inserción espera el previo uno para comprometerse.

No me puedo atribuir ningún mérito a esto; Lo aprendí todo de Itzik.

0

No es una respuesta exacta, pero además de algunas respuestas existentes

SCOPE_IDENTITY (Transact-SQL)

SCOPE_IDENTITY, IDENT_CURRENT, y @@ IDENTITY son funciones similares porque devuelven valores que son e insertado en columnas de identidad.

IDENT_CURRENT no está limitado por el alcance y la sesión; está limitado a una tabla especificada . IDENT_CURRENT devuelve el valor generado para una tabla específica en cualquier sesión y cualquier ámbito. Para obtener más información, vea IDENT_CURRENT (Transact-SQL).

Esto significa dos sesiones diferentes pueden tener un mismo número valor de identidad o secuencia, de forma de evitar esto y obtener el número único para todas las sesiones utilizan IDENT_CURRENT

0

Exactamente debido a esto IDENT_CURRENT no está limitado por el alcance y la sesión ; está limitado a una tabla especificada. necesitamos usar SCOPE_IDENTITY() porque la identidad del alcance nos dará un número único generado en nuestra sesión, y la singularidad es proporcionada por la identidad misma.

1

Si puede actualizar a SQL Server 2012 puede usar objetos SEQUENCE. Incluso SQL Server 2012 Express tiene soporte para secuencias.

CREATE SEQUENCE supplier_seq 
    AS DECIMAL(38) 
    MINVALUE 1 
    MAXVALUE 999999999999999999999999999 
    START WITH 1 
    INCREMENT BY 1 
    CACHE 20; 

SELECT NEXT VALUE FOR supplier_seq 
SELECT NEXT VALUE FOR supplier_seq 
SELECT NEXT VALUE FOR supplier_seq 
SELECT NEXT VALUE FOR supplier_seq 
SELECT NEXT VALUE FOR supplier_seq 

Resultados: en

--------------------------------------- 
1 

(1 row(s) affected) 


--------------------------------------- 
2 

(1 row(s) affected) 


--------------------------------------- 
3 

(1 row(s) affected) 


--------------------------------------- 
4 

(1 row(s) affected) 


--------------------------------------- 
5 

(1 row(s) affected) 

Sólo tenga cuidado al especificar el tipo de datos adecuado. Si no lo hubiera especificado, el MAXVALUE que ha proporcionado no sería aceptado, es por eso que he usado DECIMAL con la mayor precisión posible.

Más en las secuencias aquí: http://msdn.microsoft.com/en-us/library/ff878091.aspx

Cuestiones relacionadas