最近在做一个用户管理方面的需求,用户只能修改自己创建的用户以及派生用户,提交保存时需要对用户进行判断和识别是否为派生用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
--测试用脚本
create table T_SYS_USER
(
id INTEGER,
name VARCHAR2(20),
password VARCHAR2(20),
created_by VARCHAR2(20)
);
/
--id为主键 name唯一
insert into T_SYS_USER values (1, 'admin', '', 'admin');
insert into T_SYS_USER values (2, 'root', '', 'admin');
insert into T_SYS_USER values (3, 'user', '', 'root');
insert into T_SYS_USER values (4, 'dba', '', 'user');
insert into T_SYS_USER values (5, 'yuanrui', '', 'dba');
insert into T_SYS_USER values (6, 'banana', '', 'yuanrui');
insert into T_SYS_USER values (7, 'dog', '', 'banana');
insert into T_SYS_USER values (8, 'cat', '', 'dog');
insert into T_SYS_USER values (9, 'pig', '', 'cat');
insert into T_SYS_USER values (10, 'fish', '', 'pig');

commit
/

查询用户yuanrui的子账户

1
2
3
4
select level, id, name, created_by 
from T_SYS_USER
start with id = 5
connect by prior UPPER(name) = UPPER(created_by)

结果

LEVLE ID NAME CREATED_BY
1 5 yuanrui dba
2 6 banana yuanrui
3 7 dog banana
4 8 cat dog
5 9 pig cat
6 10 fish pig

查询用户yuanrui的父账户

1
2
3
4
select  level, id, name, created_by 
from T_SYS_USER
start with id = 5
connect by prior UPPER(created_by) = UPPER(name)

结果

LEVLE ID NAME CREATED_BY
1 5 yuanrui dba
2 4 dba user
3 3 user root
4 2 root admin
5 1 admin  

在实际使用情况中,递归查询有时候还是会抛出异常,主要原因是数据不正确造成的。
举个例子

1
2
3
4
5
6
7
8
9
update T_SYS_USER
set created_by = name
where id = 1;
commit
/
select level, id, name, created_by
from T_SYS_USER
start with id = 5
connect by prior UPPER(created_by) = UPPER(name)

执行时会抛出异常:ORA-01436:用户数据中的 CONNECT BY 循环
在CONNECT BY后加上nocycle可以结束循环调用
1
2
3
4
select  level, id, name, created_by 
from T_SYS_USER
start with id = 5
connect by prior UPPER(created_by) = UPPER(name)

注意结果有所变化,编号为1的数据行被排除掉了

LEVLE ID NAME CREATED_BY
1 5 yuanrui dba
2 4 dba user
3 3 user root
4 2 root admin

以前玩MS SQL的时候,玩过用公用表表达式(Common Table Expressions)做递归。网上查找Oracle Sql递归大多数结果都是关于用CONNECT BY来实现的,在Oracle中其实也可以用CTE来做递归。注意版本问题,本地测试环境Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production可用,听说11g r2之前的版本不支持,求证?
给一个CTE版本查询示例,查询结果是差不多的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
--查询用户yuanrui的子账户
with userCte(level_, id, name, created_by) as
(
select 1 as level_, id, name, created_by
from T_SYS_USER
where id =5
union all
select b.level_ + 1 as level_, a.id, a.name, a.created_by
from T_SYS_USER a, userCte b
where UPPER(a.created_by) = UPPER(b.name)
and a.created_by <> a.name
)
select * from userCte;

--查询用户yuanrui的父账户
with userCte(level_, id, name, created_by) as
(
select 1 as level_, id, name, created_by
from T_SYS_USER
where id =5
union all
select b.level_ + 1 as level_, a.id, a.name, a.created_by
from T_SYS_USER a, userCte b
where UPPER(a.name) = UPPER(b.created_by)
and a.created_by <> a.name
)
select * from userCte;

