0

我有一个包含几百万行的 Postgres 9.1.4 表。一个非常小的版本可能是这样的:

掌握

index  location
----------------
1      A
2      C
3      B
4      C
5      C
6      A

我需要对位置字段中的每个不同值进行昂贵的计算。我不想使用表中的这个字段,因为我会重复处理同一个位置。我想要一个具有不同location值的表,计算结果将存储在计算字段中:

清楚的

index  location  calculation
------------------------------
1      A'        X
2      C'        Y
3      B'        Z

填充distinct后, masterdistinct之间的关系不容易确定。我必须做一些数据操作才能使位置在计算中起作用。我真的需要第三张表,大致在我填充distinct的同时创建,以帮助我将 distinct 中的每个条目与master的父项相关联。

第三个表可能如下所示:

相关性

master_index  distinct_index
------------------------------
1             1
2             3
3             2
4             3
5             3
6             1

问题是我看不到如何使用任何简单的 SQL 来完成。我可以使用这样的东西作为查询的开头来填充distinct

SELECT location, array_agg(index)
FROM master
GROUP BY location;

问题是我需要另一个不同的数组列来保存这些值,然后我需要使用其他一些程序来解析数组并构造相关表。

我错过了一种更简单的方法吗?

4

5 回答 5

1

您可以按如下方式创建“不同”表(尽管我提醒您避免使用 SQL 关键字作为表名作为列):

create table TDistinct as
    select m.location, min(index) as TDindex, <whatever> as calculation
    from master m
    group by m.location

创建关联表为:

create table correlation as
    select m.index as MasterIndex, td.TDIndex
    from master m join
         TDistinct td
         on m.location = td.location

这些工作,但你可能想要更有效的东西。创建表后,您可以添加索引以提高效率。您还可以执行其他技巧,例如使用自动递增的主键预先创建不同的表,并将其用作不同的索引。然后,您将使用 insert 将数据加载到查询中。

于 2012-07-16T13:53:47.763 回答
1

您所描述的不是 n:m 而是1:n 关系。因此,您根本不需要correlation表来实现关系。

在具有“几百万行”的表中冗余拼写位置是次优设计。你应该规范它。大桌子会缩小很多(让一切都更快)。将该列替换为master.location引用专用表的新主键的外键列location。这就是它应该的方式。

master

master_id  loc_id  ...
-----------------
1          1       ...
2          2
3          3
4          3
5          3
6          1

location

loc_id  loc  loc_derived calc
-----------------------------
1       A    A'          X
2       C    C'          Y
3       B    B'          Z

昂贵的计算似乎需要在calc. loc_derived但是您的问题中没有关于列的计算。如果它不是很昂贵(并且定期使用),则不应将其包含在location表中,而应为其创建视图或函数(生成的列)

通常,您会在master和之间添加外键约束location以保证关系完整性。master将来,您将在添加引用它的行之前添加一个位置。

如何到那?

  1. location使用代理整数主键 ( )创建表loc_id

    CREATE TABLE location (
     loc_id serial
    ,loc text
    ,loc_derived  text  -- you really need redundant storage here?
    ,calc text          -- seems you need redundant storage here.
    );
    

    有些人会建议将loc自己用作自然主键。我不在其中,我认为这是一个坏主意。

    • 文本列上的字符串操作和索引比处理整数要慢得多。
    • 如果您遇到(中间)重复的位置名称,则很难更改位置名称。
    • 对位置名称的每次更改都会触发依赖表中的其他更新——代理键不会发生这种情况。
    • master表将小得多,只需一个整数而不是整个字符串来引用该location表。
  2. 填写新location表(我会使用 CTE 进行计算)。

    WITH x AS (
        SELECT DISTINCT loc
        FROM master
        )
    INSERT INTO location (loc, loc_derived, calc)
    SELECT loc, some_calc(loc), some_expensive_calc(loc)
    FROM   x;
    
  3. 将 loc_id 添加到主表。

    ALTER TABLE master ADD COLUMN loc_id integer;
    
  4. 填写列loc_id

    UPDATE master m
    SET    loc_id = l.loc_id
    FROM   location l
    WHERE  l.loc = m.loc;
    
  5. 删除列master.loc

    ALTER TABLE master DROP COLUMN loc;
    
  6. 真空/分析。

    VACUUM ANALYZE; 
    -- VACUUM FULL ANALYZE
    -- much slower, only to shrink the table and return disk space.
    
  7. 添加 fk 约束。

    ALTER TABLE master
    ADD CONSTRAINT master_loc_id_fkey FOREIGN KEY (loc_id)
    REFERENCES location(loc_id);
    
于 2012-07-16T18:21:20.000 回答
0

您可以仅使用主表和不同表来执行此操作。您的不同列表(即使来自多个字段)实际上是您的外键的基础。不同表的唯一值将与主表具有一对多的关系。如果将主表位置数据上的索引和不同表位置数据上的唯一索引重新组合在一起,则将有助于提高性能。

于 2012-07-16T13:30:27.967 回答
0

我已经完成了这个 SQL 服务器。我想这就是你要找的

您可以使用查询创建不同的表

select MIN("index") "index",location from tmaster
group by location 

以及下面查询的相关表

select M."index" master_index,A."index" distinct_index
 from tmaster M left outer join 
(select MIN("index") "index",location from tmaster
group by location)A
on A.location=M.location
于 2012-07-16T13:54:13.267 回答
0

您不需要将相关性放入表中,视图或 CTE 就足够了:(顺便说一句:索引是保留字;我用 zindex 替换了它(没有以 Z 开头的 SQL 关键字,对于 zDISTINCT 也是如此)

DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp;
SET search_path=tmp;

-- Make some data ...
CREATE TABLE master
        ( zindex INTEGER NOT NULL PRIMARY KEY
        , location CHAR(1) NOT NULL
        );
INSERT INTO master(zindex,location) VALUES
 (1, 'A')
,(2, 'C')
,(3, 'B')
,(4, 'C')
,(5, 'C')
,(6, 'A')
        ;

    -- a view with a CTE inside
CREATE VIEW correlation AS (
        WITH zdistinct AS (
                SELECT MIN(m.zindex) AS zindex
                , m.location AS location
                FROM master m
                GROUP BY m.location
                )
        SELECT m.zindex AS master_index
                , d.zindex AS distinct_index
        FROM master m
        JOIN zdistinct d ON m.location = d.location
        );

SELECT * FROM correlation;

而且,顺便说一句:zdistinct CTE 与原始问题中的“distinct”表大致相同。只需添加“计算”字段,您就到家了。(你可以先把它放到一个单独的视图中)

于 2012-07-16T14:40:13.283 回答