在 postgres psql select 语句中默认截断显示

Truncating display by default in postgres psql select statements

我有一个带有长文本列的 table。我希望能够 select 所有列但限制文本列而无需编写每一列。

select * from resources;

生成的输出太长而无法在 psql 中正确显示。我可以通过在长列上使用 substr()left() 来显示一些内容,但是我需要指定每一列。

select id, left(data, 50), file_format_version, ... from resources;

有没有办法让 psql 在我查询第一个 select * from resources 时默认截断长列?

不是真正的 psql,但代码一切皆有可能:)

这个结果非常棘手,我对最终解决方案不满意,因为它有点笨拙,但它完成了工作,但仍应将其视为概念验证。总有改进和简化的空间:)

无论如何,因为没有内置任何东西,我不喜欢在 postgres 之外做这个的想法,即。 PAGER 输出通过管道传送到 awk 而不是 less,最后传送到你 :) 我使用了函数。

Python 是我大部分时间选择的武器,所以我制作了一个 plpython 函数来进行截断,以及一个 plpgsql 包装器,以便可以很好地调用它,使用所有可爱的东西 SQL 优惠。

让我们从包装函数开始:

create or replace function wrapper(t text, x anyelement, l integer) 
returns setof anyelement as
$$
  begin
  -- call the logic bearing function
  execute format($f$select truncate('%1$s', %2$s)$f$, t, l);
  -- return results
  return query execute format($f$select * from trunc_%1$s$f$, t);
  end;
$$ language plpgsql;

如您所见,它是用多态输入声明的,因此它可以在您给它的所有 table 上工作,或者更确切地说,它可以 return 相同类型的 table你喂给它,保留你所有的约束,索引等等(这是由 plpython 函数完成的)......所以不用多说,让我们看看它:

create or replace function truncate(tbl text, l integer) returns void as
$$
  import arrow
  import json

  # Drops if needed and creates a table to hold your truncated results
  plpy.execute('drop table if exists trunc_{0}'.format(tbl))
  plpy.execute('create table trunc_{0} ( like {0} including defaults including    constraints including indexes )'.format(tbl))
  r = plpy.execute("select * from {}".format(tbl))
  for i in xrange(r.nrows()):
  # These two lists help us get the order of columns and values right
  ins = []
  cols = []
  for x in r[i]:
    if type(r[i][x]) is str:

      '''
      Two booleans below are used for type checking, I had an example table
      with some json, timestamps and integers lying around so used that for
      testing, this way we will truncate only text-like fields, but not json. 
      '''
      ts = False
      js = False

      '''
      Check if we can parse date or json, note that if you have a valid json
      stored in a text or varchar field it will still get marked as json, by 
      digging in the information_schema you could easily add better type   
      checking here.
      '''
      try: 
        arrow.get(r[i][x])
        ts = True
      except (arrow.parser.ParserError, UnicodeDecodeError):
        pass
      try:
        json.loads(r[i][x])
        js = True 
      except ValueError:
        pass
      # If it is a string and its not json or timestamp lets truncate it           
      # whatever you specify as the last argument in the call, `l`  
      if not ts and not js:
        r[i][x] = r[i][x][:l]
    # Additional check for nulls and ints, and appropriate appends
    if r[i][x] is None:
      ins.append("null")
    elif r[i][x] is int:
      ins.append(r[i[x]])
    ```
    Finally we can append our values to insert, this is done inefficiently as 
    each row will be inserted individually, again treat this as a POC, better 
    would be to first form a list of all inserts and then fire them in one statement.
    ```
    else:
      ins.append("'{}'".format(r[i][x]))
    cols.append(x)
  q = 'insert into trunc_{0}({2}) values({1})'.format(tbl, ','.join(ins), ','.join(cols))
  plpy.execute(q)
$$ language plpythonu;

如果我设法正确格式化它,你应该可以通过 运行:

调用它
select * from wrapper(resources, null::resources, 50);