where id=5为起始条件,union all 后面的子查询where UPPER(a.name) = UPPER(b.created_by) 为连接条件
注意where条件中的and a.created_by <> a.name, 这个条件主要用于结束循环(对应上文中的nocycle)

其他参考:http://www.cnblogs.com/yingsong/p/5035907.html

最近在把玩.net core, 顺便学习下用VS Code开发。
记录下开发步骤
1、安装.net core sdk. 地址:https://www.microsoft.com/net/download/core
2、下载并安装VS Code, 地址:https://code.visualstudio.com/
3、验证dotnet
安装完.net core sdk后,在命令行中用dotnet -version进行验证
netcore-version
正常情况下会显示对应的版本号
4、创建dotnet项目
使用dotnet new -t Console命令创建控制台项目
create project
创建完成后,对应目录会出现生成的Program.cs和project.json文件
同时执行dotnet restore命令
dotnet restore
5、调试项目
备注:如果是第一使用VS Code调试代码,还需要下载OmniSharp和.NET Core Debugger,注意观看控制台中的提示和右下角的Downloading packages. 下载安装完成后才可进行后续调试。提示:下载过程中不要关闭VS Code.
vs code debug error

使用Ctrl+Shift+D进行调试项目,会发现默认没有配置
debug project
选择.NET Core创建配置,创建完成后还需要对配置进行修改。eg.”program”: “${workspaceRoot}/bin/Debug/netcoreapp1.1/Test.dll”,
change config
修改完成相关配置后,可进行调试,出现经典的Hello World!
hello world

done!

Update statement

假设有如下表(T_SCORE_LEVEL)

ID SCORE LEVEL
1 60 0
2 70 0
3 75 0
4 65 0
5 95 0
6 82 0

需要更新LEVEL字段,更新规则:
Score大于等于90小于等于100,Level=1;
Score大于等于80小于等于90,Level=2;
Score大于等于70小于等于80,Level=3;
Score大于等于60小于等于70,Level=4;
Score小于60,Level=5;
请写出对应的sql

1
2
3
4
5
6
7
8
9
10
11
12
UPDATE T_SCORE_LEVEL
SET LEVEL = (
CASE
WHEN SCORE >= 90 AND SCORE <= 100 THEN 1
WHEN SCORE >= 80 AND SCORE < 90 THEN 2
WHEN SCORE >= 70 AND SCORE < 80 THEN 3
WHEN SCORE >= 60 AND SCORE < 70 THEN 4
WHEN SCORE < 60 THEN 5
);

COMMIT
/

用户相关

创建用户

1
2
--用户名test 密码123456
create user test identified by 123456;

修改用户密码
1
2
--用户名test 密码test
alter user test identified by test;

为用户指定默认表空间
1
2
3
4
--默认表空间test_DB 临时表空间test_temp
alter user test
default tablespace test_DB
temporary tablespace test_temp;

授予用户权限
1
2
3
4
5
6
7
grant 
  CREATE SESSION, CREATE ANY TABLE, CREATE ANY VIEW ,CREATE ANY INDEX, CREATE ANY PROCEDURE,
  ALTER ANY TABLE, ALTER ANY PROCEDURE,
  DROP ANY TABLE, DROP ANY VIEW, DROP ANY INDEX, DROP ANY PROCEDURE,
  SELECT ANY TABLE, INSERT ANY TABLE, UPDATE ANY TABLE, DELETE ANY TABLE
  to test;
grant resource, dba to test;

回收权限
1
revoke resource, dba from test;

表空间管理

表空间大小与数据块大小(db_block_size)有关

数据块大小 表空间文件最大值M 表空间文件最大值G
4k 16383M 16G
8k 32767M 32G
16k 65535M 64G
32k 131072M 128G
64k 262144M 256G

数据块是4k的数据库,单个表空间数据文件的限制是小于16G,而不是小于等于16G。

查询数据块大小大小

1
show parameter db_block_size

