2011-08-04 18 views
11

Uso MS SQL Server.Cómo saber si una tabla tiene algunas columnas únicas

He recibido algunas tablas de gran tamaño sin restricciones, sin claves, sin nada.

Sé que algunas de las columnas tienen valores únicos. ¿Hay alguna manera inteligente de que una tabla determinada encuentre los cols que tienen valores únicos?

Ahora mismo lo hago manualmente para cada columna contando si hay tantos valores DISTINCT como filas hay en la tabla.

SELECT COUNT(DISTINCT col) FROM table 

Podría hacer un prob cusor para recorrer todas las columnas, pero quieren saber si alguien conoce una o más inteligente construir-en función.

Gracias.

Respuesta

8

Aquí es un enfoque que es básicamente similar a @ JNK, pero en lugar de imprimir los recuentos devuelve una respuesta para cada columna que indica si una columna se compone de valores únicos única o no:

DECLARE @table varchar(100), @sql varchar(max); 
SET @table = 'some table name'; 

SELECT 
    @sql = COALESCE(@sql + ', ', '') + ColumnExpression 
FROM (
    SELECT 
    ColumnExpression = 
     'CASE COUNT(DISTINCT ' + COLUMN_NAME + ') ' + 
     'WHEN COUNT(*) THEN ''UNIQUE'' ' + 
     'ELSE '''' ' + 
     'END AS ' + COLUMN_NAME 
    FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = @table 
) s 

SET @sql = 'SELECT ' + @sql + ' FROM ' + @table; 
PRINT @sql; /* in case you want to have a look at the resulting query */ 
EXEC(@sql); 

Simplemente compara COUNT(DISTINCT column) con COUNT(*) para cada columna. El resultado será una tabla con una sola fila, donde cada columna contendrá el valor UNIQUE para aquellas columnas que no tienen duplicados, y una cadena vacía si hay duplicados presentes.

Pero la solución anterior funcionará correctamente solo para aquellas columnas que no tienen valores NULL.Cabe señalar que SQL Server no ignora los NULL cuando se desea crear una restricción/índice único en una columna. Si una columna contiene solo un NULL y todos los demás valores son únicos, aún puede crear una restricción única en la columna (sin embargo, no puede convertirla en clave primaria, lo que requiere tanto la uniquness de valores como la ausencia de valores NULL).

Por lo tanto es posible que necesite un análisis más exhaustivo de los contenidos, que se podría obtener con la siguiente secuencia de comandos:

DECLARE @table varchar(100), @sql varchar(max); 
SET @table = 'some table name'; 

SELECT 
    @sql = COALESCE(@sql + ', ', '') + ColumnExpression 
FROM (
    SELECT 
    ColumnExpression = 
     'CASE COUNT(DISTINCT ' + COLUMN_NAME + ') ' + 
     'WHEN COUNT(*) THEN ''UNIQUE'' ' + 
     'WHEN COUNT(*) - 1 THEN ' + 
     'CASE COUNT(DISTINCT ' + COLUMN_NAME + ') ' + 
     'WHEN COUNT(' + COLUMN_NAME + ') THEN ''UNIQUE WITH SINGLE NULL'' ' + 
     'ELSE '''' ' + 
     'END ' + 
     'WHEN COUNT(' + COLUMN_NAME + ') THEN ''UNIQUE with NULLs'' ' + 
     'ELSE '''' ' + 
     'END AS ' + COLUMN_NAME 
    FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = @table 
) s 

SET @sql = 'SELECT ' + @sql + ' FROM ' + @table; 
PRINT @sql; /* in case you still want to have a look at the resulting query */ 
EXEC(@sql); 

Esta solución toma valores NULL en cuenta por el control de tres valores: COUNT(DISTINCT column), COUNT(column) y COUNT(*). Se muestra los resultados de manera similar a la primera solución, pero los posibles diagnósticos para las columnas son más diversos:

  • UNIQUE significa que no hay valores duplicados y no hay valores NULL (puede ser o bien un PK o tener una restricción única/index) ;

  • UNIQUE WITH SINGLE NULL - como se puede adivinar, no hay duplicados, pero hay uno NULO (no puede ser un PK, pero puede tener una restricción/índice único);

  • UNIQUE with NULLs - no hay duplicados, dos o más valores NULL (en caso de que esté en SQL Server 2008, podría tener un índice único condicional solo para valores que no sean NULL);

  • cadena vacía - hay duplicados, posiblemente NULL también.

+0

dando error "Advertencia: el valor nulo es eliminado por un agregado u otra operación SET". No se puede usar desde la aplicación. – ratneshsinghparihar

+0

@ user998660: "De la aplicación particularmente sensible a las advertencias", ¿quiere decir? En ese caso, no, no puede, por supuesto. No todas las aplicaciones * son * tan sensibles, sin embargo. Por ejemplo, no recuerdo haber tenido problemas en Delphi con agregados que producen tales advertencias. De todos modos, mi impresión fue que OP deseaba una solución que pudieran usar "manualmente", por ejemplo, en el caso de una consulta, invocándola en una herramienta como SSMS. –

1

Unas pocas palabras lo que hace mi código:

  1. de lectura correcta de todas las tablas y columnas

  2. Crea una tabla temporal para contener la tabla/columnas con claves duplicadas

  3. Para cada tabla/columna ejecuta una consulta. Si encuentra un recuento (*)> 1 para al menos un valor , hace una inserción en la tabla temporal

  4. Seleccione la columna y los valores de las tablas del sistema que no coincidan con la tabla/columnas que se encuentran duplicados

    DECLARE @sql VARCHAR(max) 
    DECLARE @table VARCHAR(100) 
    DECLARE @column VARCHAR(100) 
    
    
    CREATE TABLE #temp (tname VARCHAR(100),cname VARCHAR(100)) 
    
    DECLARE mycursor CURSOR FOR 
    select t.name,c.name 
    from sys.tables t 
    join sys.columns c on t.object_id = c.object_id 
    where system_type_id not in (34,35,99) 
    
    OPEN mycursor 
    FETCH NEXT FROM mycursor INTO @table,@column 
    
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
    SET @sql = 'INSERT INTO #temp SELECT DISTINCT '''[email protected]+''','''[email protected]+ ''' FROM ' + @table + ' GROUP BY ' + @column +' HAVING COUNT(*)>1 ' 
    EXEC (@sql) 
    FETCH NEXT FROM mycursor INTO @table,@column 
    END 
    
    select t.name,c.name 
    from sys.tables t 
    join sys.columns c on t.object_id = c.object_id 
    left join #temp on t.name = #temp.tname and c.name = #temp.cname 
    where system_type_id not in (34,35,99) and #temp.tname IS NULL 
    
    DROP TABLE #temp 
    
    CLOSE mycursor 
    DEALLOCATE mycursor 
    
+0

gracias por su respuesta, no lo he probado ya que se ejecuta en todas las tablas, que no es lo que necesito para esta pregunta específica. – zmaster

+0

puede colocar una cláusula adicional en la definición de consulta del cursor: Y tname = su_nombre_tabla – niktrs

2

Aquí es Creo que probablemente la manera más limpia. Simplemente use sql dinámico y una sola instrucción de selección para crear una consulta que le proporcione un recuento total de filas y un recuento de valores distintos para cada campo.

Rellene el nombre de la BD y el nombre de la tabla en la parte superior. La parte del nombre de DB es realmente importante ya que OBJECT_NAME solo funciona en el contexto de la base de datos actual.

use DatabaseName 

DECLARE @Table varchar(100) = 'TableName' 

DECLARE @SQL Varchar(max) 

SET @SQL = 'SELECT COUNT(*) as ''Total''' 

SELECT @SQL = @SQL + ',COUNT(DISTINCT ' + name + ') as ''' + name + '''' 
FROM sys.columns c 
WHERE OBJECT_NAME(object_id) = @Table 

SET @SQL = @SQL + ' FROM ' + @Table 

exec @sql 
+1

Esto será muy útil suponiendo que no hay valores NULL en la (s) columna (s). Los NULL se tienen en cuenta en SQL Server al crear restricciones/índices únicos. –

0

¿Qué hay de simple línea de código:

CREATE UNIQUE INDEX index_name ON table_name (column_name); 

Si se crea el índice, entonces su COLUMN_NAME tiene sólo valores únicos. Si hay duplicados en su column_name, recibirá un mensaje de error.

Cuestiones relacionadas