¿Debo agregar un campo de incremento automático / IDENTIDAD a una tabla de referencias cruzadas solo para fines PK?

9

Estoy agregando la siguiente tabla de referencias cruzadas a mi DB alojada en SQL Server:

company_id bigint not null (FK)
org_path nvarchar (2048) not null

El company_idcampo se refiere al idcampo en otra tabla (en la cual es la clave primaria).

Dado que también puede haber múltiples registros con el mismo company_id, cualquier clave primaria tendría que usar ambos campos. Sin embargo, no puedo crear una clave con ambos campos porque org_pathes demasiado larga para SQL Server.

En cuanto a org_path, esta es la única tabla en la que existe. Es muy probable que las consultas a esta tabla soliciten todas las entradas o todas las org_pathentradas por company_id. O para decirlo de otra manera, parece dudoso que esta tabla sea consultada alguna vez org_path. Además, es poco probable que org_pathse actualice, y más probablemente se inserte y, probablemente rara vez, se elimine.

Espero que el número total de filas sea de miles.

Además, la razón nvarchar (2048)es porque el valor tiene que imitar eso en una base de datos de terceros. Un ejemplo típico será algo así como

\Translation Providers\[customer name]\[order name]\

y puede contener diacríticos.

Entonces, mi pregunta es la siguiente: ¿sería más eficiente agregar un idcampo de incremento automático y usarlo junto con company_idla clave primaria, o agregaría una sobrecarga innecesaria, y el hecho de que company_idsea ​​la clave primaria en otra tabla tenga alguna efecto aquí?

awj
fuente

Respuestas:

7

Para un índice agrupado no exclusivo comany_idsolo, SQL Server agregará automáticamente un unificador de entero de 4 bytes a todas las claves de índice agrupadas duplicadas (es decir, segundo y posterior para un valor clave) para hacerlo único. Sin embargo, esto no está expuesto al usuario.

La ventaja de agregar su propio identificador único como una columna de clave secundaria es que aún puede buscar company_idpero también buscar filas individuales de manera más eficiente (utilizando en company_id, identitycollugar de company_idcon un predicado residual activado org_path). El índice agrupado sería entonces único company_id, identitycol, por lo que no se agregarían unificadores ocultos.

Además, si termina con duplicados para (company_id,org_path), tener la columna de identidad explícita (una especie de "unificador exclusivo") hará que sea más fácil seleccionar solo uno de ellos para eliminarlo o actualizarlo.

Martin Smith
fuente
12

Una cosa a tener en cuenta es que una clave primaria y un índice agrupado no son lo mismo. Una clave primaria es una restricción y se ocupa de las reglas según las cuales viven los datos (es decir, la integridad de los datos); No tiene nada que ver con la eficiencia / rendimiento. Una clave primaria requiere que las columnas clave sean únicas (en combinación) y NO NULAS (individualmente). Un PK se aplica a través de un índice único, aunque puede estar agrupado o no agrupado.

Un índice agrupado es un medio de ordenar físicamente (es decir, en el disco) los datos en la tabla y se ocupa del rendimiento; No tiene nada que ver con la integridad de los datos. Un índice agrupado puederequieren que las columnas clave sean únicas (en combinación), pero no es necesario. Sin embargo, dado que el índice agrupado es el orden físico de los datos, necesita identificar de manera única cada fila sin importar qué. Entonces, si no lo configura para que requiera unicidad, creará su propia unicidad a través de una columna oculta de "byte" de 4 bytes. Esa columna siempre está allí en índices agrupados no únicos, pero no ocupa espacio cuando los campos clave son únicos (en combinación). Para ver de primera mano cómo funciona esta columna de "uniquifier" (tanto en el índice agrupado como en el efecto sobre los índices no agrupados), consulte este script de prueba que publiqué en PasteBin: script T-SQL para probar el tamaño del Uniquifier .

Por lo tanto, la pregunta principal de:

¿Sería más eficiente agregar un idcampo de incremento automático y usarlo junto con company_idla clave principal, o agregaría una sobrecarga innecesaria?

está combinando esos dos conceptos, por lo que deben abordarse por separado, aunque definitivamente hay cierta superposición.

¿Debería IDENTITYagregarse una columna o sería una sobrecarga innecesaria?

Si agrega una INT IDENTITYcolumna y la usa para crear una PK, suponiendo que sea una PK agrupada, eso agrega 4 bytes a cada fila. Esta columna es visible y utilizable en consultas. Se podría añadirse a otras tablas como una clave externa, aunque en este caso particular, que no va a ocurrir.

Si no agrega la INT IDENTITYcolumna, no puede crear una PK en esta tabla. Sin embargo, aún puede crear un índice agrupado en la tabla siempre que no utilice la UNIQUEopción. En este caso, SQL Server agregará una columna oculta llamada "uniquifier" que se comporta como se describe anteriormente. Debido a que la columna está oculta, no se puede usar en consultas o como referencia para claves externas.