查询表空间使用情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SELECT UPPER(F.TABLESPACE_NAME) AS "表空间名称",
ROUND(D.AVAILB_BYTES,2) AS "表空间大小(G)",
ROUND(D.MAX_BYTES,2) AS "最终表空间大小(G)",
ROUND((D.AVAILB_BYTES - F.USED_BYTES),2) AS "已使用空间(G)",
TO_CHAR(ROUND((D.AVAILB_BYTES - F.USED_BYTES) / D.AVAILB_BYTES * 100, 2), '999.99') AS "使用比",
ROUND(F.USED_BYTES, 6) AS "空闲空间(G)",
F.MAX_BYTES AS "最大块(M)"
FROM
( SELECT TABLESPACE_NAME,
ROUND(SUM(BYTES) / (1024 * 1024 * 1024), 6) USED_BYTES,
ROUND(MAX(BYTES) / (1024 * 1024 * 1024), 6) MAX_BYTES
FROM SYS.DBA_FREE_SPACE
GROUP BY TABLESPACE_NAME) F,
(SELECT DD.TABLESPACE_NAME,
ROUND(SUM(DD.BYTES) / (1024 * 1024 * 1024), 6) AVAILB_BYTES,
ROUND(SUM(DECODE(DD.MAXBYTES, 0, DD.BYTES, DD.MAXBYTES))/(1024*1024*1024),6) MAX_BYTES
FROM SYS.DBA_DATA_FILES DD
GROUP BY DD.TABLESPACE_NAME) D
WHERE D.TABLESPACE_NAME = F.TABLESPACE_NAME
ORDER BY 4 DESC

查询表空间文件地址、是否自动增长、数据文件最大值、自动增长值(8k block)

1
select TABLESPACE_NAME,FILE_NAME,AUTOEXTENSIBLE,MAXBYTES / 1024 / 1024 / 1024,INCREMENT_BY*8192/1024/1024 from dba_data_files

增加表空间数据文件
1
2
3
4
alter tablespace test_DB
add datafile 'D:\ORADB\test_DB_2.DBF' size 32M
AUTOEXTEND ON
NEXT 32M MAXSIZE 32767M;

调整数据文件大小
1
2
alter database datafile 'D:\ORADB\test_DB_2.DBF'
RESIZE 64M;

设置已存在数据文件为自动增长并设置最大值
1
2
alter database datafile 'D:\ORADB\test_DB_2.DBF'
AUTOEXTEND ON NEXT 32M MAXSIZE 32767M;

删除数据文件
1
2
alter tablespace test_DB
drop datafile 'D:\ORADB\test_DB_2.DBF';

移动表空间数据文件
1
2
3
4
5
6
7
--1.将表空间离线
alter tablespace test_DB offline normal;
--2.移动数据文件目录
--3.重命名表空间文件
alter database rename file 'D:\ORADB\test_DB.DBF' to 'D:\Oracle\Data\test_DB.DBF';
--4.表空间联机
alter tablespace test_DB online;

查看表空间或索引占用的存储空间大小

1
2
3
4
select segment_name, segment_type, sum(bytes)/1024/1024 MB
from user_segments
group by segment_name, segment_type
order by 3 desc

查询引用主键的外键以及外键表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
select a.Owner 外键拥有者,
a.Table_Name 外键表,
c.Column_Name 外键列,
b.Owner 主键拥有者,
b.Table_Name 主键表,
d.Column_Name 主键列,
c.Constraint_Name 外键名,
d.Constraint_Name 主键名
from User_Constraints a,
user_Constraints b,
user_Cons_Columns c, --外键表
user_Cons_Columns d --主键表
where a.r_Constraint_Name = b.Constraint_Name
and a.Constraint_Type = 'R'
and b.Constraint_Type = 'P'
and a.r_Owner = b.Owner
and a.Constraint_Name = c.Constraint_Name
and b.Constraint_Name = d.Constraint_Name
and a.Owner = c.Owner
and a.Table_Name = c.Table_Name
and b.Owner = d.Owner
and b.Table_Name = d.Table_Name
and b.Table_Name = 'EMP'

