web-dev-qa-db-fra.com

SQL: sélection des colonnes avec des valeurs NULL uniquement

Comment sélectionner toutes les colonnes d'une table contenant uniquement des valeurs NULL pour toutes les lignes? J'utilise MS SQL Server 2005 . J'essaie de savoir quelles colonnes ne sont pas utilisées dans le tableau afin de pouvoir les supprimer.

44
Bryan Roth

Voici la version 2005 ou ultérieure de SQL 2005: Remplacez ADDR_Address par votre nom de fichier.

declare @col varchar(255), @cmd varchar(max)

DECLARE getinfo cursor for
SELECT c.name FROM sys.tables t JOIN sys.columns c ON t.Object_ID = c.Object_ID
WHERE t.Name = 'ADDR_Address'

OPEN getinfo

FETCH NEXT FROM getinfo into @col

WHILE @@FETCH_STATUS = 0
BEGIN
    SELECT @cmd = 'IF NOT EXISTS (SELECT top 1 * FROM ADDR_Address WHERE [' + @col + '] IS NOT NULL) BEGIN print ''' + @col + ''' end'
    EXEC(@cmd)

    FETCH NEXT FROM getinfo into @col
END

CLOSE getinfo
DEALLOCATE getinfo
61
Charles Graham
SELECT cols
FROM table
WHERE cols IS NULL
22
Eight Characters

Cela devrait vous donner une liste de toutes les colonnes de la table "Personne" qui ne contient que des valeurs NULL. Vous obtiendrez les résultats sous forme de plusieurs ensembles de résultats, vides ou contenant le nom d'une seule colonne. Vous devez remplacer "Personne" à deux endroits pour l'utiliser avec une autre table.

DECLARE crs CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM syscolumns WHERE id=OBJECT_ID('Person')
OPEN crs
DECLARE @name sysname
FETCH NEXT FROM crs INTO @name
WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC('SELECT ''' + @name + ''' WHERE NOT EXISTS (SELECT * FROM Person WHERE ' + @name + ' IS NOT NULL)')
    FETCH NEXT FROM crs INTO @name
END
CLOSE crs
DEALLOCATE crs
5
MobyDX

Ou voulez-vous simplement voir si une colonne ne contient que des valeurs NULL (et est donc probablement inutilisée)?

Des précisions supplémentaires sur la question pourraient être utiles.

EDIT: .__ Ok. Voici un code très approximatif pour vous aider à démarrer ...

SET NOCOUNT ON
DECLARE @TableName Varchar(100)
SET @TableName='YourTableName'
CREATE TABLE #NullColumns (ColumnName Varchar(100), OnlyNulls BIT)
INSERT INTO #NullColumns (ColumnName, OnlyNulls) SELECT c.name, 0 FROM syscolumns c INNER JOIN sysobjects o ON c.id = o.id AND o.name = @TableName AND o.xtype = 'U'
DECLARE @DynamicSQL AS Nvarchar(2000)
DECLARE @ColumnName Varchar(100)
DECLARE @RC INT
    SELECT TOP 1 @ColumnName = ColumnName FROM #NullColumns WHERE OnlyNulls=0
    WHILE @@ROWCOUNT > 0
    BEGIN
        SET @RC=0
        SET @DynamicSQL = 'SELECT TOP 1 1 As HasNonNulls FROM ' + @TableName + ' (nolock) WHERE ''' + @ColumnName + ''' IS NOT NULL'
        EXEC sp_executesql @DynamicSQL
        set @RC=@@rowcount
        IF @RC=1
        BEGIN
            SET @DynamicSQL = 'UPDATE #NullColumns SET OnlyNulls=1 WHERE ColumnName=''' + @ColumnName + ''''
            EXEC sp_executesql @DynamicSQL
        END
        ELSE
        BEGIN
            SET @DynamicSQL = 'DELETE FROM #NullColumns WHERE ColumnName=''' + @ColumnName+ ''''
            EXEC sp_executesql @DynamicSQL
        END
    SELECT TOP 1 @ColumnName = ColumnName FROM #NullColumns WHERE OnlyNulls=0
    END

SELECT * FROM #NullColumns

DROP TABLE #NullColumns
SET NOCOUNT OFF

Oui, il existe des moyens plus faciles, mais j'ai une réunion à laquelle assister maintenant. Bonne chance!

3
Kevin Fairchild

Tu peux faire: 

select 
  count(<columnName>)
from
  <tableName>

Si le nombre retourne 0, cela signifie que toutes les lignes de cette colonne sont toutes NULL (ou qu'il n'y a aucune ligne dans la table).

peut être changé en 

select 
    case(count(<columnName>)) when 0 then 'Nulls Only' else 'Some Values' end
from 
    <tableName>

Si vous souhaitez l’automatiser, vous pouvez utiliser les tables système pour itérer les noms de colonne de la table qui vous intéresse.

2
kristof

Voici une version mise à jour de la requête de Bryan pour 2008 et les versions ultérieures. Il utilise INFORMATION_SCHEMA.COLUMNS, ajoute des variables pour le schéma et le nom de la table. Le type de données de la colonne a été ajouté à la sortie. L'inclusion du type de données de colonne facilite la recherche d'une colonne d'un type de données particulier. Je n'ai pas ajouté les largeurs de colonne ou quoi que ce soit.

RAISERROR ... WITH NOWAIT est utilisé pour que le texte s'affiche immédiatement au lieu de tout à la fois (pour la plupart) à la fin, comme le fait PRINT.

SET NOCOUNT ON;

DECLARE
 @ColumnName sysname
,@DataType nvarchar(128)
,@cmd nvarchar(max)
,@TableSchema nvarchar(128) = 'dbo'
,@TableName sysname = 'TableName';

DECLARE getinfo CURSOR FOR
SELECT
     c.COLUMN_NAME
    ,c.DATA_TYPE
FROM
    INFORMATION_SCHEMA.COLUMNS AS c
WHERE
    c.TABLE_SCHEMA = @TableSchema
    AND c.TABLE_NAME = @TableName;

OPEN getinfo;

FETCH NEXT FROM getinfo INTO @ColumnName, @DataType;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @cmd = N'IF NOT EXISTS (SELECT * FROM ' + @TableSchema + N'.' + @TableName + N' WHERE [' + @ColumnName + N'] IS NOT NULL) RAISERROR(''' + @ColumnName + N' (' + @DataType + N')'', 0, 0) WITH NOWAIT;';
    EXECUTE (@cmd);

    FETCH NEXT FROM getinfo INTO @ColumnName, @DataType;
