Columna NVARCHAR como CLAVE PRIMARIA o como columna ÚNICA

11

Estoy desarrollando una base de datos SQL Server 2012 y tengo dudas sobre las columnas nvarchar como claves principales.

Tengo esta tabla:

CREATE TABLE [dbo].[CODES]
(
    [ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
    [CODE_LEVEL] [tinyint] NOT NULL,
    [CODE] [nvarchar](20) NOT NULL,
    [FLAG] [tinyint] NOT NULL,
    [IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
     CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED 
    (
        [CODE_LEVEL] ASC,
        [CODE] ASC
    )
)

Pero ahora quiero usar la [CODE]columna como clave principal y eliminar la [ID_CODE]columna.

¿Hay algún problema o penalidad si tengo una NVARCHARcolumna como PRIMARY KEY?

[CODE]El valor de la columna debe ser único, por lo que he pensado que puedo establecer una UNIQUErestricción para esa columna.

¿Tengo que usar [CODE]como clave principal o es mejor si establezco una UNIQUErestricción en la [CODE]columna?

VansFannel
fuente
1
Una cosa bastante importante en consideración es ¿cuántas filas habrá en su tabla?
James Z
Esta no es una respuesta per se , pero me inclino a pensar que su CODEcolumna debe ser única, pero no una Clave principal. Sospecho que lleva información. Si esa información es de alguna manera modificable, entonces CODEdebería cambiar o estar desactualizado. Eso haría que su Clave primaria sea volátil, y no puedo ver que termine bien. Lo mejor es dejar que su PK sea solo una clave, y su CÓDIGO puede hacer lo que quiera. Solo una opinión.
Manngo
@Manngo, gracias por tu comentario. Sí, lo hice así: ID_CODE es la clave principal y CODE es ÚNICO.
VansFannel

Respuestas:

13

Sí, absolutamente hay consecuencias negativas por usar una cadena en lugar de un tipo numérico para una Clave primaria, y aún más si esa PK está agrupada (que en realidad es en su caso). Sin embargo, el grado en el que ve el (los) efecto (s) del uso de un campo de cadena es una función de a) cuántas filas hay en esta tabla, yb) cuántas filas en otras tablas tienen Clave externa para esta PK. Si solo tiene 10k filas en esta tabla y 100k filas en algunas otras tablas que FK a esta tabla a través de ese campo, entonces tal vez no sea tan notable. Pero esos efectos ciertamente se vuelven más notorios a medida que aumenta el recuento de filas.

Debe tener en cuenta que los campos en un índice agrupado se transfieren a índices no agrupados. Entonces, no solo está mirando hasta 40 bytes por fila, sino (40 * algún_número) bytes. Y en cualquier tabla FK que tenga esos mismos 40 bytes en la fila, más a menudo habrá un índice no agrupado en ese campo, ya que se está utilizando en JOIN, por lo que ahora se duplica en cualquier tabla que FK éste. Si uno se inclina a pensar que 40 bytes * 1 millón de filas * 10 copias no son motivo de preocupación, consulte mi artículo ¡El disco es barato! ORLY? que detalla todas (o al menos la mayoría) de las áreas afectadas por esta decisión.

La otra cosa a tener en cuenta es que filtrar y ordenar cadenas, especialmente cuando no se utiliza una intercalación binaria (supongo que está utilizando la base de datos predeterminada que generalmente no distingue entre mayúsculas y minúsculas) es mucho menos eficiente (es decir, lleva más tiempo) que cuando se usa INT/ BIGINT. Esto afecta a todas las consultas que filtran / unen / clasifican en este campo.

Por lo tanto, usar algo así CHAR(5)probablemente estaría bien para una PK agrupada, pero principalmente si también se definió con COLLATE Latin1_General_100_BIN2(o algo así).

¿Y puede [CODE]cambiar el valor de alguna vez? En caso afirmativo, esa es una razón aún más para no usarlo como PK (incluso si configura los FK en ON UPDATE CASCADE). Si no puede o no cambiará, eso está bien, pero todavía hay razones más que suficientes para no usarlo como una PK en clúster.

Por supuesto, la pregunta podría estar formulada incorrectamente, ya que parece que actualmente ya tiene este campo en su PK.

De todos modos, su mejor opción, con mucho, es usarlo [ID_CODE]como PK en clúster, usar ese campo en tablas relacionadas como FK y mantenerlo [CODE]como un UNIQUE INDEX(lo que significa que es una "clave alternativa").


Actualización
Un poco más de información basada en esta pregunta en un comentario sobre esta respuesta:

¿Es [ID_CODE], como PRIMARY KEY, la mejor opción si uso la columna [CODE] para buscar la tabla?

Todo esto depende de muchos factores, algunos de los cuales ya he mencionado pero que reafirmaré:

Una clave primaria es cómo se identifica la fila individual, independientemente de si se hace referencia a ella por cualquier clave externa. La forma en que su sistema identifica internamente la fila está relacionada, pero no necesariamente con la forma en que sus usuarios se identifican / esa fila. Cualquier columna NOT NULL con datos únicos podría funcionar, pero hay cuestiones prácticas a considerar, especialmente si la PK es, de hecho, referenciada por alguna FK. Por ejemplo, los GUID son únicos y algunas personas realmente les gusta usarlos por varias razones, pero son bastante malos para los índices agrupados ( NEWSEQUENTIALIDes mejor, pero no perfecto). Por otro lado, los GUID están bien como teclas alternativas y la aplicación los usa para buscar la fila, pero las UNIONES todavía se hacen usando una PK INT (o similar).

Hasta ahora no nos ha dicho cómo se [CODE]ajusta el campo en el sistema desde todos los ángulos, aparte de ahora mencionar que así es como se buscan las filas, pero ¿es eso para todas las consultas o solo algunas? Por lo tanto:

  • En cuanto al [CODE]valor:

    • ¿Cómo se genera?
    • ¿Es incremental o psuedo-random?
    • ¿Es de longitud uniforme o de longitud variable?
    • ¿Qué personajes se usan?
    • Si usa caracteres alfabéticos: ¿distingue entre mayúsculas y minúsculas o insensible?
    • ¿Puede alguna vez cambiar después de ser insertado?
  • Con respecto a esta tabla:

    • ¿Alguna otra tabla FK a esta tabla? ¿O se usan estos campos ( [CODE]o [ID_CODE]) en otras tablas, incluso si no están explícitamente con clave externa?
    • Si [CODE] es el único campo utilizado para obtener filas individuales, ¿para qué sirve el [ID_CODE]campo? Si no se usa, ¿por qué tenerlo en primer lugar (que podría depender de la respuesta a "¿Puede [CODE]cambiar alguna vez el campo?")?
    • ¿Cuántas filas hay en esta tabla?
    • Si otras tablas hacen referencia a esta tabla, ¿cuántas y cuántas filas hay en cada una de ellas?
    • ¿Cuáles son los índices para esta tabla?

Esta decisión no puede tomarse únicamente con la pregunta "¿NVARCHAR sí o no?". Una vez más, diré que, en términos generales, no me parece una buena idea, pero ciertamente hay momentos en que está bien. Dado que hay tan pocos campos en esta tabla, no es probable que haya más, o al menos no muchos, índices. Por lo tanto, puede estar bien de cualquier manera para tener [CODE]como Índice agrupado. Y si ninguna otra tabla hace referencia a esta tabla, entonces también podría estar bien convirtiéndola en PK. Pero, si otras tablas hacen referencia a esta tabla, optaría por el [ID_CODE]campo como PK, incluso si no está agrupado.

Solomon Rutzky
fuente
¿Le gustaría al votante anónimo anónimo (que parece haber rechazado la respuesta de @noIDonthissystem) ofrecer alguna crítica constructiva o señalar alguna lógica defectuosa?
Solomon Rutzky
Gracias por tu respuesta. ¿Es [ID_CODE], como PRIMARY KEY, la mejor opción si uso la [CODE]columna para buscar la tabla?
VansFannel
@VansFannel por favor vea mi actualización. Gracias.
Solomon Rutzky
Me uní a esta comunidad de dba para simplemente votar esta respuesta.
Ahmet Arslan
6

Tienes que separar los conceptos:

  • La clave primaria es un concepto de diseño , una propiedad lógica de las entradas de la tabla. Debe ser inmutable durante la vida útil de la entrada de la tabla y debe ser la clave utilizada en la aplicación para hacer referencia a la entrada.

  • El índice agrupado es un concepto de almacenamiento , una propiedad física. Debe ser la ruta de acceso más común para las consultas, debe servir para satisfacer el índice de cobertura en la mayoría de los casos y satisfacer la mayor cantidad posible de consultas de rango.

No es necesario que la clave primaria sea el índice agrupado. Puede tener ID_CODEcomo PK y (CODE_LEVEL, CODE)como clave agrupada. O al revés.

Una clave agrupada más grande tiene algunas repercusiones negativas, ya que la clave más amplia significa menor densidad en las páginas de índice y un mayor tamaño consumido en todos los índices no agrupados. ya se han derramado toneladas de tinta sobre este tema, por ejemplo. comience desde Más consideraciones para la clave de agrupamiento: ¡el debate del índice agrupado continúa! .

Pero la esencia del asunto es que la elección de la clave de índice agrupada es principalmente una compensación. Por un lado, tiene requisitos de tamaño de almacenamiento, con repercusiones generales en el rendimiento (clave más grande -> tamaño más grande -> más IO, y el ancho de banda de IO es probablemente el recurso más escaso que tiene). Por otro lado, elegir la clave agrupada incorrecta en nombre del ahorro de espacio puede tener consecuencias en el rendimiento de las consultas, a menudo peores que los problemas derivados de una clave amplia.