取前N条分页数据提示
采用row_number进行高效分页
参考:http://www.oracle.com/technetwork/issue-archive/2007/07-jan/o17asktom-093877.html
示例

1
2
3
4
5
6
7
8
9
select *
from (
select /*+ first_rows(25) */
your_columns,
row_number()
over (order by something unique)rn
from your_tables )
where rn between :n and :m
order by rn;

结果集缓存提示
用于缓存长期稳定的小表数据
参考:http://www.oracle.com/technetwork/articles/sql/11g-caching-pooling-088320.html
示例

1
SELECT /*+ result_cache */ * FROM Your_Tables;

字符串是否为空或为空格

1
2
3
function (value) {
return value == null || /\S/.test(value);
}

邮箱地址格式验证

1
2
3
4
5
6
7
function (value) {
//https: //html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address
///^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/

//form 163
return !/^[._-]?(?:[a-z0-9]+[._-]?)+[a-z0-9]?@[a-z0-9](?:[.-]?[a-z0-9]+)*$/i.test(value)
}

车辆号牌格式

1
2
3
function (value) {
return /(^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[A-Z0-9]{6}$)|(^[A-Z]{2}[A-Z0-9]{2}[A-Z0-9\u4E00-\u9FA5]{1}[A-Z0-9]{4}$)|(^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[A-Z0-9]{5}[挂学警军港澳]{1}$)|(^[A-Z]{2}[0-9]{5}$)|(^(08|38){1}[A-Z0-9]{4}[A-Z0-9挂学警军港澳]{1}$)/.test(value);
}

手机号码

1
2
3
4
5
function (value) {
var length = value.length;
var re = /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1}))+\d{8})$/;
return (length == 11 && re.test(value));
}

计算字符串长度

1
2
3
4
function (value) {
var utfStr = value.match(/[^\x00-\xff]/ig);
return value.length + (utfStr == null ? 0 : utfStr.length);
}

问题描述

在一般的Web后台管理系统的使用过程中,大多都是在单窗口中操作,用户所有的操作都在同一个标签页面中。
有时候会出现这种情况:
1、用户将当前页面的Url在新的标签页中打开;
2、在新的标签页面中注销当前登陆账户。
3、再返回当前页面进行操作(假设界面上的CRUD操作都是通过Ajax请求处理),这个时候的任何Ajax操作都不会返回正确的结果,如果不刷新页面,页面不会重定向到登陆页面;
解决问题的办法很简单,让用户刷新下页面就行。但是有时候用户是不知道需要刷新的。特别是打开后台管理系统,长期不操作界面,造成服务器端Session过期,需要用户重新登陆系统。
对于追求后台管理系统傻瓜式操作的追求来说,刷新页面并不是我们想要的结果。

Ajax调用处理过程

浏览器发起Ajax请求后,正常情况下调用目标地址相关方法并返回结果。服务器端的Session过期或失效后,Ajax请求的地址将会被重定向到登陆页面,返回的结果是登陆页面的html。

解决方案

搞清楚Ajax调用处理过程后,解决问题的主要关键在于,如何判别返回的结果是不是登陆页面。如果返回的结果是登陆页面,则将window.location设置为登陆页的地址,否则不做处理。
一个简单的解决方案是在登陆页中添加一个特殊的Header. 同时在母版页中使用Jquery注册ajaxComplete事件,捕获之前设置的Header, 如果存在跳转到登陆页面,不存在则不做处理。
登陆页相关代码

1
2
3
4
5
6
7
8
[HttpGet, AllowAnonymous]
public ActionResult Login(string ReturnUrl)
{
this.HttpContext.Response.AddHeader("__LoginUrl", "/Home/Login");
ViewBag.ReturnUrl = ReturnUrl;

return View();
}