En lo que respecta a la eficiencia, estas opciones son más o menos las mismas. Sí, habrá un poco menos de espacio al tener el índice agrupado no único debido a que algunas filas (aquellas con los valores de clave únicos iniciales) ocupan 0 bytes, mientras que todas las filas en IDENTITY/ PK ocuparán los 4 bytes. Pero no habrá suficientes filas de 0 bytes (especialmente con la pequeña cantidad de filas esperadas) para notar una diferencia, y mucho menos que la conveniencia de poder usar la IDcolumna en las consultas.

INT IDENTIDAD ¿Columna o hash de org_pathcolumna computada persistente?

Dado que no buscará filas en función de los org_pathvalores, entonces no tiene sentido agregar la sobrecarga de la columna calculada persistente más la necesidad de calcular ese hash en las consultas para que coincida con la columna calculada (esta fue mi sugerencia original, disponible en el historial de revisiones aquí , que se basó en la redacción inicial / detalles de la Pregunta). En este caso particular, la INT IDENTITYcolumna "ID" es probablemente la mejor.

Orden de columna clave

Dado que la IDColumna rara vez, si alguna vez, se usará en consultas, y dado que los dos casos de uso principales son obtener "todas las filas" o "todas las filas para un determinado company_id", crearía el PK en company_id, id. Y debido a que esto significa que las filas no se insertan secuencialmente, especificaría una FILLFACTORde 90. También deberá asegurarse de realizar un mantenimiento de índice regular para reducir la fragmentación.

Segunda pregunta

¿El hecho de que company_id sea la clave principal en otra tabla tiene algún efecto aquí?

No.

Desencadenar

Dado que los org_pathvalores dentro de a company_idson únicos, aún debe crear un activador INSERT, UPDATEpara hacer cumplir esto. En el activador, haga un IF EXISTScon una consulta que probablemente haga un COUNT(*)y GROUP BY company_id, org_path. Si se encuentra algo, emita un ROLLBACKpara cancelar la operación DML y luego un RAISERRORdicho que hay duplicados.

Colación

En mi respuesta inicial (basada en la redacción original / detalles escasos de la pregunta, y disponible en el historial de revisiones aquí ), sugerí que posiblemente se use una intercalación binaria (es decir, _BIN2). Ahora que tenemos una idea de qué org_pathes exactamente , no recomendaría usar una intercalación binaria. Dado que no habrá signos diacríticos, que no desea hacer uso de equivalencias lingüísticas.

Solomon Rutzky
fuente
Continuemos esta discusión en el chat .
Solomon Rutzky
0

¿Por qué necesitas una PK?

¿Por qué no simplemente ir con company_id como un índice no agrupado?

Dijiste que la mayoría de las búsquedas están en todas las entradas o por company_id.
Rara vez se actualizan.
Rara vez se borra
org_path, esta es la única tabla en la que existe.

La respuesta de Martin Smith puede darle lo que necesita
No estoy familiarizado con agregar automáticamente un unificador de enteros de 4 bytes Tal
vez me falta algo, pero si no tiene otras columnas indexadas, entonces no veo ningún propósito en este caso de uso

Si le preocupa DRI, las tablas deben usar la tabla Compañía como FK para company_id

paparazzo
fuente
Oye. Con respecto a " ¿Por qué no simplemente ir con company_id como un índice no agrupado? ": Porque eso tendría 2 inconvenientes: 1) sería 1 cosa más ocupando espacio, mientras que un Índice agrupado es la tabla, por lo que no hay ningún elemento adicional, y 2) aún requeriría una búsqueda de RID para obtener el campo NVARCHAR, a menos que fuera una INCLUDEcolumna, pero eso es aún peor, ya que simplemente duplica la tabla. Es cierto que el PK no es necesario; La parte importante es el índice agrupado. Pero una vez que tenga la IDENTIDAD, bien podría ir con PK. Y por favor vea el nuevo enlace en mi respuesta para un recorrido en Uniquifier 😃
Solomon Rutzky
@srutzky Pero evita un unificador de enteros de 4 bytes, así que lo veo como un lavado
paparazzo
Con menos de 10k filas, no importará; probablemente necesite estar en millones de filas antes de notar el efecto de solo 4 bytes. Entonces, para la consulta "obtener todas las filas", en realidad no hay ninguna diferencia en ninguna de estas opciones. Pero para la consulta "get for company_id = @param", será útil tener los datos ordenados físicamente por company_id, especialmente cuando no es necesario hacer una búsqueda RID para cada fila.
Solomon Rutzky
@srutzky Wash es un lavado: 10K o 1G. Es solo algo que el OP debe considerar.
paparazzo