PostgreSQL为每个收到的查询设计一个查询规划。选择正确的匹配查询结构和数据属性的规划对执行效率是至关重要要的,所以系统包含一个复杂的规划器来试图选择好的规划。你可以使用EXPLAIN命令查看查询规划器创建的任何查询。阅读查询规划是一门艺术,需要掌握一定的经验,本节试图涵盖一些基础知识。

以下的例子来自PostgreSQL 9.5开发版。

EXPLAIN基础

查询规划是以规划为节点的树形结构。树的最底节点是扫描节点:他返回表中的原数据行。

不同的表有不同的扫描节点类型:顺序扫描,索引扫描和位图索引扫描。

也有非表列源,如VALUES子句并设置FROM返回,他们有自己的扫描类型。

如果查询需要关联,聚合,排序或其他操作,会在扫描节点之上增加节点执行这些操作。通常有不只一种可能的方式做这些操作,所以可能出现不同的节点类型。

EXPLAIN的输出是每个树节点显示一行,内容是基本节点类型和执行节点的消耗评估。可能会出现其他行,从汇总行节点缩进显示节点的其他属性。第一行(最上节点的汇总行)是评估执行计划的总消耗,这个值越小越好。

下面是一个简单的例子:

  1. EXPLAIN SELECT * FROM tenk1;
  2. QUERY PLAN
  3. -------------------------------------------------------------
  4. Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244)

因为这个查询没有WHERE子句,所以必须扫描表中的所有行,所以规划器选择使用简单的顺序扫描规划。括号中的数字从左到右依次是:

  • 评估开始消耗。这是可以开始输出前的时间,比如排序节点的排序的时间。
  • 评估总消耗。假设查询从执行到结束的时间。有时父节点可能停止这个过程,比如LIMIT子句。
  • 评估查询节点的输出行数,假设该节点执行结束。
  • 评估查询节点的输出行的平均字节数。

这个消耗的计算依赖于规划器的设置参数,这里的例子都是在默认参数下运行。

需要知道的是:上级节点的消耗包括其子节点的消耗。这个消耗值只反映规划器关心的内容,一般这个消耗不包括将数据传输到客户端的时间。

评估的行数不是执行和扫描查询节点的数量,而是节点返回的数量。它通常会少于扫描数量,因为有WHERE条件会过滤掉一些数据。理想情况顶级行数评估近似于实际返回的数量.

回到刚才的例子,表tenk1有10000条数据分布在358个磁盘页,评估时间是(磁盘页*seq_page_cost)+(扫描行*cpu_tuple_cost)。默认seq_page_cost是1.0,cpu_tuple_cost是0.01,所以评估值是(358 * 1.0) + (10000 * 0.01) = 458

现在我们将查询加上WHERE子句:

  1. EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 7000;
  2. QUERY PLAN
  3. ------------------------------------------------------------
  4. Seq Scan on tenk1 (cost=0.00..483.00 rows=7001 width=244)
  5. Filter: (unique1 < 7000)

查询节点增加了“filter”条件。这意味着查询节点为扫描的每一行数据增加条件检查,只输入符合条件数据。评估的输出记录数因为where子句变少了,但是扫描的数据还是10000条,所以消耗没有减少,反而增加了一点cup的计算时间。

这个查询实际输出的记录数是7000,但是评估是个近似值,多次运行可能略有差别,这中情况可以通过ANALYZE命令改善。

现在再修改一下条件:

  1. EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100;
  2. QUERY PLAN
  3. ------------------------------------------------------------------------------
  4. Bitmap Heap Scan on tenk1 (cost=5.07..229.20 rows=101 width=244)
  5. Recheck Cond: (unique1 < 100)
  6. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
  7. Index Cond: (unique1 < 100)

查询规划器决定使用两步规划:首先子查询节点查看索引找到符合条件的记录索引,然后外层查询节点将这些记录从表中提取出来。分别提取数据的成本要高于顺序读取,但因为不需要读取所有磁盘页,所以总消耗比较小。(其中Bitmap是系统排序的一种机制)

现在,增加另一个查询条件:

  1. EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND stringu1 = 'xxx';
  2. QUERY PLAN
  3. ------------------------------------------------------------------------------
  4. Bitmap Heap Scan on tenk1 (cost=5.04..229.43 rows=1 width=244)
  5. Recheck Cond: (unique1 < 100)
  6. Filter: (stringu1 = 'xxx'::name)
  7. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
  8. Index Cond: (unique1 < 100)