母版页(_Layout.cshtml)中的Js事件处理
1
2
3
4
5
6
7
8
9
10
11
12
<script src="https://code.jquery.com/jquery-1.12.4.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ajaxComplete(function(event, jqxhr, settings){
if (jqxhr.status == 200) {
var loginPageUrl = jqxhr.getResponseHeader("__LoginUrl");
if(loginPageUrl && loginPageUrl !== ""){
window.location.replace(loginPageUrl);
}
return;
}
});
</script>

之前考虑过,使用ajaxError事件来处理跳转。因为实际使用过程中Ajax传输的数据大多都是Json,重定向到登陆页后,将Html转化为Json时会抛异常同时触发ajaxError事件。使用ajaxError对于ajaxComplete来说,只有Ajax有异常的情况下才处理,不用处理判断每次Ajax请求,相对效率高一点。
由于我们项目的弹窗表单都是采用的Ajax Get请求加载的,返回的是Html,不会触发ajaxError事件。权衡之后,还是使用ajaxComplete.

顺便说一下,这里也可以用Cookie来替代Header, 具体处理方式类似这里不再复述。唯一需要注意的是,如何控制Cookie过期的问题。

参考链接:http://stackoverflow.com/questions/199099/how-to-manage-a-redirect-request-after-a-jquery-ajax-call

相关配置数据无效,节点被锁定

问题描述:在Win10 x64位 IIS 10环境中,部署Asp.Net Mvc 3站点时,报“HTTP 错误 500.19 - Internal Server Error
无法访问请求的页面,因为该页的相关配置数据无效。”
配置错误:不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况。锁定是默认设置的(overrideModeDefault=”Deny”),或者是通过包含 overrideMode=”Deny” 或旧有的 allowOverride=”false” 的位置标记明确设置的。
错误描述 0x80070021

解决办法
1、在控制面板中打开”程序和功能”;
2、在程序和功能中,打开”启用或关闭Windows功能”;
3、选择”Internet Infomation Services” -> “应用程序开发功能”;
4、排除”CGI”选项
Windows功能选项
参考链接:http://stackoverflow.com/questions/9794985/iis-this-configuration-section-cannot-be-used-at-this-path-configuration-lock

Sql大部分从《Expert Oracle Database Architecture》一书拷贝而来,http://www.apress.com/9781430229469

查看当前数据库连接以及正在执行的Sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
select a.SID, a.SERIAL#,
'alter system kill session ''' || a.sid || ', ' || a.serial# || ''';' AS KILL_SESSION_SQL,
a.USERNAME,
b.Cpu_Time/1000000 AS CPU_TIME_SEC,
STATUS,
b.SQL_TEXT,
paddr,
b.SQL_ID,
a.OSUSER,
a.PROGRAM,
a.machine
from V$SESSION a
left join V$SQLAREA b on a.prev_sql_addr = b.ADDRESS
left join v$process c on a.paddr = c.addr
where a.username is not null

查看表空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
create or replace procedure show_space
( p_segname in varchar2,
p_owner in varchar2 default user,
p_type in varchar2 default 'TABLE',
p_partition in varchar2 default NULL )
-- this procedure uses authid current user so it can query DBA_*
-- views using privileges from a ROLE and so it can be installed
-- once per database, instead of once per user that wanted to use it
authid current_user
as
l_free_blks number;
l_total_blocks number;
l_total_bytes number;
l_unused_blocks number;
l_unused_bytes number;
l_LastUsedExtFileId number;
l_LastUsedExtBlockId number;
l_LAST_USED_BLOCK number;
l_segment_space_mgmt varchar2(255);
l_unformatted_blocks number;
l_unformatted_bytes number;
l_fs1_blocks number; l_fs1_bytes number;
l_fs2_blocks number; l_fs2_bytes number;
l_fs3_blocks number; l_fs3_bytes number;
l_fs4_blocks number; l_fs4_bytes number;
l_full_blocks number; l_full_bytes number;

