将二进制内容附加到PostgreSQL数据库中的bytea列

时间:2012-04-17 15:24:03

标签: postgresql aggregate-functions plpgsql

我的情况:

我有一些二进制数据,已经分解成很多部分。特殊表FileParts包含fileIdpartNodata

我需要将所有部分放在另一个表MyFilesStorage中。

现在我在两种认识中做出选择:

DO $CODE$
declare
  r record;
begin
  UPDATE public.MyFilesStorage SET mainFileData = E''::bytea WHERE id = 'e14a26c0-db4b-47e1-8b66-e091fb3ba199'::uuid;

  for r in ( select data 
             from public.FileParts 
             where fileId = '89cb8598-436b-49b3-bb1c-34534c6d068e'::uuid
             order by partNo ) loop
    UPDATE public.MyFilesStorage SET mainFileData = mainFileData || r.data WHERE id = 'e14a26c0-db4b-47e1-8b66-e091fb3ba199'::uuid;
  end loop;

end;
$CODE$

我将数据设置为空,然后逐个读取部分并将每个部分附加到主表中。

另一种变体:

DO $CODE$
declare
  r record;
  p_result bytea;
begin
  p_result = E''::bytea;

  for r in ( select data 
             from public.FileParts 
             where fileId = '89cb8598-436b-49b3-bb1c-34534c6d068e'::uuid
             order by partNo ) loop
    p_result = p_result || r.data;
  end loop;

  UPDATE public.MyFilesStorage SET mainFileData = p_result WHERE id = 'e14a26c0-db4b-47e1-8b66-e091fb3ba199'::uuid;
end;
$CODE$

这里我使用临时变量。第二个更快,但我不知道会有更多的记忆?首先我需要内存将所有文件加载到RAM,那么首先呢? postgre会在这里加载所有内容:mainFileData = mainFileData || r.data

也许还有另一种方法可以做到这一点,因为两种变体都是 veeeeery slow ?在oracle中,我使用DBMS_LOB.APPEND进行此操作。

2 个答案:

答案 0 :(得分:3)

您的方法看起来正确,请查看PostgreSQL manual here

您也可以define your own aggregate为您完成这项工作:

CREATE AGGREGATE bytea_add(bytea)
(
    sfunc = byteacat,
    stype = bytea,
    initcond = E''
);

并使用常见的SQL,如下所示:

UPDATE public.MyFIlesStorage SET mainFileData = (
    SELECT bytea_add(data) FROM public.FileParts
     WHERE fileId = '89cb8598-436b-49b3-bb1c-34534c6d068e'::uuid
     -- ORDER BY partNo -- ORDER BY partNo will not work
 )
 WHERE id = 'e14a26c0-db4b-47e1-8b66-e091fb3ba199'::uuid;

修改

UPDATE public.MyFilesStorage mfs SET mainFileData = fp.all_data
  FROM (
    SELECT bytea_add(data) OVER (ORDER BY partNo) AS all_data,
           rank() OVER (ORDER BY partNo DeSC) AS pos
      FROM public.FileParts
     WHERE fileId = '89cb8598-436b-49b3-bb1c-34534c6d068e'::uuid
 ) AS fp
 WHERE fp.pos = 1
   AND mfs.id = 'e14a26c0-db4b-47e1-8b66-e091fb3ba199'::uuid;

您可以单独检查内部SQL的输出。

答案 1 :(得分:3)

第一个版本较慢,因为PostgreSQL不在存储级别进行就地更新,它会为每个UPDATE创建一个新版本的行。 因此,对于从0Mb到100MB,增量为10Mb的行,实际写入磁盘的行不是10x10Mb,而是:10Mb + 20Mb + 30Mb + ... + 90Mb + 100Mb = 550Mb 。 另一方面,内存消耗将保持较低,一次在内存中分配不超过10Mb。

第二个版本更快,只能写入100Mb,但需要在内存中分配100Mb。

具有有序块的FileParts表的结构通常更容易管理大内容,为什么还要将其转换为单片其他结构呢?