tbwshc

#

10g中DBA_TAB_STATISTICS的STATTYPE_LOCKED列对分区锁定显示为空的解决

Oracle10g的DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列没有正确的显示结果。

10g中DBA_TAB_STATISTICS的STATTYPE_LOCKED列对分区锁定显示为空:http://yangtingkun.net/?p=1023

 

 

上文提到了DBA_TAB_STATISTICS中的STATTYPE_LOCKED列在10g中对于分tb区锁定统计信息显示为空,那么在10g中有没有办法获取到正确的结果呢:

SQL> select table_name, partition_name, last_analyzed, stattype_locked
 2 from dba_tab_statistics
 3 where wner = user
 4 and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART                                                        16-JUL-12
T_PART                         P1
T_PART                         P2                             16-JUL-12
T_PART                         PMAX                           16-JUL-12

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART', partname => 'P1')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART', partname => 'P1'); END;

*
ERROR at line 1:
ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: at "SYS.DBMS_STATS", line 15027
ORA-06512: at "SYS.DBMS_STATS", line 15049
ORA-06512: at line 1

显然虽然Oracle在DBA_TAB_STATISTICS视图中没有正确的显示分区的锁定状态,但是Oracle在内部确实记录了分区的锁定状态,既然Oracle记录了这个信息,就有办法将这个信息显示出来。

既然11g能够显示该列的值,最简单的方法莫过于对比10g和11g中DBA_TAB_STATISTICS视图的区别,10g视图的结果:

SQL> select text from dba_views where view_name = 'DBA_TAB_STATISTICS';

TEXT
--------------------------------------------------------------------------------
SELECT /* TABLES */
    u.name, o.name, NULL, NULL, NULL, NULL, 'TABLE', t.rowcnt,
