【问题标题】:Postgresql, select a "fake" rowPostgresql,选择一个“假”行
【发布时间】:2013-08-01 04:12:04
【问题描述】:

在 Postgres 8.4 或更高版本中,获取默认填充的一行数据的最有效方法是而不实际创建行。例如,作为交易(伪代码):

create table "mytable"
(
  id serial PRIMARY KEY NOT NULL,
  parent_id integer NOT NULL DEFAULT 1,
  random_id integer NOT NULL DEFAULT random(),
)

begin transaction
  fake_row = insert into mytable (id) values (0) returning *;
  delete from mytable where id=0;
  return fake_row;
end transaction

基本上,我希望查询具有单行,其中 parent_id 为 1,random_id 是随机数(或其他函数返回值),但我不希望此记录保留在表中或影响主键序列serial_id_seq。

我的选择似乎是使用上面的事务或创建视图,这些视图是添加了假行的表的副本,但我不知道每种方法的所有优缺点或是否存在更好的方法。

我正在寻找一个假设不知道任何列的数据类型或默认值的答案,除了id 或列的数量或顺序。只有表名是已知的,并且表中不应存在 id 为 0 的记录。

在过去,我创建了虚假记录 0 作为永久记录,但我开始将此记录视为一种污染(因为我通常必须将其从未来的查询中过滤掉)。

【问题讨论】:

  • 我想要一行,如果我插入它(让默认值填充到数据库中)并立即选择它,但没有使用的开销/复杂性和/或副作用一笔交易(除非交易真的是最好的方式)。
  • 对序列的查询是原子的,并且无论您是否回滚事务都会递增。这对您来说可能不是问题,但值得一提。
  • 是的,序列可能是个问题。我不希望在序列中产生很多空白。这并不重要,但它确实使调试更容易(了解可能已经存在和删除的记录与从未真正创建的记录之间的区别)。

标签: postgresql


【解决方案1】:

您可以使用以下命令复制表定义和默认值到临时表:

CREATE TEMP TABLE table_name_rt (LIKE table_name INCLUDING DEFAULTS);

并使用此临时表生成虚拟行。此类表将在会话(或事务)结束时被删除,并且仅对当前会话可见。

【讨论】:

  • 谢谢。您的代码在 LIKE 和 INCLUDING 子句周围缺少必需的括号,因此我更新了您的答案。
【解决方案2】:

您可以查询目录并构建动态查询

假设我们有这张桌子:

create table test10(
      id serial primary key,
      first_name varchar( 100 ),
      last_name  varchar( 100 ) default 'Tom',
      age int not null default 38,
      salary float  default 100.22
);

当您运行以下查询时:

SELECT string_agg( txt, ' ' order by id ) 
FROM (
select 1 id, 'SELECT ' txt
union all
select 2, -9999 || ' as id '
union all
select 3, ', '  
       || coalesce( column_default,  'null'||'::'||c.data_type ) 
       || ' as ' || c.column_name
from information_schema.columns c
where table_schema = 'public'
    and table_name = 'test10'
    and ordinal_position > 1
) xx
    ;

你会因此而受到刺痛:

"SELECT  -9999 as id  , null::character varying as first_name , 
'Tom'::character varying as last_name , 38 as age , 100.22 as salary"

然后执行此查询,您将获得“幻影行”。

我们可以构建一个函数来构建和执行查询并返回我们的行作为结果:

CREATE OR REPLACE FUNCTION get_phantom_rec (p_i test10.id%type ) 
returns test10 as $$
DECLARE 
    v_sql text;
    myrow test10%rowtype;
begin
   SELECT string_agg( txt, ' ' order by id ) 
   INTO v_sql
   FROM (
    select 1 id, 'SELECT ' txt
    union all
    select 2, p_i || ' as id '
    union all
    select 3, ', '  
           || coalesce( column_default,  'null'||'::'||c.data_type ) 
           || ' as ' || c.column_name
    from information_schema.columns c
    where table_schema = 'public'
        and table_name = 'test10'
        and ordinal_position > 1
    ) xx
    ;
    EXECUTE v_sql INTO myrow;
    RETURN  myrow;
END$$ LANGUAGE plpgsql ;

然后这个简单的查询会为您提供您想要的:

select * from get_phantom_rec ( -9999 );

  id   | first_name | last_name | age | salary
-------+------------+-----------+-----+--------
 -9999 |            | Tom       |  38 | 100.22

【讨论】:

  • 哇,你在这上面花了多长时间?仅凭努力,这个答案就值得赏金。这可能不是我采用的解决方案,因为 Igor 发布的临时表方法看起来要容易得多,但我怀疑在性能问题(在我的情况下不是)的情况下,您的答案可能会更快。无论哪种方式,您的回答都具有教育意义。
【解决方案3】:

我只会选择假值作为文字:

select 1 id, 1 parent_id, 1 user_id

返回的行将(实际上)与真实行无法区分。


从目录中获取值:

select
  0 as id, -- special case for serial type, just return 0
  (select column_default::int -- Cast to int, because we know the column is int
   from INFORMATION_SCHEMA.COLUMNS
   where table_name = 'mytable'
   and column_name = 'parent_id') as parent_id,
  (select column_default::int -- Cast to int, because we know the column is int
   from INFORMATION_SCHEMA.COLUMNS
   where table_name = 'mytable'
   and column_name = 'user_id') as user_id;

请注意,您必须知道列是什么以及它们的类型,但这是合理的。如果您更改表架构(默认值除外),则需要调整查询。

将上述内容视为SQLFiddle

【讨论】:

  • 没有。请参阅问题的最后一部分。我不想知道 somecol 的类型或默认值,这就是我想要检索的。编辑:我想在不知道表结构的情况下获取表的所有列。
  • 您想要在表中为返回的列定义的默认值吗?如果是这样,您可以查询目录表以构造“默认行”,包括在序列时返回零。我没有安装方便的 postgres 来正确查询,但我知道我可以做到。
  • 是的,我总是希望返回的默认值完全符合DEFAULT 子句的定义。
  • sqlfiddle.com/#!1/67ed4/3 已设置为您可以根据需要优化您的查询。
  • 模式目录的问题在于您获得了默认值的文字字符串定义(例如,random())而不是计算的默认值(例如,2348756 )(见更新的小提琴)。
猜你喜欢
  • 2017-11-28
  • 2017-02-23
  • 2019-01-01
  • 1970-01-01
  • 2015-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多