增加的条件stringu1='xxx'减少了输出记录数的评估,但没有减少时间消耗,因为系统还是要查询相同数量的记录。请注意stringu1不是索引条件。

如果在不同的字段上有独立的索引,规划器可能选择使用AND或者OR组合索引:

  1. EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000;
  2. QUERY PLAN
  3. -------------------------------------------------------------------------------------
  4. Bitmap Heap Scan on tenk1 (cost=25.08..60.21 rows=10 width=244)
  5. Recheck Cond: ((unique1 < 100) AND (unique2 > 9000))
  6. -> BitmapAnd (cost=25.08..25.08 rows=10 width=0)
  7. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
  8. Index Cond: (unique1 < 100)
  9. -> Bitmap Index Scan on tenk1_unique2 (cost=0.00..19.78 rows=999 width=0)
  10. Index Cond: (unique2 > 9000)

这个查询条件的两个字段都有索引,索引不需要filter。

下面我们来看看LIMIT的影响:

  1. EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;
  2. QUERY PLAN
  3. -------------------------------------------------------------------------------------
  4. Limit (cost=0.29..14.48 rows=2 width=244)
  5. -> Index Scan using tenk1_unique2 on tenk1 (cost=0.29..71.27 rows=10 width=244)
  6. Index Cond: (unique2 > 9000)
  7. Filter: (unique1 < 100)

这条查询的where条件和上面的一样,只是增加了LIMIT,所以不是所有数据都需要返回,规划器改变了规划。在索引扫描节点总消耗和返回记录数是运行玩查询之后的数值,但Limit节点预期时间消耗是15,所以总时间消耗是15.增加LIMIT会使启动时间小幅增加(0.25->0.29)。

来看一下通过索引字段的表连接:

  1. EXPLAIN SELECT *
  2. FROM tenk1 t1, tenk2 t2
  3. WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2;
  4. QUERY PLAN
  5. --------------------------------------------------------------------------------------
  6. Nested Loop (cost=4.65..118.62 rows=10 width=488)
  7. -> Bitmap Heap Scan on tenk1 t1 (cost=4.36..39.47 rows=10 width=244)
  8. Recheck Cond: (unique1 < 10)
  9. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.36 rows=10 width=0)
  10. Index Cond: (unique1 < 10)
  11. -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.91 rows=1 width=244)
  12. Index Cond: (unique2 = t1.unique2)

这个规划中有一个内连接的节点,它有两个子节点。节点摘要行的缩进反映了规划树的结构。最外层是一个连接节点,子节点是一个Bitmap扫描。外部节点位图扫描的消耗和记录数如同我们使用SELECT...WHERE unique1 < 10,因为这时t1.unique2 = t2.unique2还不相关。接下来为每一个从外部节点得到的记录运行内部查询节点。这里外部节点得到的数据的t1.unique2值是可用的,所以我们得到的计划和SELECT...WHEREt2.unique2=constant的情况类似。(考虑到缓存的因素评估的消耗可能要小一些)

外部节点的消耗加上循环内部节点的消耗(39.47+10*7.91)再加一点CPU时间就得到规划的总消耗。

再看一个例子:

  1. EXPLAIN SELECT *
  2. FROM tenk1 t1, tenk2 t2
  3. WHERE t1.unique1 < 10 AND t2.unique2 < 10 AND t1.hundred < t2.hundred;
  4. QUERY PLAN
  5. ---------------------------------------------------------------------------------------------
  6. Nested Loop (cost=4.65..49.46 rows=33 width=488)
  7. Join Filter: (t1.hundred < t2.hundred)
  8. -> Bitmap Heap Scan on tenk1 t1 (cost=4.36..39.47 rows=10 width=244)
  9. Recheck Cond: (unique1 < 10)
  10. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.36 rows=10 width=0)
  11. Index Cond: (unique1 < 10)
  12. -> Materialize (cost=0.29..8.51 rows=10 width=244)
  13. -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..8.46 rows=10 width=244)
  14. Index Cond: (unique2 < 10)

条件t1.hundred<t2.hundred不在tenk2_unique2索引中,所以这个条件出现在连接节点中。这将减少连接节点的评估输出记录数,但不会改变子节点的扫描数。

