Znajdowanie pustych kolumn tabeli w PostgreSQL

17

Jakie zapytanie zwróciłoby nazwę kolumn tabeli, w której wszystkie wiersze mają wartość NULL?

Seb
źródło
Czy masz na myśli konkretną tabelę lub wszystkie tabele w schemacie?
Jack Douglas
1
Dlaczego miałbyś to robić? Wygląda na to, że masz za dużo kolumn / tabel i powinieneś przemyśleć swój projekt.
eevar

Odpowiedzi:

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;

funkcjonować:

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;$$;

pytanie:

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

wynik:

 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)

Ponadto można uzyskać przybliżoną odpowiedź, przeszukując katalog - jeśli null_fraczero oznacza brak wartości zerowych, ale należy je dwukrotnie sprawdzić w stosunku do „rzeczywistych” danych:

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
źródło
1
To jest stare pytanie, ale ludzie, którzy używają rozszerzeń przestrzennych (postgis) powinni zauważyć, że puste kolumny przestrzenne nie pojawiają się, pg_statsjeśli są puste przy tworzeniu tabeli. Dowiedziałem się tego dzisiaj, kiedy robiłem porządki. Odkryłem, że niektóre historyczne aspatial stoły zostały przywiezione użyciu ogr2ogr. jeśli w importowanych danych nie ma kolumny przestrzennej, ogr2ogrtworzona jest kolumna geometrii pełna <NULL>. Mój pg_statsnie ma żadnych kolumn geometrii z zaimportowanych tabel przestrzennych (ma wszystkie pozostałe kolumny dla tych tabel). Całkiem dziwne, pomyślałem.
GT.
6

W Postgresql możesz uzyskać dane bezpośrednio ze statystyk:

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;

Możesz otrzymać kilka fałszywych wyników pozytywnych, więc po znalezieniu kandydatów należy ponownie sprawdzić.

Denis de Bernardy
źródło
Czy potrzebujesz innych warunków niż null_frac=1?
Jack Douglas
Nie jestem pewny. null_frac prawdopodobnie jest prawdziwy, więc może być tak, że zaokrągla się do 1 w niektórych dziwnych przypadkach. Ale nawet z 1 na 10 000 wierszy, to dałoby coś pasującego.
Denis de Bernardy,
1

Pokażę ci moje rozwiązanie w T-SQL, pracujące dla SQL Server 2008. Nie znam PostgreSQL, ale mam nadzieję, że znajdziesz wskazówki w moim rozwiązaniu.

-- 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);

Krótko mówiąc, stworzyłem tabelę testową z 5 kolumnami, ID i testTime generowanymi przez funkcje identyczności i getdate (), podczas gdy interesujące są 3 kolumny varchar. Jeden będzie miał tylko wartości NULL, jeden nie będzie miał żadnych NULL, drugi będzie połączoną kolumną. Ostatecznym rezultatem skryptu będzie to, że skrypt zgłosi kolumnę nullColumn jako zawierającą wszystkie wiersze NULL.

Chodziło o obliczenie funkcji DATALENGTH dla każdej kolumny (oblicza liczbę bajtów dla danego wyrażenia). Więc obliczyłem wartość DATALENGTH dla każdego wiersza każdej kolumny i stworzyłem SUMA na kolumnę. Jeśli SUMA na kolumnę ma wartość NULL, wówczas cała kolumna ma NULL wierszy, w przeciwnym razie wewnątrz są pewne dane.

Teraz musisz znaleźć tłumaczenie PostgreSQL i mam nadzieję, że kolega będzie w stanie Ci w tym pomóc. A może jest ładny widok systemu, który pokaże, jak głupi jestem na nowo opracowując koło :-).

Marian
źródło
1

Aby uzyskać takie informacje, należy przeszukać katalog informacyjny:

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

daje pasujące tabele dla twoich kolumn.

Nie mam pod ręką instalacji Postgres, ale reszta powinna być prosta

   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
źródło
To działa, ale jest to podejście iteracyjne :-). Wolę podejście oparte na zestawie.
Marian
0

Po połączeniu z kilku zasobów wymyśliłem tę funkcję i zapytanie, aby znaleźć wszystkie puste kolumny we wszystkich tabelach bazy danych

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))

Cieszyć się :)

obenda
źródło