web-dev-qa-db-ja.com

データベース内のすべてのテーブルの各TEXTフィールドの最大フィールド値の長さを取得する方法

データベースのデータに関する情報を取得しようとしています。複数のテーブルに複数の列があるか、「TEXT」と入力しています。次のような結果を作成するクエリを実行したいと思います。

|table_name|column_name|max_length|
-----------------------------------
|my_table  |name       |35        |

すべてのテーブルと列を取得するクエリがあります。

SELECT table_name, column_name FROM information_schema.columns
WHERE table_schema = 'public' AND data_type = 'text'
ORDER BY table_name

しかし、各列の最大長を取得するために、これらの各テーブルと列をどのように処理するかを理解できません。私は特定の列とテーブルの最大長を取得するクエリを持っています:

SELECT 'my_table', 'name', MAX(LENGTH(name)) FROM my_table

クエリの結果に基づいて動的に最大長を取得するクエリを作成して、すべてのテーブルと列を取得するにはどうすればよいですか?

1
Brian

PL/pgSQLは必要ありません。

this solution のバリエーションを使用して、1つのクエリですべてを実行できます。

_select table_schema, 
       table_name,
       column_name, 
       (xpath('/row/max/text()',query_to_xml(format('select max(length(%I)) from %I.%I', column_name, table_schema, table_name), true, true, '')))[1]::text::int as max_length
from information_schema.columns
where table_schema = 'public'       
  and data_type = 'text'
order by table_schema, table_name, column_name;
_

エヴァンのコメントは、テーブルごとに1つのステートメントでこれを行う方法があるかどうかを私に考えさせました(ただし、このパフォーマンスの最適化が「データベースクエリに関するいくつかの情報を取得する」ために本当に重要であるとは思えません)

少しのXMLジャグリングで、それは確かに可能です:

_with counts as (
  select table_schema, 
         table_name,
         query_to_xml(format('select %s from %I.%I', string_agg(format('max(length(%I)) as %I', column_name, column_name), ', '), table_schema, table_name), 
                      true, true, '') as result
  from information_schema.columns
  where table_schema = 'public'       
    and data_type = 'text'
  group by table_schema, table_name
)
select table_schema, 
       table_name, 
       substring(x::text, 2, strpos(x::text,'>') - 2) as column_name,
       (xpath('./text()', x))[1]::text::int as max_length
from counts, unnest(xpath('//row/*', result)) as c(x);
_

Pagilaデモデータベースの場合、これは以下を返します。

_table_schema | table_name                 | column_name | max_length
-------------+----------------------------+-------------+-----------
public       | actor_info                 | film_info   |        830
public       | customer_list              | notes       |          6
public       | customer_list              | name        |         21
public       | film                       | description |        130
public       | film_list                  | actors      |        216
public       | film_list                  | description |        130
public       | nicer_but_slower_film_list | actors      |        201
public       | nicer_but_slower_film_list | description |        130
public       | sales_by_store             | manager     |         12
public       | sales_by_store             | store       |         19
public       | staff_list                 | name        |         12
_

上記は最初に、各列に1つのmax(length(..))を含むテーブルごとに1つのステートメントを生成します。

次に、結果が解析され、再び行に変換されます。これを単一のJSONに集約することが可能です。


テーブルごとに1つの行を取得し、列情報をJSON値として、以下を使用できます。

_with counts as (
  select table_schema, 
         table_name,
         query_to_xml(format('select to_jsonb(t) as cols from (select %s from %I.%I) t', string_agg(format('max(length(%I)) as %I', column_name, column_name), ', '), table_schema, table_name), 
                      false, true, '') as result
  from information_schema.columns
  where table_schema = 'public'       
    and data_type = 'text'
  group by table_schema, table_name
)
select table_schema, 
       table_name, 
       (xpath('//row/cols/text()', result))[1]::text::jsonb as column_sizes
from counts;
_

Pagilaデモデータベースの場合、これは以下を返します。

_table_schema | table_name                 | column_sizes                       
-------------+----------------------------+------------------------------------
public       | actor_info                 | {"film_info": 830}                 
public       | customer_list              | {"name": 21, "notes": 6}           
public       | film                       | {"description": 130}               
public       | film_list                  | {"actors": 216, "description": 130}
public       | nicer_but_slower_film_list | {"actors": 201, "description": 130}
public       | sales_by_store             | {"store": 19, "manager": 12}       
public       | staff_list                 | {"name": 12}                       
_

これにより、一連の選択ステートメントが返されます。このステートメントは、すべてのテキスト列の最大長を返した後、各テーブルをクエリするだけです。

SELECT FORMAT(
        'SELECT %L AS table_catalog, %L AS table_schema, %L AS table_name, %s FROM %I.%I.%I;',
        table_catalog,
        table_schema,
        table_name,
        string_agg(
                FORMAT('max(length(%I)) AS %I', column_name, column_name),
                ', '
                ORDER BY column_name
        ),
        table_catalog,
        table_schema,
        table_name
)
FROM information_schema.columns
WHERE data_type = 'text'
GROUP BY table_catalog, table_schema, table_name
ORDER BY table_catalog, table_schema, table_name;

テーブルにはさまざまな量のテキスト列を含めることができるため、その時点から先に進む唯一の方法は、JSONBまたはその他のスキーマレスタイプを使用することです。

SELECT FORMAT(                                                                                                                                                           'SELECT %L AS table_catalog, %L AS table_schema, %L AS table_name, %s FROM %I.%I.%I;',
        table_catalog,
        table_schema,
        table_name,
        string_agg(
                FORMAT( 'jsonb_build_object(%L,max(length(%I)))', column_name, column_name),
                ' || '
                ORDER BY column_name
        ),
        table_catalog,
        table_schema,
        table_name
)
FROM information_schema.columns
WHERE data_type = 'text'
GROUP BY table_catalog, table_schema, table_name
ORDER BY table_catalog, table_schema, table_name;

この時点から、それらすべてをまとめてUNIONできます。

SELECT FORMAT( '%s;', string_agg(stmt, E'\nUNION ALL ') )
FROM (
  SELECT FORMAT(
    'SELECT %L AS table_catalog, %L AS table_schema, %L AS table_name, %s FROM %I.%I.%I',
    table_catalog,
    table_schema,
    table_name,
    string_agg(
      FORMAT( 'jsonb_build_object(%L,max(length(%I)))', column_name, column_name),
      ' || '
      ORDER BY column_name
    ),
    table_catalog,
    table_schema,
    table_name
  )
  FROM information_schema.columns
  WHERE data_type = 'text'
    AND table_schema <> 'pg_catalog'
  GROUP BY table_catalog, table_schema, table_name
) AS t(stmt);

実行中結果そのクエリの(タイプ\gexec psqlを使用している場合)、出力は次のようになります。

 test          | master       | entities                  | {"entity_name": 6}
 test          | public       | geography_columns         | {"type": null}
 test          | public       | ip2loc_asn                | {"name": 29}
 test          | public       | t                         | {"event": 3}
 test          | public       | txns_combined             | {"item": 6, "to_party": 5, "from_party": 4}
 test          | tec          | codes_office              | {"office_id": 22, "office_name": 44}
 test          | tec          | codes_reports             | {"report_id": 15, "report_name": 70}
 test          | tec          | codes_texas_counties      | {"county_id": 3, "county_name": 13}
 test          | tec          | codes_total               | {"total_id": 22, "total_description": 112}
 test          | tec          | expendcategory            | {"recordtype": 5, "expendcategorycodelabel": 74, "expendcategorycodevalue": 10}
0
Evan Carroll