注意这次规划器选择使用Meaterialize节点,将条件加入内部节点,这以为着内部节点的索引扫描只做一次,即使嵌套循环需要读取这些数据10次,Meterialize节点将数据保存在内存中,每次循环都从内存中读取数据。

如果我们稍微改变一下查询,会看到完全不同的规划:

  1. EXPLAIN SELECT *
  2. FROM tenk1 t1, tenk2 t2
  3. WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;
  4. QUERY PLAN
  5. ------------------------------------------------------------------------------------------
  6. Hash Join (cost=230.47..713.98 rows=101 width=488)
  7. Hash Cond: (t2.unique2 = t1.unique2)
  8. -> Seq Scan on tenk2 t2 (cost=0.00..445.00 rows=10000 width=244)
  9. -> Hash (cost=229.20..229.20 rows=101 width=244)
  10. -> Bitmap Heap Scan on tenk1 t1 (cost=5.07..229.20 rows=101 width=244)
  11. Recheck Cond: (unique1 < 100)
  12. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
  13. Index Cond: (unique1 < 100)

这里规划器选择使用hash join,将一个表的数据存入内存中的哈希表,然后扫描另一个表并和哈希表中的每一条数据进行匹配。

注意缩进反应的规划结构。在tenk1表上的bitmap扫描结果作为Hash节点的输入建立哈希表。然后Hash Join节点读取外层子节点的数据,再循环检索哈希表的数据。

另一个可能的连接类型是merge join:

  1. EXPLAIN SELECT *
  2. FROM tenk1 t1, onek t2
  3. WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;
  4. QUERY PLAN
  5. ------------------------------------------------------------------------------------------
  6. Merge Join (cost=198.11..268.19 rows=10 width=488)
  7. Merge Cond: (t1.unique2 = t2.unique2)
  8. -> Index Scan using tenk1_unique2 on tenk1 t1 (cost=0.29..656.28 rows=101 width=244)
  9. Filter: (unique1 < 100)
  10. -> Sort (cost=197.83..200.33 rows=1000 width=244)
  11. Sort Key: t2.unique2
  12. -> Seq Scan on onek t2 (cost=0.00..148.00 rows=1000 width=244)

Merge Join需要已经排序的输入数据。在这个规划中按正确顺序索引扫描tenk1的数据,但是对onek表执行排序和顺序扫描,因为需要在这个表中查询多条数据。因为索引扫描需要访问不连续的磁盘,所以索引扫描多条数据时会频繁使用排序顺序扫描(Sequential-scan-and-sort)。

有一种方法可以看到不同的规划,就是强制规划器忽略任何策略。例如,如果我们不相信排序顺序扫描(sequential-scan-and-sort)是最好的办法,我们可以尝试这样的做法:

  1. SET enable_sort = off;
  2. EXPLAIN SELECT *
  3. FROM tenk1 t1, onek t2
  4. WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;
  5. QUERY PLAN
  6. ------------------------------------------------------------------------------------------
  7. Merge Join (cost=0.56..292.65 rows=10 width=488)
  8. Merge Cond: (t1.unique2 = t2.unique2)
  9. -> Index Scan using tenk1_unique2 on tenk1 t1 (cost=0.29..656.28 rows=101 width=244)
  10. Filter: (unique1 < 100)
  11. -> Index Scan using onek_unique2 on onek t2 (cost=0.28..224.79 rows=1000 width=244)

显示测结果表明,规划器认为索引扫描比排序顺序扫描消耗高12%。当然下一个问题就是规划器的评估为什么是正确的。我们可以通过EXPLAIN ANALYZE进行考察。

EXPLAIN ANALYZE

通过EXPLAIN ANALYZE可以检查规划器评估的准确性。使用ANALYZE选项,EXPLAIN实际运行查询,显示真实的返回记录数和运行每个规划节点的时间,例如我们可以得到下面的结果:

  1. EXPLAIN ANALYZE SELECT *
  2. FROM tenk1 t1, tenk2 t2
  3. WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2;
  4. QUERY PLAN
  5. ---------------------------------------------------------------------------------------------------------------------------------
  6. Nested Loop (cost=4.65..118.62 rows=10 width=488) (actual time=0.128..0.377 rows=10 loops=1)
  7. -> Bitmap Heap Scan on tenk1 t1 (cost=4.36..39.47 rows=10 width=244) (actual time=0.057..0.121 rows=10 loops=1)
  8. Recheck Cond: (unique1 < 10)
  9. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.36 rows=10 width=0) (actual time=0.024..0.024 rows=10 loops=1)
  10. Index Cond: (unique1 < 10)
  11. -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.91 rows=1 width=244) (actual time=0.021..0.022 rows=1 loops=10)
  12. Index Cond: (unique2 = t1.unique2)
  13. Total runtime: 0.501 ms

