关于postgresql:默认情况下在postgres psql select语句中截断显示

Truncating display by default in postgres psql select statements

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

1
SELECT * FROM resources;

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

1
SELECT id, LEFT(DATA, 50), file_format_version, ... FROM resources;

当我查询第一个select * from resources时,是否有一种方法可以让psql默认截断长列?


我不知道如何使用psql的内置选项。
您可以通过@Drazen建议的功能实现您的目标 - 更简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;

电话示例:

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

SQL小提琴。

笔记

  • 适用于任何具有任何数据类型列的表。

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

    1
    2
    SELECT"FoO_id", LEFT(c_text, 11), LEFT(c_vc, 11)::CHARACTER VARYING
    FROM  "FoO";
  • 它仅缩短所选数据类型的列,并使其他数据类型单独使用。我包括了基本的字符类型:
    bpcharcharacter和所有变体的内部名称。
    varcharcharacter varying和所有变体的内部名称。
    满足您的需求。

  • 该函数返回所有列的原始列名称和数据类型。我将短列转换为text,然后输入left(),返回text,因此text列不需要另一个转换。所有其他缩短类型都需要转换回原始类型。如果你截断,有些类型会破坏!所以这不适用于所有类型。

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

  • 性能并不比普通的SELECT * FROM tbl差很多 - 除了上述LIMIT情况或其他嵌套函数的情况。设置返回的PL / pgSQL函数通常最好不要嵌套:

    • PostgreSQL存储过程性能
  • 我建在一个默认的最大值。长度为25个字符,将自定义长度作为第二个参数传递,或根据需要调整函数头中的默认值。

  • 此功能可以安全地防止通过恶意制作的标识符进行可能的SQL注入攻击。

相关答案以及更多解释和链接:

  • 用空值替换空字符串
  • 重构PL / pgSQL函数以返回各种SELECT查询的输出
  • 表名作为PostgreSQL函数参数
  • Postgres数据类型转换
  • 查询PostgreSQL中表的架构细节?
  • 如何检查给定模式中是否存在表

pgAdmin的

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

enter image description here


不是真的psql,但代码可以实现一切:)

事实证明这一点非常棘手,我对最终的解决方案并不满意,因为它有点笨拙,但是它完成了工作,但它应该被视为概念证明。总是有改进和简化的空间:)

无论如何,因为没有任何内置,我不喜欢在postgres之外做这个的想法,即。 PAGER输出通过管道输送到awk而不是less,最后输给你:)我选择了功能。

Python是我大多数时候选择的武器,所以我创建了一个执行截断的plpython函数和一个plpgsql包装器,以便可以使用SQL提供的所有可爱的东西很好地调用它。

让我们从包装函数开始:

1
2
3
4
5
6
7
8
9
10
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;

正如您所看到的,它是通过多态输入声明的,因此它可以处理您提供给它的所有表,或者它可以返回您提供给它的相同类型的表,保留所有约束,索引等(这是完成的)通过plpython函数)...所以没有进一步的麻烦,让我们看看它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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;

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

1
SELECT * FROM wrapper(resources, NULL::resources, 50);

再次,笨拙表明它丑陋的面孔,所以你给表名,表列类型,所以它知道要返回什么和截断的字符限制,你应该能够使用WHEREGROUP BY和类似没有任何问题。

显而易见的问题是性能,因为您将基本上重新插入整个表,这可能会有问题,但至少很容易解决。

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

希望这至少可以让你获得成为你想去的地方的一部分:)