2008年12月31日星期三

2008年个人总结

其实2008这一年对我来说还是挺重要的,今年的突破主要在以下几个方面:

1 ORACLE数据库上大有提高,对ORACLE数据库整体的认识在今年上了一个台阶,特别是对数据库体系结构比较清楚了,记得年前的时候,别人让我解释9i和10g架构上的差异,我都不知道如何回答。经过一年的刻苦修炼,我现在差不多能回答这个问题了。来年希望在数据库管理方面深入一下,并且在数据仓库,数据挖掘,OLAP系统方面能有所提高。

2 JAVA方面本年度提高不大,最大的进步是对RMI,CORBA,EJB的原理基本了解一点了, 还有对于基本的设计思想和设计模式算是有点感悟。希望来年把spring好好弄弄,熟悉熟悉功能,看看源码,希望提高比较大。

3 web展示层,应该是我今年提高最大方面之一,另一个是linux操作系统。从以前写根本并不会写页面到现在对 页面展示层还算比较了解,自我觉得提高很大。明年的目标就是结合spring再有点提高(嘿嘿,毕竟我的目标不是做前台)。

4 linux操作系统,给我带来的提高也很大。接触linux是因为ORACLE现在的很多架构转到linux上来了,得跟上技术的潮流。从刚开始接触,到现在已经有将近1年时间了,自己的感觉是对linux操作系统的一些基本的东西有所了解了,从开始的一点也不懂,到现在也能跟人砍一砍这个东西,进步还是蛮大的。希望来年在shell编程和服务器配置方面更进一步。

5 开源数据库和c语言,选择postgresql作为学习和研究的对象,主要是它号称基本都是c语言写的代码,而我自己c语言编程水平的提高很大一部分也来自于postgresql。当然今年只是学习了PG的SQL语言解析器部分的代码,但是仅仅这部分代码就已经让我进步很大了。 今年学会的C语言方面的知识有gcc,gdb,make,autoconfig,lex,yacc(bison),还有linux上的一大堆c语言库函数和API,回头看看进步神速,颇感欣慰。希望来年继续postgresql的学习和研究。

大概算算,今年学了很多东西,也浪费了很多时间,来年可真得抓紧时间,好好工作,努力学习了。

2008年12月28日星期日

ORACLE动态采样初探

一直以来,我都以为ORACLE的动态采样只有在没有统计信息的时候才有用,今天看了TOM老人家得一篇大作,受益匪浅啊。

dynamic sampling从ORACLE9iR2开始就可以使用了,CBO在执行hard parse的时候使用动态采样来收集相关表的统计信息同时来纠正自己的猜测数据。这个过程只在hard parse中产生,并且被用来生成更加准确的统计信息以供CBO使用,因此被称作动态采样。

优化器使用很多输入参数来产生合适的执行计划,比如它使用表上的约束,系统统计信息,查询涉及的相关对象的统计信息。优化器使用相应的统计信息来估算基数,而基数正是成本计算的最重要的变量。也可以说基数估算的正确与否,直接影响到执行计划的选择。这就是引入动态采样的直接动机--帮助优化器估算正确的基数,从而得到正确的执行计划。

动态采样提供了11个级别可以供使用(从0到10),后面我会详细解释每一个级别。ORACLE 9i R2的默认动态采样级别是1,而10g 11g的默认级别是2。

1 使用动态采样的方法
数据库级别可以调整optimizer_dynamic_sampling参数,会话级别可以使用alter session
在查询里可以使用dynamic_sampling提示

2 几个例子
针对没有统计信息的表:
SQL> create table t
2 as
3 select owner, object_type
4 from all_objects
5 /
Table created.

SQL> select count(*) from t;

COUNT(*)
------------------------
68076

下面的查询禁用了动态采样:
SQL> set autotrace traceonly explain
SQL> select /*+ dynamic_sampling(t 0) */ * from t;

Execution Plan
------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16010 | 437K| 55 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T | 16010 | 437K| 55 (0)| 00:00:01 |
--------------------------------------------------------------------------
下面的查询启用了动态采样:
SQL> select * from t;

Execution Plan
------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 77871 | 2129K| 56 (2)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T | 77871 | 2129K| 56 (2)| 00:00:01 |
--------------------------------------------------------------------------

Note
------------------------------------------
- dynamic sampling used for this statement

请注意,启用动态采样后的执行计划的基数(77871)更加接近实际的行数(68076),因此这个执行计划更可靠一点。

SQL>set autot off

当然也可能估算出差别很大的基数:

SQL> delete from t;
68076 rows deleted.

SQL> commit;
Commit complete.

SQL> set autotrace traceonly explain
禁用动态采样:
SQL> select /*+ dynamic_sampling(t 0) */ * from t;

Execution Plan
------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16010 | 437K| 55 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T | 16010 | 437K| 55 (0)| 00:00:01 |
--------------------------------------------------------------------------
启用动态采样:
SQL> select * from t;

Execution Plan
-----------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 28 | 55 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T | 1 | 28 | 5 (0)| 00:00:01 |
--------------------------------------------------------------------------

Note
---------------------------------------
- dynamic sampling used for this statement
实际上,表里现在没有数据,但是由于使用了delete而不是truncate,数据库并没有重置HWM,因此数据库猜错了结果,启用动态采样的基数要远远强于没有采样的。

以上两个例子,都是在没有对表进行统计的时候得到的,那么在有统计信息的情况下,动态采样还能有作用么?请考虑以下的例子,假设EMP表里有两个字段,birth_day date 和星座(varchar2),假设其中有1440000行数据(当然数据库表设计没有遵循第二范式)。在这种情况下,查询出生在1月份,并且星座是天平座的人,CBO会估算出多少行呢?答案是1W,但是实际上一行都不会返回,在这种数据库表设计有严重问题的时候,动态采样再一次向我们展示了其魅力所在。

SQL> create table t
2 as select decode( mod(rownum,2), 0, 'N', 'Y' ) flag1,
3 decode( mod(rownum,2), 0, 'Y', 'N' ) flag2, a.*
4 from all_objects a
5 /
Table created.

SQL > create index t_idx on t(flag1,flag2);
Index created.

SQL > begin
2 dbms_stats.gather_table_stats
3 ( user, 'T',
4 method_opt=>'for all indexed columns size 254' );
5 end;
6 /
PL/SQL procedure successfully completed.
这里做了统计,有直方图了,以下是一些统计信息:

SQL> select num_rows, num_rows/2,
num_rows/2/2 from user_tables
where table_name = 'T';

NUM_ROWS NUM_ROWS/2 NUM_ROWS/2/2
-------- ---------- ------------
68076 34038 17019

SQL> set autotrace traceonly explain
SQL> select * from t where flag1='N';

Execution Plan
------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 33479 | 3432K| 292 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 33479 | 3432K| 292 (1)| 00:00:04 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("FLAG1"='N')

SQL> select * from t where flag2='N';

Execution Plan
----------------------------
Plan hash value: 1601196873

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 34597 | 3547K| 292 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 34597 | 3547K| 292 (1)| 00:00:04 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("FLAG2"='N')

前两个执行计划都是准确的,会返回34597行数据,大概是表里数据的一半。接着执行以下查询:

SQL> select * from t where flag1 = 'N' and flag2 = 'N';

Execution Plan
----------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17014 | 1744K| 292 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 17014 | 1744K| 292 (1)| 00:00:04 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
----------------------------------------------------

1 - filter("FLAG1" = 'N' AND "FLAG2" = 'N')

这时会返回表里四分之一左右的数据,但是实际上应该是一行数据都没有,并且由于错误的基数估算导致错误的执行计划。再看看下面的这个查询,强制CBO进行动态采样。

SQL> select /*+ dynamic_sampling(t 3) */ * from t where flag1 = 'N' and flag2 = 'N';

Execution Plan
-----------------------------
Plan hash value: 470836197

------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 630 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 6 | 630 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_IDX | 6 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
----------------------------------------------------

2 - access("FLAG1"='N' AND "FLAG2"='N')

CBO估计会返回6行数据,并且进行了索引范围扫描,成本也很低,总体来说这个执行计划还令人满意。至于CBO估算出来的基数为什么是6,而不是0,这个解释起来比较复杂,在此就不详述了。

3 CBO动态采样级别:
level 0:不使用动态采样
level 1:在以下情况进行采样所有查询涉及的表:(1)查询语句里至少有一个未anylze过的表;(2)unanalyzed table连接到其他表或者出现在子查询中或者在不可合并的视图中;(3)这个表上没索引;(4)表数据块比进行动态采样的数据块多。
level 2:应用动态采样到所有unanalyzed table,表数据块至少是动态采样块的2倍。
level 3:满足level2的所有条件,并且外加所有表的标准选择率可以使用动态采样的谓词来计算
level 4:满足level3的所有标准,并且表上有参照到多个列的单个谓词。
level 5,6,7,8 and 9:满足前一个级别的所有标准,使用使用2,4,8,32,128倍的默认动态采样率。
level 10:满足level9的所有标注,并且对表里的所有块进行动态采样。

当然,前面的关于11个level的论述我可能翻译的不是很准确,有兴趣的话可以参考oracle database performance tuning.

4 何时使用动态采样以及如何使用:
(1) OLAP系统中可以大量使用
(2) 尽量避免在OLTP系统中使用
(3) 如果想在OLTP系统中使用的话,建议通过sql profiles来使用,遗憾的是从10g以后才能使用这个功能。原理上来说,SQL profiles有点像收集统计数据的查询和存储信息的字典,因此降低动态抽样的时间和hard parse的时间。

欢迎有问题一起讨论。tom关于这个问题有更加详细的论述,如果有兴趣的话,可以参考。

2008年12月25日星期四

北林为什么强于哈佛大学?

从别人那里转过来的。

1.北林是我党领导下的社会主义国家公立大学,哈佛大学是没落的资本主义国家的私立大学.两种国家的性质决定了北林地位必然高于哈佛大学,这是谁也无法抹杀的.

2.学校占地规模决定了北林强于哈佛大学,北林两个校区共占地面积约为12289亩,而哈佛大学总共占地才500亩,小的可怜.

3.学生人数上哈佛大学再次告负.目前,北林学生人数达2万多人,而哈佛大学才1万多,相比北林少了1万人.为什么会少了1万人?!不正是说明世界人民心目中喜欢北林的人多余喜欢哈佛大学的人吗?

4. 学生的素质.北林的学生不但全部流利掌握中文,并且大多数通过了大学英语四六级考试.而哈佛的学生除了会说英语,懂中文的人实在少之又少.同样,作为学校教学和科研的主力军,北林有很多老师既有中国教育背景,又有国外留学背景.而哈佛大学,全校竟找不出几个有完整中国教育背景的老师来.

5.两个学校不同的校训预示了两个学校未来的命运.北林的校训是"知山知水,树木树人" ,读起来琅琅上口,一种朝气蓬勃的社会主义优越感,而哈佛大学的校训是"以柏拉图为友,以亚历士多德为友,更要以真理为友",听起来让人有同性恋的感觉,而且还有一种暮气沉沉的感觉.

6. 另外结合本人的个人经历,我更深深体会到北林作为一所世界级名校在所有学子心目中的崇高地位.我本人作为一名美貌与智慧并存,温柔而不失刚毅的优秀有为青年,除了天资聪慧,秉赋过人外,更是每日五更勤奋苦读,经过不懈努力,终于有幸被北林录取.而哈佛大学,在本人及周边所有认识的人的高考经历中,没有一个志愿填报过这所学校.

综上所述,我可以很自豪的说:"我选择了北林,无怨无悔,因为它就是世界上最好的大学."


我们承认哈佛大学在世界范围内的名气大于北林,但这是由于美帝国主义的媒体掌握着话语权,有意压制北林的结果,我们相信通过全校校友在网络上的宣传,我们一定可以让全世界人民认识并喜欢.

2008年12月22日星期一

char型数据和绑定变量

众所周之,在ORACLE数据库中使用绑定变量是比较好的作法。今天一个同事在使用绑定变量的时候碰到一点问题,花了一点时间来解决,这个问题应该很容易碰见,对一般程序员来说挺有挑战性的。

在ORACLE数据库中做以下操作:
create table char_test(tid char(2),nid number(2));
insert into char_test select to_char(rownum),rownum from all_objects where rownum<=10;
commit;

然后分别运行以下语句:
select * from char_test where tid='1';(可以查出来值)
select * from char_test where tid='10';(也可以查出来值)
select * from char_test where tid=:ptid;

在这里将绑定变量ptid设为'1','10',可以看到前者没有查出来值,而后者有值。
原因主要是数据库在做针对字面量的查询时,做了转换,将'1',转换成了占char2,因此前者的出来正确结果,而使用绑定变量的时候,数据库不会做这个转换,因此就查不出来值。当然在都是查询tid='10'的时候,就没有这个问题。

我们也可以查看char(2)和varchar2(2)的区别:
select dump('1') from dual;
select tid from char_test where nid=1;

解决方法是在使用绑定变量并且数据类型是char的时候,将查询语句改写一下:
select * from char_test where tid=rpad(:ptid,2);

2008年12月21日星期日

apex--ORACLE的云计算开发工具

前两天看见ORACLE的APEX(application express可以在OTN直接使用了,中文应该叫快捷应用吧),介绍APEX的文章在OTN上有的是,有兴趣的话可以去看看。值得一提的是可能ORACLE会把APEX作为自己云计算的开发工具来使用,看来ORACLE也想在云计算市场上分一杯羹。话说回来,其实网格计算就可以看做是一种低空云。可以通过http://apex.oracle.com来使用使用该工具,注册后ORACLE会免费提供10M的空间供你存储数据和应用程序。

APEX是一个及其傻瓜的工具,比ACCESS更傻瓜,不过功能很强大,可以作出很多应用来,具体效果看图就知道了。
以下是我抓的图片(用APEX做的小例子):







不知各位老大如果用JAVA或者.NET要用多长时间才能把这么大的一个网站做完,如果是我的话,至少需要2周,当然实在不包括CSS的情况下。可是使用APEX只需要2天就可以做完了,而且界面美观,没什么bug,性能应该也还可以,中小型应用应该够了,毕竟这个东西支撑起了asktom。

CBO基础笔记

cursor_sharing 用于控制字面量替换
db_file_multiblock_read_count DFMRC用于在全表扫描和索引全扫描时计算成本
optimizer_index_caching 索引缓存率
optimizer_index_cost_adj 用于调整索引访问的成本
PGA_AGGREGATE_TARGET 用于控制hash和排序区的大小
optimizer_mode all_rows,first_rows_n,first_rows
star_transformation_enabled 是否允许星型转换
v$sql_cs_statistics 保存绑定变量的执行信息
低索引集群因子说明相近的锁银行集中少量列上

v$sql_plan,v$sql_plan_statistics和v$sql_plan_statistics_all可知plan
select plan_table_output from table(dbms_xplan.display());
select * from table(dbms_xplan.display('plan_table',null,'All'));
alter session set db_file_multiblock_read_count=16;

闪回查询前导列如何使用
object_id,object_value与对象表相关
全表扫描的成本计算公式:
1+高水位线下的块的数目/调整后的参数
单表选择率算法:
(numrows-numnull)/num_rows
alter session set "_optimizer_cost_model"=io;

begin
dbms_stats.gather_table_stats(user,'t1',estimate_percent=>null,method_opt=>'for all collumns size 120');
end;/

sql trace:
1 alter session set tracefile_identifier='fangyuan';
2 alter session set sql_trace=true;
do sth
3 alter session set sql_trace=false;
4 show parameter use_dump_dest
5 tkprof
or select spid from v$session s, v$precess p
where s.paddr=p.addr
and s.username=user
and module='SQL*PLUS';

10053 event:
alter session set event '10053 trace name context forever';
alter session set event '10053 trace name context off';

查询计划中的view_pushed predicate表示进行了谓词推进
优化器默认使用嵌套循环来处理anti_join,但是如果使用merge_aj,hash_aj,nl_aj的话,优化器能进行相应的转换。
优化器默认使用嵌套循环来处理semi_join,但是如果使用merge_sj,hash_sj,nl_sj的话,优化器能进行相应的转换。
但是在11g并且有统计信息的情况下,优化器不一定会使用NL来默认处理上述两种情况

内联视图
select p.pname ,c1_sum1,c2_sum2 from p,
(select id,sum(*) c1_sum1 from s1 group by id) s1,
(select id,sum(*) c2_sum2 from s2 group by id) s2
where p.id=s1.id and p.id=s2.id

标量子查询
select p.pname,
(select sum(p1) c1_sum1 from s1 where s1.id=p.id) c1_sum1,
(select sum(p2) c2_sum2 from s2 where s2.id=p.id) c2_sum2
from p

将子查询转换为内联视图是比较好的做法
提高CBO对于not in 的选择率精度,必须保证连接操作两端都不为NULL,负责可能出现错误结果

star提示可以用于进行星型连接
alter session set star_transformation_enabled=...
B树索引访问成本:
blevel+ceiling(leaf_blocks*effectvie index sel)+ceiling(clustering_factor+effectvie index sel)

连接操作选择率公式:
sel=(
(num_rows(t1)-num_nulls(t1,c1))/num_rows(t1)
*(num_rows(t2)-num_nulls(t2,c2))/num_rows(t2)
/greater(num_distinct(t1,c1),num_distinct(t2,c2))
)
基数公式:
card=
sel*filtered card(t1)*filtered card(t2)
当过滤谓词仅仅出现在一侧时,需要使用另一侧的distinct来替代最大值。
如果是多列连接,需要将多个连接条件的选择率相乘,如果在10g以后,可能会使用两个相同的条件相乘,并且选择较大的选择率
如果是范围连接,优化器使用类似与绑定变量的选择率处理
如果是不等值连接,选择率为1-等值连接的选择率

优化器在处理不等值连接且条件间为or时可能会有错误,如果加入no_expand提示就可以得到正确的结果,或者讲等值连接的条件分别写成查询最后union all.

在有重复史书的时候,计算成本总为1000k或者1,在有直方图的情况下,某些查询的成本会好一些,但是在数据不重叠时候,直方图会带来新的问题。
在建立直方图的时候,只有size远远大于列中不同的值的数量的时候,才可能使用频率直方图,否则都会使用高度均衡直方图。

如果在连接条件上有一个过滤条件,CBO可能会做闭包传递,导致过滤条件不可用以及忽略有过滤条件的连接条件,如果连接条件两边都有过滤条件,则有可能是成本和基数的差异都很大。

在进行多表连接时,需要将选择率相乘,而计算公式中的参数直接来自于语句中的谓词,在执行复杂查询的时候,连接条件非常重要

在估算code-业务表这种类型的连接查询时,如果讲很多的代码并入一张CODE表中,并且添加type列来进行区分,那么优化器将有可能进行错误的成本计算,即使建立了直方图也有这个问题。解决方法是对code表按type进行分区。