-- inline procedure to print out numbers nicely formatted
-- with a simple label
procedure p( p_label in varchar2, p_num in number )
is
begin
dbms_output.put_line( rpad(p_label,40,'.') ||
to_char(p_num,'999,999,999,999') );
end;
begin
-- this query is executed dynamically in order to allow this procedure
-- to be created by a user who has access to DBA_SEGMENTS/TABLESPACES
-- via a role as is customary.
-- NOTE: at runtime, the invoker MUST have access to these two
-- views!
-- this query determines if the object is a ASSM object or not
begin
execute immediate
'select ts.segment_space_management
from dba_segments seg, dba_tablespaces ts
where seg.segment_name = :p_segname
and (:p_partition is null or
seg.partition_name = :p_partition)
and seg.owner = :p_owner
and seg.segment_type = :p_type
and seg.tablespace_name = ts.tablespace_name'
into l_segment_space_mgmt
using p_segname, p_partition, p_partition, p_owner, p_type;
exception
when too_many_rows then
dbms_output.put_line
( 'This must be a partitioned table, use p_partition => ');
return;
end;


-- if the object is in an ASSM tablespace, we must use this API
-- call to get space information, else we use the FREE_BLOCKS
-- API for the user managed segments
if l_segment_space_mgmt = 'AUTO'
then
dbms_space.space_usage
( p_owner, p_segname, p_type, l_unformatted_blocks,
l_unformatted_bytes, l_fs1_blocks, l_fs1_bytes,
l_fs2_blocks, l_fs2_bytes, l_fs3_blocks, l_fs3_bytes,
l_fs4_blocks, l_fs4_bytes, l_full_blocks, l_full_bytes, p_partition);

p( 'Unformatted Blocks ', l_unformatted_blocks );
p( 'FS1 Blocks (0-25) ', l_fs1_blocks );
p( 'FS2 Blocks (25-50) ', l_fs2_blocks );
p( 'FS3 Blocks (50-75) ', l_fs3_blocks );
p( 'FS4 Blocks (75-100)', l_fs4_blocks );
p( 'Full Blocks ', l_full_blocks );
else
dbms_space.free_blocks(
segment_owner => p_owner,
segment_name => p_segname,
segment_type => p_type,
freelist_group_id => 0,
free_blks => l_free_blks);

p( 'Free Blocks', l_free_blks );
end if;

-- and then the unused space API call to get the rest of the
-- information
dbms_space.unused_space
( segment_owner => p_owner,
segment_name => p_segname,
segment_type => p_type,
partition_name => p_partition,
total_blocks => l_total_blocks,
total_bytes => l_total_bytes,
unused_blocks => l_unused_blocks,
unused_bytes => l_unused_bytes,
LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId,
LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId,
LAST_USED_BLOCK => l_LAST_USED_BLOCK );

p( 'Total Blocks', l_total_blocks );
p( 'Total Bytes', l_total_bytes );
p( 'Total MBytes', trunc(l_total_bytes/1024/1024) );
p( 'Unused Blocks', l_unused_blocks );
p( 'Unused Bytes', l_unused_bytes );
p( 'Last Used Ext FileId', l_LastUsedExtFileId );
p( 'Last Used Ext BlockId', l_LastUsedExtBlockId );
p( 'Last Used Block', l_LAST_USED_BLOCK );
end;

使用方式

1
2
SQL> set serveroutput on;
SQL> exec show_space('table_name');

执行统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
set echo on

drop table run_stats;
create global temporary table run_stats
( runid varchar2(15),
name varchar2(80),
value int )
on commit preserve rows;

grant select any table to ops$tkyte;
create or replace view stats
as select 'STAT...' || a.name name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
union all
select 'LATCH.' || name, gets
from v$latch
union all
select 'STAT...Elapsed Time', hsecs from v$timer;


