我有一个包含几百万行的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。
我错过了一种更简单的方法吗?
答案 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)。
通常,您会在master
和location
之间添加外键约束,以保证关系完整性。将来,您可以在引用它的master
中添加行之前添加一个位置。
使用代理整数主键(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
表会小得多。填写新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;
将loc_id添加到主表。
ALTER TABLE master ADD COLUMN loc_id integer;
填写专栏loc_id
。
UPDATE master m
SET loc_id = l.loc_id
FROM location l
WHERE l.loc = m.loc;
删除列master.loc
。
ALTER TABLE master DROP COLUMN loc;
VACUUM / ANALYZE。
VACUUM ANALYZE;
-- VACUUM FULL ANALYZE
-- much slower, only to shrink the table and return disk space.
添加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”表大致相同。只需添加“计算”字段即可回家。 (你可以先把它放到一个单独的视图中)