服务器 频道

PostgreSQL 操作常见问题

  【IT168 服务器学院】操作问题
  1) 系统看起来被逗号,小数点和日期格式弄糊涂了。
  2) 二进制游标和普通游标之间准确的区别是什么?
  3) 我如何只 select (选取)一个查询的头几行?
  4) 我如何获取一个表的列表,或者是其他我能在 psql 里看到的东西?
  5) 你怎样从一个表里面删除一个列?
  6) 一行,一个表,一个库的最大尺寸是多少?
  7) 存储一个典型的平面文件里的数据需要多少磁盘空间?
  8) 我如何查看一个数据库里面定义了那些索引或者操作?
  9) 我的查询很慢或者没有利用索引。为什么?
  10) 我如何才能看到查询优化器是怎样计算我的查询的?
  11) R-tree 索引是什么?
  12) 什么是基因查询优化(Genetic Query Optimization)?
  13) 我怎样做规则表达式搜索和大小写无关搜索?
  14) 在一个查询里,我怎样检测一个字段是否为 NULL?
  15) 各种字符类型之间有什么不同?
  16.1) 我怎样创建一个序列号/自动递增的字段?
  16.2) 我如何获得一个插入的序列号的值?
  16.3) 使用 currval() 和 nextval() 会导致一个与其他并行后端进程之间的一个竞争条件吗?
  17) 什么是 oid?什么是 tid?
  18) 里 PostgreSQL 使用的一些术语的含义是什么?
  19) 为什么我收到错误 "FATAL: palloc failure: memory exhausted?"
  20) 我如何才能知道我运行的 PostgreSQL 的版本?
  21) 为什么我的大对象操作收到invalid large obj descriptor(非法大对象描述符)。?
  22) 我如何创建一个缺省值是当前时间的字段?
  23) 为什么我的使用 IN 的子查询这么慢?
  
  1) 系统看起来被逗号,小数点和日期格式弄糊涂了。
  检查你的本地化(locale)配置。PostgreSQL 使用用户的本地化配置运行 postmaster 进程。可以用 postgres 和 psql SET 命令控制日期格式。根据你的操作环境设置那些值。
  
  2) 二进制游标和普通游标之间准确的区别是什么?
  参阅 DECLARE 手册页获取信息。
  
  3)我如何只 SELECT (选取)一个查询的头几行?
  参阅 FETCH 手册页,或者使用 SELECT ... LIMIT....
  
  即使你只需要开头的几行,也会涉及到整个查询。试着使用带有 ORDER BY 的查询。如果有一个索引与 ORDER BY 匹配,PostgreSQL 可能就只计算要求的头几条记录,否则将对整个查询进行计算直到生成需要的行。
  
  4) 我如何获取一个表的列表,或者是其他我能在 psql 里看到的东西?
  你可以阅读 psql 的源代码,文件 pgsql/src/bin/psql/psql.c。它包括为生成 psql 的反斜杠命令的输出的 SQL 命令。你还可以带着 -E 选项启动 psql,这样它将打印出执行你给出的命令所用的查询。
  
  5) 你怎样从一个表里面删除一个列?
  我们不支持 ALTER TABLE DROP COLUMN,但可以这样做:
  
  SELECT ...  -- select all columns but the one you want to remove
  INTO TABLE new_table
  FROM old_table;
  DROP TABLE old_table;
  ALTER TABLE new_table RENAME TO old_table;
  
  6) 一行,一个表,一个库的最大尺寸是多少?
  行被限制在 8K 字节以内,但是可以通过编辑 include/config.h 和修改 BLCKSZ 而改变。要使用大于 8K 的字段,你还可以使用大对象接口。
  
  行不会折叠 8k 的边界,所以 5k 的行将需要 8k 存储空间。
  
  表和数据库尺寸没有限制。有许多数据库有几十G字节大,可能还有几百G字节的数据库。
  
  7)存储一个典型的平面文件里的数据需要多少磁盘空间?
  一个 Postgres 数据库可能需要大约相当于在一个平面文件里存储相同数据的6.5倍的磁盘空间。
  
  假设一个文件有 300,000 行,每行有两个整数。平面文件是 2.4MB。而包含这些数据的 PostgreSQL 数据库文件的大小预计可达 14MB: 
  
      36 bytes: each row header (approximate)(每行的头,估计值)
     + 8 bytes: two int fields @ 4 bytes each(两个整数字段,每个4字节)
     + 4 bytes: pointer on page to tuple(页面里指向记录的指针)
     ----------------------------------------
      48 bytes per row(每行 48 字节)
  
     The data page size in PostgreSQL is 8192 bytes (8 KB), so:(PostgreSQL 里的数据页面的尺寸是 8K,因此:)
  
     8192 bytes per page
     -------------------   =  171 rows per database page (rounded up)(圆整后 117行/数据库页)
       48 bytes per row
  
     300000 data rows
     --------------------  =  1755 database pages(1755数据库页面)
        171 rows per page
  
  1755 database pages * 8192 bytes per page  =  14,376,960 bytes (14MB)
  
  索引没有这么多额外的东西,但是还是包含被索引的数据,所以他们可能也很大。
  
  8) 我如何查看一个数据库里面定义了那些索引或者操作?
  psql 有许多反斜杠命令用于显示这些信息。用 \? 看看都有那些。
  
  同样可以试试文件 pgsql/src/tutorial/syscat.source。它演示了许多从数据库系统表里获取信息需要的 SELECT。
  
  9) 我的查询很慢或者没有利用索引。为什么?
  PostgreSQL 并不自动维护统计数据。我们必须进行一次显式的 VACUUM 调用来更新统计数据。在统计数据更新之后,优化器就知道表里面有多少数据行,因而就可以更好的判断是否应该使用索引。要注意当表很小的时候优化器并不使用索引,因为这时候一次顺序扫描会更快。
  
  对于字段相关的优化统计,使用 VACUUM ANALYZE。VACUUM ANALYZE 对于复杂的多联合查询是非常重要的,因为这样优化器可以计算从每个表里面返回的行的数目,然后选择合适的联合顺序。后端本身并不跟踪字段统计,因而必须周期的运行 VACUUM ANALYZE 以便收集这些信息。
  
  索引通常不用于 ORDER BY 操作:对一个大表的一次顺序扫描然后跟着一个显式的排序比对所有记录的索引扫描要快,因为前者的磁盘访问更少。
  
  当使用模糊操作符,比如 LIKE 或 ~,只有在搜索的开始是挂在字串的开头部分时才用得到索引。因而要使用索引,LIKE 搜索不应该以 % 开头,而~(规则表达式搜索)应该以^ 开头。 
  
  10) 我如何才能看到查询优化器是怎样计算我的查询的?
  参考 EXPLAIN 手册页。
  
  11) R-tree 索引是什么?
  r-tree 索引用于索引空间数据。一个哈希索引无法处理范围搜索。而 B-tree 索引只能处理一维的范围搜索。R-tree 索引可以处理多维数据。例如,如果可以在一个类型为 point 的字段上建立一个 R-tree 索引,那么系统在回答类似 select all points within a bounding rectangle (选择在一个长方形范围内的所有点)这样的查询时有更高的效率。
  
  描述最初的 R-Tree 的设计的规范里面写到:
  
  Guttman, A. "R-Trees: A Dynamic Index Structure for Spatial Searching." Proc of the 1984 ACM SIGMOD Int''l Conf on Mgmt of Data, 45-57.
  
  你还可以在 Stonebraker 的 "Readings in Database Systems" 找到这篇文章。
  
  建立 R-Trees 可以处理多边形和方形。理论上说,R-trees 可以扩展为处理更多维数。不过在实践上,扩展 R-trees 需要一定的工作量,而我们目前没有如何做的文档。
  
  12) 什么是基因查询优化(Genetic Query Optimization)?
  PostgreSQL 里面的 GEQO 模块试图使用一种叫基因算法( Genetic Algorithm (GA))解决联合许多表的查询优化问题。它允许通过非穷尽搜索处理大的联合查询。
  
  更多信息请参考文档。
  
  13) 我怎样做规则表达式搜索和大小写无关搜索?
  操作符 ~ 处理规则表达式匹配,而 ~* 处理大小写无关的规则表达式匹配。LIKE 操作符里面没有大小写无关的用法,不过你可以通过下面的用法获得大小写无关的 LIKE:
  
  WHERE lower(textfield) LIKE lower(pattern)
  
  14) 在一个查询里,我怎样检测一个字段是否为 NULL?
  你用 IS NULL 和 IS NOT NULL 测试这个字段。
  
  15) 各种字符类型之间有什么不同?
  Type            Internal Name   Notes
  --------------------------------------------------
  "char"          char            1 character
  CHAR(#)         bpchar          blank padded to the specified fixed length
  VARCHAR(#)      varchar         size specifies maximum length, no padding
  TEXT            text            length limited only by maximum row length
  BYTEA           bytea           variable-length array of bytes
  
  在查看系统表和在一些错误信息里你将看到内部名称。
  
  上面最后四种类型是"varlena"(变长)类型(也就是说,开头的四个字节是长度,后面跟着数据)。char(#) 分配最多个数字节,不管在数据域里面有多少数据。 text,varchar(#),和 bytea 都在磁盘上有变长的长度,因此,使用它们有一点点的性能损失。准确地说,性能损失发生在第一个这种类型的字段后对所有其他字段的访问的时候。
  
  16.1) 我怎样创建一个序列号/自动递增的字段?
  PostgreSQL 支持 SERIAL 数据类型。它在字段上自动创建一个序列和索引。例如,这样... 
  
  CREATE TABLE person ( 
  id   SERIAL, 
  name TEXT 
  );
  
  ...会自动转换为这样... 
  
  CREATE SEQUENCE person_id_seq;
  CREATE TABLE person ( 
  id   INT4 NOT NULL DEFAULT nextval(''person_id_seq''),
  name TEXT 
  );
  CREATE UNIQUE INDEX person_id_key ON person ( id );
  
  参考 create_sequence 手册页获取关于序列的更多信息。你还可以用每行的 oid 字段作为一个唯一值。不过,如果你需要倾倒和重载数据库,你需要使用 pg_dump 的 -o 选项或者 COPY WITH OIDS 选项以保留 oid。
  
  更多信息,参阅 Bruce Momjian 的 行计数 章节。
  
  16.2) 我如何获得一个插入后生成的序列号( SERIAL )的值?
  可能实现这个要求的最简单的方法是:在插入之前先用函数 nextval() 从序列对象里检索出下一个 SERIAL 值,然后再显式插入。利用 16.1 里的例子表,这样做看起来象下面这样:
  
  $newSerialID = nextval(''person_id_seq'');
  INSERT INTO person (id, name) VALUES ($newSerialID, ''Blaise Pascal'');
  
  你还能获得存储在 $newSerialID 里面的新值,可以用于其他查询(例如,作为 person 表的外键)。要注意自动创建的 SEQUENCE 对象的名称将会是命名为  __seq,这里 table 和 serialcolumn 分别是你的表的名称和你的 SERIAL 字段的名称。
  
  类似的,在 SERIAL 对象缺省插入后你可以用函数 currval() 检索刚赋值的 SERIAL 值,例如,
  
  INSERT INTO person (name) VALUES (''Blaise Pascal'');
  $newID = currval(''person_id_seq'');
  
  最后,你可以使用从 INSERT 语句返回的 oid 查找缺省值,尽管这可能是最缺乏移植性的方法。在 perl 里,使用带有 Edmund Mergl 的 DBD::Pg 模块的 DBI,oid 值可以通过 $sth->execute() 后的 $sth->{pg_oid_status} 获得。
  
  16.3) 使用 currval() 和 nextval() 会导致一个与其他并行后端进程之间的一个竞争条件吗?
  不会。这个问题由后端处理。
  
  17) 什么是 oid?什么是 tid?
  Oid 是 PostgreSQL 的唯一行标识。PostgreSQL 里创建的每一行都获得一个唯一的 oid。所有在 initdb 过程中创建的 oid 都小于 16384 (来自 backend/access/transam.h)。所有用户创建的 oid 都大于或等于这个值。缺省时,所有这些 oid 不仅在一个表,一个数据库里面唯一,而且在整个 PostgreSQL 安装里也是唯一的。
  
  PostgreSQL 在它的内部系统表里使用 oid 在表之间联接行。这些 oid 可以用于标识特定的用户行以及用在联合里。我们建议你使用字段类型 oid 存储 oid 值。参阅 sql(l) 手册页查找其他内部字段。你可以在 oid 字段上创建一个索引以获取快速访问。
  
  Oid 从被所有数据库使用的某个区域里赋值给所有新行。如果你想把 oid 该成别的值,或者你想做一份表的带着原始 oid 的拷贝,你可以做到:
  
          CREATE TABLE new_table(old_oid oid, mycol int);
          SELECT INTO new SELECT old_oid, mycol FROM old;
          COPY new TO ''/tmp/pgtable'';
          DELETE FROM new;
          COPY new WITH OIDS FROM ''/tmp/pgtable'';
  
  
  Tid 用于标识带着数据块和偏移量值的特定的物理行。Tid 在每行的更改或者重载后被改变。它们被索引记录用于指引物理行。
  
  18) 里 PostgreSQL 使用的一些术语的含义是什么?
  一些源代码和一些旧一点的文档使用一些有更常用用法的术语。下面是其中一部分:
  
  table, relation, class 
  row, record, tuple 
  column, field, attribute 
  retrieve, select 
  replace, update 
  append, insert 
  oid, serial value 
  portal, cursor 
  range variable, table name, table alias 
  19) 为什么我收到错误 "FATAL: palloc failure: memory exhausted?"
  这很可能是你系统的虚拟内存用光了,或者你的内核对这样的资源有较底的限制值。在启动 postmaster 之前试试下面的命令:
  
  ulimit -d 65536
  limit datasize 64m
  
  取决于你用的 shell,上面命令只有一条能成功,但是它将把你的进程数据段设置得比较高,因而也许能让查询能够运行完成。这条命令应用于当前进程,以及所有在这条命令运行后创建的子进程。如果你碰到了因为后端运行返回了太多数据的客户端 SQL 的问题,试着先运行这些命令再运行客户端。
  
  20) 我如何才能知道我运行的 PostgreSQL 的版本?
  从 psql 里,键入 select version();
  
  21) 为什么我的大对象操作收到invalid large obj descriptor(非法大对象描述符)。?
  你需要在任何操作大对象的周围放上 BEGIN WORK 和 COMMIT,也就是说,包围 lo_open ... lo_close。
  
  目前 PostgreSQL 强制这样的规则:在事务提交时关闭大对象句柄,这样,如果你没有处于一个事务里面,就会是紧跟在一个 lo_open 命令完成的后面。所以对这个句柄做任何事情的企图都会导致一个 invalid large obj descriptor。所以,如果你没有使用一个事务,以前能工作的代码(至少是大多数时间)将会生成这么一个错误信息。
  
  如果你使用客户端接口,如 ODBC,你可能需要 set auto-commit off。
  
  22) 我如何创建一个缺省值是当前时间的字段?
  
  下面的方法总可以工作:
  
  
  CREATE TABLE test (x int, modtime timestamp default now() );
  
  在版本 7.0 或更新,你可以使用:
  
  
  create table test (x int, modtime timestamp default ''now'');
  
  23) 为什么我的使用 IN 的子查询这么慢?
  目前,我们通过为外层查询的每一行顺序扫描子查询的结果来联合子查询和外层查询。可以用 EXISTS 替换 IN 来绕开这个限制。例如,把:
  
  
  SELECT *
  FROM tab
  WHERE col1 IN (SELECT col2 FROM TAB2)
  
  改为: 
  
  
  SELECT *
  FROM tab
  WHERE EXISTS (SELECT col2 FROM TAB2 WHERE col1 = col2)
  
  我们希望在未来的版本里修补这个限制。
  
0
相关文章