我需要一个主表用于我的表,它有一个UNIQUE(复合4列),其中一个可以是NULL?

时间:2012-05-09 12:59:12

标签: postgresql database-design null indexing primary-key

我有下表(PostgreSQL 8.3),它存储了一些产品的价格。价格与另一个数据库同步,基本上下面的大多数字段(除了一个)都没有被我们的客户更新 - 而是每隔一段时间就丢弃并刷新以与另一个库存数据库同步:

CREATE TABLE product_pricebands (
    template_sku varchar(20) NOT NULL,
    colourid integer REFERENCES colour (colourid) ON DELETE CASCADE,        
    currencyid integer NOT NULL REFERENCES currency (currencyid) ON DELETE CASCADE,
    siteid integer NOT NULL REFERENCES site (siteid) ON DELETE CASCADE,

    master_price numeric(10,2),

    my_custom_field boolean, 

    UNIQUE (template_sku, siteid, currencyid, colourid)
);

在同步上,我基本上删除了上面的大部分数据,除了数据WHERE my_custom_field为TRUE(如果它为TRUE,则表示客户端通过CMS更新了该字段,因此不应删除此记录)。然后我将100s到100s的行插入到表中,并且INSERT失败的UPDATE(即(template_sku,siteid,currencyid,colourid)的组合已经存在)。

我的问题是 - 在这里应该采用什么最佳做法来创建主键?是否需要主键?我想使主键=(template_sku,siteid,currencyid,colourid) - 但是colourid字段可以为NULL,并且无法在复合主键中使用它。

从我在其他论坛帖子上看到的内容,我认为我已经正确完成了上述内容,只需澄清一下:

1)我是否应该使用“串行”主键以防我需要?目前我没有,也不认为我会,因为表中的重要数据是价格和我的自定义字段,只能通过(template_sku,siteid,currencyid,colourid)组合来识别。

2)由于(template_sku,siteid,currencyid,colourid)是我将用于查询产品价格的组合,我是否应该在我的列中添加任何进一步的索引,例如“template_sku”是一个varchar?或者UNIQUE约束是否已成为我的SELECT的良好索引?

1 个答案:

答案 0 :(得分:11)

  

我是否应该使用“串行”主键以防我需要?

如果您需要,可以稍后轻松添加序列列:

ALTER TABLE product_pricebands ADD COLUMN id serial;

该列将自动填充唯一值。您甚至可以将它作为同一语句中的主键(如果尚未定义主键):

ALTER TABLE product_pricebands ADD COLUMN id serial PRIMARY KEY;

如果您从其他表中引用该表,我建议使用这样的代理主键,因为链接四列相当笨重。在使用JOIN的SELECT中,它也会变慢。

无论哪种方式,都应该定义主键。包含可空列的UNIQUE索引不是完全替换。它允许复制包括NULL值的组合,因为两个NULL值永远不会被认为是相同的。这可能会导致麻烦。


作为

  

colourid字段可以为NULL

您可能想要创建两个唯一索引。组合(template_sku, siteid, currencyid, colourid)不能是PRIMARY KEY,因为可以为colourid,但您可以创建一个UNIQUE约束,就像您已经拥有的那样(自动实现索引):

ALTER TABLE product_pricebands ADD CONSTRAINT product_pricebands_uni_idx
UNIQUE (template_sku, siteid, currencyid, colourid)

此索引完美地涵盖了您在2)中提到的查询 如果您想避免使用(colourid IS NULL)

“重复”,请另外创建一个部分唯一索引
CREATE UNIQUE INDEX product_pricebands_uni_null_idx
ON product_pricebands (template_sku, siteid, currencyid)
WHERE colourid IS NULL;

涵盖所有基础。我在related answer on dba.SE中写了更多关于这种技术的文章。


上述的简单替代方法是使colourid NOT NULL并创建主键而不是上面的product_pricebands_uni_idx


另外,正如你

  

基本上删除了大部分数据

对于您的重新填充操作,在重新填充操作期间删除不需要的索引会更快,并在之后重新创建。从头开始构建索引比以递增方式添加所有行更快一个数量级。

你怎么知道,使用(需要)哪些索引?

  • 使用EXPLAIN ANALYZE
  • 测试您的查询
  • 或使用built-in statisticspgAdmin在所选对象的单独标签中显示统计信息。

my_custom_field = TRUE的几行选入临时表TRUNCATE基表并重新插入幸存者也可能更快。取决于您是否定义了外键。看起来像这样:

CREATE TEMP TABLE pr_tmp AS
SELECT * FROM product_pricebands WHERE my_custom_field;

TRUNCATE product_pricebands;
INSERT INTO product_pricebands SELECT * FROM pr_tmp;

这可以避免大量的吸尘。