Localizando colunas vazias de uma tabela no PostgreSQL

17

Qual consulta retornaria o nome das colunas de uma tabela em que todas as linhas são NULL?

Seb
fonte
Você quer dizer uma tabela específica ou todas as tabelas em um esquema?
Jack Douglas
1
Por que você precisaria fazer isso? Parece que você tem muitas colunas / tabelas e deve repensar seu design.
eevar

Respostas:

13

testbed:

create role stack;
create schema authorization stack;
set role stack;

create table my_table as 
select generate_series(0,9) as id, 1 as val1, null::integer as val2;

create table my_table2 as 
select generate_series(0,9) as id, 1 as val1, null::integer as val2, 3 as val3;

função:

create function has_nonnulls(p_schema in text, p_table in text, p_column in text)
                returns boolean language plpgsql as $$
declare 
  b boolean;
begin
  execute 'select exists(select * from '||
          p_table||' where '||p_column||' is not null)' into b;
  return b;
end;$$;

inquerir:

select table_schema, table_name, column_name, 
       has_nonnulls(table_schema, table_name, column_name)
from information_schema.columns
where table_schema='stack';

resultado:

 table_schema | table_name | column_name | has_nonnulls
--------------+------------+-------------+--------------
 stack        | my_table   | id          | t
 stack        | my_table   | val1        | t
 stack        | my_table   | val2        | f
 stack        | my_table2  | id          | t
 stack        | my_table2  | val1        | t
 stack        | my_table2  | val2        | f
 stack        | my_table2  | val3        | t
(7 rows)

Além disso, você pode obter uma resposta aproximada consultando o catálogo - se null_fracfor zero que não indica nulos, mas deve ser verificado novamente com dados 'reais':

select tablename, attname, null_frac from pg_stats where schemaname='stack';

 tablename | attname | null_frac
-----------+---------+-----------
 my_table  | id      |         0
 my_table  | val1    |         0
 my_table  | val2    |         1
 my_table2 | id      |         0
 my_table2 | val1    |         0
 my_table2 | val2    |         1
 my_table2 | val3    |         0
(7 rows)
Jack Douglas
fonte
1
Esta é uma pergunta antiga, mas as pessoas que usam extensões espaciais (postgis) devem observar que colunas espaciais vazias não aparecem pg_statsse estiverem vazias na criação da tabela. Descobri isso hoje ao fazer algumas tarefas domésticas. Descobri que algumas tabelas espaciais históricas foram importadas usando ogr2ogr. se não houver nenhuma coluna espacial nos dados sendo importados, ogr2ogrcriará uma coluna de geometria cheia <NULL>. Meu pg_statsnão possui colunas geométricas das tabelas aspatiais importadas (ele tem todas as outras colunas para essas tabelas). Muito estranho, pensei.
GT.
6

No Postgresql, você pode obter os dados diretamente das estatísticas:

vacuum analyze; -- if needed

select schemaname, tablename, attname
from pg_stats
where most_common_vals is null
and most_common_freqs is null
and histogram_bounds is null
and correlation is null
and null_frac = 1;

Você pode obter alguns falsos positivos; portanto, é necessário fazer uma nova verificação depois de encontrar os candidatos.

Denis de Bernardy
fonte
Você precisa de outras condições além disso null_frac=1?
Jack Douglas
Não tenho certeza. null_frac presumivelmente é real, então pode ser que arredonde para 1 em alguns casos estranhos. Mas mesmo com 1 de 10 mil linhas, isso resultaria em algo adequado.
Denis de Bernardy
1

Mostrarei minha solução em T-SQL, trabalhando para o SQL Server 2008. Não estou familiarizado com o PostgreSQL, mas espero que você encontre alguma orientação em minha solução.

-- create test table
IF object_id ('dbo.TestTable') is not null
    DROP table testTable
go
create table testTable (
    id int identity primary key clustered,
    nullColumn varchar(100) NULL,
    notNullColumn varchar(100) not null,
    combinedColumn varchar(100) NULL,
    testTime datetime default getdate()
);
go