注意,实际时间(actual time)的值是已毫秒为单位的实际时间,cost是评估的消耗,是个虚拟单位时间,所以他们看起来不匹配。

通常最重要的是看评估的记录数是否和实际得到的记录数接近。在这个例子里评估数完全和实际一样,但这种情况很少出现。

某些查询规划可能执行多次子规划。比如之前提过的内循环规划(nested-loop),内部索引扫描的次数是外部数据的数量。在这种情况下,报告显示循环执行的总次数、平均实际执行时间和数据条数。这样做是为了和评估值表示方式一至。由循环次数和平均值相乘得到总消耗时间。

某些情况EXPLAIN ANALYZE会显示额外的信息,比如sort和hash节点的时候:

  1. EXPLAIN ANALYZE SELECT *
  2. FROM tenk1 t1, tenk2 t2
  3. WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2 ORDER BY t1.fivethous;
  4. QUERY PLAN
  5. --------------------------------------------------------------------------------------------------------------------------------------------
  6. Sort (cost=717.34..717.59 rows=101 width=488) (actual time=7.761..7.774 rows=100 loops=1)
  7. Sort Key: t1.fivethous
  8. Sort Method: quicksort Memory: 77kB
  9. -> Hash Join (cost=230.47..713.98 rows=101 width=488) (actual time=0.711..7.427 rows=100 loops=1)
  10. Hash Cond: (t2.unique2 = t1.unique2)
  11. -> Seq Scan on tenk2 t2 (cost=0.00..445.00 rows=10000 width=244) (actual time=0.007..2.583 rows=10000 loops=1)
  12. -> Hash (cost=229.20..229.20 rows=101 width=244) (actual time=0.659..0.659 rows=100 loops=1)
  13. Buckets: 1024 Batches: 1 Memory Usage: 28kB
  14. -> Bitmap Heap Scan on tenk1 t1 (cost=5.07..229.20 rows=101 width=244) (actual time=0.080..0.526 rows=100 loops=1)
  15. Recheck Cond: (unique1 < 100)
  16. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) (actual time=0.049..0.049 rows=100 loops=1)
  17. Index Cond: (unique1 < 100)
  18. Total runtime: 8.008 ms

排序节点(Sort)显示排序类型(一般是在内存还是在磁盘)和使用多少内存。哈希节点(Hash)显示哈希桶和批数以及使用内存的峰值。

另一种额外信息是过滤条件过滤掉的记录数:

  1. EXPLAIN ANALYZE SELECT * FROM tenk1 WHERE ten < 7;
  2. QUERY PLAN
  3. ---------------------------------------------------------------------------------------------------------
  4. Seq Scan on tenk1 (cost=0.00..483.00 rows=7000 width=244) (actual time=0.016..5.107 rows=7000 loops=1)
  5. Filter: (ten < 7)
  6. Rows Removed by Filter: 3000
  7. Total runtime: 5.905 ms

这个值在join节点上尤其有价值。"Rows Removed"只有在过滤条件过滤掉数据时才显示。

类似条件过滤的情况也会在"lossy"索引扫描时发生,比如这样一个查询,一个多边形含有的特定的点:

  1. EXPLAIN ANALYZE SELECT * FROM polygon_tbl WHERE f1 @> polygon '(0.5,2.0)';
  2. QUERY PLAN
  3. ------------------------------------------------------------------------------------------------------
  4. Seq Scan on polygon_tbl (cost=0.00..1.05 rows=1 width=32) (actual time=0.044..0.044 rows=0 loops=1)
  5. Filter: (f1 @> '((0.5,2))'::polygon)
  6. Rows Removed by Filter: 4
  7. Total runtime: 0.083 ms

规划器认为(正确的)这样的表太小以至于不需要索引扫描,所以采用顺序扫描所有行经行条件检查。

