空值的质量合并
Mass-Coalescing of Null Values
我在 Postgres 数据库中有一个 table,其中包含从 2012 年到 2018 年底的每月列:
create table sales_data (
part_number text not null,
customer text not null,
qty_2012_01 numeric,
qty_2012_02 numeric,
qty_2012_03 numeric,
...
qty_2018_10 numeric,
qty_2018_11 numeric,
qty_2018_12 numeric,
constraint sales_data_pk primary key (part_number, customer)
);
数据由一个大型函数填充,该函数从极其广泛的来源中提取数据。它涉及许多左连接——例如,结合历史数据和未来数据,其中单个项目可能有历史但没有未来需求,反之亦然。或者,某些客户可能没有我们想要的那么远的数据。
我遇到的问题是由于左连接(以及我提取的数据的性质),我提取的大量值都是空的。我希望任何 null 都简单地为零,以简化针对此 table 的任何查询,特别是聚合函数 1 + null + 2 = null.
我可以修改函数并添加数百个合并语句。但是,我希望有另一种解决方法,即使这意味着事后修改值。也就是说,这意味着在函数末尾添加 84 个更新语句:
update sales_data set qty_2012_01 = 0 where qty_2012_01 is null;
update sales_data set qty_2012_02 = 0 where qty_2012_02 is null;
update sales_data set qty_2012_03 = 0 where qty_2012_03 is null;
... 78 more like this...
update sales_data set qty_2018_10 = 0 where qty_2018_10 is null;
update sales_data set qty_2018_11 = 0 where qty_2018_11 is null;
update sales_data set qty_2018_12 = 0 where qty_2018_12 is null;
我错过了什么,对吧?有没有更简单的方法?
我希望列上的 default
设置会强制为零,但是当函数明确告诉它插入空值时它不起作用。同样,如果我使该列不可为 null,它只会在我的插入中呕吐——我希望这可能会强制调用默认值。
顺便说一下,插入然后更新策略是我责备其他人的策略,所以我知道这不太理想。这个功能有点像野兽,它确实需要一些偶尔的维护(长话短说)。我的主要目标是尽可能保持函数的可读性和可维护性——而不是使函数超级高效。 table 本身并不大——毕竟不到一百万条记录——而且我们 运行 每个月填充一次或两次的功能。
虽然 INSERT 语句本身您可以 COALESCE (col_name, 0) 将解决此问题。您也可以添加 NOT NULL 以保持数据完整性。
假设从临时文件插入数据Table
INSERT INTO sales_data (qty_2012_01, qty_2012_02)
SELECT COALESCE(qty_2012_01, 0), COALESCE(qty_2012_01, 0)
FROM temp_sales_data;
单一更新
UPDATE sales_date SET
qty_2012_01 = COALESCE(qty_2012_01, 0),
qty_2012_02 = COALESCE(qty_2012_02, 0)
..
..
WHERE qty_2012_01 IS NULL
OR qty_2012_02 IS NULL
...
....
以上查询将在一次更新中更新所有列。
没有内置功能(我会知道)。除了在任何地方拼出 COALESCE(col, 0)
之外,您还可以编写一个函数来将 table 的所有 numeric
列中的所有 NULL
值替换为 0
:
CREATE OR REPLACE FUNCTION f_convert_numeric_null(_tbl regclass)
RETURNS void AS
$func$
BEGIN
RAISE NOTICE '%', -- test output for debugging
-- EXECUTE -- payload
(SELECT 'UPDATE ' || _tbl
|| ' SET ' || string_agg(format('%1$s = COALESCE(%1$s, 0)', col), ', ')
|| ' WHERE ' || string_agg(col || ' IS NULL', ' OR ')
FROM (
SELECT quote_ident(attname) AS col
FROM pg_attribute
WHERE attrelid = _tbl -- valid, visible, legal table name
AND attnum >= 1 -- exclude tableoid & friends
AND NOT attisdropped -- exclude dropped columns
AND NOT attnotnull -- exclude columns defined NOT NULL
AND atttypid = 'numeric'::regtype -- only numeric columns
ORDER BY attnum
) sub
);
END
$func$ LANGUAGE plpgsql;
连接并执行以下形式的查询:
UPDATE sales_data
SET qty_2012_01 = COALESCE(qty_2012_01, 0)
, qty_2012_02 = COALESCE(qty_2012_02, 0)
, qty_2012_03 = COALESCE(qty_2012_03, 0)
...
WHERE qty_2012_01 IS NULL OR
qty_2012_02 IS NULL OR
qty_2012_03 IS NULL ... ;
适用于具有 any 列名称的 any table。所有 numeric
列均已更新。只触及实际更改的行。
由于该功能具有大量侵入性,因此我添加了一个儿童安全装置。引用 RAISE NOTICE
行并取消引用 EXECUTE
以启动炸弹。
通话:
SELECT f_convert_numeric_null('sales_data');
My primary goal is to keep the function as readable and maintainable as possible.
应该这样做。
参数类型为regclass
,因此传递table名称,可能是模式限定的,非标准标识符必须用双引号引起来-名称如"mySchema"."0dumb tablename"
.
将您的查询结果写入临时 table、运行 临时函数 table 和 then INSERT
变成实际的table.
相关:
- Replace empty strings with null values
- Table name as a PostgreSQL function parameter
- Generate DEFAULT values in a CTE UPSERT using PostgreSQL 9.3
我在 Postgres 数据库中有一个 table,其中包含从 2012 年到 2018 年底的每月列:
create table sales_data (
part_number text not null,
customer text not null,
qty_2012_01 numeric,
qty_2012_02 numeric,
qty_2012_03 numeric,
...
qty_2018_10 numeric,
qty_2018_11 numeric,
qty_2018_12 numeric,
constraint sales_data_pk primary key (part_number, customer)
);
数据由一个大型函数填充,该函数从极其广泛的来源中提取数据。它涉及许多左连接——例如,结合历史数据和未来数据,其中单个项目可能有历史但没有未来需求,反之亦然。或者,某些客户可能没有我们想要的那么远的数据。
我遇到的问题是由于左连接(以及我提取的数据的性质),我提取的大量值都是空的。我希望任何 null 都简单地为零,以简化针对此 table 的任何查询,特别是聚合函数 1 + null + 2 = null.
我可以修改函数并添加数百个合并语句。但是,我希望有另一种解决方法,即使这意味着事后修改值。也就是说,这意味着在函数末尾添加 84 个更新语句:
update sales_data set qty_2012_01 = 0 where qty_2012_01 is null;
update sales_data set qty_2012_02 = 0 where qty_2012_02 is null;
update sales_data set qty_2012_03 = 0 where qty_2012_03 is null;
... 78 more like this...
update sales_data set qty_2018_10 = 0 where qty_2018_10 is null;
update sales_data set qty_2018_11 = 0 where qty_2018_11 is null;
update sales_data set qty_2018_12 = 0 where qty_2018_12 is null;
我错过了什么,对吧?有没有更简单的方法?
我希望列上的 default
设置会强制为零,但是当函数明确告诉它插入空值时它不起作用。同样,如果我使该列不可为 null,它只会在我的插入中呕吐——我希望这可能会强制调用默认值。
顺便说一下,插入然后更新策略是我责备其他人的策略,所以我知道这不太理想。这个功能有点像野兽,它确实需要一些偶尔的维护(长话短说)。我的主要目标是尽可能保持函数的可读性和可维护性——而不是使函数超级高效。 table 本身并不大——毕竟不到一百万条记录——而且我们 运行 每个月填充一次或两次的功能。
虽然 INSERT 语句本身您可以 COALESCE (col_name, 0) 将解决此问题。您也可以添加 NOT NULL 以保持数据完整性。
假设从临时文件插入数据Table
INSERT INTO sales_data (qty_2012_01, qty_2012_02)
SELECT COALESCE(qty_2012_01, 0), COALESCE(qty_2012_01, 0)
FROM temp_sales_data;
单一更新
UPDATE sales_date SET
qty_2012_01 = COALESCE(qty_2012_01, 0),
qty_2012_02 = COALESCE(qty_2012_02, 0)
..
..
WHERE qty_2012_01 IS NULL
OR qty_2012_02 IS NULL
...
....
以上查询将在一次更新中更新所有列。
没有内置功能(我会知道)。除了在任何地方拼出 COALESCE(col, 0)
之外,您还可以编写一个函数来将 table 的所有 numeric
列中的所有 NULL
值替换为 0
:
CREATE OR REPLACE FUNCTION f_convert_numeric_null(_tbl regclass)
RETURNS void AS
$func$
BEGIN
RAISE NOTICE '%', -- test output for debugging
-- EXECUTE -- payload
(SELECT 'UPDATE ' || _tbl
|| ' SET ' || string_agg(format('%1$s = COALESCE(%1$s, 0)', col), ', ')
|| ' WHERE ' || string_agg(col || ' IS NULL', ' OR ')
FROM (
SELECT quote_ident(attname) AS col
FROM pg_attribute
WHERE attrelid = _tbl -- valid, visible, legal table name
AND attnum >= 1 -- exclude tableoid & friends
AND NOT attisdropped -- exclude dropped columns
AND NOT attnotnull -- exclude columns defined NOT NULL
AND atttypid = 'numeric'::regtype -- only numeric columns
ORDER BY attnum
) sub
);
END
$func$ LANGUAGE plpgsql;
连接并执行以下形式的查询:
UPDATE sales_data
SET qty_2012_01 = COALESCE(qty_2012_01, 0)
, qty_2012_02 = COALESCE(qty_2012_02, 0)
, qty_2012_03 = COALESCE(qty_2012_03, 0)
...
WHERE qty_2012_01 IS NULL OR
qty_2012_02 IS NULL OR
qty_2012_03 IS NULL ... ;
适用于具有 any 列名称的 any table。所有 numeric
列均已更新。只触及实际更改的行。
由于该功能具有大量侵入性,因此我添加了一个儿童安全装置。引用 RAISE NOTICE
行并取消引用 EXECUTE
以启动炸弹。
通话:
SELECT f_convert_numeric_null('sales_data');
My primary goal is to keep the function as readable and maintainable as possible.
应该这样做。
参数类型为regclass
,因此传递table名称,可能是模式限定的,非标准标识符必须用双引号引起来-名称如"mySchema"."0dumb tablename"
.
将您的查询结果写入临时 table、运行 临时函数 table 和 then INSERT
变成实际的table.
相关:
- Replace empty strings with null values
- Table name as a PostgreSQL function parameter
- Generate DEFAULT values in a CTE UPSERT using PostgreSQL 9.3