En cuanto a la elección de la clave principal, ni siquiera debería ser un problema: su modelo de datos, la lógica de su aplicación, debe dictar cuál es la clave principal.

Dicho esto, mi 2c: noNVARCHAR(20) es ancho. Es un tamaño de clave en clúster perfectamente aceptable, incluso para una tabla grande.

Remus Rusanu
fuente
Gracias por tu respuesta. ¿Es [ID_CODE], como PRIMARY KEY, la mejor opción si uso la [CODE]columna (y tal vez [CODE_LEVEL]) para buscar la tabla?
VansFannel
@VansFannel solo puedes responder eso.
Remus Rusanu
Pero en su opinión ...
VansFannel
2
Mi opinión tendría que considerar el DDL exacto de toda la tabla y todos los índices, las claves externas que hacen referencia a él, el número estimado de filas, la carga de trabajo de consulta esperada, los SLA esperados de la aplicación y no menos importante el presupuesto disponible para hardware y licencias.
Remus Rusanu
Gracias. Voy a utilizar [CODE]la columna como clave principal.
VansFannel
4

Nunca permitiría que nadie haga nvarchar(20)un PK en mi base de datos. Pierdes espacio en disco y memoria caché. Todos los índices de esta tabla y todos los FKs replican este amplio valor. Tal vez un char (20) si pueden justificarlo. ¿En qué tipo de datos está tratando de almacenar CODE? ¿Realmente necesitas almacenar caracteres nvarchar? Tiendo a hacer valores PK "internos" no vistos por los usuarios, y trato de mantener los valores que se muestran por separado. Los valores mostrados a veces necesitan ser cambiados, lo que se vuelve muy problemático con PKs + FKs.

Además, ¿se da cuenta de que una 'identidad bigint (1,1)' puede aumentar hasta 9.223.372.036.854.775.807?

[ID_CODE] [bigint] IDENTITY(1,1)

A menos que esté construyendo esta base de datos para Google, ¿no será suficiente una normalidad int identity (1,1)con un límite superior a los 2 mil millones?

sin identificación en este sistema
fuente
int es de 4 bytes en SQL, lo que le da -2.1Billion a + 2.1Billion.
datagod
@datagod, ja, gracias, ¡tantos dígitos que conté mal!
Sin identificación en este sistema
Gracias por tu respuesta. ¿Es [ID_CODE], como PRIMARY KEY, la mejor opción si uso la [CODE]columna para buscar la tabla? Gracias.
VansFannel
Solía ​​estar en este bote hasta que alguien usaba la naturaleza secuencial de "int" para predecir datos / usuarios en mi base de datos y cosechaba casi todo lo que tenía. Nunca más. Las bases de datos públicas deben ser un poco más difíciles de obtener información.
DaBlue
3

No debe haber una penalización inherente / notable que no sea el riesgo de usar teclas anchas cuando se usa nvarchar / varchar si no está al tanto. Especialmente si comienzas a combinarlos en teclas compuestas.

Pero en tu ejemplo de una longitud (20) deberías estar bien y no me preocuparía mucho por eso. Porque si el CÓDIGO es cómo consulta principalmente sus datos, un índice agrupado que suena muy sensato.

Sin embargo, debe considerar si realmente lo quiere como clave principal o simplemente como un índice único (agrupado). Hay una (pequeña) diferencia entre el índice agrupado y la clave primaria (básicamente, la clave primaria identifica sus datos, pero el índice es cómo consulta los datos), por lo que si lo desea, podría hacer su ID_Code tan fácilmente como una clave primaria y crea un índice agrupado único sobre CODE. (aviso: SQL Server convertirá automáticamente su clave principal en un índice agrupado, a menos que usted mismo haya creado manualmente el índice agrupado)

También considere si realmente necesita ID_Code ahora que tiene un CÓDIGO único.

Allan S. Hansen
fuente
2
En realidad, NVARCHAR(20)tiene un tamaño de 40 bytes (máximo), y dado que es una columna de longitud variable , no es realmente la mejor opción para un índice agrupado. ID_CODEser un BIGINT IDENTITYsería la mejor opción aquí!
marc_s
Sé que son 40 bytes, pero no había muchas razones para especificarlo, ya que no está cerca de los 900 bytes. Y si consulta principalmente los datos de CODE, sería una mejor opción para evitar tener índices redundantes que mantener, porque aún necesitaría un índice y luego tendría que buscar a través de los grupos agrupados más adelante
Allan S. Hansen
Vale la pena mencionar: lo que olvidé mencionar y sospecho que es a donde se dirige @marc_s es que un índice de este tipo puede conducir a una mayor fragmentación del índice que una identidad secuencial, pero aún lo veo como un índice sensible en esta situación específica basada en el factor de consulta.
Allan S. Hansen