但是,如果我们强制使用索引扫描,将会看到:

  1. SET enable_seqscan TO off;
  2. EXPLAIN ANALYZE SELECT * FROM polygon_tbl WHERE f1 @> polygon '(0.5,2.0)';
  3. QUERY PLAN
  4. --------------------------------------------------------------------------------------------------------------------------
  5. Index Scan using gpolygonind on polygon_tbl (cost=0.13..8.15 rows=1 width=32) (actual time=0.062..0.062 rows=0 loops=1)
  6. Index Cond: (f1 @> '((0.5,2))'::polygon)
  7. Rows Removed by Index Recheck: 1
  8. Total runtime: 0.144 ms

这里我们可以看到索引返回一条候选数据,但被过滤条件拒绝。这是因为GiST索引在多边形包含检测上是松散的"lossy":它实际返回哪些和多边形交叠的数据,然后我们还需要针对这些数据做包含检测。

EXPLAIN还有BUFFERS选项可以和ANALYZE一起使用,来得到更多的运行时间分析:

  1. EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000;
  2. QUERY PLAN
  3. ---------------------------------------------------------------------------------------------------------------------------------
  4. Bitmap Heap Scan on tenk1 (cost=25.08..60.21 rows=10 width=244) (actual time=0.323..0.342 rows=10 loops=1)
  5. Recheck Cond: ((unique1 < 100) AND (unique2 > 9000))
  6. Buffers: shared hit=15
  7. -> BitmapAnd (cost=25.08..25.08 rows=10 width=0) (actual time=0.309..0.309 rows=0 loops=1)
  8. Buffers: shared hit=7
  9. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) (actual time=0.043..0.043 rows=100 loops=1)
  10. Index Cond: (unique1 < 100)
  11. Buffers: shared hit=2
  12. -> Bitmap Index Scan on tenk1_unique2 (cost=0.00..19.78 rows=999 width=0) (actual time=0.227..0.227 rows=999 loops=1)
  13. Index Cond: (unique2 > 9000)
  14. Buffers: shared hit=5
  15. Total runtime: 0.423 ms

Buffers提供的数据可以帮助确定哪些查询是I/O密集型的。

请注意EXPLAIN ANALYZE实际运行查询,任何实际影响都会发生。如果要分析一个修改数据的查询又不想改变你的表,你可以使用roll back命令进行回滚,比如:

  1. BEGIN;
  2. EXPLAIN ANALYZE UPDATE tenk1 SET hundred = hundred + 1 WHERE unique1 < 100;
  3. QUERY PLAN
  4. --------------------------------------------------------------------------------------------------------------------------------
  5. Update on tenk1 (cost=5.07..229.46 rows=101 width=250) (actual time=14.628..14.628 rows=0 loops=1)
  6. -> Bitmap Heap Scan on tenk1 (cost=5.07..229.46 rows=101 width=250) (actual time=0.101..0.439 rows=100 loops=1)
  7. Recheck Cond: (unique1 < 100)
  8. -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) (actual time=0.043..0.043 rows=100 loops=1)
  9. Index Cond: (unique1 < 100)
  10. Total runtime: 14.727 ms
  11. ROLLBACK;

当查询是INSERT,UPDATE或DELETE命令时,在顶级节点是实施对表的变更。在这下面的节点实行定位旧数据计算新数据的工作。所以我们看到一样的bitmap索引扫描,并返回给Update节点。值得注意的是虽然修改数据的节点可能需要相当长的运行时间(在这里它消耗了大部分的时间),规划器却没有再评估时间中添加任何消耗,这是因为更新工作对于任何查询规划都是一样的,所以并不影响规划器的决策。

EXPLAIN ANALYZE的"Total runtime"包括执行启动和关闭时间,以及运行被激发的任何处触发器的时间,但不包括分析、重写或规划时间。执行时间包括BEFORE触发器,但不包括AFTER触发器,因为AFTER是在查询运行结束之后才触发的。每个触发器(无论BEFORE还是AFTER)的时间也会单独显示出来。注意,延迟的触发器在事务结束前都不会被执行,所以EXPLAIN ANALYZE不会显示。

本文内容主要来自:http://www.postgres.cn/docs/9.5/using-explain.html

