(ロジックの一部として)文字列をトリミングし、データベースに挿入する前に空の文字列をNULLに置き換えるアプリケーションがあります。これが確実に実行されるようにするための1つの方法は、VARCHAR
、TEXT
(または同様の)列を持つすべてのテーブルにCHECKを書き込むことだと思います。
データベース内のテキスト列に空の文字列が含まれているかどうかを確認する(データベースのメタデータからテーブルと列の名前を取得する)単純で汎用的なSQLクエリを作成する方法はありますか? ?
空の値のカウント(_''
_)と、それらが_NOT NULL
_で定義されているかどうかを指定して、指定されたテーブルのすべての文字型列を返します。
_CREATE OR REPLACE FUNCTION f_tbl_empty_status(_tbl regclass)
RETURNS TABLE (tbl text, col text, empty_ct bigint, not_null bool) AS
$func$
DECLARE
-- basic char types, possibly extend with citext, domains or custom types:
_typ CONSTANT regtype[] := '{text, bpchar, varchar, "\"char\""}';
_sql text;
_col_arr text[];
_null_arr bool[];
BEGIN
-- Build command
SELECT INTO _col_arr, _null_arr, _sql
array_agg(s.col)
, array_agg(s.attnotnull)
, '
SELECT $1
,unnest($2)
,unnest(ARRAY [count('
|| string_agg(s.col, ' = '''' OR NULL), count(')
|| ' = '''' OR NULL)])
,unnest($3)
FROM ' || _tbl
FROM (
SELECT quote_ident(attname) AS col, attnotnull
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 -- include columns defined NOT NULL
AND atttypid = ANY(_typ) -- only character types
ORDER BY attnum
) AS s;
-- Debug
-- RAISE NOTICE '%', _sql;
-- Execute
IF _sql IS NULL THEN
-- do nothing, nothing to return
ELSE
RETURN QUERY EXECUTE _sql
USING _tbl::text, _col_arr, _null_arr;
END IF;
END
$func$ LANGUAGE plpgsql;
_
コール:
_SELECT * FROM f_tbl_empty_status('tbl_name'); -- optionally schema-qualified
_
戻り値:
_tbl | col | empty_ct | not_null
------+------------+----------+---------
tbl1 | txt | 0 | f
tbl1 | vc | 3 | f
tbl1 | "oDD name" | 7 | f
_
Postgres 9.1以降で機能します。
現在の_search_path
_に従って、出力テーブル名は必要に応じて自動的にスキーマ修飾されます。
出力テーブル名と列名は、必要に応じて自動的にエスケープされます。
_empty_ct
_は、列の値が空の文字列である行の数です
_not_null
_は、列が定義されているかどうかをレポートします_NOT NULL
_(したがって、空の文字列をNULLに変換できません!)
入力テーブル名は、オプションでスキーマ修飾できます。それ以外の場合は、デフォルトで現在の_search_path
_になります。
ロールには、指定されたテーブルから実際に読み取る権限が必要です。
この関数は高度に最適化されており、指定されたテーブルに対してシングルスキャンを実行するだけで、all関連する列をチェックします。
SQLインジェクションに対して安全でなければなりません。
並列unnest()
を使用して、複雑なコードを多少簡略化します。
SO to 実際に空の文字列を置き換える-でこの関連する回答に興味があります詳細説明:
コメントどおり 、trim(s.col, ' ') = ''
はうまく機能します。
しかし、ここにショートカットがあります:
_s.col::char = ''
_
どうやって?char
はcharacter(1)
のエイリアスであり、ほとんど使用されない空白埋めタイプです。値は、長さ指定子(この場合は_1
_ですが、無関係です)まで、右側にスペース文字が埋め込まれます。末尾のスペースは、このタイプでは事実上重要ではありません。したがって、_' '
_は_''
_または_' '
_と同じです。ボイラ。そして、はい、それもより高速です。私はテストしました。
空白文字のみの文字列も検索するには(他の空白文字ではなく!)、上記の行にキャストを追加します。
_ || string_agg(s.col, '::char = '''' OR NULL), count(')
|| '::char = '''' OR NULL)])
_
_CREATE OR REPLACE FUNCTION f_schema_empty_status(_sch text DEFAULT 'public')
RETURNS TABLE (tbl text, col text, empty_ct bigint, not_null bool) AS
$func$
DECLARE
_tbl regclass;
BEGIN
FOR _tbl IN
SELECT c.oid
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = _sch -- 'public' by default
-- AND c.relname LIKE 'my_pattern%' -- optionally filter table names
AND c.relkind = 'r' -- regular tables only
ORDER BY relname
LOOP
-- Debug
-- RAISE NOTICE 'table: %', _tbl;
RETURN QUERY
SELECT * FROM f_tbl_empty_status(_tbl);
END LOOP;
END
$func$ LANGUAGE plpgsql;
_
コール:
_SELECT * FROM f_schema_empty_status(); -- defaults to 'public' without parameter
_
戻り値:
_tbl | col | empty_ct | not_null
------+------------+----------+---------
tbl1 | txt | 0 | f
tbl1 | vc | 3 | f
tbl1 | "oDD name" | 7 | f
tbl2 | some_text | 123 | t
...
_
これを行う最良の方法は、あなたが指摘したように、おそらくCHECK
を介してDOMAIN
制約を使用することです。
CREATE DOMAIN nonempty_string AS text
CONSTRAINT non_empty CHECK (length(VALUE) > 0);
次に、ドメインを使用するALTER
既存の列。
それが不可能な場合は、INFORMATION_SCHEMA
にクエリを実行して、すべてのテーブルからターゲットタイプのすべての列を検索し、各列について、チェックするクエリを動的に生成する必要があります。これには、PL/PgSQLとEXECUTE
ステートメントを使用できます。 Stack Overflowの他の場所にも、この方法での使用例が多数あります。
単一のクエリを記述して必要なことを行うことはできません。うまくいきません。 information_schema
からのクエリ生成を使用する必要があります。