在单独的表中将行与DISTINCT版本相关联

时间:2012-07-16 13:25:12

标签: sql postgresql distinct

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

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

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

不同

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

填充不同后,很难确定不同之间的关系。我必须做一些小数据操作才能使位置在计算中起作用。我真的需要第三个表,大致在我填充 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;

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

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

5 个答案:

答案 0 :(得分:1)

您可以按如下方式创建“distinct”表(但是,我提醒您避免将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将数据加载到查询中。

答案 1 :(得分: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表中,而是为其创建视图或function (generated column)

通常,您会在masterlocation之间添加外键约束,以保证关系完整性。将来,您可以在引用它的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 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);
    

答案 2 :(得分:0)

您只需使用主表和不同表即可完成此操作。您的独特列表(即使来自多个字段)实际上是您的外键的基础。不同表的唯一值将具有返回主表的一对多关系。主表的位置数据上的索引和不同表的位置数据上的唯一索引将有助于提高性能,如果将它们重新连接在一起。

答案 3 :(得分: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

答案 4 :(得分:0)

你不需要将相关性放入表中,视图或CTE就足够了:(顺便说一句:索引是保留字;我用zindex替换它(没有以Z开头的SQL关键字,相同for 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”表大致相同。只需添加“计算”字段即可回家。 (你可以先把它放到一个单独的视图中)