计算行的NULL属性数

时间:2015-07-16 02:47:41

标签: sql postgresql count null plpgsql

我想在表中添加一个新列,以记录每个元组(行)的值为null的属性数。如何使用SQL获取数字?

例如,如果元组是这样的:

Name | Age | Sex
-----+-----+-----
Blice| 100 | null

我想更新元组:

Name | Age | Sex | nNULL
-----+-----+-----+--------
Blice| 100 | null|  1

另外,因为我正在编写PL / pgSQL函数并且表名是从参数中获得的,所以我事先并不知道表的模式。这意味着我需要使用输入表名更新表。有谁知道怎么做?

5 个答案:

答案 0 :(得分:6)

您可以使用而不拼写列。将列旋转到行和计数。聚合函数count()仅计算非空值,而count(*)计算所有行。对多个列计算NULL值的最短且最快的方法是count(*) - count(col) ...

适用于任何表,其中包含任意any数据类型的列。

Postgres 9.3+内置JSON functions

SELECT *
    , (SELECT count(*) - count(v) FROM json_each_text(row_to_json(t)) x(k,v)) AS ct_nulls
FROM   tbl t;
  • 根据评论:什么是x(k,v)

json_each_text()返回一列或两列的行。默认列名称为keyvalue,如manual I linked to中所示。我提供了表和列别名,因此我们不必依赖默认名称。第二列名为v

或者,在任何Postgres版本中,至少8.3安装了附加模块hstore,更短,更快一点:

SELECT *,  (SELECT count(*) - count(v) FROM svals(hstore(t)) v) AS ct_nulls
FROM   tbl t;

这个更简单的版本只返回一组单个值。我只提供了一个简单的别名v,它自动被视为表列别名。

由于附加列功能相关,我会认为 not 根本不会将其保留在表中。而是像上面演示的那样在运行中计算它,或者为此目的创建一个具有polymorphic输入类型的微小函数:

CREATE OR REPLACE FUNCTION f_ct_nulls(_row anyelement)
  RETURNS int AS
$func$
SELECT (count(*) - count(v))::int FROM svals(hstore(_row)) v
$func$  LANGUAGE sql IMMUTABLE;

然后:

SELECT *, f_ct_nulls(t) AS ct_nulls
FROM   tbl t;

如果你想要......你可以将它包装成VIEW

SQL Fiddle展示所有。

这也应该回答你的第二个问题:

  

...表名是从参数中获取的,我事先并不知道表的模式。这意味着我需要使用输入表名更新表。

答案 1 :(得分:2)

在Postgres中,您可以将其表达为:

select t.*,
       ((name is null)::int +
        (age is null)::int +
        (sex is null)::int
       ) as numnulls
from table t;

为了在未知表上实现此功能,您需要使用动态SQL并获取列列表(例如来自information_schema.columns))。

答案 2 :(得分:1)

我刚创建了一个函数来执行OP的要求,使用Gordon Linoff's answer和下面的表和数据:

det

CREATE TABLE det (
  name text,
  age integer,
  sex text
);

数据:

insert into det (name,age,sex) values
  ('Blice',100,NULL),
  ('Glizz',NULL,NULL),
  (NULL,NULL,NULL);

功能:

create or replace function fn_alter_nulls(tbl text,new_col text)  returns void as 
$$
declare vals text;
begin
   -- dynamically getting list of columns *
select string_agg(format('(%s is null)::int',column_name),'+') into vals
from information_schema.columns 
where table_schema='public' and table_name=''||tbl||''  and table_catalog='yourDB_Name';
-- adds new column
execute format('alter table %s add column "%s" int',tbl,new_col);
--updates new column
execute format('update det set %s =(%s)',new_col,vals);
end;
$$
language plpgsql

函数调用:

select fn_alter_nulls('det','nnulls')

答案 3 :(得分:1)

自动添加列的功能

这是每个请求@winged panther posted的审核版本。

该函数将具有给定名称的列添加到任何现有表中,该调用角色具有以下所需的权限:

CREATE OR REPLACE FUNCTION f_add_null_count(_tbl regclass, _newcol text)
  RETURNS void AS
$func$
BEGIN
   -- add new col
   EXECUTE format('ALTER TABLE %s ADD COLUMN %I smallint', _tbl, _newcol);

   -- update new col with dynamic count of nulls
   EXECUTE (
      SELECT format('UPDATE %s SET %I = (', _tbl, _newcol)  -- regclass used as text
          || string_agg(quote_ident(attname), ' IS NULL)::int + (')
          || ' IS NULL)::int'
      FROM   pg_catalog.pg_attribute
      WHERE  attnum > 0
      AND    NOT attisdropped
      AND    attrelid = _tbl  -- regclass used as OID
      AND    attname <> _newcol  -- no escaping here, it's the *text*!
      );
END
$func$  LANGUAGE plpgsql;

SQL Fiddle demo.

如何正确处理标识符

我用粗体格式化了相关的代码片段。

其他要点

  • 我的查询基于pg_catalog.pg_attribute,但这是可选的决定,有利有弊。使我的查询更简单,更快,因为我可以使用表的OID。相关:

  • 您必须从计数中排除新添加的列,否则计数将被关闭。

  • 使用数据类型 smallint 作为计数,因为表格中的列数不能超过1600列。

  • 我没有使用变量,而是直接执行SELECT语句的结果。在plpgsql中,赋值相对较高。不过没什么大不了的。也是品味和风格的问题。

  • 我使用下划线(_tbl)来设置参数和变量的习惯,以排除变量和列名之间的歧义。

答案 4 :(得分:0)

由于空计数是派生数据而且在查询时确定简单/便宜,为什么不创建一个视图:

create view MyTableWithNullCount as
select
  *, 
  case when nullableColumn1 is null then 1 else 0 end +
  case when nullableColumn2 is null then 1 else 0 end +
  ...
  case when nullableColumnn is null then 1 else 0 end as nNull
from myTable

只需使用视图。

这样做的好处是不必编写触发器/代码来维护物理空计数列,这将比这种方法更令人头痛。

相关问题