delete from run_stats;
commit;

create or replace package runstats_pkg
as
procedure rs_start;
procedure rs_middle;
procedure rs_stop( p_difference_threshold in number default 0 );
end;
/

create or replace package body runstats_pkg
as

g_start number;
g_run1 number;
g_run2 number;

procedure rs_start
is
begin
delete from run_stats;

insert into run_stats
select 'before', stats.* from stats;

g_start := dbms_utility.get_cpu_time;
end;

procedure rs_middle
is
begin
g_run1 := (dbms_utility.get_cpu_time-g_start);

insert into run_stats
select 'after 1', stats.* from stats;
g_start := dbms_utility.get_cpu_time;

end;

procedure rs_stop(p_difference_threshold in number default 0)
is
begin
g_run2 := (dbms_utility.get_cpu_time-g_start);

dbms_output.put_line
( 'Run1 ran in ' || g_run1 || ' cpu hsecs' );
dbms_output.put_line
( 'Run2 ran in ' || g_run2 || ' cpu hsecs' );
if ( g_run2 <> 0 )
then
dbms_output.put_line
( 'run 1 ran in ' || round(g_run1/g_run2*100,2) ||
'% of the time' );
end if;
dbms_output.put_line( chr(9) );

insert into run_stats
select 'after 2', stats.* from stats;

dbms_output.put_line
( rpad( 'Name', 30 ) || lpad( 'Run1', 12 ) ||
lpad( 'Run2', 12 ) || lpad( 'Diff', 12 ) );

for x in
( select rpad( a.name, 30 ) ||
to_char( b.value-a.value, '999,999,999' ) ||
to_char( c.value-b.value, '999,999,999' ) ||
to_char( ( (c.value-b.value)-(b.value-a.value)), '999,999,999' ) data
from run_stats a, run_stats b, run_stats c
where a.name = b.name
and b.name = c.name
and a.runid = 'before'
and b.runid = 'after 1'
and c.runid = 'after 2'
-- and (c.value-a.value) > 0
and abs( (c.value-b.value) - (b.value-a.value) )
> p_difference_threshold
order by abs( (c.value-b.value)-(b.value-a.value))
) loop
dbms_output.put_line( x.data );
end loop;

dbms_output.put_line( chr(9) );
dbms_output.put_line
( 'Run1 latches total versus runs -- difference and pct' );
dbms_output.put_line
( lpad( 'Run1', 12 ) || lpad( 'Run2', 12 ) ||
lpad( 'Diff', 12 ) || lpad( 'Pct', 10 ) );

for x in
( select to_char( run1, '999,999,999' ) ||
to_char( run2, '999,999,999' ) ||
to_char( diff, '999,999,999' ) ||
to_char( round( run1/decode( run2, 0, to_number(0), run2) *100,2 ), '99,999.99' ) || '%' data
from ( select sum(b.value-a.value) run1, sum(c.value-b.value) run2,
sum( (c.value-b.value)-(b.value-a.value)) diff
from run_stats a, run_stats b, run_stats c
where a.name = b.name
and b.name = c.name
and a.runid = 'before'
and b.runid = 'after 1'
and c.runid = 'after 2'
and a.name like 'LATCH%'
)
) loop
dbms_output.put_line( x.data );
end loop;
end;

end;
/

调用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
exec runStats_pkg.rs_start;
exec runStats_pkg.rs_middle;
exec runStats_pkg.rs_stop;
*/

set echo off
set verify off
column diff format a18
select a.name, b.value Value, to_char(b.value-&V,'999,999,999,999') diff
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) like '%' || lower('&S')||'%'
/
set echo on