.
.
.
    decode(bitand(t.trigflag, 67108864) + bitand(t.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tab$ t, sys.tab_stats$ ts, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* PARTITIONS, NOT IOT */
    u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab_stats$ ts, sys.tab$ tab,
    sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* IOT Partitions */
    u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab, sys.mon_mods_all$ m

 WHERE
.
.
.
 UNION ALL
 SELECT /* COMPOSITE PARTITIONS */
    u.name, o.name, o.subname, tcp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabcompartv$ tcp,
    sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* SUBPARTITIONS */
    u.name, po.name, po.subname, tcp.part#, so.subname, tsp.subpart#,
.
.
.
    decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ po, sys.obj$ so, sys.tabcompartv$ tcp,
    sys.tabsubpartv$ tsp, sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* FIXED TABLES */
    'SYS', t.kqftanam, NULL, NULL, NULL, NULL, 'FIXED TABLE',
.
.
.

对比一下11g的查询结果tb

SQL> select text from dba_views where view_name = 'DBA_TAB_STATISTICS';

TEXT
--------------------------------------------------------------------------------
SELECT /* TABLES */
    u.name, o.name, NULL, NULL, NULL, NULL, 'TABLE', t.rowcnt,
.
.
.
    decode(bitand(t.trigflag, 67108864) + bitand(t.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tab$ t, sys.tab_stats$ ts, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* PARTITIONS, NOT IOT */
    u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(
      /*
       * Following decode returns 1 if DATA stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
      /*
       * Following decode returns 2 if CACHE stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
      /* if 0 => not locked, 3 => data and cache stats locked */
      0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab_stats$ ts, sys.tab$ tab,
    sys.mon_mods_all$ m
.
.
.
 UNION ALL
 SELECT /* IOT Partitions */
    u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(
      /*
       * Following decode returns 1 if DATA stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
      /*
       * Following decode returns 2 if CACHE stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
      /* if 0 => not locked, 3 => data and cache stats locked */
      0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab, sys.mon_mods_all$ m

 WHERE
.
.
.
 UNION ALL
 SELECT /* COMPOSITE PARTITIONS */
    u.name, o.name, o.subname, tcp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(
      /*
       * Following decode returns 1 if DATA stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
      /*
       * Following decode returns 2 if CACHE stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
      /* if 0 => not locked, 3 => data and cache stats locked */
      0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabcompartv$ tcp,
    sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* SUBPARTITIONS */
    u.name, po.name, po.subname, tcp.part#, so.subname, tsp.subpart#,
.
.
.
    decode(
      /*
       * Following decode returns 1 if DATA stats locked for partition
       * or at table level.
       * Note that dbms_stats does n't allow locking subpartition stats.
       * If the composite partition is locked, all subpartitions are
       * considered locked. Hence decode checks for tcp entry.
       */
      decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
      /*
       * Following decode returns 2 if CACHE stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
      /* if 0 => not locked, 3 => data and cache stats locked */
      0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ po, sys.obj$ so, sys.tabcompartv$ tcp,
    sys.tabsubpartv$ tsp, sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* FIXED TABLES */
.
.

显然在11g中Oracle对于分区锁定的显示采用了新的算法,那么可以仿照11g中建立一个视图,来解决10g中分区显示存在错误的问题:

SQL> CREATE OR REPLACE VIEW DBA_TAB_STATISTICS_LOCK
 2 (OWNER, TABLE_NAME, PARTITION_NAME,
 3 SUBPARTITION_NAME, OBJECT_TYPE, STATTYPE_LOCKED)
 4 AS
 5 SELECT u.name, o.name, NULL, NULL, 'TABLE',
 6      decode(bitand(t.trigflag, 67108864) + bitand(t.trigflag, 134217728),
 7             0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL')
 8    FROM sys.user$ u, sys.obj$ o, sys.tab$ t
 9    WHERE o.owner# = u.user#
 10      and o.obj# = t.obj#
 11      and bitand(t.property, 1) = 0
 12      and o.subname IS NULL
 13      and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
 14      and bitand(o.flags, 128) = 0
 15 UNION ALL
 16 SELECT u.name, o.name, o.subname, NULL, 'PARTITION',
 17      decode(
 18        decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
 19        decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
 20        0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
 21    FROM sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab
 22    WHERE o.owner# = u.user#
 23      and o.obj# = tp.obj#
 24      and tp.bo# = tab.obj#
 25      and bitand(tab.property, 64) = 0
 26      and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
 27      and bitand(o.flags, 128) = 0
 28 UNION ALL
 29    SELECT u.name, o.name, o.subname, NULL, 'PARTITION',
 30      decode(
 31        decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
 32        decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
 33        0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
 34   FROM sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab
 35    WHERE o.owner# = u.user#
 36      and o.obj# = tp.obj#
 37      and tp.bo# = tab.obj#
 38      and bitand(tab.property, 64) = 64
 39      and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
 40      and bitand(o.flags, 128) = 0
 41 UNION ALL
 42    SELECT u.name, o.name, o.subname, NULL, 'PARTITION',
 43      decode(
 44        decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
 45        decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
 46        0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
 47    FROM sys.user$ u, sys.obj$ o, sys.tabcompartv$ tcp, sys.tab$ tab
 48    WHERE o.owner# = u.user#
 49      and o.obj# = tcp.obj#
 50      and tcp.bo# = tab.obj#
 51      and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
 52      and bitand(o.flags, 128) = 0
 53 UNION ALL
 54    SELECT u.name, po.name, po.subname, so.subname, 'SUBPARTITION',
 55      decode(
 56        decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
 57        decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
 58        0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
 59    FROM sys.user$ u, sys.obj$ po, sys.obj$ so, sys.tabcompartv$ tcp, sys.tabsubpartv$ tsp, sys.tab$ tab
 60    WHERE so.obj# = tsp.obj#
 61      and po.obj# = tcp.obj#
 62      and tcp.obj# = tsp.pobj#
 63      and tcp.bo# = tab.obj#
 64      and u.user# = po.owner#
 65      and bitand(tab.property, 64) = 0
 66      and po.namespace = 1 and po.remoteowner IS NULL and po.linkname IS NULL
 67      and bitand(po.flags, 128) = 0
 68    ;

View created.

SQL> select table_name, partition_name, object_type, stattype_locked
  2 from dba_tab_statistics_lock
 3 where wner = 'TEST'
 4 and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 OBJECT_TYPE STATT
------------------------------ ------------------------------ ------------ -----
T_PART                                                        TABLE
T_PART                         P1                             PARTITION    ALL
T_PART                         P2                             PARTITION
T_PART                         PMAX                           PARTITION

使用新创建的这个视图,就可以解决锁定分区的统计信息显示问题。

 


posted @ 2012-08-20 13:08 chen11-1| 编辑 收藏

分区表UNUSED列后的EXCHANGE PARTITION操作

碰到一个有意思的问题,如果分区表执行过SET UNUSED操作,那么是否还可以进行分区的EXCHANGE操作。

 

 

一个简单的测试就可以说明这个问题:

SQL> create table t_part_unused
 2 (id number, name varchar2(30), other varchar2(30))
 3 partition by range (id)
 4 (partition p1 values less than (10),
 5 partition pmax values less than (maxvalue));

Table created.

SQL> insert into t_part_unused
 2 select rownum, table_name, 'abc'
 3 from user_tables;

48 rows created.

SQL> commit;

Commit complete.

SQL> alter table t_part_unused set unused (other);

Table altered.

SQL> desc t_part_unused
 Name                                    Null?   Type
 ---------------------------------------- -------- ------------------------
 ID                                               NUMBER
 NAME                                             VARCHAR2(30)

SQL> create table t_temp_unused as
 2 select *
 3 from t_part_unused
 4 where 1 = 2;

Table created.

SQL> desc t_temp_unused
 Name                                    Null?   Type
 ---------------------------------------- -------- ------------------------
 ID                                               NUMBER
 NAME                                             VARCHAR2(30)

SQL> alter table t_part_unused
 2 exchange partition p1
 3 with table t_temp_unused;
with table t_temp_unused
          *
ERROR at line 3:
ORA-14097: column type or size mismatch in ALTER TABLE EXCHANGE PARTITION TB


SQL> alter table t_temp_unused add (other varchar2(30));

Table altered.

SQL> alter table t_part_unused
 2 exchange partition p1
 3 with table t_temp_unused;
with table t_temp_unused
          *
ERROR at line 3:
ORA-14096: tables in ALTER TABLE EXCHANGE PARTITION must have the same number of columns


SQL> alter table t_temp_unused set unused (other);

Table altered.

SQL> alter table t_part_unused
 2 exchange partition p1
 3 with table t_temp_unused;

Table altered.

很明显执行了SET UNUSED操作后的表,和普通的表是存在区别的,这种区别导致要求进行EXCHANGE的表必须同样执行SET UNUSED操作,否则就无法执行EXCHANGE的操作。

当目标表中不包含SETE UNUSED的列时,EXCHANGE操作会出现ORA-14097的错误,而如果把列添加到目标表,则会报错ORA-14096,必须在目标表同样对列执行SET UNUSED操作,才能通过EXCHANGE之前的检查。

其实这也不难理解,执行SET UNUSED命令后,数据字典虽然发生了改变,但是表上的数据并没有删除,而EXCHANGE操作只是将两个段的数据字典进行互换,因此如果目标表上缺少SET UNUSED列,是无法执行EXCHANGE操作的。

解决问题的方法有两个,第一个就是例子中展示的可以在目标表上建立列然后同样的执行SET UNUSED操作;另外的一个方法就是对于SET UNUSED列执行DROP COLUMN操作,彻底删除该列。

 


posted @ 2012-08-17 14:56 chen11-1| 编辑 收藏

小议ROWNUM

如何使用ROWNUM是个老生常谈的问题了,本来没有打算专门强调这个问题,但是最近在看Oracle的官方PL/SQL文档时发现了一个严重的错误,借这个机会还是简单说一下。

 

 

首先来看Oracle文档的描述,在10.2的PL/SQL文档中,Oracle关于PL/SQL中直接使用SELECT的查询描述为:

Selecting At Most One Row: SELECT INTO Statement
If you expect a query to only return one row, you can write a regular SQL SELECT statement with an additional INTO clause specifying the PL/SQL variable to hold the result.
If the query might return more than one row, but you do not care about tb values after the first, you can restrict any result set to a single row by comparing the ROWNUM value. If the query might return no rows at all, use an exception handler to specify any actions to take when no data is found.

这个描述是没有问题的,但是到了11.2中,文档的描述变成了:

Single-Row Result Sets
If you expect the query to return only one row, then use the SELECT INTO statement to store values from that row in either one or more scalar variables (see "Assigning Values to Variables with the SELECT INTO Statement") or one record variable (see "SELECT INTO Statement for Assigning Row to Record Variable").
If the query might return multiple rows, but you care about only the nth row, then restrict the result set to that row with the clause WHERE ROWNUM=n. For more information about the ROWNUM pseudocolumn, see Oracle Database SQL Language Reference.

第一个反应是不是我看错了,居然可以通过WHERE ROWNUM = N来限制只返回第N条记录 ,再仔细看了一遍,并和10g的文档对比,发现11.2和10.2中的不同。于是第二个反应是Oracle在11.2中提供了新特性,使得PL/SQL语句中直接SELECT可以通过WHERE ROWNUM来直接控制游标,于是特意在11.2上进行了测试,发现结果和10.2上没有区别,ROWNUM = N是行不通的,除非N等于1。

SQL> select * from v$version;

BANNER
------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE   11.2.0.1.0     Production
TNS for 32-bit Windows: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production

SQL> select * from tab;

TNAME                         TABTYPE CLUSTERID
------------------------------ ------- ----------
COMPANIES                     TABLE
MLOG$_T_F                     TABLE
MLOG$_T_P                     TABLE
MV_T_ORACLE                   TABLE
SERVICES                      TABLE
SERVICE_RATES                 TABLE
SERVICE_USAGE                 TABLE
SERVICE_USERS                 TABLE
T                             TABLE
T_DEFER                       TABLE
T_F                           TABLE
T_LOAD_LOB                    TABLE
T_P                           TABLE
T_PART                        TABLE

已选择14行。

SQL> set serverout on
SQL> declare
 2 v_name varchar2(30);
 3 begin
 4 select tname into v_name from tab where rownum = 5;
 5 dbms_output.put_line(v_name);
 6 exception
 7 when no_data_found then
 8 dbms_output.put_line('rownum equal the num bigger than 1 is incorrect!');
 9 end;
 10 /
rownum equal the num bigger than 1 is incorrect!

PL/SQL过程已成功完成。

显然Oracle文档这里出现了严重的错误,如果要使用ROWNUM来控制返回第几行结果,那么至少需要2层嵌套查询才可以。

最后简单总结一下ROWNUM,很多人都知道ROWNUM只适用于小于或小于等于,如果进行等于判断,那么只能等于1,不能进行大于的比较。但是却并不了解造成这种限制条件的机制是什么。

其实ROWNUM的返回很简单,ROWNUM总是从1开始,不管当前的记录是否满足查询结果,ROWNUM返回的值都是1,如果这条记录的值最终满足所有的条件,那么ROWNUM会递加,下一条记录的ROWNUM会返回2,否则下一条记录的ROWNUM仍然返回1。

理解了这一点,就清楚为什么一般的ROWNUM大于某个值或等于某个不为1的值是无法返回结果的,因此对于每条记录的ROWNUM都是1,而ROWNUM为1不满足查询的结果,所以下一条记录的ROWNUM不会递增,仍然是1,因此所有的记录都不满足条件。

了解了原理,就可以很容易的写出ROWNUM大于某值的例子:

SQL> select * from tab where rownum = 1 or rownum > 1;

TNAME                         TABTYPE CLUSTERID
------------------------------ ------- ----------
COMPANIES                     TABLE
MLOG$_T_F                     TABLE
MLOG$_T_P                     TABLE
MV_T_ORACLE                   TABLE
SERVICES                      TABLE
SERVICE_RATES                 TABLE
SERVICE_USAGE                 TABLE
SERVICE_USERS                 TABLE
T                             TABLE
T_DEFER                       TABLE
T_F                           TABLE
T_LOAD_LOB                    TABLE
T_P                           TABLE
T_PART                        TABLE

posted @ 2012-08-17 14:53 chen11-1| 编辑 收藏

物化视图刷新出现临时空间不足的问题

朋友解决一个物化视图刷新时碰到的问题。

 

 

数据库版本为10.2.0.4,一次本地聚集物化视图的快速刷新执行了3个小时后出现了临时表空间不足的错误:

ORA-12008: error in materialized view refresh path
ORA-01652: unable to extend temp segment by 32 in tablespace TEMP01

这个物化视图以前的刷新是正常的,只是最近偶尔会出现这个错误。上次出现这个错误,tb通过添加临时数据文件并重启数据库解决了问题。目前数据库的临时表空间已经超过1T的大小,如果不找到问题的原因,仅靠通过添加临时文件显然是不现实的。

通过跟踪刷新物化视图的会话,发现问题确实出在物化视图的快速刷新操作上,而会话的等待事件主要集中在临时表空间的写操作上:direct path write temp。

做了一个awrsql报告,检查了SQL语句和执行计划。由于这个SQL本身相对比较复杂,这就使得快速刷新的MERGE语句更加复杂,简单格式化后,语句长度超过300行,这里就不列出来了。这个SQL的执行计划为:

Execution Plan
Id  Operation  Name  Rows  Bytes  TempSpc Cost (%CPU) Time
0  MERGE STATEMENT      129K(100) 
1  MERGE  MV_NW_KHXX_YDKH_ALL      
2  VIEW       
3  NESTED LOOPS OUTER   4  1592   129K (1) 00:38:46
4  VIEW   4  1084   129K (1) 00:38:46
5  TEMP TABLE TRANSFORMATION       
6  LOAD AS SELECT       
7  VIEW   33  3300   123 (1) 00:00:03
8  WINDOW SORT   33  6600   123 (1) 00:00:03
9  TABLE ACCESS FULL  MLOG$_MV_NW_KHXX_YDKH_2  33  6600   122 (0) 00:00:03
10  LOAD AS SELECT       
11  VIEW   19151  2244K  3533 (1) 00:01:04
12  WINDOW SORT   19151  4114K 9376K 3533 (1) 00:01:04
13  TABLE ACCESS FULL  MLOG$_MV_NW_KHXX_YDKH_1  19151  4114K  3169 (1) 00:00:58
14  SORT GROUP BY   4  1040   125K (1) 00:37:40
15  VIEW   4  1040   125K (1) 00:37:40
16  UNION-ALL       
17  HASH JOIN   1  289   21023 (1) 00:06:19
18  HASH JOIN   1  220   20984 (1) 00:06:18
19  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  1  171   20982 (1) 00:06:18
20  VIEW   33  1617   2 (0) 00:00:01
21  TABLE ACCESS FULL  SYS_TEMP_0FD9D6744_E9EB0662  33  2013   2 (0) 00:00:01
22  VIEW   19151  1290K  38 (0) 00:00:01
23  TABLE ACCESS FULL  SYS_TEMP_0FD9D6745_E9EB0662  19151  1514K  38 (0) 00:00:01
24  HASH JOIN ANTI   1  281   41338 (1) 00:12:25
25  HASH JOIN   1  267   41300 (1) 00:12:24
26  HASH JOIN   1  220   20984 (1) 00:06:18
27  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  1  171   20982 (1) 00:06:18
28  VIEW   33  1617   2 (0) 00:00:01
29  TABLE ACCESS FULL  SYS_TEMP_0FD9D6744_E9EB0662  33  2013   2 (0) 00:00:01
30  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_1  9008K 403M  20279 (1) 00:06:06
31  VIEW   19151  261K  38 (0) 00:00:01
32  TABLE ACCESS FULL  SYS_TEMP_0FD9D6745_E9EB0662  19151  1514K  38 (0) 00:00:01
33  HASH JOIN ANTI   1  284   21437 (1) 00:06:26
34  HASH JOIN   1  270   21435 (1) 00:06:26
35  HASH JOIN   1  240   21020 (1) 00:06:19
36  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  1  171   20982 (1) 00:06:18
37  VIEW   19151  1290K  38 (0) 00:00:01
38  TABLE ACCESS FULL  SYS_TEMP_0FD9D6745_E9EB0662  19151  1514K  38 (0) 00:00:01
39  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_2  283K 8293K  413 (2) 00:00:08
40  VIEW   33  462   2 (0) 00:00:01
41  TABLE ACCESS FULL  SYS_TEMP_0FD9D6744_E9EB0662  33  2013   2 (0) 00:00:01
42  HASH JOIN ANTI   1  276   41753 (1) 00:12:32
43  HASH JOIN ANTI   1  262   41714 (1) 00:12:31
44  HASH JOIN   1  248   41711 (1) 00:12:31
45  HASH JOIN   1  201   21396 (1) 00:06:26
46  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  1  171   20982 (1) 00:06:18
47  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_2  283K 8293K  413 (2) 00:00:08
48  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_1  9008K 403M  20279 (1) 00:06:06
49  VIEW   33  462   2 (0) 00:00:01
50  TABLE ACCESS FULL  SYS_TEMP_0FD9D6744_E9EB0662  33  2013   2 (0) 00:00:01
51  VIEW   19151  261K  38 (0) 00:00:01
52  TABLE ACCESS FULL  SYS_TEMP_0FD9D6745_E9EB0662  19151  1514K  38 (0) 00:00:01
53  MAT_VIEW ACCESS BY INDEX ROWID MV_NW_KHXX_YDKH_ALL  1  127   2 (0) 00:00:01
54  INDEX UNIQUE SCAN  I_SNAP$_MV_NW_KHXX_YDKH_AL  1    1 (0) 00:00:01
 

从执行计划上看出一些疑问,MLOG$_DW_YH_JBXX表的结果只有1行,而实际上这张表的大小超过100W。

在默认情况下,收集SCHEMA的统计信息是不会收集物化视图日志的,而且即使Oracle收集统计信息时可以收集物化视图日志的统计信息,对于当前的情况,也无济于事。因为当前刷新的物化视图是第一个嵌套物化视图,它建立在其他两个物化视图的基础上,也就是说,只有刷新了其他两个物化视图之后,对应的物化视图日志中才会有记录,而其他时候,物化视图日志中的记录都是0。

其实现在问题已经确定了,由于物化视图日志没有统计信息,Oracle认为物化视图日志中记录很少,产生了一个最外层为NESTED LOOP的执行计划,导致刷新效率十分低下。对于这种情况,其实可以通过一次完全刷新来解决问题,但是对于当前的情况,仍然是不可行的。因为这个物化视图是数据仓库系统中的一个中间结果表,下游还有很多物化视图以及其他系统依赖于当前物化视图的增量数据。一旦这个物化视图执行了完全刷新,就会导致所有依赖当前对象的下游物化视图的增量刷新变成了完全刷新。

当前快速刷新碰到的问题其实就是Oracle的默认策略认为物化视图日志中的数据量应该远小于基表的数据量,这样快速刷新才会有性能上的优势,但是当前情况下,物化视图日志的数据量和基表的数据量处于同一个数量级,因此缺少统计信息后,快速刷新的执行计划变得十分的低效。而如果采用完全刷新来解决当前物化视图的问题,那么实际上是把这个问题扩大到下游所有有依赖关系的物化视图上。

为了解决这个问题,首先想到的是optimizer_dynamic_samping参数,通过设置会话级的参数,控制物化视图日志刷新之前,进行详细的动态统计信息采样,使之可以得到一个适合的执行计划。

但是将optimizer_dynamic_samping设置为10后,发现对MLOG$_DW_YH_JBXX表不起任何作用,刷新的执行计划中,MLOG$_DW_YH_JBXX表的行数仍然为1。

看来没有别的办法,只有手工显示的对物化视图日志表执行统计信息的收集工作,当统计信息收集完成后,再次运行物化视图的快速刷新,结果用了不到10分钟的时间,物化视图就刷新成功了。

这次执行计划变为:

Execution Plan

Id  Operation  Name  Rows  Bytes  TempSpc Cost (%CPU) Time
0  MERGE STATEMENT      141K(100) 
1  MERGE  MV_NW_KHXX_YDKH_ALL      
2  VIEW       
3  HASH JOIN OUTER   16997  6606K 4704K 141K (1) 00:42:19
4  VIEW   16997  4498K  129K (1) 00:38:50
5  TEMP TABLE TRANSFORMATION       
6  LOAD AS SELECT       
7  VIEW   33  3300   123 (1) 00:00:03
8  WINDOW SORT   33  6600   123 (1) 00:00:03
9  TABLE ACCESS FULL  MLOG$_MV_NW_KHXX_YDKH_2  33  6600   122 (0) 00:00:03
10  LOAD AS SELECT       
11  VIEW   19151  2244K  3533 (1) 00:01:04
12  WINDOW SORT   19151  4114K 9376K 3533 (1) 00:01:04
13  TABLE ACCESS FULL  MLOG$_MV_NW_KHXX_YDKH_1  19151  4114K  3169 (1) 00:00:58
14  SORT GROUP BY   16997  4315K  125K (1) 00:37:44
15  VIEW   16997  4315K  125K (1) 00:37:44
16  UNION-ALL       
17  HASH JOIN   267  50196   21078 (1) 00:06:20
18  HASH JOIN   191  26549   21076 (1) 00:06:20
19  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  11859  810K  21037 (1) 00:06:19
20  VIEW   19151  1290K  38 (0) 00:00:01
21  TABLE ACCESS FULL  SYS_TEMP_0FD9D674D_E9EB0662  19151  1514K  38 (0) 00:00:01
22  VIEW   33  1617   2 (0) 00:00:01
23  TABLE ACCESS FULL  SYS_TEMP_0FD9D674C_E9EB0662  33  2013   2 (0) 00:00:01
24  HASH JOIN   16188  2845K  41394 (1) 00:12:26
25  VIEW   33  1617   2 (0) 00:00:01
26  TABLE ACCESS FULL  SYS_TEMP_0FD9D674C_E9EB0662  33  2013   2 (0) 00:00:01
27  HASH JOIN RIGHT ANTI   11594  1483K  41391 (1) 00:12:26
28  VIEW   19151  261K  38 (0) 00:00:01
29  TABLE ACCESS FULL  SYS_TEMP_0FD9D674D_E9EB0662  19151  1514K  38 (0) 00:00:01
30  HASH JOIN   11594  1324K  41352 (1) 00:12:25
31  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  11859  810K  21037 (1) 00:06:19
32  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_1  9008K 403M  20279 (1) 00:06:06
33  HASH JOIN ANTI   9  1647   21492 (1) 00:06:27
34  HASH JOIN   9  1521   21490 (1) 00:06:27
35  HASH JOIN   191  26549   21076 (1) 00:06:20
36  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  11859  810K  21037 (1) 00:06:19
37  VIEW   19151  1290K  38 (0) 00:00:01
38  TABLE ACCESS FULL  SYS_TEMP_0FD9D674D_E9EB0662  19151  1514K  38 (0) 00:00:01
39  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_2  283K 8293K  413 (2) 00:00:08
40  VIEW   33  462   2 (0) 00:00:01
41  TABLE ACCESS FULL  SYS_TEMP_0FD9D674C_E9EB0662  33  2013   2 (0) 00:00:01
42  HASH JOIN ANTI   533  93275   41808 (1) 00:12:33
43  HASH JOIN RIGHT ANTI   533  85813   41769 (1) 00:12:32
44  VIEW   33  462   2 (0) 00:00:01
45  TABLE ACCESS FULL  SYS_TEMP_0FD9D674C_E9EB0662  33  2013   2 (0) 00:00:01
46  HASH JOIN   533  78351   41766 (1) 00:12:32
47  HASH JOIN   11594  1324K  41352 (1) 00:12:25
48  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  11859  810K  21037 (1) 00:06:19
49  MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_1  9008K 403M  20279 (1) 00:06:06
50  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_2  283K 8293K  413 (2) 00:00:08
51  VIEW   19151  261K  38 (0) 00:00:01
52  TABLE ACCESS FULL  SYS_TEMP_0FD9D674D_E9EB0662  19151  1514K  38 (0) 00:00:01
53  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_ALL  1701K 206M  3888 (2) 00:01:10

可以看到,最外层的执行计划已经变成了HASH JOIN OUTER,而且虽然物化视图日志MLOG$_DW_YH_JBXX的统计信息仍然少了2个数量级,但是比原本的1条记录要靠谱多了。

至此,问题得以解决,不过为了避免这种情况的再次发生,最好的办法是将物化视图日志的统计信息收集工作放到物化视图刷新之前进行,这样可以确保物化视图的快速刷新可以得到最精确的统计信息,从而得到最优的执行计划。

posted @ 2012-08-17 09:36 chen11-1| 编辑 收藏

提高代码可读性的十大注释技巧

很多程序员在写代码的时候往往都不注意代码的可读性,让别人在阅读代码时花费更多的时间。其实,只要程序员在写代码的时候,注意为代码加注释,并以合理的格式为代码加注释,这样就方便别人查看代码,也方便自己以后查看了。下面分享十个加注释的技巧:

1. 逐层注释

为每个代码块添加注释,并在每一层使用统一的注释方法和风格。例如:

针对每个类:包括摘要信息、作者信息、以及最近修改日期等;

针对每个方法:包括用途、功能、参数和返回值等。

在团队工作中,采用标准化的注释尤为重要。当然,使用注释规范和工具(例如C#里的XML,Java里的Javadoc)可以tb更好的推动注释工作完成得更好。

2. 使用分段注释

如果有多个代码块,而每个代码块完成一个单一任务,则在每个代码块前添加一个注释来向读者说明这段代码的功能。例子如下:

// Check that all data records
// are correct
foreach (Record record in records)
...{
if (rec.checkStatus()==Status.OK)
...{
. . .
}
}
// Now we begin to perform
// transactions
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
. . .

3. 在代码行后添加注释

如果多行代码的每行都要添加注释,则在每行代码后添加该行的注释,这将很容易理解。例如:

const MAX_ITEMS = 10; // maximum number of packets
const MASK = 0x1F;    // mask bit TCP
在分隔代码和注释时,有的开发者使用tab键,而另一些则使用空格键。然而由于tab键在各编辑器和IDE工具之间的表现不一致,因此最好的方法还是使用空格键。

4. 不要侮辱读者的智慧

避免以下显而易见的注释:写这些无用的注释会浪费你的时间,并将转移读者对该代码细节的理解。

if (a == 5)      // if a equals 5
counter = 0; // set the counter to zero

5. 礼貌点

避免粗鲁的注释,如:“注意,愚蠢的使用者才会输入一个负数”或“刚修复的这个问题出于最初的无能开发者之手”。这样的注释能够反映到它的作者是多么的拙劣,你也永远不知道谁将会阅读这些注释,可能是:你的老板,客户,或者是你刚才侮辱过的无能开发者。

6. 关注要点

不要写过多的需要转意且不易理解的注释。避免ASCII艺术,搞笑,诗情画意,hyperverbosity的注释。简而言之,保持注释简单直接。

7. 使用一致的注释风格

一些人坚信注释应该写到能被非编程者理解的程度。而其他的人则认为注释只要能被开发人员理解就行了。无论如何,Successful Strategies for Commenting Code已经规定和阐述了注释的一致性和针对的读者。就个人而言,我怀疑大部分非编程人员将会去阅读代码,因此注释应该是针对其他的开发者而言。

8. 使用特有的标签

在一个团队工作中工作时,为了便于与其它程序员沟通,应该采用一致的标签集进行注释。例如,在很多团队中用TODO标签表示该代码段还需要额外的工作。

int Estimate(int x, int y)
...{
// TODO: implement the calculations
return 0;
}
注释标签切忌不要用于解释代码,它只是引起注意或传递信息。如果你使用这个技巧,记得追踪并确认这些信息所表示的是什么。

9. 在代码时添加注释

在写代码时就添加注释,这时在你脑海里的是清晰完整的思路。如果在代码最后再添加同样注释,它将多花费你一倍的时间。而“我没有时间写注释”,“我很忙”和“项目已经延期了”这都是不愿写注释而找的借口。一些开发者觉得应该write comments before code,用于理清头绪。例如:

public void ProcessOrder()
...{
// Make sure the products are available
// Check that the customer is valid
// Send the order to the store
// Generate bill
}

10. 为自己注释代码

当注释代码时,要考虑到不仅将来维护你代码的开发人员要看,而且你自己也可能要看。用Phil Haack大师的话来说就是:“一旦一行代码显示屏幕上,你也就成了这段代码的维护者”。因此,对于我们写得好(差)的注释而言,我们将是第一个受益者(受害者)。

posted @ 2012-08-16 14:50 chen11-1 阅读(1753) | 评论 (2)编辑 收藏

PHP-FPM高负载的解决办法

这里只是介绍了php-fpm的优化方法的,但一般情况下和nginx组合使用的时候,单独优化其中一项的话,作用不是特别的大,同时还需要对nginx进行优化.nginx的做法方法参考:http://blog.haohtml.com/archives/6213.上面的优化前和优化后的图,看得出前后差距还是特别的大的.

导致nginx 502 bad gateway的PHP-CGI(FASTCGI)

NGINX频爆502 BAD GATEWAY的错误,看了网上的教程,仍没有彻底解决。

目前我总结的解决502 BAD GATEWAY的方式有:

1.视服务器的性能,在php-fmp.conf里增加max_children的值,我目前用的15.

2.用reload参数定时重载php-fpm。这个主要原因是php脚本执行时间过长造成的,重载php-fpm能杜绝这个问题。如何彻底解决php-cgi脚本占用大量内存从而导致502错误的产生还值得进一步探讨,目前该做法不失为一种好办法。

具体的做法是,用crontab让php-fpm平滑重启,从而不影响PHP脚本的tb运行。

*/10 * * * * /usr/local/php/sbin/php-fpm reload

=================== 优化设置 =========================


When you running a highload website with PHP-FPM via FastCGI, the following tips may be useful to you : )

如果您高负载网站使用PHP-FPM管理FastCGI,这些技巧也许对您有用:)

1. Compile PHP’s modules as less as possible, the simple the best (fast);

1.尽量少安装PHP模块,最简单是最好(快)的

2. Increas PHP FastCGI child number to 100 and even more. Sometime, 200 is OK! ( On 4GB memory server);

2.把您的PHP FastCGI子进程数调到100或以上,在4G内存的服务器上200就可以

注:我的1g测试机,开64个是最好的,建议使用压力测试获取最佳值

3. Using SOCKET PHP FastCGI, and put into /dev/shm on Linux;

3.使用socket连接FastCGI,linux操作系统可以放在 /dev/shm中

注:在php-fpm.cnf里设置<value name=”listen_address”>/tmp/nginx.socket</value>就可以通过socket连接FastCGI了,/dev/shm是内存文件系统,放在内存中肯定会快了.记得这时也要在nginx里的配置里进行修改,保持一致.

location ~ .*\.(php|php5)?$
{
#将Nginx与FastCGI的通信方式由TCP改为Unix Socket。TCP在高并发访问下比Unix Socket稳定,但Unix Socket速度要比TCP快。
fastcgi_pass  unix:/tmp/php-cgi.sock;
#fastcgi_pass  127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}

4. Increase Linux “max open files”, using the following command (must be root):

# echo ‘ulimit -HSn 65536′ >> /etc/profile
 # echo ‘ulimit -HSn 65536 >> /etc/rc.local
 # source /etc/profile 


4.调高linux内核打开文件数量,可以使用这些命令(必须是root帐号)

echo ‘ulimit -HSn 65536′ >> /etc/profile
 echo ‘ulimit -HSn 65536′ >> /etc/rc.local
 source /etc/profile 


注:我是修改/etc/rc.local,加入ulimit -SHn 51200的

5. Increase PHP-FPM open file description rlimit:

# vi /path/to/php-fpm.conf
 Find “<value name=”rlimit_files”>1024</value>”
 Change 1024 to 4096 or higher number.
 Restart PHP-FPM.


5. 增加 PHP-FPM 打开文件描述符的限制:

# vi /path/to/php-fpm.conf
 找到“<value name=”rlimit_files”>1024</value>”
 把1024 更改为 4096 或者更高.
重启 PHP-FPM.


6. Using PHP code accelerator, e.g eAccelerator, XCache. And set “cache_dir” to /dev/shm on Linux.
6.使用php代码加速器,例如 eAccelerator, XCache.在linux平台上可以把`cache_dir`指向 /dev/shm

至于其它的优化见李宴的bltbog一篇文章:http://blog.s135.com/post/375/

This entry was posted in js框架 and tagged nginx, php-fpm by admin. Bookmark the permalink.

One thought on “PHP-FPM高负载的解决办法

posted @ 2012-08-16 14:48 chen11-1| 编辑 收藏

php下用iconv函数转换字符编码的问题

昨天在调试 WAP 网站时发现,在增加了 GB2312 到 UTF-8 转化以后,有些页面显示不正常了——有些页面只有一半的内容,另一半被截掉了。因为被截掉的部分包含了<p>的后半个标签</p>,因此整个页面都显示不出来,而报告错误。经过猜测、尝试,最后终于把问题集中在了 iconv 函数上。在经过高人指点以后,发现这个函数的第二个参数,除了可以指定要转化到的编码以外,还可以增加两个后缀://TRANSLIT 和 //IGNORE,其中 //TRANSLIT 会自动将不能直接转化的字符变成一个或多个近似的字符,//IGNORE 会忽略掉不能转化的字符,而默认效果是从第一个非法字符截断。但是我尝试了//TRANSLIT 和 //IGNORE 这两个后缀,效果还是不对。于是我想问题可能不是出在这里。

从 GB2312 到 UTF-8 转化应该不会有不能转化的字符,因为 UTF-8 的字符集完全包含了 GB2312 中的字符,所以我tb想大概是前面要转化的字符集指定错了,于是我尝试着把 GB2312 改成 GBK

$ary=addslashes(iconv("GB2312", "UTF-8", $ary));

问题解决!虽然那两个后缀在这里没派上用场,不过也算学了一招,以后肯定会用到的。补记:改成 GBK 后,发现仍然有一封邮件的内容解析不正确。在另一位高人指点下,先换成 GB18030,问题依旧,然后改用 mb_convert_encoding 进行转换,问题解决!不知道是 mb_convert_encoding 问题,还是我的系统问题,我用 mb_convert_encoding 时不支持 GB18030 编码。另外,用 GBK 或者GB18030 作为输入编码,并在输出编码中加上 //IGNORE 后缀,用 iconv 函数也能解决那封含有错误编码的邮件内容解析不正确的问题。不过用 mb_convert_encoding 可以指定多种输入编码,它会根据内容自动识别,这个比 iconv 要好的多。 这里可以将iconv改成从gbk到utf8的转换,不使用gb2312.

$ary=addslashes(iconv("GBK", "UTF-8", $ary));

其实,同事在生成图片文字水印的时候也遇到了这种问题,同事最初用的是GB2312字符集,结果直接报错,说是字符串的offset有问题,但仔细检查后却没有这种问题。后来才发现是直接调用的这个iconv转换出错了。
原来的转换是从gb2312往 UTF8转换,表面上确实没有什么问题,然而,现在的人特别爱装酷,受影响的那位同志,用的是繁体字,繁体字的字库大多情况是属于GBK的,所以后来换成GBK后就正常了。
估计以后再遇上用火星文的朋友,就真的只能使用andot提出的这种方法了。转换成18030,再使用ignore参数。哈哈

mbstring好象最初的版本里没有使用,如果换成这个,估计代码工作量非常大,先将就着点了

posted @ 2012-08-16 14:45 chen11-1| 编辑 收藏

忘记 VSS Admin 密码 ! .

一不小心将VSS 6 admin用户的密码忘记(再此证明我的粗心),Google了一番,找到以下信息

the secret is to hack the um.dat file to remove the Admin password

from offset 80 the bytes are (all numbers are hex)

0:80  55 55 bc 7f 41 64 6d 69 6e 00 00 00 00 00 00 00
0:90  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0:a0  00 00 00 00 90 6e 00 00 a8 01 00 00 00 00 00 00

Just load the um.dat file into a hex editor and change the bytes from =
offset 80 to exactly what is shown above. When the SourceSafe admin tool =
starts it believes that the admin password has never been set.

the hex values above are taken from a 'virgin' um.dat file

of course, you didn't get this from me....and ALWAYS backup first (just =
in case I'm wrong)

有朋友看不懂,我用中文解释一下

如果忘记了密码,打开你vss数据库所在的文件夹,打开data目录,
找到um.dat文件,用hex编辑器打开编辑它,从offset 80的55 55 开始
将值改为如上文所述的样子,然后保存,这样um.dat文件就回到了
状态(virgin ?  :)),然后打开vss admin,用admin用户登录,tb不需要
密码了。

posted @ 2012-08-16 10:48 chen11-1| 编辑 收藏

jquery.validate remote 和 自定义验证方法

$(function(){

var validator = $("#enterRegForm").validate({
debug:false, //调试模式取消submit的默认提交功能
//errorClass: "error",//默认为错误的样式类为:error
//validClass: "check",//验证成功后的样式,默认字符串valid
focusInvalid: true,//表单提交时,焦点会指向第一个没有通过验证的域
//focusCleanup:true;//焦点指向错误域时,隐藏错误信息,不可与focusInvalid一起使用!
onkeyup: true,
errorElement: "div",
submitHandler: function(form){ //表单提交句柄,为一回调函数,带一个参数:form
form.submit(); //提交表单
},

rules: {
"enterprise.enName": {
required: true,
minlength: 6,
remote:{
url: "/nameServlet",     //后台处理程序
type: "get",               //数据发送方式
dataType: "json",           //接受数据格式
data: {                     //要传递的数据
enName: function() {
return $("#enName").val();
}
}

}
},

"user.passWord":{
required:true,
rangelength:[6,18]
},
passWordConf:{
required:true,
rangelength:[6,18],
equalTo:"#passWord" //此处的passWord 是<input id="passWord"> 一开始还以为是name的值呢,气死了
}

},

messages: { //自定义验证消息
"enterprise.enName": {
required: "请填写企业名称!",
minlength: $.format("至少要{0}个字符!"),
remote:$.format("该企业名称已存在!")

},
"user.passWord":{
required:"请填写确认密码!",
rangelength:$.format("密码要在{0}-{1}个字符之间!")
},
passWordConf:{
required:"请填写确认密码!",
rangelength:$.format("确认密码要在{0}-{1}个字符之间!"),
equalTo:"确认密码要和密码一致!"
},

errorPlacement: function(error, element) { //验证消息放置的地方
//error.appendTo( element.parent("td").next("td").children(".msg") );
error.appendTo( element.parent(".field").next("div"));
},
highlight: function(element, errorClass) { //针对验证的表单设置高亮
$(element).addClass(errorClass);
},
success: function(div) {
div.addClass("valid");
}
});

});

自定义方法;

新建一个js文件:$(document).ready(function(){
// 字符最小长度验证(一个中文字符长度为2)
jQuery.validator.addMethod("stringMinLength", function(value, element, param) {
var length = value.length;
for ( var i = 0; i < value.length; i++) {
if (value.charCodeAt(i) > 127) {
length++;
}
}
return this.optional(element) || (length >= param);
}, $.validator.format("长度不能小于{0}!"));

// 字符最大长度验证(一个中文字符长度为2)
jQuery.validator.addMethod("stringMaxLength", function(value, element, param) {
var length = value.length;
for ( var i = 0; i < value.length; i++) {
if (value.charCodeAt(i) > 127) {
length++;
}
}
return this.optional(element) || (length <= param);
}, $.validator.format("长度不能大于{0}!"));

// 字符验证
jQuery.validator.addMethod("stringCheck", function(value, element) {
return this.optional(element) || /^[\u0391-\uFFE5\w]+$/.test(value);
}, "只能包括中文字、英文字母、数字和下划线");

// 中文字两个字节
jQuery.validator.addMethod("byteRangeLength", function(value, element, param) {
var length = value.length;
for(var i = 0; i < value.length; i++){
if(value.charCodeAt(i) > 127){
length++;
}
}
return this.optional(element) || ( length >= param[0] && length <= param[1] );
}, "请确保输入的值在3-15个字节之间(一个中文字算2个字节)");

// 字符验证
jQuery.validator.addMethod("string", function(value, element) {
return this.optional(element) || /^[\u0391-\uFFE5\w]+$/.test(value);
}, "不允许包含特殊符号!");
// 必须以特定字符串开头验证
jQuery.validator.addMethod("begin", function(value, element, param) {
var begin = new RegExp("^" + param);
return this.optional(element) || (begin.test(value));
}, $.validator.format("必须以 {0} 开头!"));
// 验证两次输入值是否不相同
jQuery.validator.addMethod("notEqualTo", function(value, element, param) {
return value != $(param).val();
}, $.validator.format("两次输入不能相同!"));
// 验证值不允许与特定值等于
jQuery.validator.addMethod("notEqual", function(value, element, param) {
return value != param;
}, $.validator.format("输入值不允许为{0}!"));

// 验证值必须大于特定值(不能等于)
jQuery.validator.addMethod("gt", function(value, element, param) {
return value > param;
}, $.validator.format("输入值必须大于{0}!"));

// 验证值小数位数不能超过两位
jQuery.validator.addMethod("decimal", function(value, element) {
var decimal = /^-?\d+(\.\d{1,2})?$/;
return this.optional(element) || (decimal.test(value));
}, $.validator.format("小数位数不能超过两位!"));
//字母数字
jQuery.validator.addMethod("alnum", function(value, element) {
return this.optional(element) || /^[a-zA-Z0-9]+$/.test(value);
}, "只能包括英文字母和数字");
// 汉字
jQuery.validator.addMethod("chcharacter", function(value, element) {
var tel = /^[\u4e00-\u9fa5]+$/;
return this.optional(element) || (tel.test(value));
}, "请输入汉字");
// 身份证号码验证
jQuery.validator.addMethod("isIdCardNo", function(value, element) {
return this.optional(element) || /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$/.test(value)||/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[A-Z])$/.test(value);
}, "请正确输入您的身份证号码");

// 手机号码验证
jQuery.validator.addMethod("isMobile", function(value, element) {
var length = value.length;
var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/;
return this.optional(element) || (length == 11 && mobile.test(value));
}, "请tb正确填写您的手机号码");

// 电话号码验证
jQuery.validator.addMethod("isTel", function(value, element) {
var tel = /^\d{3,4}-?\d{7,9}$/;    //电话号码格式010-12345678
return this.optional(element) || (tel.test(value));
}, "请正确填写您的电话号码");

// 联系电话(手机/电话皆可)验证
jQuery.validator.addMethod("isPhone", function(value,element) {
var length = value.length;
var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/;
var tel = /^\d{3,4}-?\d{7,9}$/;
return this.optional(element) || (tel.test(value) || mobile.test(value));

}, "请正确填写您的联系电话");

// 邮政编码验证
jQuery.validator.addMethod("isZipCode", function(value, element) {
var tel = /^[0-9]{6}$/;
return this.optional(element) || (tel.test(value));
}, "请正确填写您的邮政编码");

});

该文件要先被引用再对form进行验证。

注意:remote 中的url 为servlet,但是这个servlet该怎么写呢

一开始以为是跟平时的一样:out.println("用户名"+name+"已存在!");

虽然可以验证,但是还是存在问题:输入个不存在的name还是提示存在。

后来还是Google,看到别人也是犯了这样的错误,解决方法时返回true,false.

但是 doGet 是void类型的,return true 是行不通的。

后来又请教群里的高手,正确写法是out.println("true");就可以了。

哎,费了老长时间

总算是把问题给解决了。

posted @ 2012-08-15 15:02 chen11-1| 编辑 收藏

jQuery mouseover mouseout事件在IE下闪烁的解决方法

$("#category ul").find("li").each(
    function() {
        $(this).mouseover(
            function() {
                $("#" + this.id + "_menu").show();
                $(this).addClass("a");
            }
        );
        $(this).mouseout(
            function() {
                $(this).removeClass("a");
                $("#" + this.id + "_menu").hide();
            }
        );
    }
);

浏览器之间的不兼容一直令前端开发者的头疼,而 IE 更是噩梦。鼠标在下拉菜单移动时菜单会不断闪烁,tb说明不断触发了 mouseover 和 mouseout 事件。

这貌似涉及到所谓的“事件冒泡”,我不懂 JavaScript,就不在误人子弟了,详情请自己 Google,这里只给出解决方法:将 mouseover 改成 mouseentermouseout 改成 mouseleave

$("#category ul").find("li").each(
    function() {
        $(this).mouseenter(
            function() {
                $("#" + this.id + "_menu").show();
                $(this).addClass("a");
            }
        );
        $(this).mouseleave(
            function() {
                $(this).removeClass("a");
                $("#" + this.id + "_menu").hide();
            }
        );
    }
);

最后指出一点,mouseenter 和 mouseleave 事件是 jQuery 库中实现的,似乎(再次声明我不懂 JavaScript,所以这里用“似乎”)并不是浏览器的原生事件,所以如果你想自己写 JavaScript 实现的话,请自行 Google,或者查看 jQuery 源码,如果你能看懂的话。

参考链接:JQuery HowTo: Problems with jQuery mouseover / mouseout events

转自:http://demon.tw/programming/jquery-mouseover-mouseout.html

posted @ 2012-08-15 15:01 chen11-1| 编辑 收藏

仅列出标题
共20页: First 上一页 5 6 7 8 9 10 11 12 13 下一页 Last