-- insert test data:
INSERT INTO testTable(nullColumn, notNullColumn, combinedColumn)
SELECT NULL, 'Test', 'Combination'
from sys.objects
union all
SELECT NULL, 'Test2', NULL
from sys.objects

select *
from testTable

-- FIXED SCRIPT FOR KNOWN TABLE (known structure) - find all completely NULL columns
select sum(datalength(id)) as SumColLength,
    'id' as ColumnName
from dbo.testTable
UNION ALL
select sum(datalength(nullColumn)) as SumColLength,
    'nullColumn' as ColumnName
from dbo.testTable
UNION ALL
select sum(datalength(notNullColumn)) as SumColLength,
    'notNullColumn' as ColumnName
from dbo.testTable
UNION ALL
select sum(datalength(combinedColumn)) as SumColLength,
    'combinedColumn' as ColumnName
from dbo.testTable
UNION ALL
select sum(datalength(testTime)) as SumColLength,
    'testTime' as ColumnName
from dbo.testTable

-- DYNAMIC SCRIPT (unknown structure) - find all completely NULL columns
declare @sql varchar(max) = '', @tableName sysname = 'testTable';

SELECT @sql +=
        'select sum(datalength(' + c.COLUMN_NAME + ')) as SumColLength,
    ''' + c.COLUMN_NAME + ''' as ColumnName
from ' + c.TABLE_SCHEMA + '.' + c.TABLE_NAME --as StatementToExecute
+ '
UNION ALL
'
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = @tableName;

SET @sql = left(@sql, len(@sql)-11)
print @sql;
exec (@sql);

Em resumo, o que fiz foi criar uma tabela de teste com 5 colunas, sendo ID e testTime gerados pela função identity e getdate (), enquanto as 3 colunas varchar são as de interesse. Um terá apenas valores NULL, um não terá NULLs, o outro será uma coluna combinada. O resultado final do script será que ele reportará a coluna nullColumn como tendo todas as linhas NULL.

A ideia era calcular a função DATALENGTH para cada coluna (calcula o número de bytes para uma determinada expressão). Portanto, calculei o valor DATALENGTH para cada linha de cada coluna e fiz um SUM por coluna. Se o SUM por coluna for NULL, a coluna completa terá NULL linhas, caso contrário, existem alguns dados.

Agora você tem que encontrar a tradução para o PostgreSQL e espero que um colega possa ajudá-lo com isso. Ou talvez haja uma boa visão do sistema que mostre como sou burra por reinventar a roda :-).

Marian
fonte
1

Você precisa consultar o catálogo de informações para obter essas informações:

SELECT column_name FROM information_schema.columns WHERE table_name='your_table'

fornece as tabelas correspondentes para suas colunas.

Não tenho uma instalação do postgres atualmente em mãos, mas o resto deve ser simples

   loop over the results of the above query and foreach result
        send a COUNT(*) to the table
        if the count is null, give back the column,
                 else ignore it
   end foreach
DrColossos
fonte
Isso está funcionando, mas é uma abordagem iterativa :-). Eu prefiro a abordagem baseada em conjunto.
Marian
0

Após combinar vários recursos, criei essa função e consulta para encontrar todas as colunas vazias em todas as tabelas do banco de dados

CREATE OR REPLACE FUNCTION public.isEmptyColumn(IN table_name varchar, IN column_name varchar)
RETURNS boolean AS $$
declare 
    count integer;
BEGIN
    execute FORMAT('SELECT COUNT(*) from %s WHERE %s IS NOT NULL', table_name, quote_ident(column_name)) into count;
    RETURN (count = 0);
END; $$
LANGUAGE PLPGSQL; 


SELECT s.table_name, s.column_name
FROM information_schema.columns s
WHERE (s.table_schema LIKE 'public') AND
      (s.table_name NOT LIKE 'pg_%') AND
      (public.isEmptyColumn(s.table_name, s.column_name))

Desfrutar :)

obenda
fonte