END;

CLOSE getinfo;
DEALLOCATE getinfo;

2
user2466387

Pas vraiment sûr de 2005, mais 2008 l'a mangé:

USE [DATABASE_NAME] -- !
GO

DECLARE @SQL NVARCHAR(MAX)
DECLARE @TableName VARCHAR(255)

SET @TableName = 'TABLE_NAME'   -- !

SELECT @SQL = 
(
    SELECT 
        CHAR(10)
        +'DELETE FROM ['+t1.TABLE_CATALOG+'].['+t1.TABLE_SCHEMA+'].['+t1.TABLE_NAME+'] WHERE '
        +(
            SELECT  
            CASE t2.ORDINAL_POSITION 
                WHEN (SELECT MIN(t3.ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS t3 WHERE t3.TABLE_NAME=t2.TABLE_NAME) THEN ''
                ELSE  'AND '
            END
            +'['+COLUMN_NAME+'] IS NULL' AS 'data()'
            FROM INFORMATION_SCHEMA.COLUMNS t2 WHERE t2.TABLE_NAME=t1.TABLE_NAME FOR XML PATH('')
         )  AS 'data()'
    FROM INFORMATION_SCHEMA.TABLES t1 WHERE t1.TABLE_NAME = @TableName FOR XML PATH('')
)

SELECT @SQL -- EXEC(@SQL)
1
user8120267

Je recommanderais également de rechercher des champs qui ont tous la même valeur, pas seulement NULL.

Autrement dit, pour chaque colonne de chaque table, effectuez la requête:

SELECT COUNT(DISTINCT field) FROM tableName

et se concentrer sur ceux qui retournent 1 à la suite.

1
squadette

Si vous avez besoin de lister toutes les lignes où toutes les valeurs de colonne sont NULL, alors j'utiliserais la fonction COLLATE. Cela prend une liste de valeurs et retourne la première valeur non NULL. Si vous ajoutez tous les noms de colonne à la liste, puis utilisez IS NULL, vous devriez obtenir toutes les lignes contenant uniquement des valeurs NULL.

SELECT * FROM MyTable WHERE COLLATE(Col1, Col2, Col3, Col4......) IS NULL

Vous ne devriez pas vraiment avoir de table avec TOUTES les columns null, car cela signifie que vous n'avez pas de primary key (non autorisé à être null). Ne pas avoir de clé primaire est quelque chose à éviter. cela casse la première forme normale.

1
The Doc
SELECT  t.column_name
FROM    user_tab_columns t
WHERE   t.nullable = 'Y' AND t.table_name = 'table name here' AND t.num_distinct = 0;
0
user3827049

Une version mise à jour de la version 'user2466387', avec un petit test supplémentaire qui peut améliorer les performances, car il est inutile de tester des colonnes non nullables:

AND IS_NULLABLE = 'YES'

Le code complet:

SET NOCOUNT ON;

DECLARE
 @ColumnName sysname
,@DataType nvarchar(128)
,@cmd nvarchar(max)
,@TableSchema nvarchar(128) = 'dbo'
,@TableName sysname = 'TableName';

DECLARE getinfo CURSOR FOR
SELECT
     c.COLUMN_NAME
    ,c.DATA_TYPE
FROM
    INFORMATION_SCHEMA.COLUMNS AS c
WHERE
    c.TABLE_SCHEMA = @TableSchema
    AND c.TABLE_NAME = @TableName
    AND IS_NULLABLE = 'YES';

OPEN getinfo;

FETCH NEXT FROM getinfo INTO @ColumnName, @DataType;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @cmd = N'IF NOT EXISTS (SELECT * FROM ' + @TableSchema + N'.' + @TableName + N' WHERE [' + @ColumnName + N'] IS NOT NULL) RAISERROR(''' + @ColumnName + N' (' + @DataType + N')'', 0, 0) WITH NOWAIT;';
    EXECUTE (@cmd);

    FETCH NEXT FROM getinfo INTO @ColumnName, @DataType;
END;

CLOSE getinfo;
DEALLOCATE getinfo;
0
Sylvain Bruyere

Essaye ça -

DECLARE @table VARCHAR(100) = 'dbo.table'

DECLARE @sql NVARCHAR(MAX) = ''

SELECT @sql = @sql + 'IF NOT EXISTS(SELECT 1 FROM ' + @table + ' WHERE ' + c.name + ' IS NOT NULL) PRINT ''' + c.name + ''''
FROM sys.objects o
JOIN sys.columns c ON o.[object_id] = c.[object_id]
WHERE o.[type] = 'U'
    AND o.[object_id] = OBJECT_ID(@table)
    AND c.is_nullable = 1

EXEC(@sql)
0

Vous devrez parcourir l'ensemble des colonnes et vérifier chacune d'elles. Vous devriez pouvoir obtenir une liste de toutes les colonnes avec une commande de table DESCRIBE.

Pseudo-code:


foreach $column ($cols) {
   query("SELECT count(*) FROM table WHERE $column IS NOT NULL")
   if($result is zero)  {
      # $column contains only null values"
      Push @onlyNullColumns, $column;
   } else {
      # $column contains non-null values
   }
}
return @onlyNullColumns;

Je sais que cela semble un peu contre-intuitif, mais SQL ne fournit pas de méthode native pour sélectionner des colonnes, mais uniquement des lignes.

0
Daniel Papasian