绑定变量的选择率为:density or 1/num_distinct
optimizer_index_caching设为75在OLTP系统中比较合理

嵌套循环的成本:
从第一个表取数据的成本+从第一个表得到结果的基数*对第二个表访问一次的成本
其实说白了就是嵌套循环的原理

hash_join的成本:
(探查遍数+1)*ceiling(大表数据块大小/调整IO后的单表值)+ceiling(小表表数据块大小/调整IO后的单表值)+hash_join前的成本和
其实就是hash_join的原理

10104事件用于查看hash_join的细节

dbms_lock.request(1,dbms_lock.xmode,release_on_commit=>true);
用于加排他锁,这时在其他会话中执行童谣的代码时就会等待,可以用这个方法来模拟并发。

begin
execute immediate 'purge recylebin';
end;

alter session set work_size_policy=mamual;
later session set hash_area_size=;

event 10032用于报告排序时系统内部相关活动的统计信息
event 10046 level 8用于记录等待状态
event 10033列出并发的IO细节
alter session flush shared_pool;
alter session flush buffer_cache;

一般来说,增加sort_area_size可以改善排序,但也可以增加对CPU的占用
在使用union,minus,intersect操作时,不要使用distinct关键字,否则计算card,cost有错误

10053事件的追踪报告通常包括:
绑定变量
参数设定
查询块
基础统计信息
完备性检查
一般执行计划:
在这里会衡量各个访问路径和顺序,但是在表的数量较多(4个表就有24种可能,4个表通常ORACLE只会估算13-16个路径)的情况下,ORACLE不会遍历所有的路径,这也是SQL需要优化的原因。

2008年12月18日星期四

ORACLE REA --很牛!


这两天刚看见ORACLE RICH ENTERPRISE APPLICATIONS,这个东西可以在OTN上直接看到,感觉做的很强,里面有一些哥哥姐姐们的视频,可以用来学习ORACLE在JAVA方面的一些技术,主要是ADF。刚看完他们的视频,感觉jdeveloper这个工具的功能越来越强了,毕竟是ORACLE精心打造的。

可以点击这里去学习一下。

ORACLE concepts笔记

commit comment参考sql ref
reco进程在两段式提交里的作用 参考concepts 114
自治plsql块 查pl sql ref
表大于255列的情况可以参考 concepts 121
数据行p122

表压缩部分:
表压缩只用于只读环境
表压缩与重定义的包相关 dbms_redefinition
null值在b树索引不会被计入索引,在集群列上的索引或着位图索引则会计入索引

什么情况下使用临时表需要再查查
查询视图也可以使用绑定变量
to_char,to_date,to_number三个函数的NLS参数需要注意,在视图定义的时候也可以指定。

外部表和sql loader作为数据加载的工具很好,需要看看。
escape处理转义字符
likec是针对国家字符集的,比如NCHAR
any和some是要其中任何一个,但是二者也有差别,需要再查查
all是所有的

视图也可以增删改,但是应该如何写DDL
物化视图可以用于分布式环境下的数据复制,也可以用于移动环境下的数据同步,也就是说在ETL操作中很好用
查到的资料说多维物化视图可以提供对多维数据的访问能力,但是OC上说MV伤的约束也可以做,具体怎么做?
物化视图的日志用于记录基表的变更信息
dimension用于数据仓库表示层次关系,结合物化视图学习

可见和不可见索引
基于函数的索引的deterministic关键字
将索引和表置于不同的表空间,OC里的解释似乎有问题
索引组指标上的位图索引和普通表的区别在于,由于索引组指标没有真正的物理ROWID,因此用一个映射表来存储ROWID和键值,之后对映射表做索引
关于OC p152的UROWID的问题
关于应用域索引的问题可以查阅oracle db cartridge developer's guide
hash cluster适用于大量返回值的等值查询

查看系统对象依赖:DBA_DEPENDENCIES,DEPTREE.....
远端程序调用的依赖控制,时间戳检查,特征检查(仅用于RPC)
设置会话级别的RPC:
alter session set remote_dependencies_mode={signature|timestamp}
DB级别:alter system
建议:
1 server端PLSQL用户使用timestamp
2 如果在分布式环境下避免不必要的重编译,应该使用signature mode
3 客户端应该为signature mode,因为,其一不需要重新编译过程,就建立新的应用程序,其二,不影响客户端就可以进行server端升级
4 使用signature mode是,在package中间或者末尾添加新函数或者新过程是会重编译
一些对象被重编译后,共享池内的sql会被标记为无效,下次进行重解析

数据字典保护:07_dictionary_accessibility=false,只允许sys用户访问
v$result_cache...用于查看结果缓存
使用result cache提示来缓存结果集
设置result_cache_mode来控制缓存的结果集用于所有查询还是仅用于带提示的查询
v$bgprocess视图获取后台进程的信息
job$视图查询job信息
private_sga可以限制共享服务器模式下会话私有数据的大小
数据库初始化参数shared_servers和max_shared_servers用于控制共享服务器模式下的连接数

使用ORACLE内部的连接池(池化模式)
以sysdba登陆:
exec dbms_connection_pool.start_pool('sys_default_connection_pool');
tns中的连接字符串
...(server=pooled)
purity可以指定重用池化session/new session

listener这块还有问题,需要再看看书

alter session用于改变用户会话参数
alter system用于改变实例参数
alter database才是改变数据库参数

contention争用的问题怎么解决?

2008年12月16日星期二

in vs exists

in和exists的问题我们都谈论了好多年了,今天又有课长提到这个问题,并且宣称in没有exists高效。一直以来大家都这么想,也就是说in比较慢。因此大家都用exists来替代in,其实这是一种不一定正确的做法。

在很多情况下,exists是没有办法替代in的,比如select * from emp where deptno in(10,20);我就看不出来这种条件下exists怎么能替代in。再者,如果exists真的能完全替代in的话,那么in为什么不从最新的sql 2003标准里除名呢。要知道制定这个标准的是数据库领域内的专家。

当然了,在大部分情况下,是可以使用exists来替代in的,同理也可以使用not exists来替代not in。但是即使在能替代的情况下,也不能保证二者究竟谁更高效一些。他们的效率问题,tom老人家早有论述,原话如下:

Well, the two are processed very very differently.

Select * from T1 where x in ( select y from T2 )

is typically processed as:

select *
from t1, ( select distinct y from t2 ) t2
where t1.x = t2.y;

The subquery is evaluated, distinct'ed, indexed (or hashed or sorted) and then joined to
the original table -- typically.


As opposed to

select * from t1 where exists ( select null from t2 where y = x )

That is processed more like:


for x in ( select * from t1 )
loop
if ( exists ( select null from t2 where y = x.x )
then
OUTPUT THE RECORD
end if
end loop

It always results in a full scan of T1 whereas the first query can make use of an index
on T1(x).


So, when is where exists appropriate and in appropriate?

Lets say the result of the subquery
( select y from T2 )

is "huge" and takes a long time. But the table T1 is relatively small and executing (
select null from t2 where y = x.x ) is very very fast (nice index on t2(y)). Then the
exists will be faster as the time to full scan T1 and do the index probe into T2 could be
less then the time to simply full scan T2 to build the subquery we need to distinct on.


Lets say the result of the subquery is small -- then IN is typicaly more appropriate.


If both the subquery and the outer table are huge -- either might work as well as the
other -- depends on the indexes and other factors.

英语不错的同学可以看看,翻译过来的意思是:in实际上是拿到内层查询的所有值,然后在去外层查找对应的值,得到查询结果;而exists实际上是拿到内层的每一个值都去外层查找一次。因此in更适合处理小结果集合的内层查询,而exists更适合处理大结果的内层查询。

以下是他给的例子:
rem create table big as select * from all_objects;
rem insert /*+ append */ into big select * from big;
rem commit;
rem insert /*+ append */ into big select * from big;
rem commit;
rem insert /*+ append */ into big select * from big;
rem create index big_idx on big(object_id);
rem
rem
rem create table small as select * from all_objects where rownum < 100;
rem create index small_idx on small(object_id);
rem
rem analyze table big compute statistics
rem for table
rem for all indexes
rem for all indexed columns
rem /
rem analyze table small compute statistics
rem for table
rem for all indexes
rem for all indexed columns
rem /

so, small has 99 rows, big has 133,000+

select count(subobject_name)
from big
where object_id in ( select object_id from small )

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.01 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.02 0.02 0 993 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.03 0.03 0 993 0 1

Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT GOAL: CHOOSE
1 SORT (AGGREGATE)
792 MERGE JOIN
100 SORT (JOIN)
100 VIEW OF 'VW_NSO_1'
99 SORT (UNIQUE)
792 INDEX GOAL: ANALYZED (FULL SCAN) OF 'SMALL_IDX'
(NON-UNIQUE)
891 SORT (JOIN)
0 TABLE ACCESS GOAL: ANALYZED (FULL) OF 'BIG'


versus:

select count(subobject_name)
from big
where exists ( select null from small where small.object_id = big.object_id )

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 4.12 4.12 0 135356 15 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 4.12 4.12 0 135356 15 1

Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT GOAL: CHOOSE
1 SORT (AGGREGATE)
792 FILTER
135297 TABLE ACCESS GOAL: ANALYZED (FULL) OF 'BIG'
133504 INDEX GOAL: ANALYZED (RANGE SCAN) OF 'SMALL_IDX'
(NON-UNIQUE)

That shows if the outer query is "big" and the inner query is "small", in is generally more
efficient then NOT EXISTS.

Now:

select count(subobject_name)
from small
where object_id in ( select object_id from big )

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.01 0 0 0 0
Execute 2 0.00 0.00 0 0 0 0
Fetch 2 0.51 0.82 50 298 22 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 5 0.52 0.83 50 298 22 1



Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT GOAL: CHOOSE
1 SORT (AGGREGATE)
99 MERGE JOIN
16913 SORT (JOIN)
16912 VIEW OF 'VW_NSO_1'
16912 SORT (UNIQUE)
135296 INDEX GOAL: ANALYZED (FAST FULL SCAN) OF 'BIG_IDX'
(NON-UNIQUE)
99 SORT (JOIN)
99 TABLE ACCESS GOAL: ANALYZED (FULL) OF 'SMALL'


versus:
select count(subobject_name)
from small
where exists ( select null from big where small.object_id = big.object_id )

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.01 0.01 0 204 12 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.01 0 204 12 1

EGATE)
99 FILTER
100 TABLE ACCESS GOAL: ANALYZED (FULL) OF 'SMALL'
99 INDEX GOAL: ANALYZED (RANGE SCAN) OF 'BIG_IDX' (NON-UNIQUE)

shows that is the outer query is "small" and the inner query is "big" -- a WHERE EXISTS can be
quite efficient.

不过这个例子我在ORACLE 11g上运行的时候并没有得到相同的结果,主要是因为我开启了查询重写,使得ORACLE可以自己重写查询,根据实际情况将exists和in进行互换(你不能指望每次数据库的查询重写都正确,特别是在数据库设计的不怎么好的情况下,查询重写总会出错)。禁用查询重写后得到了类似的结论,在9i和10g上我没有试过,有兴趣的话,可以测试一下。

欢迎有问题一起讨论。

2008年12月15日星期一

触发器做数据验证的性能问题

还是继续上次的话题,用触发器做数据验证,上次已经讨论了用触发器做数据验证有可能会出错,是功能方面的问题。这次想讨论一下性能问题。

首先建立两个测试表:
create table t1 (c1 number,constraint myt1_ck1 check( c1 between 0 and 100000)) ;
create talbe t2 (c1 number);
create or replace
TRIGGER MYT2_TRG
AFTER INSERT OR UPDATE ON T2
FOR EACH ROW
BEGIN
IF :NEW.C1<0 OR :NEW.C1>100000 THEN
RAISE_APPLICATION_ERROR(-20001,'Value is too large or too small.');
END IF;
END;

然后set timing on
运行以下语句:
insert into t1 select rownum from all_objects where rownum<=10000;
insert into t2 select rownum from all_objects where rownum<=10000;
第一个语句耗时0.51M
第二个语句耗时0.59M
insert into t1 select rownum from all_objects where rownum<=100000;
insert into t2 select rownum from all_objects where rownum<=100000;
第一个语句耗时35.25M
第二个语句耗时32.56M

这是在没有错误发生的情况下测试,如果有错误发生,性能相差更大,有兴趣可以写个小例子测试一下。

接下来使用tom老人家的runstats工具来进行比较测量。

fy@ORCL> exec runstats_pkg.rs_start();

PL/SQL procedure successfully completed.

fy@ORCL> insert into myt1 select rownum from all_objects where rownum<=100000;

71844 rows created.

fy@ORCL> exec runstats_pkg.rs_middle();

PL/SQL procedure successfully completed.

fy@ORCL> insert into myt2 select rownum from all_objects where rownum<=100000;

71844 rows created.

fy@ORCL> exec runstats_pkg.rs_stop(1000);
Run1 ran in 10488 hsecs
Run2 ran in 12552 hsecs
run 1 ran in 83.56% of the time

Name Run1 Run2 Diff
LATCH.object queue header oper 502 1,616 1,114
LATCH.simulator lru latch 9,022 12,771 3,749
LATCH.simulator hash latch 9,053 12,905 3,852
STAT...no work - consistent re 81,400 72,775 -8,625
STAT...consistent gets from ca 225,445 216,746 -8,699
STAT...consistent gets 225,445 216,746 -8,699
STAT...buffer is not pinned co 283,146 274,428 -8,718
STAT...buffer is pinned count 74,052 82,792 8,740
STAT...consistent gets from ca 82,070 73,301 -8,769
STAT...session uga memory -15,772 0 15,772
STAT...physical read total byt 32,768 49,152 16,384
STAT...physical read bytes 32,768 49,152 16,384
STAT...session logical reads 226,812 290,606 63,794
STAT...HSC Heap Segment Block 405 71,858 71,453
STAT...redo entries 900 72,356 71,456
STAT...db block gets from cach 1,367 73,860 72,493
STAT...db block gets 1,367 73,860 72,493
STAT...calls to kcmgrs 37,858 119,922 82,064
STAT...db block changes 1,384 144,841 143,457
STAT...session pga memory -196,608 0 196,608
LATCH.cache buffers chains 237,315 583,505 346,190
STAT...undo change vector size 172,280 4,600,964 4,428,684
STAT...redo size 1,120,676 17,033,476 15,912,800

Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
838,049 1,190,743 352,694 70.38%

PL/SQL procedure successfully completed.

上面 Diff的差值是负数的情况,估计是由于数据缓存造成的,在真实环境下,应该不会有这种情况发生。主要可以看到不论是运行时间还是latch数量,第一个方法都小于第二个方法。因此,用什么方法做数据验证,大家应该都没问题了吧。

2008年12月14日星期日

美国教授:中国的研究生基本靠自学成材

转帖的文章,原文请看这里,十分惊讶老美居然这么了解中国。

中国和美国学术环境不同的地方很多,最大的不同是,研究人员对自己研究工作的投入多少不同。当然,在中国有很多负责任并工作努力的研究人员,但也有不少人打着学术的旗号捞好处﹑争权夺利。同时还不忘时时炫耀一下自己的知识分子地位;为了爬上领导的位子而勾心斗角;尽力安排媒体对自己的采访以提高知名度;出去演讲以赚取高额的报酬;加入各种公司的董事会以谋得某种好听的头衔和利益。如此这般,这些教授哪里还有什么时间做研究呢?他们如何及时完成自己的科研成果呢?那就只有依靠自己学生们的努力。

  这些教授因为能够争取到研究资金,而且也拥有行政职位,因此能够招到最好的学生。学生们一方面有追求成功的动力,一方面不敢也不愿得罪自己的指导教师。为了自己的将来,学生们就不惜余力地满足教授的各种要求,加深教授对自己的好印象。教授也正好乐得坐享其成。

  如果在这个过程中,教授能够提供一些指导,情况还不算糟糕。但一般来讲,很多学生在面对巨大的研究压力的时候,所得到的教授的帮助是相当不足的,基本上都要靠自己。当然,通过自学,他们学到了一些新的知识,却会不可避免地走一些弯路,也达不到本应达到的效果。

在中国,许多请学生帮忙的教授,虽然没提供过什么指导或指导不够,在文章发表的时候还是会把自己的名字放在学生的文章上面,好像是自己尽心指导了一样。还有一些为数不多的教授,直接就把文章拿来,占为已用,学生的名字根本不在文章上出现。这些教授,虽然文章没写一个字,却得到好评。在美国,这种情况叫做 “plagiarism”(剽窃),是非常严重的错误,也是学校可以把教授开除的原因之一。这样的事情不应该发生,而且它也是中国经济发展的巨大障碍。

  另一个中国和美国比较明显的差别,和学院权力的分配有关。老实说,在美国,确实也常常会碰到一个人或“一小团人”想控制整个学院的例子,不过美国的学院制度会约束这样的情况。比如在美国,刚获得博士学位的毕业生是不允许留校的,他们至少要有三到五年在其他学校工作的经历,以证明他们有独立研究和教书的能力,才可以回到母校去。

  但在中国,与握有实权的教授关系好的毕业生,可以直接留校教书,而教授就可以由此形成自己的“团派”。学院的一切也尽在教授的掌握之中,学术自由从而受到了限制。这么严重的情况在美国是不存在的。

  由此所衍生出来的同一大学各学院的领导关系也错综复杂,学校的研究工作也受到极大的影响。例如,某个有权力控制学院“王国”的领导,与其他某个学院的领导交恶,那么他的权力范围内的学院,会对这个学院敬而远之。其结果导致学院之间基本没有什么合作活动,研究工作就会受到不利的影响。这个情况有点像一个幼稚的游戏,阻碍着中国学术的发展。

  与此相反,在美国,大学非常重视学院之间的合作,这能够产生更有意义的研究结果。因为各个学院分属不同的领域,这样跨学科的研究合作既有利于整个学校的发展,也有利于各学院之间的互相学习,而这样的学习在中国是不常见的。

  最后,我想指出的一个中国和美国之间的不同,则和学术概念有关。美国的学者基本上都热爱学术,可以说是满腔热忱。这种态度是一种动力,激励他们去努力。大部分美国的学者也对知识保持好奇的态度,努力把自己的视野范围扩大起来。他们大部分也都会负起学术方面的责任,而且尊重他们领域内其他的人。虽然教授之间的学术概念经常不一样,但这种不同一般不会产生什么关系不愉快的结果,学术的质量比数量更加受到重视。

  但是,相当多的中国教授进行学术研究的动力,好像和“钱”或“权”有关系,教授之间也常常存在很多个人仇恨或嫉妒。在中国,被考虑的主要问题经常是:这一年你写了几篇文章?这些文章是哪里发表的?你是哪一个学校毕业的?你的指导老师是谁?你和哪一些领导关系比较好?还有你能够申请到多少钱的课题?你曾经在国外待过吗?是不是名校?等等。

  与此相反,在美国,如果你在一年甚至五年之内只发表一篇、但品质堪称一流的文章,人人都会很看重你,因为从文章的内容,可以看出作者所倾注的心血和努力。然而在中国,这样做几乎是不可能的。因为这里所有的成绩和统计数字有关,质量并不重要,重要的是你写了没有,发表了没有,在什么级别的杂志发表的。没有人真正关心文章是否真有价值,也没有人真正关心学生究竟在学校里学到了什么。

