Hay alguna forma de obtener la funcionalidad de Sql Server 2005+ Secuencial Guid generador sin necesidad de insertar registros a leer de nuevo en viaje de ida o de la invocación de un nativo de ganar dll llamada? Vi a alguien que contesta con una forma de utilizar la rpcrt4.dll pero no estoy seguro de si sería capaz de trabajar desde mi entorno hospedado para la producción.

Edición: de Trabajo con @Juan Boker la respuesta que he intentado convertirlo en más de un GuidComb generador en lugar de depender de la última Guid generado otra que volver a empezar. Que para la semilla en lugar de comenzar con el Guid.Vacío que yo uso

public SequentialGuid()
{
    var tempGuid = Guid.NewGuid();
    var bytes = tempGuid.ToByteArray();
    var time = DateTime.Now;
    bytes[3] = (byte) time.Year;
    bytes[2] = (byte) time.Month;
    bytes[1] = (byte) time.Day;
    bytes[0] = (byte) time.Hour;
    bytes[5] = (byte) time.Minute;
    bytes[4] = (byte) time.Second;
    CurrentGuid = new Guid(bytes);
}

He basado en que los comentarios sobre

//3 - the least significant byte in Guid ByteArray 
        [for SQL Server ORDER BY clause]
//10 - the most significant byte in Guid ByteArray 
        [for SQL Server ORDERY BY clause]
SqlOrderMap = new[] {3, 2, 1, 0, 5, 4, 7, 6, 9, 8, 15, 14, 13, 12, 11, 10};

A qué se parece esto de la manera que me gustaría semilla de un guid con el DateTime o a qué se parece que debo hacerlo a la inversa y trabajar hacia atrás desde el final de la SqlOrderMap índices? No estoy demasiado preocupado por su paginación romper en cualquier momento inicial guid sería creado, ya que sólo se produciría durante la aplicación se recicla.

  • Primero de todo ¿qué es un propósito para esto? Yo estoy pidiendo esto porque todo el propósito de la Guid es aleatoria y único. Si sólo se necesita algo incremental, puede utilizar el tipo de Int de campo y ponerlo en AutoIncremental.
  • de yafla.com/dennisforbes/Sequential-GUIDs-in-SQL-Server/… : Como se discutió en la entrada sobre el uso de los Identificadores de la base de datos Guid en SQL Server 2000, al menos desde la perspectiva del usuario, «al azar». Esto puede conducir a una fragmentación y la divide en sus datos, y es una razón común para evitar Guid en el primer lugar.
  • Bueno, que en realidad podría ayudar. Yo diría que probablemente hay un malentendido con el propósito de Guid. Si Chris sólo necesita algo incremental, hay otra buena solución llamada Int + AutoIncrement. No hay necesidad para GUID en este caso. Puede alguien explicar por qué alguien tendría que tener incremental Guid?!
  • Vitaly, se está perdiendo el punto. SQL200x tiene un Secuencial generador Guid para optimizar la indexación basada en los Guid. Un montón de términos seearch en.
  • Henk, optimización tiene que ser realizada sólo por los resultados de las pruebas de velocidad. Hay muchos ejemplos de cuando se hacían suposiciones erróneas y «optimización» con base en estos supuestos, en realidad no funciona. Me preguntaba si hay algún propósito real secuencial guid además de la hipótesis de que esta es la mejor manera de ir para cualquier estructura de la tabla y cualquier duda por que en la tabla. También consulte este artículo con pruebas de velocidad en Guid que se compara entero vs secuencial guid: sql-server-performance.com/articles/per/…
  • El propósito de este para tener un guid que va a generar las claves de cerca uno de otro desde mi aplicación para ser enviados a la base de datos que puedo tener un índice agrupado en mi columna de clave principal (guid).
  • Veo lo que quieres decir, esto es exactamente lo que yo estaba diciendo acerca de. Guid tienen pros y contras. Pros de Guid es absoluta singularidad. Así, por ejemplo, si la combinación de 2 bases de datos, no es necesario crear nuevos identificadores (debido a que nunca se intersecan). Los contras son que Guid es más largo que el entero. Usted puede utilizar Secuencial Guid y obtener un mejor rendimiento de azar, pero perderá la ventaja: la unicidad absoluta. Y que podría estar bien: no nos fusionar bases de datos muy a menudo, pero tiene otra alternativa entero claves que apoyan autoincrement, así que por favor, considere el uso de ellos.
  • Según la base de datos para generar las llaves para mí no es aceptable la OMI.
  • estamos hablando acerca de SQL Server 2005+ aquí.
  • ¿De qué manera es su propuesta de solución ‘secuencial’, dado que se llame NewGuid() cada vez que para la construcción de la base? Lo que realmente se quiere hacer?
  • Ah, acabo de leer @Juan Boker del artículo enlazado; ahora todo tiene sentido… sin embargo no su código de producir dos posiblemente no secuencial Guid si se ejecuta dos veces en 1 segundo? (Porque las partes de NewGuid() que no cambie no cambie de forma secuencial).
  • Quiero Identificadores generados por la aplicación, no la base de datos.
  • Todavía estaría secuencial, pero uno podría ser menor que el otro. El orden no importa, porque un índice agrupado les ordenen de bajo a alto (o algo similar) de todos modos.
  • Posibles duplicados de hay una .NET equalent a los Servidores SQL server newsequentialid()
  • no están duplicados, esa pregunta fue acerca de cómo hacerlo, mientras que depender de SQL Server, mientras que la mía era hacerlo sin la dependencia. No estoy tratando de reformular casi una década de antigüedad pregunta si tienen tan poco que hacer, por favor, hacer un intento.

