Suche nach leeren Spalten einer Tabelle in PostgreSQL


17

Welche Abfrage würde den Namen der Spalten einer Tabelle zurückgeben, in der alle Zeilen NULL sind?


Meinen Sie eine bestimmte Tabelle oder alle Tabellen in einem Schema?
Jack Douglas

1
Warum müssten Sie das tun? Klingt so, als hätten Sie zu viele Spalten / Tabellen und sollten Ihr Design überdenken.
17.

Antworten:


13

Prüfstand:

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;

Funktion:

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

Abfrage:

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

Ergebnis:

 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)

Darüber hinaus können Sie eine ungefähre Antwort erhalten, indem Sie den Katalog abfragen. Wenn null_fracNull ist, werden keine Nullen angezeigt, es sollte jedoch eine doppelte Überprüfung anhand der tatsächlichen Daten durchgeführt werden:

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)

1
Dies ist eine sehr alte Frage, aber Leute, die räumliche Erweiterungen (Postgis) verwenden, sollten beachten, dass leere räumliche Spalten nicht angezeigt werden, pg_statswenn sie bei der Tabellenerstellung leer sind. Das habe ich heute bei der Hauswirtschaft herausgefunden. Ich entdeckte, dass einige historische aspatial Tabellen mit importiert worden waren ogr2ogr. Wenn die zu importierenden Daten keine räumliche Spalte enthalten, ogr2ogrwird eine Geometriespalte mit erstellt <NULL>. My pg_statshat keine Geometriespalten aus den importierten Aspatial-Tabellen (es hat alle anderen Spalten für diese Tabellen). Merkwürdig, dachte ich.
GT.

6

In Postgresql können Sie die Daten direkt aus den Statistiken abrufen:

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;

Möglicherweise erhalten Sie ein paar Fehlalarme. Nach dem Auffinden der Kandidaten ist eine erneute Überprüfung angebracht.


Benötigen Sie andere Bedingungen als null_frac=1?
Jack Douglas

Ich bin mir nicht sicher. null_frac ist vermutlich ein Real, daher kann es sein, dass es in einigen ungeraden Fällen auf 1 rundet. Aber selbst mit 1 von 10.000 Zeilen würde das zu etwas Passendem führen.
Denis de Bernardy

1

Ich werde Ihnen meine Lösung in T-SQL für SQL Server 2008 zeigen. Ich bin nicht mit PostgreSQL vertraut, aber ich hoffe, dass Sie in meiner Lösung eine Anleitung finden.

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

Kurz gesagt, ich habe eine Testtabelle mit 5 Spalten erstellt, wobei ID und testTime durch die Funktion identity und getdate () generiert wurden und die 3 varchar-Spalten von Interesse sind. Einer hat nur NULL-Werte, einer hat keine NULL-Werte, der andere ist eine kombinierte Spalte. Das Endergebnis des Skripts ist, dass das Skript die Spalte nullColumn als alle Zeilen NULL aufweisend meldet.

Die Idee war, die Funktion DATALENGTH für jede Spalte zu berechnen (berechnet die Anzahl der Bytes für einen bestimmten Ausdruck). Also habe ich den DATALENGTH-Wert für jede Zeile jeder Spalte berechnet und eine SUMME pro Spalte erstellt. Wenn die Summe pro Spalte NULL ist, enthält die gesamte Spalte NULL-Zeilen, andernfalls sind einige Daten enthalten.

Jetzt müssen Sie die Übersetzung für PostgreSQL finden und hoffentlich kann Ihnen ein Kollege dabei helfen. Oder vielleicht gibt es eine schöne Systemansicht, die zeigt, wie dumm ich bin, das Rad neu zu erfinden :-).


1

Sie müssen den Informationskatalog nach solchen Informationen abfragen:

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

gibt Ihnen die passenden Tabellen für Ihre Spalten.

Ich habe momentan keine Postgres-Installation zur Hand, aber der Rest sollte einfach sein

   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

Das funktioniert, aber es ist ein iterativer Ansatz :-). Ich bevorzuge den satzbasierten Ansatz.
Marian

0

Nachdem ich mehrere Ressourcen kombiniert hatte, kam ich zu dieser Funktion und Abfrage, um alle leeren Spalten in allen Datenbanktabellen zu finden

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

Genießen :)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.