我的GIS之路

从接触GIS软件开始,不知不觉就已经4个年头了。上次邓老师让我介绍一下自己的学习经验,一时没想好,最近大概整理了一下,想写出来,给非地理新系统或者计算机类专业的同学参考一下。

虽然说起来我接触GIS已经4年了,但是前两年几乎等于没有接触。主要是因为自己没有买电脑,就算想学,也没机会学。其实大三的时候,邓老师上GIS那课的时候,我还是好好学了一下。那时候,看了一本很好的书,书的名字我记不住了。好像是陈健飞翻译的一本老美的书。由于老师的严格要求和自己的努力,我对GIS概念上的那套东西有了基本的了解。大四的时候,我神使鬼差的找到一份GIS二次开发的工作,这才算是正真的开始学习和了解GIS。但是那个时候主要还是以学习编程语言为主,当时用了慧图公司的top map SDK,现在想想,那个软件真的还挺适合上手的。虽然很多功能不能做,但是学习的门槛很低而且学习曲线也不陡峭。

做了三个月基于TOPMAP的二次开发后,又开始基于ARCOBJECTS做一个GIS的框架,作为以后公司CS模式GIS的框架。很遗憾由于后来别的项目的原因,我没有完成这个开发任务,但是ESRI清晰的设计还是给我留下很深刻的印象。在开发的过程中,我时不时的需要做一些图,来给客户看,因此学会了一些基本的操作。但是除了那些基本的操作之外,我建立起来了,地图,图层,实体,属性这些基本的概念。当然我建立的不是死的概念,而是很具体的概念和操作的对应,从此我才算是正真的踏上GIS之路。

后来一年的工作里,我其实一直都没怎么接触GIS,除了当时协助邓老师,指导一位本科毕业生。由于我当时的工作比较忙,除了搭好框架外,实际上没有给他太大的帮助。虽然我没有接触GIS,但是我在Database方面有了长足进步。而且十分幸运的是,我学习的是ORACLE数据库,并且在数据库编程方面下了点功夫。当然,当时对于XML Db这块的学习,更是锦上添花。因为其实ORACLE数据库中的XML Db和SPATIAL Db的构成原理有类似之处。因此这也是我对想真正学习GIS的同学的一点建议,那就是花3个月到半年时间来学习一门数据库,当然我更建议学习ORACLE或者Postgresql,主要原因是他们的功能性很强,学习数据库编程的时候更加有回旋余地。而且这两个数据库上面都有已经成型的,非常强大的空间数据库扩展。需要注意的是,不要去学数据库原理这样的课程,主要是因为这样的课程比较空泛,理论太多而实际用的到的内容太少。

八月份后我就开始准备复习和出国的事情了。这时我看了两本书,对我的帮助很大。一本书是《为我们的世界建模》,英文名字叫做《modeling our world》,是ESRI公司的一个人写的。另外一本书是《空间数据库》,英文名字叫做《Spatial database a tour》,是明尼苏达大学的一个老师写的。前者主要是针对geodatabase的一些理论说明,后者则非常全面和深入的讨论了空间数据库的很多方面的内容,但是想读懂这本书需要很强的数学,数据库以及JAVA方面的功底,强烈推荐想学的同学看看。当时还旁听了一个老师的GIS课程,当然他讲的挺垃圾的,估计全班90人没几个人听懂了。

大概是从11月份以后吧,我使用GIS的时候就如有神助了。其实最重要的是,我已经对GIS的术语,概念和基本模型很了解了。因此,面对问题的时候,我能很快的使用GIS的数据模型来模拟,而碰见不太懂的操作的时候只需google一下就好了。这一段时间,我还看了一些map server的源代码,当然主要是想糊弄明大的那个老师,不过他鹰眼犀利,一下就看出我是个草包。因此,我建议想学的话,可以先把基本的概念弄明白,然后学习如何将这些概念模型应用到自己的领域中,动手实践,发现问题,反过来再看书学习......到帮助邓老师代05级学生环境规划实习的时候,我已经对GIS领域内的内容了如指掌(哈哈,吹牛了)。

放假回家后,闲来无事就继续研究研究GIS。这时候,我才正真接触了PGSQL和POSTGIS。关于这两个东西介绍,网上有很多,只是都不怎么好,如果想学习的话,建议大家看看POSTGIS的手册,和数据库里面的源代码。注意,需要看的数据库里的PLSQL源码,而不是C语言写的那些代码。对于想学空间数据库的朋友来说,POSTGIS是一个不可多得的财富。有时候宝藏就在咱们手笔,只是百分之九十九的人不知道怎么念芝麻开门。还有一个很好的工具是QGIS,它是一个很好的POSTGIS的前端软件。我自己觉得POSTGIS和QGIS,再加上MAP server,你就可以搞定GIS内80%的问题,而且全部都免费。在家里最大的进步就在于对于一些GIS软件的基本实现有所了解。如果在达到我上面说的了如指掌的境界后,还想继续学习GIS软件的基本实现的话,那我建议好好看看POSTGIS(空间数据存储),QGIS(空间数据显示),MAP SERVER(空间数据的WEB服务器),当然后这三个对于C语言要求挺高的,熟悉C语言是前提要求,而POSTGIS除了对C语言要求挺高之外,还需要精通PLSQL编程,因此在学习这些东西之前,要有足够的心理准备和知识储备。如果是熟悉JAVA语言的话,可以看看UDIG(空间数据显示),GEO SERVER(空间数据的WEB服务器)。另外,MYSQL的空间数据库扩展MYSPATIAL也很有特色,走了一条跟现在的ORACLE和PGSQL都不一样的路(跟ORACLE8I的空间数据库差不多)。

说了这么多,还是想总结一下,多看书+多思考+多实践=GIS Expert。

2008年12月13日星期六

PostgreSql源码学习(7)

PG源码之parser(4)

前文已经大概描述了PGSQL,在解析SQL语句时所做的工作,接下来需要对在这个过程中的一些主要文件进行分析。

主要分析文件:
    // 基础节点定义
    src/include/nodes/nodes.h
    // SQL语句被解析后得到的节点结构体定义
//SQL语句经过parser解析后都先对应该该文件的一个struct
    src/include/nodes/parsenodes.h
    // List定义
    src/include/nodes/pg_list.h
    // List实现
    src/backend/nodes/list.c   
    // utility Stmt运行文件
    src/backend/tcop/utility.c
    // SQL analyze and rewrite
    src/backend/parser/analyze.c