Postgresql中的explain的更多相关文章

  1. PostgreSQL中的partition-wise join

    与基于继承的分区(inheritance-based partitioning)不同,PostgreSQL 10中引入的声明式分区对数据如何划分没有任何影响.PostgreSQL 11的查询优化器正准 ...

  2. PostgreSQL中的索引(一)

    引言 这一系列文章主要关注PostgreSQL中的索引. 可以从不同的角度考虑任何主题.我们将讨论那些使用DMBS的应用开发人员感兴趣的事项:有哪些可用的索引:为什么会有这么多不同的索引:以及如何使用 ...

  3. 在PostgreSQL中CREATE STATISTICS

    如果你用Postgres做了一些性能调优,你可能用过EXPLAIN.EXPLAIN向你展示了PostgreSQL计划器为所提供的语句生成的执行计划,它显示了语句所引用的表如何被扫描(使用顺序扫描.索引 ...

  4. 通过arcgis在PostgreSQL中创建企业级地理数据库

    部署环境: Win7 64位旗舰版 软件版本: PostgreSQL-9.1.3-2-windows-x64 Postgis-pg91x64-setup-2.0.6-1 Arcgis 10.1 SP1 ...

  5. PostgreSQL 中日期类型转换与变量使用及相关问题

    PostgreSQL中日期类型与字符串类型的转换方法 示例如下: postgres=# select current_date; date ------------ 2015-08-31 (1 row ...

  6. PostgreSQL 中定义自己需要的数据类型

    PostgreSQL解决某系数据库中的tinyint数据类型问题,创建自己需要的数据类型如下: CREATE DOMAIN tinyint AS smallint CONSTRAINT tinyint ...

  7. 在PostgreSQL中使用oracle_fdw访问Oracle

    本文讲述如何在PostgreSQL中使用oracle_fdw访问Oracle上的数据. 1. 安装oracle_fdw 可以参照:oracle_fdw in github 编译安装oracle_fdw ...

  8. [原创]PostgreSQL中十进制、二进制、十六进制之间的相互转换

    在PostgreSQL中,二进制.十进制.十六进制之间的转换是非常方便的,如下: 十进制转十六进制和二进制 mydb=# SELECT to_hex(10); to_hex -------- a (1 ...

  9. 详解 MySQL 中的 explain

    来源:persister 链接:http://www.blogjava.net/persister/archive/2008/10/27/236813.html 在 explain的帮助下,您就知道什 ...

随机推荐

  1. Ionic3 启动页以及应用图标

    将新的启动页和应用图标图片(最好是高清png)上传到根目录 resources 使用命令自动生成,通过CMD进入项目所在文件夹,分别执行 ionic cordova resources android ...

  2. 2015ACM/ICPC亚洲区沈阳站 Pagodas

    Pagodas Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Sub ...

  3. TestNG并行测试

    并行(多线程)技术在软件术语里被定义为软件.操作系统或者程序可以并行地执行另外一段程序中多个部分或者子组件的能力.TestNG允许我们以并行(多线程)的方式来执行测试.这就意味着基于TestNG测试组 ...

  4. filereader api 类型

    filereader类似XMLHttpRequest,只是它用来从文件系统读取文件,提供了不同的方法去读取文件数据:1.readAsText2.readAsDataURL3.readAsBinaryS ...

  5. JAVA 的关键字 、

    关键字: 被JAVA语言赋予特定含义的单词, 特点: 组成关键字的单词的字母全部小写 注意: A:goto 和 const 是保留字 B: 类似于Notepad++ 这样的高级记事本,针对关键字有特殊 ...

  6. MongoDB入门系列(一):基础概念和安装

    概述 MongoDB是目前非常流行的一种非关系型数据库,作为入门系列的第一篇本篇文章主要介绍Mongdb的基础概念知识包括命名规则.数据类型.功能以及安装等. 环境: OS:Windows Versi ...

  7. [转载] NodeJS无所不能:细数十个令人惊讶的NodeJS开源项目

    转载自http://www.searchsoa.com.cn/showcontent_79099.htm 在几年的时间里,Node.JS逐渐发展成一个成熟的开发平台,吸引了许多开发者.有许多大型高流量 ...

  8. Robotium 框架学习之概述

    框架目的: Robotium is an Android test automation framework that has full support for native and hybrid a ...

  9. LKD: Chapter 5 System Call

    在Linux中,处理器所作的事可以归纳为3种情况: 1.In user-space, executing user code in a process; 2.In kernel-space, in p ...

  10. 在SQL Server Express版本中没有代理功能如何自动备份数据库

    因为是免费的且单个数据库可以支持到10GB,对于一般企业完全足够了,也就将就使用了,备份将分为两步: 1.创建备份脚本 2.创建系统的计划任务进行每天的备份 详细做法如下: 1.创建备份脚本 打开SS ...