再一次笨拙显示它丑陋的脸,所以你给 table 名称,table 列类型所以它知道 return 和截断的字符限制,你应该是能够毫无问题地使用 WHEREGROUP BY 和类似的东西。

明显的问题是性能,因为您实际上将重新插入整个 table,这可能会有问题,但至少很容易解决。

作为事后的想法,如果您不熟悉 plpython,您可以在 psql 中通过 运行 create extension plpythonu 启用它。 json 模块内置于 python,而 arrow 可以通过 运行 pip install arrow 从您选择的 shell 安装,提供 pip 和 python 是有序的,以防他们不是 Google 是你的朋友 ;)

希望这至少能让你部分地到达你想去的地方:)

我不知道 psql 的内置选项是什么。
您可以使用像 这样的函数来实现您的目标 - 只是 更简单 :

CREATE OR REPLACE FUNCTION f_trunc_columns(_tbl anyelement, _len int = 25)
  RETURNS SETOF anyelement AS
$func$
DECLARE
   _typ  CONSTANT regtype[] := '{bpchar, varchar}';  -- types to shorten
BEGIN
   RETURN QUERY EXECUTE (
   SELECT format('SELECT %s FROM %s'
               , string_agg(CASE WHEN a.atttypid = 'text'::regtype  -- simple case text
                              THEN format('left(%I, %s)', a.attname, _len)
                            WHEN a.atttypid = ANY(_typ)             -- other short types
                              THEN format('left(%I::text, %s)::%s'
                                 , a.attname, _len, format_type(a.atttypid, a.atttypmod))
                            ELSE quote_ident(a.attname) END         -- rest
                          , ', ' ORDER BY a.attnum)
               , pg_typeof(_tbl))
   FROM   pg_attribute a
   WHERE  a.attrelid = pg_typeof(_tbl)::text::regclass
   AND    NOT a.attisdropped  -- no dropped (dead) columns
   AND    a.attnum > 0        -- no system columns
   );
END
$func$  LANGUAGE plpgsql;

调用示例:

SELECT * FROM f_trunc_columns(NULL::my_table);
SELECT * FROM f_trunc_columns(NULL::"MySchema"."My_funny_tbl", 11);

SQL Fiddle.

备注

  • 适用于 any table 数据类型为 any 的列。

  • 这将构建并执行以下形式的查询:

    SELECT "FoO_id", left(c_text, 11), left(c_vc, 11)::character varying
    FROM   "FoO";
    
  • 它只缩短所选数据类型的列,而保留其他列。我包括基本字符类型:
    bpcharcharacter 和所有变体的内部名称。
    varcharcharacter varying 和所有变体的内部名称。
    扩展到您的需求。

  • 函数returns所有列的原始列名和数据类型。我先将短列投射到 text,然后再投射到 left(),后者是 returns text,因此 text 列不需要再次投射。所有其他缩短的类型都需要转换回原始类型。 某些类型在截断时会中断! 所以这并不适用于所有类型。

  • 您可以将 LIMIT n 附加到函数调用,但可以使用内置的 LIMIT 轻松扩展该函数 - 这是 much 对于大 tables 更有效,因为 plpgsql 函数内的查询是独立计划的。

  • 性能并不比普通的SELECT * FROM tbl差多少——除了说的LIMIT 嵌套函数的情况或其他情况。集合返回 PL/pgSQL 函数通常最好不要嵌套:

  • 我内置了默认最大值。长度为 25 个字符,将自定义长度作为第二个参数传递,或者根据您的需要调整函数头中的默认值。

  • 此函数可以安全地抵御通过恶意制作的标识符可能发生的 SQL 注入攻击。

具有更多解释和链接的相关答案:

  • Replace empty strings with null values
  • Refactor a PL/pgSQL function to return the output of various SELECT queries
  • Table name as a PostgreSQL function parameter
  • Postgres data type cast
  • Query the schema details of a table in PostgreSQL?
  • How to check if a table exists in a given schema

pgAdmin

... 具有您要求的功能,顺便说一句(对于所有列):