逻辑读和物理读统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
select ses.username, ses.sid, ses.status,    
max(decode(sta.name, 'session logical reads', sest.value)) as "LogicalRead",
max(decode(sta.name, 'physical reads', sest.value)) as "PhysicalRead",
round(max(decode(sta.name, 'session logical reads', sest.value)) /
(3600 * 24 * (sysdate - ses.logon_time)), 2) as "LogicalRead/S",
round(max(decode(sta.name, 'physical reads', sest.value)) /
(3600 * 24 * (sysdate - ses.logon_time)), 2) as "PhysicalRead/S",
trunc(60 * 24 * (sysdate - ses.logon_time)) as "Minutes"
from v$session ses, v$sesstat sest, v$statname sta, v$sqlarea sarea
where ses.sid = sest.sid and sest.statistic# = sta.statistic#
and sta.name in ('session logical reads', 'physical reads')
and ses.username is not null
group by ses.username, ses.sid, ses.status, ses.logon_time
order by 1, 2;

为什么需要版本号处理

在项目开发部署过程中,经常会修改样式和脚本文件。在部署时经常会因为浏览器缓存的原因造成加载的样式和执行的脚本还是以前的版本。
解决浏览器缓存最常用的办法是,在服务器端渲染生成资源链接时,加上一个版本号,用于刷新客户端缓存。这个版本号只在第一次请求时重新加载资源文件,在后续的请求中从浏览器缓存中获取文件。
版本号相对来说是固定的,不是随机生成的。随机生成的版本号会造成每次加载页面时都会去服务器端请求数据,这不是我们想要的效果。

实现方式

生成版本号的方式有几种:
1、从配置文件中读取版本号
2、计算当前文件的hash值,用hash值作为版本号
3、使用程序集(DLL)发布时间作为版本号
第1种,发布部署时可以灵活控制,但是也容易忘记修改版本号,导致版本还是使用的以前的版本。也许可以通过自动发布的方式,自动修改版本号(没有尝试过)。
第2种,如果使用计算hash值的方式来处理,文件每次请求时都会去计算hash值。每次请求都计算hash有点消耗资源,改进办法是将路径和计算后的值放大缓存中,先判断hash值是否存在,不存在则计算并加入到缓存中。
第3种,可以将版本值的计算设计为静态类,并在静态类的静态构造方法中初始化静态变量,静态构造方法只需执行一次。每次发布替换DLL后,静态类会重新初始化。相比1、2种方式,灵活而不失简便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static class VersionUtils
{
public readonly static DateTime VersionDate;

public readonly static Int32 VersionNumber;

static VersionUtils()
{
VersionDate = System.IO.File.GetLastWriteTime(typeof(VersionUtils).Assembly.Location);
VersionNumber = Int32.Parse(VersionDate.ToString("yyyyMMddHHmm"));
}
}

public static class HtmlHelperExtension
{
public static MvcHtmlString Script(this HtmlHelper html, string contentPath)
{
return VersionedContent(html, "<script src=\"{0}\" type=\"text/javascript\"></script>", contentPath);
}

public static MvcHtmlString Style(this HtmlHelper html, string contentPath)
{
return VersionedContent(html, "<link href=\"{0}\" rel=\"stylesheet\" type=\"text/css\">", contentPath);
}

private static MvcHtmlString VersionedContent(this HtmlHelper html, string template, string contentPath)
{
contentPath = UrlHelper.GenerateContentUrl(contentPath, html.ViewContext.HttpContext) + "?v=" + VersionUtils.VersionNumber;
return MvcHtmlString.Create(string.Format(template, contentPath));
}
}

public static class UrlHelperExtension
{
public static string ContentVersioned(this UrlHelper urlHelper, string contentPath)
{
return String.Format("{0}?v={1}", urlHelper.Content(contentPath), VersionUtils.VersionNumber);
}

}

使用方式

1
2
3
<link href="@Url.ContentVersioned("~/Content/Site.css")" rel="stylesheet" type="text/css" />
@Html.Style("~/Content/bootstrap.css");
@Html.Script("~/Scripts/angular.js");