11 Comentarios

  1. 68

    Sólo podría utilizar el mismo Función de API de Win32 que utiliza SQL Server:

    y aplicar algunas de desplazamiento de bits a poner los valores en orden big-endian.

    Y ya lo quieres en C#:

    private class NativeMethods
    {
    [DllImport("rpcrt4.dll", SetLastError=true)]
    public static extern int UuidCreateSequential(out Guid guid);
    }
    public static Guid NewSequentialID()
    {
    //Code is released into the public domain; no attribution required
    const int RPC_S_OK = 0;
    Guid guid;
    int result = NativeMethods.UuidCreateSequential(out guid);
    if (result != RPC_S_OK)
    return Guid.NewGuid();
    //Endian swap the UInt32, UInt16, and UInt16 into the big-endian order (RFC specified order) that SQL Server expects
    //See https://stackoverflow.com/a/47682820/12597
    //Short version: UuidCreateSequential writes out three numbers in litte, rather than big, endian order
    var s = guid.ToByteArray();
    var t = new byte[16];
    //Endian swap UInt32
    t[3] = s[0];
    t[2] = s[1];
    t[1] = s[2];
    t[0] = s[3];
    //Endian swap UInt16
    t[5] = s[4];
    t[4] = s[5];
    //Endian swap UInt16
    t[7] = s[6];
    t[6] = s[7];
    //The rest are already in the proper order
    t[8] = s[8];
    t[9] = s[9];
    t[10] = s[10];
    t[11] = s[11];
    t[12] = s[12];
    t[13] = s[13];
    t[14] = s[14];
    t[15] = s[15];
    return new Guid(t);
    }

    Ver también


    De Microsoft UuidCreateSequential es sólo una aplicación de un tipo 1 uuid de RFC 4122.

    Un uuid tiene tres partes importantes:

    • node: (6 bytes) – dirección MAC de la computadora
    • timestamp: (7 bytes) – número de 100 ns intervalos desde las 00:00:00.00, 15 de octubre de 1582 (la fecha de la reforma Gregoriana del calendario Cristiano)
    • clockSequenceNumber (2 bytes) – contador en caso de generar un guid más rápido que el de 100 ns, o cambia su dirección mac

    El algoritmo básico es:

    1. obtener un amplio sistema de bloqueo
    2. leer el último node, timestamp y clockSequenceNumber de almacenamiento persistente (registro/archivo)
    3. obtener la corriente node (es decir, dirección MAC)
    4. obtener la corriente timestamp
      • a) si el estado guardado no estaba disponible o está dañado, o la dirección mac ha cambiado, generar un azar clockSequenceNumber
      • b) si el estado estaba disponible, pero el actual timestamp es la misma o mayor que la salvó de marca de tiempo, incremento de la clockSequenceNumber
    5. guardar node, timestamp y clockSequenceNumber parte posterior para el almacenamiento persistente
    6. liberar el bloqueo global
    7. formato el guid de la estructura de acuerdo a la rfc

    Hay una de 4 bits, el número de versión y 2 bits variante que también deben ser ANDed en los datos:

    guid = new Guid(
    timestamp & 0xFFFFFFFF,  //timestamp low
    (timestamp >> 32) & 0xFFFF, //timestamp mid
    ((timestamp >> 40) & 0x0FFF), | (1 << 12) //timestamp high and version (version 1)
    (clockSequenceNumber & 0x3F) | (0x80), //clock sequence number and reserved
    node[0], node[1], node[2], node[3], node[4], node[5], node[6]);

    Nota: Completamente probados; acabo de eyeballed de la RFC.

    • el orden de bytes puede ser cambiado (Aquí está el orden de los bytes para sql server)
    • es posible que desee crear su propia versión, por ejemplo, la Versión 6 (versión 1-5 se definen). De esa manera usted está garantizado para ser universalmente único
    • Esto es útil para los demás, yo originalmente había preguntado cómo NO hacerlo con el archivo DLL llamada.
    • Oh, lo siento, estoy totalmente perdido. Re-leer la pregunta que yo…sorta ver.
    • el guid generado por UuidCreateSequential no se pueden clasificar utilizando el guid comparador, hay una manera de hacer eso?
    • He actualizado la respuesta, de modo que la generan los Guid se pueden ordenar de SQL Server (consulte mi respuesta aquí. Versión corta: UuidCreateSequential escribe los números en little endian orden en lugar de big-endian. SQL Server se supone big-endian a la hora de ordenar; por lo que puede endian intercambiar los valores generados por UuidCreateSequential.
  2. 17

    Aquí es como NHibernate implementa el Guid.Peine algoritmo:

    private Guid GenerateComb()
    {
    byte[] guidArray = Guid.NewGuid().ToByteArray();
    DateTime baseDate = new DateTime(1900, 1, 1);
    DateTime now = DateTime.Now;
    //Get the days and milliseconds which will be used to build the byte string 
    TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
    TimeSpan msecs = now.TimeOfDay;
    //Convert to a byte array 
    //Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
    byte[] daysArray = BitConverter.GetBytes(days.Days);
    byte[] msecsArray = BitConverter.GetBytes((long) (msecs.TotalMilliseconds / 3.333333));
    //Reverse the bytes to match SQL Servers ordering 
    Array.Reverse(daysArray);
    Array.Reverse(msecsArray);
    //Copy the bytes into the guid 
    Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
    Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);
    return new Guid(guidArray);
    }
    • La convirtieron en una extensión de here
    • Ben Dhaou: ¿puedo preguntar por qué sólo se ha cambiado la última sección de la GUID? No es mejor el cambio de la primera parte?
    • Ben Dhaou: ¿Cómo Sql Server ordenar?
    • Una versión actualizada en la rama master: github.com/nhibernate/nhibernate-core/blob/master/src/…
  3. 7

    Secuencial guid que se actualiza a menudo (al menos 3 veces por milisecond), se puede encontrar aquí. Es crear regulares de código de C# (nº de código nativo de la llamada).

    • Se ve muy interesante, me pregunto si podría ser actualizado para el usuario enclavada en lugar de un objeto de bloqueo.
    • Gracias! Sin bloqueo 3.000.000 nueva SequentialGuid es sólo alrededor de un segundo más rápido que el bloqueo (11 segundos en comparación con el 12 segundos). El Incremento se realiza de forma condicional. Entrelazada es difícil de usar en este caso porque no hay Entrelazada.CompareIncrement (sólo Entrelazada.CompareExchange y Entrelazada.De incremento). El SequentialGuid se usa en el escenario de que la inserción de toma de horas. Me temo que voy a hacer que el método menos seguro cuando se usa Entrelazada y no es mucho más rápido.
    • Realmente recomiendo la creación de un paquete de nuget con ella. Apuesto a que te gustaría conseguir una buena cantidad de descargas!
    • Voy a considerar seriamente la posibilidad de hacerlo. Y cuando listo yo te anuncio un nuevo comentario para decirle que está disponible.
    • No paquete de nuget entonces 🙂
  4. 4

    La Versión De C#

        public static Guid ToSeqGuid()
    {
    Int64 lastTicks = -1;
    long ticks = System.DateTime.UtcNow.Ticks;
    if (ticks <= lastTicks)
    {
    ticks = lastTicks + 1;
    }
    lastTicks = ticks;
    byte[] ticksBytes = BitConverter.GetBytes(ticks);
    Array.Reverse(ticksBytes);
    Guid myGuid = new Guid();
    byte[] guidBytes = myGuid.ToByteArray();
    Array.Copy(ticksBytes, 0, guidBytes, 10, 6);
    Array.Copy(ticksBytes, 6, guidBytes, 8, 2);
    Guid newGuid = new Guid(guidBytes);
    string filepath = @"C:\temp\TheNewGuids.txt";
    using (StreamWriter writer = new StreamWriter(filepath, true))
    {
    writer.WriteLine("GUID Created =  " + newGuid.ToString());
    }
    return newGuid;
    }
    }

    }

  5. 4

    Tal vez interesante para comparar con las otras sugerencias:

    EntityFramework Core también implementa un sequentialGuidValueGenerator.
    Se generará aleatoriamente guid para cada valor de y sólo cambia la parte más significativa de bytes que se basa en una marca de tiempo y el hilo de seguridad incrementos para la clasificación en SQL Server.

    el enlace de la fuente

    Esto conduce a valores que son todos muy diferentes, pero con una marca de tiempo se pueden ordenar.

  6. 3

    Mi solución (en VB pero fácil de convertir). Los cambios de la más importante (para SQL Server clasificación) 8 primeros bytes de la GUID DateTime.UtcNow.Las garrapatas y también tiene un código extra para ayudar a la cuestión de la obtención de la misma Garrapatas varias veces, si usted llame para un nuevo GUID más rápido que el reloj del sistema actualizaciones.

    Private ReadOnly _toSeqGuidLock As New Object()
    ''' <summary>
    ''' Replaces the most significant eight bytes of the GUID (according to SQL Server ordering) with the current UTC-timestamp.
    ''' </summary>
    ''' <remarks>Thread-Safe</remarks>
    <System.Runtime.CompilerServices.Extension()> _
    Public Function ToSeqGuid(ByVal guid As Guid) As Guid
    Static lastTicks As Int64 = -1
    Dim ticks = DateTime.UtcNow.Ticks
    SyncLock _toSeqGuidLock
    If ticks <= lastTicks Then
    ticks = lastTicks + 1
    End If
    lastTicks = ticks
    End SyncLock
    Dim ticksBytes = BitConverter.GetBytes(ticks)
    Array.Reverse(ticksBytes)
    Dim guidBytes = guid.ToByteArray()
    Array.Copy(ticksBytes, 0, guidBytes, 10, 6)
    Array.Copy(ticksBytes, 6, guidBytes, 8, 2)
    Return New Guid(guidBytes)
    End Function
  7. 3

    Sólo tomé el NHibernate de respuesta basado en por Musulmanes Ben Dhaou y la función de extensión:

    using System;
    namespace Atlas.Core.Kernel.Extensions
    {
    public static class Guids
    {
    public static Guid Comb(this Guid source)
    {
    byte[] guidArray = source.ToByteArray();
    DateTime baseDate = new DateTime(1900, 1, 1);
    DateTime now = DateTime.Now;
    //Get the days and milliseconds which will be used to build the byte string 
    TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
    TimeSpan msecs = now.TimeOfDay;
    //Convert to a byte array 
    //Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
    byte[] daysArray = BitConverter.GetBytes(days.Days);
    byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds / 3.333333));
    //Reverse the bytes to match SQL Servers ordering 
    Array.Reverse(daysArray);
    Array.Reverse(msecsArray);
    //Copy the bytes into the guid 
    Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
    Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);
    return new Guid(guidArray);
    }
    }
    }
  8. 2

    Acabo de ver esta pregunta… yo soy el autor de una pequeña de código abierto .Biblioteca de RED para la generación de PEINE de estilo Guid.

    La biblioteca admite tanto el método original (compatible con SQL Server datetime tipo) y en uno con las marcas de tiempo de Unix, que tienen más tiempo de precisión. También incluye una variante que funciona mejor para PostgrSQL:

    https://github.com/richardtallent/RT.Comb

    • Dejado algunos comentarios en github

Dejar respuesta

Please enter your comment!
Please enter your name here