PostgreSQL用一种非常简单的形式实现了类似C++的继承,请看nodes.h :
typedef struct Node
{
  NodeTag  type;
} Node;
然后请看:parsenodes.h
假设有一个create table的sql:
create table test (name varchar(100, pass varchar(100));
将会被解析到如下structure:
typedef struct CreateStmt
{
  NodeTag  type;
  RangeVar   *relation;  /* 关系名*/
  List    *tableElts;  /* 列定义*/
  List    *inhRelations; /*表继承关系(PG里的继承) */
  List    *constraints; /* 约束条件 */
  List    *options;  /*其他的可选条件*/
  OnCommitAction oncommit; /* 提交时做的事情 */
  char    *tablespacename; /* 表空间*/
} CreateStmt;

以下是select语句对应的结构体:
typedef struct SelectStmt
{
NodeTag type;

/*
* These fields are used only in "leaf" SelectStmts.
*/
List *distinctClause; /* 处理distinct关键字 */
IntoClause *intoClause; /*处理select into /create table tbn as select ... */
List *targetList; /* 列*/
List *fromClause; /* from 子句*/
Node *whereClause; /* WHERE条件子句 */
List *groupClause; /* GROUP BY子句*/
Node *havingClause; /* HAVING 子句 */

/*
* In a "leaf" node representing a VALUES list, the above fields are all
* null, and instead this field is set. Note that the elements of the
* sublists are just expressions, without ResTarget decoration. Also note
* that a list element can be DEFAULT (represented as a SetToDefault
* node), regardless of the context of the VALUES list. It's up to parse
* analysis to reject that where not valid.(这段话的意思是valuesLists只在叶查询节点里出 * 现,就是最内层的子查询)
*/
List *valuesLists; /*表达式值列表*/

/*
* These fields are used in both "leaf" SelectStmts and upper-level
* SelectStmts.
*/
List *sortClause; /* 排序*/
Node *limitOffset; /* 从第几个开始limit子句的第一个参数 */
Node *limitCount; /* 到哪里结束limit子句的第二个参数*/
List *lockingClause; /* FOR UPDATE (list of LockingClause's) */

/*
* These fields are used only in upper-level SelectStmts.
*/
SetOperation op; /*set操作的类型*/
bool all; /*因该是处理any关键字的*/
struct SelectStmt *larg; /* left child */
struct SelectStmt *rarg; /* right child */
/* Eventually add fields for CORRESPONDING spec here */
} SelectStmt;

这上面是两个对应于create语句和select语句的结构体,对应于其他语句的结构体,也都可以在这个文件里找到,这两个结构体里面各个成员变量的含义我已经做了注释。这两个struct的第一个元素都是type(NodeTag)。PG的很多struct的第一个元素都是NodeTag,这样在函数中传递指针变量时,可以很简单的把参数设置成:Node*。说简单点,其实有点像是所有的struct都继承了Node这个struct。

在nodes.h中有每个Node的值的定义,比如:上面说的CreateStmt的type的值就是:T_CreateStmt。其他的每个node的定义也可以在这个文件里找到。

接下来需要看看的是链表,链表在pg_list.h中有定义:
typedef struct List
{
  NodeTag  type;   /* T_List, T_IntList, or T_OidList */
  int   length;
  ListCell   *head;
  ListCell   *tail;
} List;
可以看到,List的定义是基于基类Node来进行的。
常用的List操作函数有:
//取List第一个元素
ListCell *y = list_head(List *l);
//得到List的元素个数
list_length(List *l);
// 遍历List
foreach(cell, l)
{
 …
}
其他的很多函数具体可以参考pg_list.h和list.c

Database is the core of universe

记得刚接触ORACLE的时候,有人给我讲了个故事。他说他在第一次看到ORACLE7的时候,当时叫做ORACLE universal server,翻译成汉语就叫做ORACLE通用服务器,他的第一反应是ORACLE宇宙服务器。然后他说,等你以后明白为什么叫做宇宙服务器了,你就大概了解ORACLE数据库库了。

现在我接触这个东西都两年多了,也算是对这个东西有所了解了,逐渐体会到所谓宇宙服务器的含义。CS模式兴起的时候,ORACLE给数据库也加一个客户端。网络走红的时候,就干脆把数据库服务器当作web服务器来用,网页就干脆是数据库里的一堆存储过程。一直到现在的apex(application express),都是走的这个思路,著名的Ask tom就是用这个东西做的。搜索热门的时候,ORACLE7就集成了全文检索和多媒体检索功能,说白了就是在数据库里建立几张表来做索引,进行检索。到了搜索引擎时代,ORACLE AS 里就集成了搜索引擎的功能,而这个功能说白了也就是在数据库里建了几张表,进行全文检索。更不要提JAVA,在JAVA大紫大红的年代,ORACLE就把JAVA直接装入数据库,让JAVA程序员可以用JAVA来写存储过程。当然了,还有PRO C,PRO COBOL...一系列装载在数据库里的编程语言。

于是,ORACLE 8i 集成了空间数据库(ORACLE Spatial)来存储空间信息。ORACLE 9i把XML数据作为原生的本地数据库。10g又进一步增强了很多应用方面的功能。到了11g我们已经能存储真三维数据和在数据库里做web service了,更不要说那些OLAP,BI,DM方面的功能,感觉什么功能都具备了,什么东西都可以放在数据库里(这个感觉很像星际之门亚特兰迪斯里远古人的那个超大超强的database)。

理所当然的,我第一次听见OEL (ORACLE enterprise LINUX)的时候,我的感觉就是这是个装在数据库里的操作系统,第一次听见BTRFS的时候,我认为这是个装在数据库里的文件系统,而ORACLE AS,那种从数据库里强行扒出来的感觉更是让人印象深刻。

因此,在ORACLE公司看来,Database is the core of universe.

闲来无事,就把上面的文章翻译了一下,好长时间没写英文了,感觉有点生疏。

When using ORACLE database firstly , my manger told me a story. His colleague called oracle universal server to oracle universe server by mistake, when playing ORACLE database version 7. And he told me that whether understanding why the databasewas called universe server is the key to study oracle database.

Now,i have used this serveral years. To some extent,i am a pre expert in this field. I begin to understand what's the universe server mean. When c/s patten began, Oracle would add a client to the database server. When net era was coming, the database server had been used as a web server.Indeed, the web page was sereral procedures in DB. Now as the son of OWA, application express continue this thinking.The most famous website of dba in oracle field--asktom was completed by this tool.When searching technology was in fashion,Oracle intigrate text searching and multimedia searching technology in database.Of course,the logo of ORACLE database should be add "JAVA inside" label,which means java could be used to write procedure in DB.

Further,ORACLE combine spatial database to ORACLE DB. At the same time, they extend DB to store XML data as a native XML database.And now , we could store the real 3D data and build web service on database,besides powerful OLAP,BI and DM functions.My feeling is everything could be stored in Database, that like the super database of the ancients in stargate atlantis.

Naturally,I thinked the OEL as a operation system in database and the BTRFS as a file system in database when finding them firstly. The feeling of ORACLE AS was generated from ORACLE database was impressive.

So, for ORACLE company, database is the core of universe.

2008年12月12日星期五

触发器是用来做数据验证的么

前几天听同事说,他们做了一个触发器来进行数据验证。我知道触发器有很多用途,但是用触发器来做数据验证,多半就有问题。

举例说明如下:
在emp表里希望每个部门的人数都不能少于三人,在EMP表建立如下触发器:
create or replace
TRIGGER
emp_trg AFTER INSERT OR DELETE OR UPDATE ON EMP
BEGIN
for irec in (
select deptno, count(*) emps
from emp
GROUP by deptno
HAVING count(*) < 3
)
loop
RAISE_Application_error(-20000,'dept wrong number');
end LOOP;
END;

在第一个会话里执行如下语句(员工号为7499,7521,7654,7900的员工都是部门号为30的员工,这个部门共有6个人):
delete from emp where empno in (7499,7521,7654);
3 rows deleted.
这里触发器看到的部门号为30的员工有三个。
在第二个会话里执行如下语句:
delete from emp where empno=7900;
1 row deleted.
这里触发器看到的部门号为30的员工有5个。
commit;
Commit complete.
这是再回到第一个会话执行commit;
Commit complete.

然而这时部门号为三十的部门只有两个人,因此触发器验证失败。
这是功能上的问题,性能上,一个触发器对增删改操作的性能影响,至少和一个索引相当。因此在性能上来说,也得不偿失。如果是做数据验证的话,用check来做多半好一些。

2008年12月10日星期三

ORACLE数据库的成本是什么

在应用ORACLE数据库的过程中,我们经常需要查看执行计划。执行计划里最重要的两个指标就是基数和成本。很多人都对成本的含义不清楚,因此有必要弄清楚其意义到底是什么。

根据ORACLE database performance tuning guide,
cost=(
#SRds*sreadtim+
#MRds*mreadtim+
#CPUCycles/cpuspeed
)
/sreadtim

where
#SRds number of single block reads(单块读取数量)
#MRds number of multi block reads(多块读取数量)
#CPUCycles number of cpu cycles(CPU转数)

sreadtim single block read time(单块读取时间)
mreadtim multi block read time(多块读取时间)
cpuspeed cpu cycles per seconde(CPU每秒钟转数)

实际上,成本就是,操作花费在单块读上的总时间,加上花费在多快读上的总时间,再加上CPU处理总时间,除以一个单块读需要花费的总时间。

有人说COST仅仅是个相对的数值,只是在比较不同操作的成本时才有用,当然是不正确的,上式表明COST是尤其自己的意义的(跟时间相关)。

其实ORACLE选取这样一个奇怪的单位来衡量成本的大小,一点也不让人吃惊。当然也有人说,ORACLE使用这个公式只是为了保持跟以前版本的延续。但是很显然这个说法是错误的。为什么ORACLE在第一次出现CBO时会选择这个古怪的单位来表示成本呢,这个上述说法显然无法解释这个问题。我以为,使用这个单位的原因主要是它可以消除由于硬件带来的差异。比如一台IBM最高端的服务器和随便一台笔记本电脑的运行速度明显不一样。这就会造成运行于这两台机器上的ORACLE数据库在执行同样的操作的时候,消耗的时间不同。而通过以上的计算,选择那个古怪的单位作为成本的单位,就可以消除硬件带来的差异(当然软件环境一致的情况下才能在不通的机器上得到同样的成本)。

有问题欢迎与我讨论。

有关附件存储方式的探讨

在oracle中国家语言支持的索引问题一文中,我已经提到了优化器在处理长字符串时的一些问题。

在高丽棒子做的数据库设计中(其实我们中国人很多时候也会这么做),要么是把附件作为BLOB直接存储,要么是把附件地址保存在一个VARCHAR2的字符串中。

第一种做法非常不可取,原因在于这种做法会极大的增加数据库服务器的负担,并且在程序里处理BLOB的痛苦不是常人可以忍受的。

第二种做饭看似不错,但是考虑到通常服务器是unix/linux操作系统,因此路径会被保存为很长的路径,比如:/home/fanng/documents/attfiles/username/...(其实这还不算长),这种情况下优化器在处理长字符串时会选择错误的执行计划。因此对数据库服务器性能的影响也会很大。

个人以为,首先来说,数据库服务器本身是不适合同时存储附件的,因此第一种做法几乎可以立即排除。因为数据库服务器的资源有限,特别是I/O开销太大,最好还是只跑数据库。而应用服务器上主要是跑应用业务,因此CPU开销较大,而I/O开销相对较小,因此将附件置于应用服务器应该更好一些。可以基于文件系统来控制,并且在数据库里保存少量的信息来定位,这样就可以避免出现上文所说的两种情况。

欢迎有问题与我探讨。

再探ORACLE数据库字符集乱码问题

一 服务器字符集

如前文所述,ORACLE数据库有数据库字符集和国家语言字符集两种。数据库字符集用于存储char,varchar2,clob,long等数据类型,用来标示表名,列名,sql和PLSQL存储单元等。而国家字符集通常只用来存储nvarchar2,nchar,vclob这些数据类型。建库时就已经决定了数据库字符集和国家语言字符集。对于简体中文操作系统,如果安装数据库的时候没有特别指定,默认字符集就是ZHS16GBK。



ORACLE数据库中字符集的命名遵循以下规则:
Language\bit size\encoding
ZHS 16 GBK
当然有一些字符集命名不在此例:
AL16UTF16,其中AL代表ALL,指的是all language。

二 客户端字符集

NLS_LANG参数由以下组成:
NLS_LANG=language_Territory.client_characterset
language指定ORACLE所用的提示语言,日期中月份和日的显示。
Territory指定货币和数字格式,地区和计算星期及日期的习惯。
client_characterset控制客户端应用程序的字符集。
如果是在windows平台,可以使用chcp查看系统页代码,比如中文winxp操作系统就会显示“活动的代码页:936”。
在sqlplus里可以使用alter session set nls_lang=...来修改这个参数,在sql developer中,需要在tools->preference->database->NLS_PARAMETERS来修改。

三 乱码产生

乱码的产生是由于下面三个字符集引起的:
数据库字符集
客户端字符集
客户端NLS_LANG参数的设置

(1)当客户端字符集和数据库字符集不同,同时NLS_LANG不同于服务器字符集。
在这种情况下存在两种可能性:
a 客户端输入的字符在NLS_LANG中没有对应的字符,这时会以?来代替。
b 客户端输入的字符在NLS_LANG中对了其他字符,传递给数据库后,字符被存储下来,但是元数据丢失,因此数据库中存储的字符不再代表客户输入的字符。(注意:这个过程不可逆,因为通常情况下我们不知道客户端字符集)

(2)NLS_LANG和服务器字符集相同时
在这种情况下,数据库不用对客户端传递过来的数据进行编码转换,直接存入数据库。如果客户端传过来的字符集在数据库中有正确的对应关系,则存储正确,否则就被替换为?,于是就产生了乱码。
比如如果数据库字符集是WE8MSWIN1252时,即使NLS_LANG设置相同,数据库也不能正确的存储汉字。


四 常用的转换方法
通常使用dblink,并组合utl_raw.cast_to_raw以及utl_raw.cast_to_varchar2来转化,具体步骤请参考ORACLE公司的文档。

2008年12月9日星期二

日期数据应该以什么数据类型存储

日期类型应该存储成什么数据一直是很多人容易弄错的问题。总的来说,在各种数据库中日期类型都应该存储成date类型。对于mysql和postgresql来说,存储成日期类型是没有问题的;对于ORACLE数据库来说,日期类型有其特殊的问题。因为ORACLE数据库的date数据类型的精度是到秒,因此如果只是需要精确到天的话,处理起来要麻烦一点。
mysql和pgsql可以使用如下语句进行查询:

select * from emp where birth_day=a date;

但是ORACLE里就不能这么写查询,必须写成:

select * from emp where birth_day between to_date(datestr||' 00:00:00','yyyy/mm/dd hh24:mi:ss') and to_date(datestr||'23:59:59','yyyy/mm/dd hh24:mi:ss') ;

注意不要用这种写法:

select * from emp where birth_day>=to_date(datestr||' 00:00:00','yyyy/mm/dd hh24:mi:ss') and birth_day<= to_date(datestr||'23:59:59','yyyy/mm/dd hh24:mi:ss') ;

这种写法有其本身的问题。

在这种情况下很多人会直接这样写查询(因为between写起来麻烦一点):

select * from where datestr=to_char(birth_day,'yyyy/mm/dd');

熟悉数据库的人都知道这么写会给数据库带来多大的影响,即使加fbi。

因此很多人为了省事,就直接在数据库中把日期型数据存储成为字符型或者数字型(高丽棒子就常这么做),比如:

create table t1 (tid number,birth_day date);
create table t2 (tid number,birth_day char(8));
create table t3 (tid number,birth_day number(8));

insert into t1 select rownum,sysdate-rownum from all_objects where rownum<=365*3;
insert into t2 select rownum,to_char(sysdate-rownum,'yyyymmdd') from all_objects where rownum<=365*3;
insert into t3 select rownum,to_number(to_char(sysdate-rownum,'yyyymmdd')) from all_objects where rownum<=365*3;

commit;

create index t1_bd_idx on t1(birth_day);
create index t2_bd_idx on t2(birth_day);
create index t3_bd_idx on t3(birth_day);

set autot traceonly explain

执行以下查询:

select * from t1 where birth_day between to_date('20070101','yyyymmdd') and to_date('20071231','yyyymmdd');
因为有三分之一的数据会命中,所以ORACLE肯定会使用全表扫描,执行计划中基数是365,成本是3。

select * from t2 where birth_day between '20070101' and '20071231';
这个查询其实应该和上一个查询命中同样多的数据,有同样的执行计划。但是很遗憾,ORACLE执行了索引范围扫描,然后通过ROWID再拿到数据。整个查询中基数是43,成本是3,(这只是优化器估算出来的成本,实际的成本应该是26)。

select * from t3 where birth_day between 20070101 and 20071231;
这个查询跟上一个查询有同样的执行计划。

很显然后两个查询的执行计划是有问题的。这个问题就是由于错误的数据类型导致ORACLE在估算数据的范围的时候出了差错,比如第三个查询数据库的估算方法是:
(20071231-20070101)--条件里范围差
/(20081210-20051210) --实际数据的范围差
*1095(表里的行数)
最后的出来的基数是42。字符类型的计算也与此类似。
当然如果在表上建立了直方图的话,情况会好一些,成本估算所得的基数与实际基数近似。

由此可见将日期类型存储成字符或者数值类型会有很大问题,虽然程序员在处理问题时方便了一点,但是数据库的性能确会遭到影响(程序员都喜欢这么设计数据库表)。
另外由于pgsql和mysql的优化查询算法是不完全与ORACLE类似,因此这种设计表的方法在这两种数据库上运行的如何有待继续研究。

有问题欢迎与我联系。

JAVA语言学校的危险性

转自CSDN,原文请看这里

下面的文章是More Joel on Software一书的第8篇。

我觉得翻译难度很大,整整两个工作日,每天8小时以上,才译出了5000字。除了Joel大量使用俚语,另一个原因是原文涉及“编程原理”,好多东西我根本不懂。希望懂的朋友帮我看看,译文有没有错误,包括我写的注解。

====================
JAVA语言学校的危险性
作者:Joel Spolsky
译者:阮一峰
原文: http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html
发表日期 2005年12月29日,星期四


如今的孩子变懒了。
多吃一点苦,又会怎么样呢?

我一定是变老了,才会这样喋喋不休地抱怨和感叹“如今的孩子”。为什么他们不再愿意、或者说不再能够做艰苦的工作呢。
当我还是孩子的时候,学习编程需要用到穿孔卡片(punched cards)。那时可没有任何类似“退格”键(Backspace key)这样的现代化功能,如果你出错了,就没有办法更正,只好扔掉出错的卡片,从头再来。

回想1991年,我开始面试程序员的时候。我一般会出一些编程题,允许用任何编程语言解题。在99%的情况下,面试者选择C语言。

如今,面试者一般会选择Java语言。

说到这里,不要误会我的意思。Java语言本身作为一种开发工具,并没有什么错。、

等一等,我要做个更正。我只是在本篇特定的文章中,不会提到Java语言作为一种开发工具,有什么不好的地方。事实上,它有许许多多不好的地方,不过这些只有另找时间来谈了。

我在这篇文章中,真正想要说的是,总的来看,Java不是一种非常难的编程语言,无法用来区分优秀程序员和普通程序员。它可能很适合用来完成工作,但是这个不是今天的主题。我甚至想说,Java语言不够难,其实是它的特色,不能算缺点。但是不管怎样,它就是有这个问题。

如果我听上去像是妄下论断,那么我想说一点我自己的微不足道的经历。大学计算机系的课程里,传统上有两个知识点,许多人从来都没有真正搞懂过的,那就是指针(pointers)和递归(recursion)。

你进大学后,一开始总要上一门“数据结构”课(data structure), 然后会有线性链表(linkedlist)、哈希表(hashtable),以及其他诸如此类的课程。这些课会大量使用“指针”。它们经常起到一种优胜劣汰的作用。因为这些课程非常难,那些学不会的人,就表明他们的能力不足以达到计算机科学学士学位的要求,只能选择放弃这个专业。这是一件好事,因为如果你连指针很觉得很难,那么等学到后面,要你证明不动点定理(fixed point theory)的时候,你该怎么办呢?

有些孩子读高中的时候,就能用BASIC语言在AppleII型个人电脑上,写出漂亮的乒乓球游戏。等他们进了大学,都会去选修计算机科学101课程,那门课讲的就是数据结构。当他们接触到指针那些玩意以后,就一下子完全傻眼了,后面的事情你都可以想像,他们就去改学政治学,因为看上去法学院是一个更好的出路[1]。关于计算机系的淘汰率,我见过各式各样的数字,通常在40%到70%之间。校方一般会觉得,学生拿不到学位很可惜,我则视其为必要的筛选,淘汰那些没有兴趣编程或者没有能力编程的人。

对于许多计算机系的青年学生来说,另一门有难度的课程是有关函数式编程(functionalprogramming)的课程,其中就包括递归程序设计(recursiveprogramming)。MIT将这些课程的标准提得很高,还专门设立了一门必修课(课程代号6.001[2]),它的教材(Structureand Interpretation of Computer Programs,作者为Harold Abelson和Gerald JaySussmanAbelson,MIT出版社1996年版)被几十所、甚至几百所著名高校的计算系机采用,充当事实上的计算机科学导论课程。(你能在网上找到这本教材的旧版本,应该读一下。)

这些课程难得惊人。在第一堂课,你就要学完Scheme语言[3]的几乎所有内容,你还会遇到一个不动点函数(fixed- pointfunction),它的自变量本身就是另一个函数。我读的这门导论课,是宾夕法尼亚大学的CSE121课程,真是读得苦不堪言。我注意到很多学生,也许是大部分的学生,都无法完成这门课。课程的内容实在太难了。我给教授写了一封长长的声泪俱下的Email,控诉这门课不是给人学的。宾夕法尼亚大学里一定有人听到了我的呼声(或者听到了其他抱怨者的呼声),因为如今这门课讲授的计算机语言是Java。

我现在觉得,他们还不如没有听见呢。

这就是争议所在。许多年来,像当年的我一样懒惰的计算机系本科生不停地抱怨,再加上计算机业界也在抱怨毕业生不够用,这一切终于造成了重大恶果。过去十年中,大量本来堪称完美的好学校,都百分之百转向了Java语言的怀抱。这真是好得没话说了,那些用“grep”命令[4]过滤简历的企业招聘主管,大概会很喜欢这样。最妙不可言的是,Java语言中没有什么太难的地方,不会真的淘汰什么人,你搞不懂指针或者递归也没关系。所以,计算系的淘汰率就降低了,学生人数上升了,经费预算变大了,可谓皆大欢喜。

学习Java语言的孩子是幸运的,因为当他们用到以指针为基础的哈希表时,他们永远也不会遇到古怪的“段错误”[5] (segfault)。他们永远不会因为无法将数据塞进有限的内存空间,而急得发疯。他们也永远不用苦苦思索,为什么在一个纯函数的程序中,一个变量的值一会保持不变,一会又变个不停!多么自相矛盾啊!

他们不需要怎么动脑筋,就可以在专业上得到4.0的绩点。

我是不是有点太苛刻了?就像电视里的“四个约克郡男人”[6](Four Yorkshiremen)那样,成了老古板?就在这里吹嘘我是多么刻苦,完成了所有那些高难度的课程?

我再告诉你一件事。1900年的时候,拉丁语和希腊语都是大学里的必修课,原因不是因为它们有什么特别的作用,而是因为它们有点被看成是受过高等教育的人士的标志。在某种程度上,我的观点同拉丁语支持者的观点没有不同(下面的四点理由都是如此):“(拉丁语)训练你的思维,锻炼你的记忆。分析拉丁语的句法结构,是思考能力的最佳练习,是真正对智力的挑战,能够很好地培养逻辑能力。”以上出自Scott Barker之口(http://www.promotelatin.org/whylatin.htm)。但是,今天我找不到一所大学,还把拉丁语作为必修课。指针和递归不正像计算机科学中的拉丁语和希腊语吗?

说到这里,我坦率地承认,当今的软件代码中90%都不需要使用指针。事实上,如果在正式产品中使用指针,这将是十分危险的。好的,这一点没有异议。与此同时,函数式编程在实际开发中用到的也不多。这一点我也同意。

但是,对于某些最激动人心的编程任务来说,指针仍然是非常重要的。比如说,如果不用指针,你根本没办法开发Linux的内核。如果你不是真正地理解了指针,你连一行Linux的代码也看不懂,说实话,任何操作系统的代码你都看不懂。

如果你不懂函数式编程,你就无法创造出MapReduce[7],正是这种算法使得Google的可扩展性(scalable)达到如此巨大的规模。单词“Map”(映射)和“Reduce”(化简)分别来自Lisp语言和函数式编程。回想起来,在类似6.001这样的编程课程中,都有提到纯粹的函数式编程没有副作用,因此可以直接用于并行计算(parallelizable)。任何人只要还记得这些内容,那么MapRuduce对他来说就是显而易见的。发明MapReduce的公司是Google,而不是微软,这个简单的事实说出了原因,为什么微软至今还在追赶,还在试图提供最基本的搜索服务,而Google已经转向了下一个阶段,开发世界上最大的并行式超级计算机——Skynet[8]的H次方的H次方的H次方的H次方的H次方的H次方。我觉得,微软并没有完全明白,在这一波竞争中它落后多远。

除了上面那些直接就能想到的重要性,指针和递归的真正价值,在于那种你在学习它们的过程中,所得到的思维深度,以及你因为害怕在这些课程中被淘汰,所产生的心理抗压能力,它们都是在建造大型系统的过程中必不可少的。指针和递归要求一定水平的推理能力、抽象思考能力,以及最重要的,在若干个不同的抽象层次上,同时审视同一个问题的能力。因此,是否真正理解指针和递归,与是否是一个优秀程序员直接相关。

如果计算机系的课程都与Java语言有关,那么对于那些在智力上无法应付复杂概念的学生,就没有东西可以真的淘汰他们。作为一个雇主,我发现那些100%Java教学的计算机系,已经培养出了相当一大批毕业生,这些学生只能勉强完成难度日益降低的课程作业,只会用Java语言编写简单的记账程序,如果你让他们编写一个更难的东西,他们就束手无策了。他们的智力不足以成为程序员。这些学生永远也通不过MIT的6.001课程,或者耶鲁大学的 CS323课程。坦率地说,为什么在一个雇主的心目中,MIT或者耶鲁大学计算机系的学位的份量,要重于杜克大学,这就是原因之一。因为杜克大学最近已经全部转为用Java语言教学。宾夕法尼亚大学的情况也很类似,当初CSE121课程中的Scheme语言和ML语言,几乎将我和我的同学折磨至死,如今已经全部被Java语言替代。我的意思不是说,我不想雇佣来自杜克大学或者宾夕法尼亚大学的聪明学生,我真的愿意雇佣他们,只是对于我来说,确定他们是否真的聪明,如今变得难多了。以前,我能够分辨出谁是聪明学生,因为他们可以在一分钟内看懂一个递归算法,或者可以迅速在计算机上实现一个线性链表操作函数,所用的时间同黑板上写一遍差不多。但是对于Java语言学校的毕业生,看着他们面对上述问题苦苦思索、做不出来的样子,我分辨不出这到底是因为学校里没教,还是因为他们不具备编写优秀软件作品的素质。PaulGraham将这一类程序员称为“Blub程序员”[9](www.paulgraham.com/avg.html)。

Java语言学校无法淘汰那些永远也成不了优秀程序员的学生,这已经是很糟糕的事情了。但是,学校可以无可厚非地辩解,这不是校方的错。整个软件行业,或者说至少是其中那些使用grep命令过滤简历的招聘经理,确实是在一直叫嚷,要求学校使用Java语言教学。

但是,即使如此,Java语言学校的教学也还是失败的,因为学校没有成功训练好学生的头脑,没有使他们变得足够熟练、敏捷、灵活,能够做出高质量的软件设计(我不是指面向对象式的“设计”,那种编程只不过是要求你花上无数个小时,重写你的代码,使它们能够满足面向对象编程的等级制继承式结构,或者说要求你思考到底对象之间是“has-a”从属关系,还是“is-a”继承关系,这种“伪问题”将你搞得烦躁不安)。你需要的是那种能够在多个抽象层次上,同时思考问题的训练。这种思考能力正是设计出优秀软件架构所必需的。

你也许想知道,在教学中,面向对象编程(object-orientedprogramming,缩写OOP)是否是指针和递归的优质替代品,是不是也能起到淘汰作用。简单的回答是:“不”。我在这里不讨论OOP的优点,我只指出OOP不够难,无法淘汰平庸的程序员。大多数时候,OOP教学的主要内容就是记住一堆专有名词,比如“封装”(encapsulation)和“继承”(inheritance)”,然后再做一堆多选题小测验,考你是不是明白“多态”(polymorphism)和“重载”(overloading)的区别。这同历史课上,要求你记住重要的日期和人名,难度差不多。OOP不构成对智力的太大挑战,吓不跑一年级新生。据说,如果你没学好OOP,你的程序依然可以运行,只是维护起来有点难。但是如果你没学好指针,你的程序就会输出一行段错误信息,而且你对什么地方出错了毫无想法,然后你只好停下来,深吸一口气,真正开始努力在两个不同的抽象层次上,同时思考你的程序是如何运行的。

顺便说一句,我有充分理由在这里说,那些使用grep命令过滤简历的招聘经理真是荒谬可笑。我从来没有见过哪个能用Scheme语言、 Haskell语言和C语言中的指针编程的人,竟然不能在二天里面学会Java语言,并且写出的Java程序,质量竟然不能胜过那些有5年Java编程经验的人士。不过,人力资源部里那些平庸的懒汉,是无法指望他们听进去这些话的。
再说,计算机系承担的发扬光大计算机科学的使命该怎么办呢?计算机系毕竟不是职业学校啊!训练学生如何在这个行业里工作,不应该是计算机系的任务。这应该是社区高校和政府就业培训计划的任务,那些地方会教给你工作技能。计算机系给予学生的,理应是他们日后生活所需要的基础知识,而不是为学生第一周上班做准备。对不对?

还有,计算机科学是由证明(递归)、算法(递归)、语言(λ演算[10])、操作系统(指针)、编译器(λ演算)所组成的。所以,这就是说那些不教C语言、不教Scheme语言、只教Java语言的学校,实际上根本不是在教授计算机科学。虽然对于真实世界来说,有些概念可能毫无用处,比如函数的科里化(functioncurrying)[11],但是这些知识显然是进入计算机科学研究生院的前提。我不明白,计算机系课程设置委员会中的教授为什么会同意,将课程的难度下降到如此低的地步,以至于他们既无法培养出合格的程序员,甚至也无法培养出合格的能够得到哲学博士PhD学位[12]、进而能够申请教职、与他们竞争工作岗位的研究生。噢,且慢,我说错了。也许我明白原因了。

实际上,如果你回顾和研究学术界在“Java大迁移”(Great Java Shift)中的争论,你会注意到,最大的议题是Java语言是否还不够简单,不适合作为一种教学语言。

我的老天啊,我心里说,他们还在设法让课程变得更简单。为什么不用匙子,干脆把所有东西一勺勺都喂到学生嘴里呢?让我们再请助教帮他们接管考试,这样一来就没有学生会改学“美国研究”[13](Americanstudies)了。如果课程被精心设计,使得所有内容都比原有内容更容易,那么怎么可能期望任何人从这个地方学到任何东西呢?看上去似乎有一个工作小组(Java taskforce)正在开展工作,创造出一个简化的Java的子集,以便在课堂上教学[14]。这些人的目标是生成一个简化的文档,小心地不让学生纤弱的思想,接触到任何EJB/J2EE的脏东西[15]。这样一来,学生的小脑袋就不会因为遇到有点难度的课程,而感到烦恼了,除非那门课里只要求做一些空前简单的计算机习题。

计算机系如此积极地降低课程难度,有一个理由可以得到最多的赞同,那就是节省出更多的时间,教授真正的属于计算机科学的概念。但是,前提是不能花费整整两节课,向学生讲解诸如Java语言中int和Integer有何区别[16]。好的,如果真是这样,课程6.001就是你的完美选择。你可以先讲Scheme语言,这种教学语言简单到聪明学生大约只用10分钟,就能全部学会。然后,你将这个学期剩下的时间,都用来讲解不动点。

唉。

说了半天,我还是在说要学1和0。

(你学到了1?真幸运啊!我们那时所有人学到的都是0。)

================
注解:
[1] 在美国,法学院的入学者都必须具有本科学位。通常来说,主修政治学的学生升入法学院的机会最大。
[2] 在麻省理工学院,计算机系的课程代码都是以6开头的,6.001表明这是计算机系的最基础课程。
[3] Scheme语言是LISP语言的一个变种,诞生于1975年的MIT,以其对函数式编程的支持而闻名。这种语言在商业领域的应用很少,但是在计算机教育领域内有着广泛影响。
[4] grep是Unix/Linux环境中用于搜索或者过滤内容的命令。这里指的是,某些招聘人员仅仅根据一些关键词来过滤简历,比如本文中的Java。
[5] 段错误(segfault)是segmentation fault的缩写,指的是软件中的一类特定的错误,通常发生在程序试图读取不允许读取的内存地址、或者以非法方式读取内存的时候。
[6] 《四个约克郡男人》(Four Yorkshiremen),是英国电视系列喜剧At Last the 1948 Show中的一部,与上个世纪70年代问世。内容是四个约克郡男人竞相吹嘘,各自的童年是多么困苦,由于内容太夸张,所以显得非常可笑。
[7] MapReduce是一种由Google引入使用的软件框架,用于支持计算机集群环境下,海量数据(PB级别)的并行计算。
[8] Skynet是美国系列电影《终结者》(Terminator)中一个控制一切、与人类为敌的超级计算机系统的名称,通常将其看作虚构的人工智能的代表。
[9] Blub程序员(Blub programmers)指的是那些企图用一种语言,解决所有问题的程序员。Blub是Paul Graham假设的一种高级编程语言。
[10] λ演算(lambda calculus)是一套用于研究函数定义、函数应用和递归的形式系统,在递归理论和函数式编程中有着广泛的应用。
[11] 函数的科里化(function currying)指的是一种多元函数的消元技巧,将其变为一系列只有一元的链式函数。它最早是由美国数学家哈斯格尔·科里(Haskell Curry)提出的,因此而得名。
[12] 在美国,所有基础理论的学科,一律授予的都是哲学博士学位(Doctor of Philosophy),计算机科学系亦是如此。
[13] 美国研究(American studies)是对美国社会的经济、历史、文化等各个方面进行研究的一门学科。这里指的是,计算机系学生不会因为课程太难被淘汰,所以就不用改学相对容易的“美国研究”。
[14] 参见http://www.sigcse.org/topics/javataskforce/java-task-force.pdf。
[15] J2EE是Java2平台企业版(Java 2 Platform,EnterpriseEdition),指的是一整套企业级开发架构。EJB(EnterpriseJavaBean)属于J2EE的一部分,是一个基于组件的企业级开发规范。它们通常被认为是Java中相对较难的部分。
[16] 在Java语言中,int是一种数据类型,表示整数,而Integer是一个适用于面向对象编程的类,表示整数对象。两者的涵义和性质都不一样。
(完)

2008年12月8日星期一

抵制


大家又要叫嚣抵制法国了,实在看不出来有什么必要。而且政府取消给法国的大单,看起来风光无限,其实也未必是那么回事。大家也都明白,中国的经济增长主要靠出口,你能制裁别人,别人也能制裁你。我们可以不买空客,但是转手还是得买波音。法国人不从中国买纺织品,也可以从越南,南美一些国家买(这些国家的东西可能更便宜)。到头来可能还是自己的企业吃亏。政府打脸充胖子,自己作孽却要老百姓来承受。

到底该抵制什么?

手把手教你如何抵制法国货

转帖+补充

1.坚决不当法国总统。

2.去爱丽舍宫门口撒尿。

3.坚决不坐国航、南航、东航、海航、厦航、川航。。。。他们居然用法国的空客,靠。以后乘飞机只坐波音,不坐空客。

4.坚决不用中国移动、中国联通、中国电信,他们的基站不知道用了多少法国阿尔卡特的东西,靠。

5. 坚决不看《巴黎圣母院》、《悲惨世界》、《哲学原理》、《哲学通信》、《人间喜剧》、《鼠疫》、《基督山伯爵》、《社会契约论》、《论法的精神》、《茶花女》、《约翰;克利斯朵夫》、《基督山伯爵》、《包法利夫人》、《思想录》、《西方哲学史》、《存在与虚无》、《论美国的民!主》、《忏悔录》、《三个火枪手》、《爱弥儿》、《新爱洛伊丝》、《人间喜剧》、《羊脂球》、《红与黑》、《巨人传》

6.坚决不看吕克贝松,苏菲玛索,阿兰德龙的电影。

7.不准再用直角坐标系,一律改用极坐标系。

今后算极限的时候不准再用罗必塔法则,全部用定义硬算!

发明一种新的数学方法代替傅里叶变换。

不准再用泊松方程和泊松积分,一律用F分布代替泊松分布。

将拉格朗日中值定理,拉格朗日插值多项式,四平方和定理剔除出《高等数学》

坚决不学概率论,

把拉普拉斯方程踢出数学物理方程


8 将法国的绘画,音乐全部从课本中删除,并且雇用百度来屏蔽所有和法国相关的查询。

9 抵制国际歌

10 附带着抵制马克思,恩格斯,以及共产主义。

11 砍光全国的法国梧桐。

.........................

2008年12月7日星期日

ORACLE使用索引的一个小问题

其实从9i以后ORACLE就一直使用CBO,如果说ORACLE要使用索引的话,只有两个条件:

一,查询过滤条件所在字段上有索引

二,使用索引扫描的成本低于表扫描成本

前两天有人举例子讲ORACLE在什么情况下使用索引,举的例子是:

create table t1 (tid number(1),tidc varchar2(1));
insert /*+append* into t1 select rownum,rownum from all_objects where rownum<10;
create index i1 on t1(tid);

这时一共有九行数据,在数据文件里应该不会超过1个数据块。他本来想演示在加函数的情况下,索引会无法使用。但是这时发现即使用以下查询依然不会使用索引:
select * from t1 where tid=1;

于是奇怪之,删掉索引,重新建了一个唯一索引,这时数据库才能正常使用索引(在做范围扫描的时候肯定会失效)。

他的查询没有使用索引的原因在于他没有对表进行分析,优化器拿到了错误的信息,因此觉得全表扫描更划算(当然在这种情况下实际上全表扫描更划算一些)。

但是按我的估计,ORACLE在执行这个查询的时候,什么情况下都不该使用索引。因为在只有9行数据(占一个块)的情况下,全表扫描更划算。后来对表做了分析,发现t4表占据了5个数据块(可能是因为元数据的关系,需要查证)(pct_free10,pct_used90),这样的话就可以解释为什么数据库会选择使用索引了。因为全表扫描需要扫描五个块,而索引扫描只需要两个块。

只是我不清楚,ORACLE在计算成本的时候是不是应该将包含表的元数据的数据块抛去不算,这样的话计算才正确一些。

oracle中国家语言支持的索引问题

ORACLE数据库中可以使用国家语言支持,但是好像索引效率有点问题,在估算选择基数的时候会估算错误,甚至有可能会产生错误的执行计划。

create table t1 (v1 nvarchar2(20)) ;
create index i1 on t1(v1);
insert into t1 select lpad(rownum,20,0) from all_objects;
create index i1 on t1()

create table t2 (v1 varchar2(20))
create index i2 on t1(v1);
insert into t2 select lpad(rownum,20,0) from all_objects;

dbms_stats.gather_table_Stats(user,'T1',cascade=>true,method_opt=>'for all columns size 254');

dbms_stats.gather_table_Stats(user,'T2',cascade=>true,method_opt=>'for all columns size 254');

然后:

set autot traceonly explain

select * from t1 where v1='00000000000000000001';

这时得到的成本是3,基数是100

select * from t2 where v1='00000000000000000001';

这时得到的成本是1,基数是1

运行select * from user_tab_histograms where table_name='T1';
select * from user_tab_histograms where table_name='T2';

比较endpoint_actual_value,可以发现对t1和t2的不同,t1只对前16个字符进行了记录。因此在索引扫描的时候会得出错误的选择率。(有99个值满足条件,但是索引有两层,因此基数是100)

测试发现ORACLE只有在使用nchar,nvarchar2这些类型的列时才会出现这种问题。即使把数据库字符集也改成UTF8编码,数据库在处理char,varchar2这些类型的列时,依然不会出错。看来ORACLE是根据列类型来进行判断,而跟数据库字符集没关系。

在过滤条件里,可以看到v1=U'00000000000000000001',可能是ORACLE对这个字符串做了处理,将其转化为UTF8编码。(我没用启动10053事件,不确定ORACLE具体做了什么)

如果所建的索引是唯一索引:

select * from t1 where v1='00000000000000000001';

这时得到的成本是2,基数是1

select * from t2 where v1='00000000000000000001';

这时得到的成本是1,基数是1

这时基数是正确的,但是成本还是高(成本高的原因可能在于字符编码的转化)。

再看以下情况:

create table t3 (v1 varchar2(40));
create index i3 on t1(v1);
insert into t3 select lpad(rownum,40,0) from all_objects;

dbms_stats.gather_table_Stats(user,'T3',cascade=>true,method_opt=>'for all columns size 254');

这时得到的成本是64,基数是38744(整个表里的数据)。如果大概估算的话,可能ORACLE只会对前32b的数据进行统计。

select * from t2 where v1='0000000000...0000000001';

如果建唯一索引的话,就又可以得到成本是2,基数是1。这也是唯一索引的好处,在等值查询时可以唯一扫描。不过我们不能指望唯一索引的这种特性,运行下列查询:


select * from t2 where v1 between '0000000000...0000000001' and '0000000000...0000000010';

这时得到的成本是64,基数是38744。

在建表的时候选择合适的数据类型真的很重要!

Fedora10下VIM的BUG

fedora10已经出来两个礼拜了,今天升级到F10。值得高兴的是ORACLE DB和Postgresql可以继续使用,本来这两个数据库是我最担心的,一直怕从F9升级后不能使用了。升级后发现F10还是很好用,比F9的bug感觉少一些。只是由于我从F9直接升级F10,所以发现了一点VIM的问题。
升级后运行vi/vim/gvim会报以下错误:
vim: error while loading shared libraries: libgpm.so.1: cannot open shared object file: No such file or directory
原因是没有找到libgpm.so.1共享库。
解决方法:
ln /usr/lib/libgpm.so /usr/lib/libgpm.so.1

2008年11月26日星期三

方文山北大演讲:华人是一个没有质感的民族

转摘自独角兽咨询 http://www.unicornblog.cn/user1/unicornblog/14240.html
方文山北大演讲:华人是一个没有质感的民族
独角兽资讯 发表于 2008-2-8 0:54:00
很高兴来到北京大学和各位同学进行这场文化上的交流与经验的分享。我今天所要演讲的题目是:“一个没有质感的民族。”我在这里所指的民族,不是别人,就是我们华人自己。而华人一词的称谓泛指包括内地、台湾、香港、星马、与东南亚一带的传统华侨,以及美加地区的华裔新移民等。

  我想可能很多同学都已注意到,我写了很多所谓“中国风”的歌词作品,譬如《娘子》、《双截棍》、《爷爷泡的茶》、《东风破》,还有最近的《菊花台》、《千里之外》等。其实这些歌词中浓厚的古诗词韵味并不是偶然间产生的,作品是最能够反映一个人的价值观跟性格的。一直以来我就是一个民族意识很重的人,长期性的关注跟民族、传统、与文化相关联的议题。当我这种文化意识跟音乐相结合起来时,就自然而然的孕育出所谓“中国风”的歌词,这是首先跟大家解释为何我会创作出如此倾向的作品。

  现在开始进入我们的主题。我长期以来观察到一个现象,那就是,我认为我们华人并不是一个很有“质感”的民族。所谓的“质感”指的就是一个东西的材质、与它外观上的美学设计,及其整体的精致度。我们会喜欢买一些价钱偏高的名牌包,譬如LV、Gucci等,是因为直觉的就喜欢那些名牌所代表的价值与品味。一般人总是很自然的会去追求名牌衣饰的质感,但却忽略了买此类商品的人,其本身却往往没有什么质感。我对台湾比较熟,所以现主要以我在台湾所观察到的现象举例。像一些在北市闹区逛街的年轻女生,很多时候的穿着并不是很协调:她可能上身套一件优雅的洋装,下半身却穿着嘻哈风的牛仔裤,然后脚上硬生生的一双廉价的夹趾拖鞋。当然此类的造型,某些时候美其名为混搭,问题是,大部份的人都搭配的很没有风格。常常自己的穿着品味的调性并不具备一致性,没有基本的美学素养,但却盲目地去追求名牌,这是精神错乱式的荒谬。大部份的消费者在挑选商品时,都懂得会去追求衣服、皮包、汽车等物品的质感。因为美的东西其价值是有共通性的,没有人不喜欢美的东西。但大家在拼命追求商品外在质感的同时,却常常忽略了自身对穿着打扮很基本的美学素养,变成需借助与依赖这些名牌的材质去衬托自己的行情。

  举例来说,以台湾人对婚嫁与丧礼等此类人生大事的态度来看,台湾人对美的基本尊重,便已严重的不及格。那是个应该要很庄严的场合,但来参加的来宾中通常都一定会有人直接就穿夹趾拖鞋或是汗衫来。不管是尊重已往生的死者,或对婚礼上正沐浴喜悦中的男女双方而言,穿载整齐是合乎礼的一个最起码的要求,但大多数的人却不这么严肃看待。我们再观看时下流行音乐的MTV,或是正在热门档期中的国片,如果你有心观察的话你会发现,明明就是华人的电影、华人的歌手,但只要有婚礼或葬礼出现的场景就一定是西式的,因为大家都觉得西式丧礼比较肃穆,而西式的婚礼的确也比较浪漫。要拍唯美的婚礼就一定上教堂,尽管男女主角并不是基督徒。难道是因为大家都觉得西式的婚丧形式比较有美,比较适合戏剧上的表现吗?台湾很多电影和MV的场景,都喜欢用西式的婚礼和葬礼去呈现。莫非因为华人的婚礼的形式比较吵杂、丧礼的仪式比较低俗?所以比较不适合表现在强调美学构图,与故事张力的戏剧上!?

  现再以城市为例来说明,不禁要问为何大多数华人居住的地区都不是很有质感。城市建筑是国家形象最具体的象征。但在台北市,据我所知,大楼外观的建筑形式与色彩调性从来就没有被规范过。市区街上,每个店面的装潢都很有特色,看的出有某种美学的追求,都可以被独栋独店的欣赏。但你只要把镜头一拉开,一扩大,用较大的视野全方位的去看整条街、整个城市的轮廓,你就会发现根本就杂乱无章,城市基调混乱,房子盖的毫无章法。一个城市的建筑反映了一个民族的质感。有质感的东西是可以深入骨髓的,那是文化底蕴浓稠度的一种展现。当你犹如追星族去追求昂贵的舶来品时,实际上正是以某种自卑的心态去追求你自己认为有价值的东西,一种心理上的反差与补偿心理。而台北就像个浓妆艳抹下的模特儿,急于讨好国际上的眼光,却根本就已模糊了自己原本的面貌,完全没有了自己的风格。

  台湾的一些老旧建筑,比如传统的四合院、日据时期留下的官舍等。这些建筑如果屋瓦毁损了,一般就用很简单很便宜的铁片去搭盖。很少有人会按原先建筑的形式,用原来的建材、风格去修复,这其实就是对传统文化的一种漠视,因为从没有人认为恢复古厝的外观是一件很重要的事,也几乎没人实际这么做。四年前当我初次来北京时,我发现北京很多胡同、四合院都在拆,因为要迎接奥运。那时候我和出版社的人去那些待拆的胡同捡拾门牌,比如刻有“某某胡同”的门牌,我喜欢那种带着旧时记忆象征性的小东西。虽然知道拆掉那些老旧胡同是为了整个城市建设的需要,可我心里还是觉得很可惜。因为“胡同”是北京的城市建筑的象征,也是一种有形的文化资产。当观光客来到上海、香港、台北,北京等华人聚集的城市闹区时,只要不把镜头对准那些具有象征性的地标,你根本就分不清楚你现在位于哪个城市,因为它门所有建筑结构都是西式的,所有的大楼都是玻璃帷幕,或钢骨结构,每个城市的景观都很像。上述那些城市都只有林立的建筑而没有城市文化。胡同是北京的一种象征,外地人来北京看到胡同会感受到一种文化的具体表现。当然,不拆这些胡同也无法大幅度地去建设或改进交通,这也是两难。其实刚开始我老觉得那些旧建筑拆了可惜,但后来发现如果不拆,它整体看起来也是破破旧旧的,散发一种家道中落破败的感觉,一个衰弱的王朝的象征,外观老旧斑驳,也并不是那么吸引人。那到底哪里出了问题呢?是我们传统的建筑形式根本就不适合现代生活的需要吗?

 我小时候看过一些欧洲小镇的风景明信片,当时以为那只是为了拍照印刷好看,而特地去找来很罕见的景致,目的也只是为了让画面更赏心悦目。一直到后来才知道风景明信片里的村庄是真的都有人居住,那不是欧洲版的中影文化城、他们是一整个村庄、一整个城市,甚至整个国家的风景都是如此的美。我以为明信片上美丽的风景只是在取景时回避掉某些角度,只单拍这个景(比如一座古堡、一段城墙),就像美编过的电影海报一样。后来看了一些在欧洲取景的电影,藉由镜头 360度的回转,发现他们是整座村庄、整个城市都是一种统一的色调与建筑风格。台湾的老建筑通常只是得到“点”的保留。所谓的“点”就是一栋独立的建筑物被评为三级古迹,但附近就只有它孤零零的一栋古迹而已,在它旁边很可能是一栋钢筋水泥的玻璃帷幕大楼。古迹旁边的景观视野通常并没有被重视与限建。所以对古迹的保护,很难形成一条线、一个面。当然还有个更重要的原因是,台湾根本也没有年代久远且数量庞大到能够让人连成一条街的古建筑群,更别说能够形成一个像村镇一样面积的古城区,这是做梦都不会发生的事。

 欧洲有很多中世纪的古建筑,它数量庞大到轻易就能连成一条街,以及一个构成一个村庄。欧洲对于城市建筑的外在色调,很重视其一致性,在盖新的建筑时,会考虑到建物本身与周遭环境的配合,譬如建物外观的颜色、风格,不能太突兀。整个城市的规划和外形是一致的。像捷克首都布拉格的城市基本调性,就是红瓦、白墙,整齐的天际线,犹如童话世界一般。而同样都是老建筑,北京胡同的高度却都只有一楼高,就算不拆,外观上也很低矮,不会很新颖。北京胡同的基调就是灰色的,屋顶的瓦与墙上的砖都是同一种色调,一种保守而没有生气的“灰 ”。

 一个民族美学的素养与人的质感是反映在各个方面的。在台湾,有些人就直接在路边摊上狼吞虎咽的吃起东西。你说这是风土人情也罢,说是生活习惯也行,但在路边摊翘起二郎腿来吃东西,而旁边就有人在洗碗筷。其实既不卫生也不美观。但大多数的人都认为无所谓,也不觉得哪里不对劲。问题是,如此一来,街道就像是工寮,整个城市的景观就被破坏了。还有很多民众家里私人的装潢空间都很讲究设计感,布置的很漂亮,但只要一出了门,到了公有地,就不再会去关心周遭环境的景观。那种心态好像只要是公众的利益,大家就默不关心,自私的心态赤裸裸的显露无遗。这现象好像还不只有台北会发生,香港、上海,以及北京等地,居民的心态与状况也没有改善到哪里去。大部分人不会去在意城市整体景观的维护与协调,甚至花一点时间做环保义工都不愿意,但对于私人住宅,花再多钱装潢都毫不心软。  

 其实美学的鉴赏是需要从小到大一点一滴的教育,向下扎根,慢慢培养。这种公民意识的形成是需要花很长的时间去蕴酿,养成一整个民族的素质则更需要几百年,甚至上千年。对我而言,出国旅游是投资报酬率最高的行为,尤其是到历史悠久,人文荟萃的欧洲国家。因为他们整个民族花了几百年、甚至上千年时间建立起来的文明,你可能只需一、二万人民币就能浏览他们细心维护下的城市景观,那其实是很震摄人心的。

  第二次世界大战期间,布拉格的捷克守军为了维护古城的完整,宁愿选择投降,也不忍先人所耗费心力所建立的城市文明成为瓦砾。而几乎在同一时间,远在东方的中国,在抗战时期却做了完全不同的选择。当时湖南长沙聚集了许多战备物资,国民党为避免日军掠夺这些物资,但又不想浪费一兵一卒去守城,折损自己部队的战力。不待日军进攻,竟自行纵火焚烧整座长沙古城,引发中外著名的《文夕大火》。一个屹立数百年古色古香的长沙城就这样瞬间化为灰尘。当政者就为了保存自己的军力,还有所谓的面子,竟可毫不顾及历代祖先辛苦营建的心血。反观捷克的守军,宁愿当代的自己受辱投降,也不愿历代祖先的心血化为乌有。两相对比之下,不禁令人感概万分。完全不同文化背景所熏陶下的民族,对自身文化遗产的态度,及其所下的决定迥然不同, 当然命运也就不一样。

 我们的城市规划主要还是以建设、商业为目的去进行。台湾的老旧建筑汰旧换新的速度很快,现举台湾特有的“眷村”为例。所谓“眷村”就是当初跟国民党部队撤退到台湾去的那些军人与眷属所住的地方,其村落设置的地点遍布台湾各地。但政府从来就不认为那些眷村有保留的价值,因为它实际上也就四、五十年的历史,因为要百年以上的建物才够资格被认定为古迹。但如果现在连这种四、五十年的建物都不设法保留而一律拆毁的话,那到底台湾要怎么累积百年以上的建物呢?“眷村”它代表的是一个族群(外省人)共同的生活记忆。台湾的政府就是如此的不尊重住民的历史记忆,这是一个可以用来衡量为政者到底有没有“文化质感 ”的一个指标。

  有外国人说,台北是一个最没有自己风格与气质的城市。在台湾,任何人长大后回到十几二十年前的家乡,几乎都已找不到儿时的记忆空间,因为改建的速度很快。可能小时候看过的树、走过的桥、和邻居小孩玩耍的树林全部都因为改建而消失,几乎再也找不到共同记忆,因为共同记忆都被拆光了。建筑物本身所代表的历史记忆不被认为是有意义的。你能够想够想象你儿时生活过的地方,游戏过的公园、住过的小巷、吃过的小馆子,这些生活记忆十几二十年后都彻底的消失是个怎样的状况吗?而台湾每天都在重复一样的戏码,拆了又盖,盖了又拆。我想北京现阶段为了迎接奥运,老旧建筑拆得很快。

  在台湾,传统节日时很多人会来参加庙会的活动,比如迎神赛会、舞龙舞狮,但他们的穿著却很奇怪:上面的传统服装可能是唐衫或道袍,下面却是牛仔裤甚至是拖鞋。他没有意识到一个小细节的维护就是对传统的尊重。但这也不是台湾独有的现象,我透过报章杂志、与网络信息的了解,也很少看到内地有哪个城市的居民会集体在那个传统节日穿着传统服装出来一起庆祝。国外比如西班牙、法国、意大利等国家的传统节庆,整个小镇的居民都会不约而同自发性的穿载起传统服装,那画面很令人感动。

 现在大家拼命在讲全球化,但大家有没有想过,出国旅游什么最吸引你?就是那迥然不同的文化差异。去印度,看到印度教的僧侣在冥想;去西班牙,看到他们在斗牛;去南美洲感受桑巴舞;去日本看他们传统的樱花祭,就是那股异国的情调与神秘感在吸引着我们。如果全世界的建筑都盖一模一样,所有的人穿着打扮也都一个样,饮食、庆典、风土民情也不再有各自的特色,那其实已经没有观光的必要了,因为去到哪里都是一样的。所以就我的认知,全球化应该指的是经贸方面的交流,而绝对不应该是文化上的统一。

 台湾很多歌手都喜欢去国外拍MV,比如意大利的罗马、日本的北海道,拍出来都很宏伟、精致与耐看。但我却没有发现过有外国歌手的MV来台湾取景。因为我们的市容不整齐,整个镜头一拉开,这边可能是钢筋水泥的建筑,那边可能是四合院,景观的调性很不一致。我去了好几趟日本,日本人对传统聚落的维持一直很用心保存的良好,去那取景所拍出来的MV也很耐看。最讽刺的就是,比如青岛的租界区、上海的十里洋场了。这些曾被殖民者占据过的地方,都反而留下了为数不少而且很有质感的西式建筑。青岛这城市的天际线、上海外滩上林立的大楼都很漂亮。这不免有些讽刺,怎么那些占领时期所留下的建筑都保存得这么好?甚至已变成这些城市建筑里最美的元素。那我们是否应该感谢曾被占领过呢?否则那里来这么好的建筑艺术。   

 前一阵子我去北京王府井闲逛时看到一个外国教堂,教会对教堂的建筑外观通常都很讲究的,那整个教堂就维持的就很好。因为王府井的整条街都是近代才兴建起的西式办公大楼,看不到任何一栋有传统风格的中式建筑。虽然王府井号称是北京最古老的一条商业街,但我所看到最古老的建筑,却是那座外国教堂。除非有人告诉我这是曾经是清朝的最热闹的商业街,否则我根本就完全看不出来那里曾是所谓的百年老街。台湾状况也是如此,日本占领时期留下了很多漂亮的巴洛克建筑,台湾的一些重要的政府机关都还一直沿用这些建筑。这问题跟上海外滩一样都很反讽,台北市最精致的建筑元素也都是当初的占领者所留下来的。大家有没有想过到底问题出在那呢?莫非中式的古建筑不大气,建筑的格局与形式无法适应现代社会的要求!   

 就我的认知,新加坡可能是一个特外,它是一个由移民的华人所成立的国家,国家本身的历史不长但他们却很重视自己的传统文化,有些旧式的建筑保存得很好,他们认为那是国家的象征,甚至是观光旅游的资源。而且新加坡人大概是所有的华人中最守法的,其国民的法治观念很强,整体而言,素质较高。  

 常常我们旅游到了欧洲国家,你会发现他们都很注重与维护自己传统建筑的外观,当地住民也很重视自己独特的节庆。常在传统节日穿戴起自己国家的民族服装,整条街道、整个村庄的人都盛装出席。这点是自诩为文明古国的我们所不及的。我很少,甚至可说没有见过在什么中秋或端午节等传统节日看见我们华人会整个村庄的人特地穿着传统服饰去庆祝。我今天所谈到的都是一些自己的观察和心得,并非严谨的学术调查,因为我想认真的调查起来或许问题更严重。这些发生在你我身边随时随地都在上演的文化现象,各位同学不妨偶尔静下心来观察,绝对会让你有完全不一样的体悟。我一向很关注这些文化现象,有自己所坚持的价值观。后来我的文字因缘际会的跟杰伦的音乐相结合,所以才创作出那么多“中国风”的歌曲来,并且很荣幸的得到了许多人的肯定与喜爱。

 讲到质感的问题,在这里不得不提到日本这个国家。日本人很奇怪,他对中国人一直有种崇古蔑今的心态。他们极度崇拜古代的中国,像《西游记》、《封神榜》、《三国演义》、《水浒传》等典故,他们都很熟悉,甚至比中国内地的一些少数民族都还要熟悉。他们不仅崇拜而且还继承下了一些中华文化。比如日本的小学生到现在还有书道比赛,国家也很重视书法这项传统,也并不认为这是外国的东西。如果到东京街头,随便找个路人问他们关羽、张飞、孔子、孙悟空等,他们都知道是谁,甚至已经将他们吸收进自己文化里,并且消化成电动玩具、漫画等等。   

 我去过日本,发现他们对美学设计是很讲究的,整个城市里的居民穿着打扮都有很自己的风格,也就是有型。街道很干净,连一个排水沟的盖子都有城市的特有标志。整个街道的招牌、店面,都很素雅。他们很重视产品的包装;也很重视古建筑的维护。美学的观念很发达。如果说拿中国文化跟西方文明来强加比较,或许有人会说因为文化的源头不一样,思维本来就会不一样,因为一开始他们的建筑、绘画、音乐等文化的方向就跟我们不一样。如果你是这样认为的话。那么日本就是最好比较的对象了,一样受儒家学说影响并且在使用汉字的东方人,文化源头的同构型很高。但他们对传统文化却很用心去维系,举凡茶道、剑道、书道、能剧,以及成年礼等,还有相扑。相扑是一个国际运动项目,延续了几千年都不曾中断。我觉得这是很值得我们去观察、比较的。

 一般而言,电影、MV中如有需要出现婚丧的场景,有需要唯美的画面时,通常剧组都会整队拉去教堂拍摄,或是到充满十字架的墓园取景。就算没有受过正统美学的教育和训练,一般我们人的眼睛都还是会凭直觉分辨出好跟不好,美与不美。每当我看到犹太人逃难的纪录片时,我就深感讶异,五、六十年前犹太人逃难时的穿著,甚至在市区被德军集体遣送时的服装都是很整齐,有一定的素质:男性都穿西装大衣,戴帽子;女性则都会拿个小皮夹,或拎个皮包之类的;也没看到任何一个犹太小孩是打赤脚的。此时他们的身份是难民,但其民族整体表现出来的教育水平,则很令人敬佩与感慨。犹太人是一个很优秀的民族,这个民族出了许许多多几乎与其人口数等量的科学家与艺术家,对人类文明的贡献是很大。我个人觉得他们是个质感很高的民族。我以前在想为何关于二次大战跟犹太人相关的电影拍不完呢?原来因为他们之中很多人都是科学家、艺术家,每个人对美学的追求有基本的素养,整个民族文化的有一定层次的水平,所以这里面很多关于心理层面的故事可以发掘用来拍电影。但关于我们被日军侵略迫害的电影,至今我还没有看到一部很好看、拍的有质感的。我常在反思,到底是因为我们的电影工业不强,还是因为种族的心理层面很肤浅,没有东西可以发掘的?如果你个人不要求与累积身为人的质感,最后极可能就会影响到整体文化份量的累积。  

 举个最极端的例子。假设一百年前有颗陨石从外层空间掉到地球,刚好就落在中国的正中央,整个中华民族从此消失了,但旁边的国家都完全没有被波及。那么回过头来看百年后的今天,现阶段世界上各项的科技文明,与现代化的生活水平都还是会维持着,没有丝毫的影响。并没有因为我们消失了,整个人类文明倒退一百年。现代文明会不中断的持续,还是一样会发明手机、DV、电话、网络、飞机和汽车等,所有的物质文明一样都不会缺少。因为一百年前,我们的国家和民族处于十分衰落的境地,对世界的文明进展并没有太大的贡献。简而言之,假设一百年前中华民族彻底绝灭了,我们来看现在的科技文明,也不会缺少了什么东西。还有受过大学教育的人都知道,当你学习晋升到某个阶段时都会直接读英文原文书,尤其是医学、数学、物理、化学等领域。也就是摆明了说,近代的汉字文明有其严重的断层,读到某些高等教育时,汉字就完全无用武之地,再也无法提供学习所需的养份了。  

 教育是提升民族质感一个很关键的因素。如果一个民族不重视现代化的教育,对自己的传统文化又缺少认同感,长期以往,教育的资源分配不均,造成本国人民的素质参疵不齐,你可能人口很多,但并没有等量的优秀。当然在座的各位同学无庸置疑的都很优秀,是金字塔顶层出类拔粹的北大学生,但若跟内地庞大的人口数相比,人数还是少的可怜。反观犹太人,犹太人大概是世界上种族质感最高的民族之一了。犹太人虽然颠沛流离,去很多国家都被歧视,受到很多限制,比如不得从事什么工作等,但在这种环境下,他们反而更努力地去维系与巩固自己的传统文化,对自身民族的文化认同感很强。一个民族一定要对自己文化有很强的认同感,才会塑造出自己的风格,形成自己的美学观,这无形中也带动了国家创意产业的发展。我今天强调的是,如果个人不重视与提升自己的质感,不管是在教育训练、穿着打扮、以及美学设计等各个方面,那么你我这个没有质感的个体,将会像癌症般慢慢的影响它周遭的人事物。最后,影响到一个团体、一个公司、甚至一个城市、一个社会,以及一整个民族。   

 我希望大家先从重视自己本身所谓“华人”的民族气质与质感开始。除了使用与欣赏国际名牌的质感外,同时也要了解那些品牌之所以能够在国际市场屹立不摇的原因,正是因为他们对美学品味的追求从不懈怠。你不能只是为了暴发户式的证明你穿戴得起名牌,但却对自己服装上的褡配品味完全不会,或者根本不协调。名牌在你身上反而呈现出俗艳的质感。例假日时我自己常在台北郊区开车闲逛,眼睛每次总会停留在刚刚惊鸿一瞥路过的日式官舍、闽南式四合院,以及古厝周遭景色调性很一致的风景上。如果你可以在一百年前就展开环绕全球性的自助旅行,那你肯定是很幸福的,因为那时还没有全球化,所有国家的建筑都具备浓厚的本民族特色。譬如你到了越南的河内,看到的就是越式风情;到东京,看到的就是和式建筑;不论去到任何一个国家与民族,他们穿的衣服和城市的建筑都有着很鲜明的本民族特色。这时你看到的是一个完整的,传承自身文化的国家,没有外来的文化杂质,整座城市,不论是传统建筑,人民身上的服饰,其文化的调性都很一致,可以想象那真是一种铺天盖地属于本民族极致美学的画面。

但现在因为全球化的缘故,所有民族的传统服饰、所有国家的建筑、所有的价值观、所有的美学几乎都被现代化了,也就是所谓的西化,大家都采取同一种标准在看事情。像工厂同一套模具的输出品一样,城市与城市间的建筑,人民与人民间的穿著,格局越来越像,打扮越来越一样。我觉得这真的很可惜,很多不同文化体系的美不见了,很多独特的思维也就会跟着消失了。我们会重视物种的灭绝,假如青蛙、蝴蝶消失了一两个品种,就觉得很可惜。可是对于自己的文化、语言、文字正在快速的消失,我们难道就没有查觉那里不对劲吗?没有任何警觉吗?   

我一直都很喜欢汉字,尤其是传统的繁体字,是因为汉字是现今世界上唯一的表意文字,其它所有的文字都是拼音文字。不论是从拉丁文衍生出的法文、英文、德文,或是中东世界通用的阿拉伯文,或是我们亚洲近邻的韩文、泰文、以及日本的假名等,这些全都是以字母为发音基楚的拼音文字,地球上现还在通用并且流传的活文字里,唯有汉字是唯一一种尚在使用的表意文字,这是一项何等骄傲的事情啊!在现今强调物种多元、文化多元的国际社会,相异于基督教文明与回教传统的东方文化,其地位显得很特别。维护中华文化中独一无二与无可被取代的特性,更显其有意义。最后恳请大家重视今天所讨论的议题,当一个有质感的华人,就从你我间的改变开始……谢谢大家!

2008年11月25日星期二

Fedora 10正式放出


feodra10已经正式发布了,打算现在虚拟机上装一个,不打算直接升级到10。这里是fedora的发行注记。

2008年11月24日星期一

Berkeley DB JAVA EDTION

最近打算学习一下BDB JAVA EDTION,从OTN下载源码后,将源代码导入eclipse,然后加入junit,以及从sun的官方网站下载sun的JAVA EE参考实现并导入,就可以测试了,example,src,test三个文件夹里都是代码,example里是一些小例子,test里是针对源代码的测试方法,src里是源码,使用可以参考OTN上的文档,研究源码可以去这个哥哥的博客看看。另外已经有个哥哥翻译了一些BDBJE的文档在这里,万分感谢。

2008年11月19日星期三

How Oracle Store Number internal?

作者:eygle |English Version 【转载时请以超链接形式标明文章出处和作者信息及本声明】
链接:http://www.eygle.com/archives/2005/12/how_oracle_stor.html

Oracle在数据库内部通过相应的算法转换来进行数据存储,本文简单介绍Oracle的Number型数值存储及转换.这个内容是为了回答留言板上的2119号问题.

我们可以通过DUMP函数来转换数字的存储形式,一个简单的输出类似如下格式:

SQL> select dump(1) from dual;

DUMP(1)
------------------
Typ=2 Len=2: 193,2

DUMP函数的输出格式类似:

类型 <[长度]>,符号/指数位 [数字1,数字2,数字3,......,数字20]

各位的含义如下:

1.类型: Number型,Type=2 (类型代码可以从Oracle的文档上查到)

2.长度:指存储的字节数

3.符号/指数位

在存储上,Oracle对正数和负数分别进行存储转换:

正数:加1存储(为了避免Null)
负数:被101减,如果总长度小于21个字节,最后加一个102(是为了排序的需要)

指数位换算:

正数:指数=符号/指数位 - 193 (最高位为1是代表正数)
负数:指数=62 - 第一字节

4.从<数字1>开始是有效的数据位

从<数字1>开始是最高有效位,所存储的数值计算方法为:

将下面计算的结果加起来:

每个<数字位>乘以100^(指数-N) (N是有效位数的顺序位,第一个有效位的N=0)

5. 举例说明

SQL> select dump(123456.789) from dual;

DUMP(123456.789)
-------------------------------
Typ=2 Len=6: 195,13,35,57,79,91

<指数>: 195 - 193 = 2
<数字1> 13 - 1 = 12 *100^(2-0) 120000
<数字2> 35 - 1 = 34 *100^(2-1) 3400
<数字3> 57 - 1 = 56 *100^(2-2) 56
<数字4> 79 - 1 = 78 *100^(2-3) .78
<数字5> 91 - 1 = 90 *100^(2-4) .009
  123456.789

SQL> select dump(-123456.789) from dual;

DUMP(-123456.789)
----------------------------------
Typ=2 Len=7: 60,89,67,45,23,11,102

<指数> 62 - 60 = 2(最高位是0,代表为负数)
<数字1> 101 - 89 = 12 *100^(2-0) 120000
<数字2> 101 - 67 = 34 *100^(2-1) 3400
<数字3> 101 - 45 = 56 *100^(2-2) 56
<数字4> 101 - 23 = 78 *100^(2-3) .78
<数字5> 101 - 11 = 90 *100^(2-4) .009
  123456.789(-)

现在再考虑一下为什么在最后加102是为了排序的需要,-123456.789在数据库中实际存储为

60,89,67,45,23,11

而-123456.78901在数据库中实际存储为

60,89,67,45,23,11,91

可见,如果不在最后加上102,在排序时会出现-123456.789<-123456.78901的情况。

ORACLE里的char,varchar2,nchar,nvarchar2讨论

转自ITPUB,原文请看这里。

这篇文章对这几种数据类型的讨论非常清楚,另外还推荐看看TOM的大作,ORACLE9i&10g编程艺术--深入数据库体系结构,一书的关于数据类型的那一章。

今天上午和杨老大讨论了一下char,varchar2,nchar,nvarchar2几种字符类型,发现有几种类型不大容易
分的清除,于是做了一下试验,希望能够对大家有所帮助。(高手请跳过)
如果你也搞不太清楚这几种类型,下面的东东可能对你有所帮助。
首先介绍一下环境,字符集是ZHS16GBK,国家字符集是Al16UTF16
第一步:建一张表:
SQL> create table typetest(cha char(10),varcha varchar2(10),ncha nchar(10),nvarcha
nvarchar2(10));
表已创建。
第二步:插入一行:
SQL> insert into typetest values('a','a','a','a');
已创建 1 行。
SQL> commit;
提交完成。
然后用DUMP查看一下库中的实际存储的内容:
SQL> select dump(cha),dump(varcha),dump(ncha),dump(nvarcha) from typetest;
DUMP(CHA)
--------------------------------------------------------------------------------
DUMP(VARCHA)
--------------------------------------------------------------------------------
DUMP(NCHA)
--------------------------------------------------------------------------------
DUMP(NVARCHA)
--------------------------------------------------------------------------------
Typ=96 Len=10: 97,32,32,32,32,32,32,32,32,32
Typ=1 Len=1: 97
Typ=96 Len=20: 0,97,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32
Typ=1 Len=2: 0,97
发现了什么?没发现什么?滚一边去
这样我们得出了结论1:
char nchar 类型是按照定义的长度存储数据的,如果数据不够,后面补空格
varchar nvarchar2 是变长数据类型,实际长度随着存放的数据量改变
进一步思考,可以得出以下结论,用char就是空间换时间,用varchar2就是时间换空间,因为char占用较多
的空间,但是因为规则,处理起来会比较快,varchar2比较节省空间,但是处理起来比较慢,而且如果频
繁的做UPDATE还会产生行转移,会造成数据库性能下降。(什么叫行转移?你真的想知道?有空的时候来
找我,我慢慢将给你听)
接着做试验:

SQL> insert into typetest values('我','我','我','我');
已创建 1 行。
SQL> commit;
提交完成。
SQL> select dump(cha),dump(varcha),dump(ncha),dump(nvarcha) from typetest;
DUMP(CHA)
--------------------------------------------------------------------------------
DUMP(VARCHA)
--------------------------------------------------------------------------------
DUMP(NCHA)
--------------------------------------------------------------------------------
DUMP(NVARCHA)
--------------------------------------------------------------------------------
Typ=96 Len=10: 206,210,32,32,32,32,32,32,32,32
Typ=1 Len=2: 206,210
Typ=96 Len=20: 98,17,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32,0,32
Typ=1 Len=2: 98,17
可以看到char和varhcar是用的GBK编码的,nchar和nvarchar2是用的utf16,国家字符集进行编码的
继续向下看
SQL> insert into typetest values('我我我我我','我我我我我','我我我我我','我我我我我');
已创建 1 行。
SQL> commit;
提交完成。
SQL> select dump(cha),dump(varcha),dump(ncha),dump(nvarcha) from typetest;


DUMP(CHA)
--------------------------------------------------------------------------------
DUMP(VARCHA)
--------------------------------------------------------------------------------
DUMP(NCHA)
--------------------------------------------------------------------------------
DUMP(NVARCHA)
--------------------------------------------------------------------------------
Typ=96 Len=10: 206,210,206,210,206,210,206,210,206,210
Typ=1 Len=10: 206,210,206,210,206,210,206,210,206,210
Typ=96 Len=20: 98,17,98,17,98,17,98,17,98,17,0,32,0,32,0,32,0,32,0,32
Typ=1 Len=10: 98,17,98,17,98,17,98,17,98,17

SQL> insert into typetest values('我我我我我我','我我我我我','我我我我我','我我我我我');
insert into typetest values('我我我我我我','我我我我我','我我我我我','我我我我我')
*
第 1 行出现错误:
ORA-12899: 列 "COMM"."TYPETEST"."CHA" 的值太大 (实际值: 12, 最大值: 10)

SQL> insert into typetest values('我我我我我','我我我我我','我我我我我我我我我我','我我我我
我我我我我我');
已创建 1 行。
SQL> commit;
提交完成。
SQL> select dump(cha),dump(varcha),dump(ncha),dump(nvarcha) from typetest;

DUMP(CHA)
--------------------------------------------------------------------------------
DUMP(VARCHA)
--------------------------------------------------------------------------------
DUMP(NCHA)
--------------------------------------------------------------------------------
DUMP(NVARCHA)
--------------------------------------------------------------------------------
Typ=96 Len=10: 206,210,206,210,206,210,206,210,206,210
Typ=1 Len=10: 206,210,206,210,206,210,206,210,206,210
Typ=96 Len=20: 98,17,98,17,98,17,98,17,98,17,98,17,98,17,98,17,98,17,98,17
Typ=1 Len=20: 98,17,98,17,98,17,98,17,98,17,98,17,98,17,98,17,98,17,98,17
OK,看出结论没,char和varchar是通过数据实际占用的比特数计算长度的,nchar和nvarchar是实际能够存
放的字数为计算长度的,如果是al16utf16,那它可以存放10个汉字,那么实际占用的比特数就是20。
(可以想一想如果用UTF8做为国家字符集会产生什么样的结果)
最后讲一下,测字符串长度
你能告诉我length('我我我我我')的值是多少吗?lengthb('我我我我我')呢?lengthc('我我我我我')呢

给大家看一下结果,看看你猜的对不对
SQL> select * from typetest;
CHA VARCHA NCHA NVARCHA
---------- ---------- -------------------- --------------------
a a a a
我 我 我 我
我我我我我 我我我我我 我我我我我 我我我我我
我我我我我 我我我我我 我我我我我我我我我我 我我我我我我我我我我

SQL> select length(cha),length(varcha),length(ncha),length(nvarcha) from typetest;
LENGTH(CHA) LENGTH(VARCHA) LENGTH(NCHA) LENGTH(NVARCHA)
----------- -------------- ------------ ---------------
10 1 10 1
9 1 10 1
5 5 10 5
5 5 10 10
SQL> select lengthb(cha),length(varcha),length(ncha),length(nvarcha) from typetest;
LENGTHB(CHA) LENGTH(VARCHA) LENGTH(NCHA) LENGTH(NVARCHA)
------------ -------------- ------------ ---------------
10 1 10 1
10 1 10 1
10 5 10 5
10 5 10 10

SQL> select lengthc(cha),lengthc(varcha),lengthc(ncha),lengthc(nvarcha) from typetest;
LENGTHC(CHA) LENGTHC(VARCHA) LENGTHC(NCHA) LENGTHC(NVARCHA)
------------ --------------- ------------- ----------------
10 1 10 1
9 1 10 1
5 5 10 5
5 5 10 10
有问题欢迎和我讨论!

ORACLE数据库中的字符集和国家字符集

今天同事的项目碰见了乱码问题,查了基本情况以后,我认为他们没有弄清楚数据库字符集和国家语言字符集的区别。查资料后,叙述如下:
原文:
Character Set Types
The CREATE DATABASE statement has the CHARACTER SET clause and the
additional optional clause NATIONAL CHARACTER SET to declare the character set
to be used as the database character set and the national character set. Neither
character set can be changed after creating the database. If no NATIONAL
CHARACTER SET clause is present, the national character set defaults to the
database character set.
Because the database character set is used to identify and to hold SQL and PL/SQL
source code, it must have either EBCDIC or 7-bit ASCII as a subset, whichever is
native to the platform. Therefore, it is not possible to use a fixed-width, multibyte
character set as the database character set, only as the national character set.
The data types NCHAR, NVARCHAR2, and NCLOB are provided to declare columns
as variants of the basic types CHAR, VARCHAR2, and CLOB, to note that they are
stored using the national character set and not the database character set.
To declare a fixed-length character item that uses the national character set, use the
data type specification NCHAR [(size)].
To declare a variable-length character item that uses the national character set, use
the data type specification NVARCHAR2 (size).
To declare a character large object (CLOB) item containing fixed-width, multibyte
characters that uses the national character set, use the data type specification
NCLOB (size).
译文:
字符集类型

CREATE DATABASE语句中有CHARACTER SET从句和附加的NATIONAL CHARACTER SET从句用来定义
数据库的字符集和国家字符集。这两个字符集在数据库创建之后都无法修改。如果不指明NATIONAL
CHARACTER SET从句,则国家字符集缺省取数据库字符集。
因为数据库字符集用于标识并装载SQL和PL/SQL源代码,所以数据库字符集必须将EBCDIC或7位ASCII
作为子集。因此,固定宽度,多字节字符集不可能作为数据库字符集,而只能作为国家字符集。数据类型
NCHAR,NVARCHAR2和NCLOB是基本数据类型CHAR,VARCHAR2和BLOB的变体,来指明它们用国家字符集而
不是数据库字符集存储数据。

NCHAR用于使用国家字符集定义固定长度的字符项。
NVARCHAR2用于使用国家字符集定义变长度的字符项。
NCLOB用于使用国家字符集定义字符大对象,来保存固定宽度,多字节字符。

数据库字符集存储变宽度字符,国家字符集存储固定宽度和变宽度多字节字符。

2008年11月18日星期二

ORACLE列转行

今天同事问我怎么进行列转行(ORACLE)里面,他需要对求和所得的一行6列数据,转换成6行一列数据。举例如下:
select 11 num1,15 num2 from dual
得到的结果应该是:
num1 |num2
11 15
需要转成:
num |name
11 num1
15 num2

写出来的SQL如下(其他的数据库也可以套用这个):
select decode(rownum,1,num1,num2) num,decode(rownum,1,'num1','num2') name from
(select 11 num1,15 num2 from dual connect by level<=2)
如果列的数目再多一些的话只需吧level增大,多加decode判断即可。当然出于便于阅读的原因我还是建议将decode换成case。

我的户口之旅

转载

我出生在河南一个平原村庄,那一年天有异象,老人说是“天塌地陷闹耗子,因为毛(猫)没了”。

父亲是民办老师,在村子里教书,每月五块钱工资,77年上师范转正,成了吃国粮的干部,拿到了几十块。我,我妈妈,都是农村户口,沾了这个光,又有了弟弟和妹妹,父亲说,要是在城市咱们就少了两个劳力。我是长子,出生后不久就分到了地,一亩二分五。妈妈常教育我,好好上学,考上小中专,就不用在家打泥腿子了。父亲的师范属于小中专,那是通往吃国粮的门槛。
刚上小学时,有城市里的小学到村边油田慰问演出,我们全村倾巢出动,看那些城市里的孩子。他们抹着红脸蛋,拿着大红花,穿着花衣裳。不知什么样的爸爸妈妈,把孩子养得这么漂亮,我们流着鼻涕,看这些来自远道而来的天使。
小学四年级的时候,父亲当了乡中心小学的校长,我跟着到了乡里,五年级,父亲又去了市里一个小学当老师,我又跟着到了市里。那是我从来没见过的大城市,有几万人,一个年级就有四个班。我的户口不合格,但因为是教师子弟,上学就没有耽误。见到同学们恍然大悟,原来小时候看的天使就是他们。也交了几个朋友,放学之后,同学会带我去游戏厅,或者台球。我们花的钱从哪里来呢?原来是同学用偷家里的粮票换的,跑到卖馒头的作坊,把粮票换成钞票,河南粮票便宜,全国粮票贵些。那时候整个城市买粮食油都用粮票,我家只有父亲一人有粮票,我实在无票可偷。
随后,妈妈弟弟妹妹都搬到市里,而且转成了城市户口,吃商品粮了。他们有了一个大红户口本,每个人的名字写在上面,但唯独漏了我一个。父亲对我解释说, “办户口得花钱,你学习还可以,考上大学就转成城市户口,这样咱就能省两个。如果你考不上大学,村里还有你一亩二分五的地呢。”父亲这个激励办法给我带来很大的危机感,全家都是城市人,就我是农村人,如果考不上大学,他们都吃国粮,我得自己回村子种地,这让我忧心忡忡,人生的贵贱在此一举了。四叔跟我一样大,在村子里已经辍学,14岁就跟爷爷奶奶种地兼卖油条了。从此,那一亩二分地成了我的一块心病,那是多大的一块地啊,我一天才能锄二分地。
上高中比较顺利,然后准备考大学,高三的时候,一个同学突然转学了,他在班里排四十多名,属于根本没戏的差生。后来听说,他转到了北京去考大学,弄了个北京户口,分数线比河南低好几百分,他考上了北京工业大学。那是我第一次知道北京户口,知道北京户口很牛。
我去郑州大学报道,户口也转到学校,身份证上的地址写着,郑州市大学路某某号。老师说,你们现在的身份是干部了。在我小时候的印象里,干部就是中山装,四个兜。我们是干部,国家包分配,从此衣食无忧了。但悲惨的是,我们是第一届并轨生,学费高了,布包分配了,有农村来的同学痛不欲生,当初怎么不报师范啊,那个还包分配,现在一毕业就要失业了。上大学是1995年,有城市户口也早没粮票可拿了,我对户口没有了概念,反倒生出了优越感,我村子里的地还瞒着没退,我跟同学一样都是城市人,可是我还有地啊。
毕业去河南日报,有省人事厅的编制,户口也随即转到郑州市纬一路一号,金水河派出所,成了郑州市市民。而且,到现在户口还在那里。一旦户口在那儿,如果没有下一个接收单位,我的户口会在河南日报躺一辈子,尽管它对我早已没了意义,但我理论上还是单位里的人。
这两年,听说郑州放开了户口,只要在郑州买房子,或者亲戚朋友是郑州人,都可以领到郑州户口。政策一出,人们疯了,然后,政策被紧急叫停。这说明户口依然神圣,在广大农村人眼中。郑州的朋友说,并不是郑州市政府大发慈悲,而是他们有一个直辖市之梦,需要人口大量扩张,膨胀成一个特大型城市,就有底气申请搞个直辖市了。
到北京之后,一直在私营单位,或在国营报社打黑工,所以,我和我的户口两地分居了。户口在河南日报,档案寄存在河南省人才交流中心,在北京结了婚,老婆上研究生,户口在大学,我们在老家医院生了孩子。于是你知道,我的孩子办出生会有多复杂,结婚证、准生证、出生证,开单位介绍信成了不可能的任务,最后的结果是,我的介绍信全驴头不对马嘴,只有孩子是真的。
我2000年到的北京,住在北京而没有北京户口,这叫北漂,叫暂住在北京,要办暂住证的,凡是没暂住证上街,警察查到了马上流放到昌平挖沙子,然后遣返回原籍。具体经办此事的是收容遣送站,在里面会挨打,还要家属交钱领人。有人把收容遣送站改名叫国营绑架公司。大学生孙志刚在广州没有暂住证,在送往收容遣送站的过程中被打死了。那两年,我走在北京的街上,不敢向警察瞄上一眼,但由于我肥头大耳,从来也没遭到警察盘问。反倒领了任务,去采访收容遣送制度。后来全国废除了收容遣送制度,但北京一直在搞。我一直坚信,北京真的很牛。
媳妇毕业了,分到国有机构,有了北京户口,成了北京市民,把女儿户口也带过来,她是我这个暂住人口的保护伞。媳妇如果失业,北京市会有低保,我如果失业,只能去大街乞讨,现在连乞讨也不让了,北京有关部门呼吁过,不要施舍给乞丐。好在女儿可以上学,如果不挑肥拣瘦,可以在片内上小学,如果在别的片上,可叫三到十万的赞助费。一位同事也有个女儿,单位帮忙上了一个小学,交了三万。但他一直在极度郁闷中,小学是上了,但小升初不考试,仍然需要北京户口,到时他的女儿就要回河北保定老家上初中,而且考试也要在河北。但她的爸爸妈妈都在北京,而且要在北京暂住一辈子了。

媳妇说,等她有了中级职称,就可以把配偶办过来。我是她的配偶,托她的福,若干年之后会成为北京人,成为首善之区天子脚下皇城根的人,那时我也是有保障的人了,老了也有人管了。一个农村高粱地里出来的孩子,先后成了村里的人、市里的人、省里的人,现在终于成了“中央”的人了。这是不是很幸运呢?但是我知道,像我这样的,只是极少数的幸运儿,绝大多数的同胞们还都是泥腿子,泥腿子洗脚上岸,到城市讨生活,但最终还是泥腿子。城市里的我父亲,看病花了二十万,村子里的我二叔,同样的病花了两万就回家了。并不是父亲多有钱,只是因为父亲在城市工作,二叔在农村工作,谁说在农村种地就不是工作呢?

2008年11月17日星期一

Oracle SQL Developer Data Modeling


今天上OTN,发现SQL DEV正在做数据建模工具,马上我们就可以使用ORACLE的免费数据建模工具了。这个工具现在还是一个单独的产品,但是以后会集成到SQL DEV中。

小说业务与技术

昨天来了个韩国人,给同事培训业务。他说在韩国懂业务的人比懂技术的人挣的钱多很多。这话本身是很不严谨的,业务和技术并不矛盾。做技术的可以分为四种人,懂业务又懂技术的,懂业务而不怎么懂技术的,懂技术但业务比较生疏的,业务技术都不行的,当然我觉得他的意思是既懂技术又懂业务的人比只懂技术的人挣的钱多。

其实这是个说了很多年的话题。通常来说搞业务为主的人觉得业务重要,搞技术为主的人认为技术重要。我是搞技术的,理所当然的认为技术更重要一些。

离开了技术,业务是无从实现的。我实在无法想象一个技术不怎么样的人,却凭自己熟悉业务就能开发或者整合出一套系统(好多做SAP的人就是这样,连后台ORACLE的实例调优都不懂,却敢号称自己的系统最优)。对大部分企业来说/个人也是这样,很多需求是很难归纳成同一的实现来处理,尤其是在中国。在这种情况下,我们很难套用ERP这样的成型业务软件来解决问题,这也是为什么ERP项目失败的比成功的多的原因。因此业务很明白而技术不行的人,其实是很难在软件方面立足的。

对程序员来说,一旦跳槽,公司的业务就可能完全不通,但是技术总是那几样。因此,技术是永恒的东西,而业务却不是。再者,像他们讲的那么麻烦的业务,在实现上脑力劳动不多而体力劳动多。程序员在大部分情况下不愿意做那么多体力活。因此很多程序员并不喜欢花很大力气在业务上。

由此想到公司所谓的愿景--2010年进入企业级服务的top 10。觉得这个所谓的愿景挺可笑的。就拿所谓的SAP ERP咨询业务来说吧(公司想做业务型的公司)。可能在韩国能够做成(依靠他们在韩国的地位),但是这个模式在其他国家是不可复制的。比如在中国,SAP ERP软件只有大型企业才用的起,而这种大型企业是根本不会把实施SAP ERP的工作交给公司来做的(他们可以直接找SAP或者ORACLE)。而小公司呢,则根本用不起SAP ERP。自己的看法是公司一辈子也成不了所谓的TOP 10,因为他们既没有牛逼的技术,也没有创新的业务。如果说他们能想出很好的方式来服务广大的中小型企业,那么这个所谓的愿景才有可能实现。Technolog should not be treated so lightly.

2008年11月16日星期日

PostgreSql源码学习(6)

PG源码之parser(3)

以下是几个小例子(有自己写的,有查资料得到的):
select ename from emp e
树的顶端节点是 SelectStmt 节点,没有查询条件,所以没有别的节点。

select ename from emp where empno>2000

树的顶端节点是 SelectStmt 节点,由于有范围查询条件,使用SQL 查询的 where 子句构建的操作符树,这个操作符树附加到了 SelectStmt 节点的字段 qual 上。为在查询里出现的常量(2000)条款创建一个 Const 节点(makeintconst),存放常量值。

select e.ename, d.dname
from EMP e, dept d
where e.empno > 2000 and e.deptno = d.deptno;


树的顶端节点是 SelectStmt 节点。对每个在 SQL 查询的 from子句里出现的元素创建一个 RangeVar 节点,存放 alias(别名)的名称和一个指向一个存放关系名称的 RelExpr 节点的指针。所有 RangeVar 节点都收集到一个列表里,然后附加到 SelectStmt 节点的 fromClause 字段上。

对于 SQL 查询里面的 select列表 里出现的每个元素都创建一个 ResTarget 节点,存放一个指向 Attr 节点的指针。Attr 节点存放元素的关系名称和一个指向存放着字段名称的 Value 节点的指针。所有 ResTarget 节点都收集到一个列表里,然后链接到 SelectStmt 节点的 targetList 字段里。

SQL 查询的 where 子句构建的操作符树,这个操作符树附加到了 SelectStmt 节点的字段 qual 上。操作符树的顶端节点是一个代表 AND 操作的 A_Expr 节点。这个节点有两个后继节点, lexpr 和 rexpr 分别指向两个子树。附加到 lexpr 的子树代表条件 empno > 2000 而附加到 rexpr 的子树代表e.deptno = d.deptno。为每个字段创建一个 Attr 节点,存放关系名和一个指向存放着字段名的 Value 节点的指针。为在查询里出现的常量条款创建一个 Const 节点,存放常量值。

PostgreSql源码学习(5)

PG源码之parser(2)

上文大概描述了pgsql处理查询的第一步,下面我将分析parser部分的原代码,(src/backend/parser里的源代码),可能需要写的东西要比较多一点。
1 scan.l
这个文件包含了几个头文件,其中postgres.h主要是一些在后台进程中经常用到的常量以及自定义变量(这个以后再说);gramparse.h用于避免使用bison做语法分析时的缺省模式(bison使用行列号来标示单词);keywords.h主要是跟关键字相关的内容(要区分开非关键字0,列名关键字1,类型/函数名关键字2,保留关键字3,ScanKeyword结构体就是用于区分关键字的,实际上是一个关键字符号表);scanup.h是与扫描相关的代码(还没弄的太懂);pg_wchar则引入对多字节字符的支持(比如汉语)。
以下是对一些成员变量的解释:
fprintf用于打印出错误。
backslash_quote好像是用于处理跟GNU工具相关的内容。(但是我不明白具体是做什么的)
literalbuf用于在满足多个规则时聚合相应的字面量。
使用startlit重置buffer,使用addlit来添加字符。buffer用palloc来分配,随着每次解析循环重新开始和刷新buffer。
接下来的这部分主要是处理各种情况,比如双引号,单引号(字符串中的),美元符号等等。
从331(8.3.1版本中)行开始就是对各种情况的处理。
从764行开始是几个函数,lexer_errposition用于确定错误的单词位置,yyerror用于报错,scanner_init在实际的解析开始之间做用于分配内存,scanner_finish用于释放内存,addlit来添加字符,addlitchar用于给传入的字符串的长度加1,litbufdup用于对缓冲区重写,unescape_single_char用于检测escape表达式中的特殊字符(like语句的正则表达式部分),check_string_escape_warning和check_escape_warning与escape语句表达式的错误相关(具体是处理什么我没太看明白。)。

2 gram.y
第81行的指针变量parsetree实际上就是gram的返回结果,这个变量在parser.c中定义,这样就可以返回一个解析好的语法树。从151到358行是声明针对各种情况的解析函数。从369行到453行是SQL关键字。从465行到503行是操作符的优先级定义。从514开始到9433行是各种语法定义,包括create/alter/drop role,create/alter/drop user,create/alter/drop group,create schema等等。
makeColumnRef函数的作用不明,欢迎高手指教。
makeTypeCast函数用于SQL数据类型转换。
make...Const这几个函数似乎是用于生成常量节点。
makeOverlaps生成函数调用节点。
check_qualified_name和check_func_name函数的作用不是很清楚。
extractArgTypes函数用于在给定一系列函数参数名的情况下,提取参数的类型。(好像是这样子,不是很确定)
findLeftmostSelect是获取最左端的select语句。
insertSelectOptions是用于处理如下语句:insert into tablename select * from another_tablename。
makeSetOp的作用也不是很明白,似乎是和SET语句的选项有关系。
SystemFuncName用于返回系统函数名(pg_catalog里定义)。SystemTypeName用于返回系统type名。
parser_init用于初始化。
doNegate和doNegateFloat用于处理负数的情况。
makeXmlExpr是用于处理XML表达式和XML函数。

2008年11月14日星期五

如果你是狼——中国房地产的寓言

作者不详

猪通过勤劳致富有5元钱存在老鼠开的钱庄里。猪打算拿这5元钱建一个小窝,大概要花2元卖地,花3元搭窝。
王八是搞工程的,他想在猪身上挣更多的钱,于是找来当投资顾问的狐狸想办法。
狐狸说:这好办。于是找来管地盘的狼,开钱庄的老鼠一起来商议。
结果王八从老鼠那里借来200元,用100元买了狼的地,花了3元把猪窝盖好,花了50元给了狐狸咨询服务费。
猪没有地,只好求王八把窝卖给它,王八要价500元。
老猪说只有5元买不起,这时候狐狸说服猪去向老鼠借钱;
老鼠答应借500给猪,前提是要他连本带利还600元,可以分10年还清,并且产权证拿来抵押。结果成交。
猪到最后花了600元买来了猪窝,比地价加建设成本高了11倍,猪努力了十年去挣钱还贷。
在这场交易里面,狼,老鼠,狐狸还有王八都挣了钱。以后他们就如法炮制,迫使更多的猪去贷款买房子了。
这时候,驴看到有机可乘,到老鼠那里贷了好多好多的款,把王八盖的房子都买下来,然后以更高的价格卖给了猪。猪的还贷期就越来越长,吃的越来越差,小猪崽子也不敢生了。
由于猪的数目越来越少,狼觉得这样下去自己没有猪肉吃了,非饿死不可,于是开始调控,不让老鼠再借钱了。但是王八还没有停止盖房,把自己挣的钱和贷的钱全投入生产了。驴手上的猪窝囤积的很多,卖不动了被套牢了。结果,老鼠,王八,还有驴都挣了好多的猪窝。钱到最后集中到狼手上。
如今,谁都等着狼把钱拿出来救命。聪明的你,如果你是狼,你会拿钱救哪个?

2008年11月9日星期日

鼓励未来的程序员

转自CSDN,原文请看这里

今天我在BBC新闻网站上看到了这篇有趣的文章,是关于英国游戏产业的,英国是如何一直在发展他们能力范围之上的游戏产业。这个问题总是被推向那些伴随着BBC Micro,ZX-81等的计算机长大的那一代人身上。那时候的计算机通常会有一个内置的BASIC解释器,你需要使用它来运行磁带或磁盘程序。

幸亏这些本地居民和便宜的计算机,英国多年来在游戏市场都占据强有力的地位。程序员通过进入杂志的列表开始了解编程,进而一步一步开始写自己的游戏代码。那时候的机器明显是比现在的机器要难用,但却是文档齐全,更具有编程文化——如果你想要使用,你至少需要学习少数命令。

现在,与今年的这篇文章比较一下,这篇文章就显得很压抑。很少人在学习编程,学校在全神贯注地教学生如何使用Word和Excel。难怪很多人都不愿意考虑去试一试OpenOffice!(好像老外也教小孩使用office。)

我现在将近30岁,在9或10岁的时候拥有了我的第一个计算机(Amstrad CPC 6128)。所以我认为我是处于伴随着第一代计算机长大的那一代人的末梢儿。像很多其他人一样,我按照电话本大小的手册书里的指南去编写程序,建立一个地址本应用程序,花费几个小时键入程序,同时调试来自杂志的游戏。

90年代被认为是计算机产业的一个糟糕时期,尽管计算机网络是出现于这个时代。Commodore沉没,Apple也只是死里逃生,在短短几年之内,Windows开始占据桌面统治地位,其它的几乎都消失了。

Windows毋庸置疑在使计算机更易使用方面做出了杰出贡献(其实我不喜欢承认这件事情,我是一个Linux用户),同时还提供了写信,制作电子表格,到后来的网络冲浪等一些了不起的功能。一定程度上来说,它使计算机变成了一个黑匣子,使人们对学习编码不感兴趣。(很正确。)

同时,游戏控制台开始占据更大的游戏市场。一些流派(如RTS即时战略),已经在家庭计算机上占有了很强的地位,而且现在仍然如此。平台游戏如Mario和Sonic游戏渐渐退去舞台。所以,像过去那种为了玩游戏买计算机,然后开始编程的情况已经很少见了。

现在,如果你买一台Windows PC,没有任何方式会鼓励你用它编程序。没有任何种类的指导,也没有BASIC解释器(或者任何其它解释器)。现在的PC杂志似乎没有做任何鼓励人们学习编程的事情,我觉得这有一些悲哀。(我刚开始学BASIC的时候,需要从软盘安装BASIC的开发环境。)

拥有一台Windows计算机,在我看来,你实际上应该从它开始对编程产生兴趣,应该知道你对它感兴趣,你可以使用它作为一个编程平台。我的意思是,它并不会鼓励你学习特定的编程语言。据我所知,微软的操作系统到目前为止还不包括任何编程工具。

让人感到受挫的是,他们其实已经有了近乎完美的编程工具,以Visual Studio Express Edition的形式。我希望人们能学习一种开源语言而不是依靠Windows,但我更希望看见人们能首先被鼓励编程。为什么微软的Windows操作系统不能包含至少一种Visual Studio Express Edition IDE(我预期他们大概主张Visual Basic,尽管那不是我的首选),以及一个用户手册(打印或pdf版本)?他们可以在桌面上放置一个链接“开始学习编程!”,让人们在好奇心的驱使下自然而然地去学习。如果人们不喜欢,他们可以删除它,那不是很好。事实上,现在你不得不到微软的网站上去寻找它。他们应该鼓励人们去学习他们的软件,并为之开发更好的应用。(VS的免费版本功能很强大,在做windows系统下的桌面应用的时候很好用。)

我认为在很多方面,Apple做的更好。通过在OS X的每一个拷贝中包含Xcode IDE,伴随计算机同时提供了一个有力的编程工具。而且,OS X本质上来说是Unix的一种形式,很适合编程用。但是,事实上,Xcode不是预安装的,意味着它不是默认安装的,使得使用性变小,人们可能会出于好奇心而安装它。同时,尽管XCode确实装载了文档,但却没有包含任何“初学者向导”,看起来像是只为那些已经成为程序员的人提供的。他们应该明确地做更多能鼓励人们开始编程的事情。所以,我再一次感觉到像Windows,OS X都是很少的努力来鼓励人们开始学习编程。(没用过MAC 不清楚。)

另外一个Apple和微软的共同点是,他们过于强调IDE而不是文本编辑器。我曾经短暂地试过使用Visual Studio Express Edition,也试过Xcode,我同意这种观点,就是IDE是你开始编程的最后一个选择。学习一个全新且复杂的应用程序,同时学习编程,对大多数人来说都有些困难,但任何人都会使用基本的文本编辑器。甚至是更复杂的,如Vim(我个人喜欢的)也远比使用大多数IDE容易得多。毫无疑问IDE提供了大量特征供用户体验,但当某人开始他编程第一步时,却是极其不应该选择的。(windows平台的程序员很多人连编辑器和解释器都分不清楚,这实际上是IDE的功劳。我自己喜欢VIM和EMACS这两个编辑器,各有各得好处。IDE在JAVA EE开发的时候很有用,可以避免自己手动配置那些麻烦的参数。)

有一件事情让我喜欢Linux超过OS X和Windows。目前,在某些流行套件如Ubuntu中,你实际上不需要为日常作业而使用终端,现在有一个很强的文化在做这件事情,你可以使用它来解决问题。我认为部分原因是由于主要支持来源于论坛,对某个人来说,很容易就可以给你粘贴一个命令,粘贴到终端,然后告诉你“点击这里,然后这里,然后这里……”。同时,bash shell比Windows的配对产品更有力。总的来说,可能会存在一个学习曲线,使用bash通常是完成一项任务的最快、最方便的方法。从使用 bash,通过编辑文本文件改变设置,然后到编写shell脚本或是学习使用Perl或Python编程并不是很困难的事情。很多套件包含语言的同时也会包含大量文档。同时,Linux中使用的语言几乎是一成不变的,可以使用一个简单的文本编辑器而不是强迫你去学习使用某个IDE,我感觉这让他们更容易理解。(linux下的编译器和编辑器是分开的。很多人可以几十年的使用VIM做编辑器,使用GCC来编译程序,使用FLEX和BISON做语法分析。每一个工具只完成一项工作,这样程序员就不用老更换工具。而且linux下的编程语言主要是C语言,好像这么多年来几乎没变过。)

但是,我还是感觉甚至是Linux也可以做跟多鼓励人们学习编程的事情。我不得不想做的最后一件事情是,当人们不想学习编程的时候强制他们去学习。但我认为我们不应该采取更多的措施去鼓励新程序员。可能只是手头/home目录下的一个pdf文件,叫做“学习编程”的就足够了。

最近,我开始学习Python。很快地我就开始喜欢学习它了。它可能比BASIC更强大,但我发现它正如入门容易那样简单。过去我曾学过Perl和 Java,发现学习一个新语言或多或少会有点困难,但Python让我感到很惊讶。所以一个新计算机应该包含一种理想的语言。OS X和大部分Linux套件已经包含了Python。我找不到任何微软的产品不包含它的理由。只需要添加一到两个pdf指南,放在那里。如果处于某种理由他们不想使用Python,我们当然还可以为初学者提供其他好的语言。

还有其他可以让人们对编程产生兴趣的方法吗?我们应该推动哪种语言?我们是否应该鼓励他们使用IDE,或者使用像Vim这样的文本编辑器?欢迎给出你的答案!(要推动的语言我觉得C和JAVA,PATHON,对初级程序员应该推广VIM/EMACS,如果使用IDE的话推荐使用eclipse。有了这些工具我们就能学习编程了,连操作系统和数据库都可以用这些工具做出来。)