Qual consulta retornaria o nome das colunas de uma tabela em que todas as linhas são NULL?
Qual consulta retornaria o nome das colunas de uma tabela em que todas as linhas são NULL?
Respostas:
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_frac
for 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)
pg_stats
se 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, ogr2ogr
criará uma coluna de geometria cheia <NULL>
. Meu pg_stats
não possui colunas geométricas das tabelas aspatiais importadas (ele tem todas as outras colunas para essas tabelas). Muito estranho, pensei.
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.
null_frac=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 :-).
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
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 :)