如何在postgres上的create index语句中使用嵌套查询的输出

时间:2014-09-16 14:44:56

标签: sql postgresql grails postgresql-9.2

我有一种情况,我想在表中添加部分唯一索引。此索引必须仅应用于将在以后添加的行和行的子集。有多个数据库,我必须把这个索引。问题是我必须使用每个数据库表中的数据来确定'where'子句。

以下查询给出错误:

create unique index user_car_unique 
ON car_usage(user_id,car_id) 
where  date_created > 
(select Max(date_created) from car_usage where id > 10)

我得到的错误是:

ERROR: cannot use subquery in index predicate
SQL state: 0A000
Character: 98

但是以下查询有效:

create unique index user_car_unique 
ON car_usage(user_id,car_id) 
where  date_created > '2014-08-12'

有没有办法解决“无法在索引谓词中使用子查询”的错误?

我的项目使用Grails。我将编写一个数据库迁移(DBM)脚本来添加将在启动时执行的索引。

我需要这个的原因:

我有一个包含重复条目的表。由于业务需求,这些条目在那里。为了实现它,我不得不在表中插入重复项。现在该要求已经改变。 “今后”表中应该没有重复项,旧的重复项应该保持不变。为了在DB层强制执行,我想放置唯一约束。我不能把应用约束,因为有旧的重复。因此我选择使用部分键。还有其他方法可以达到这个目的吗?

1 个答案:

答案 0 :(得分:1)

由于您已声明这是由于业务要求的更改以及导致重复项不再发生的问题,我建议您将重复的条目移动到存档表中inherits来自主要表。

然后,对主表的查询也将下拉到继承的表中,除非您使用select ... from only,并且一旦将重复的条目移动到归档表中,您就可以在主表上有一个常规的唯一索引

所以,如果你有类似的东西:

create table foo
(
  table_id serial,
  id int not null,
  val text not null,
  created timestamp without time zone default now() not null
);


insert into foo (id, val, created)
  values (1, 'one', '2014-09-14 09:00:00'),
         (2, 'two', '2014-09-14 11:00:00'),
         (2, 'two', '2014-09-14 12:00:00'),
         (2, 'two', '2014-09-14 13:00:00'),
         (3, 'three', now());


create table foo_archive
(
) inherits (foo);

然后您可以执行类似这样的操作来删除主表中的重复项(假设您使用的某种唯一标识符超出了您尝试添加的唯一索引;如果没有,您可以选择如何决定要保留哪一个 - 最早创建的时间,或者那种性质的东西;最适合您的用例最适合您拥有的数据):

create temp table min_table_ids as
select id, val, min(table_id) as min_table_id
from foo
group by id, val
order by id, val;

insert into foo_archive
select *
from foo
where table_id NOT IN (select min(table_id)
                       from foo
                       group by id, val
                       order by id, val
                      );

delete from only foo
where table_id NOT IN (select min_table_id
                       from min_table_ids
                      );

select *
from only foo
order by id, val;

select *
from foo_archive
order by id, val;

以下是相关的sqlfiddle

我过去曾使用这种通用技术来分隔不同类别的其他类似数据,并发现它的效果非常好。

如果您希望通过select ... from only或通过常规select保留较旧的数据,还可以使主表更小并且更容易将旧数据从查询中拼接出来

然后,一旦删除了重复项,就可以在主表上启用常规unique constraint(而不是unique index),即

alter table foo
  add constraint foo_uniq_key unique (id, val);