朋友做一个项目,遇到一个多表联合查询的需求。

  • A表SYSTEM_ID和B表SYSTEM_ID关联;
  • C表中ROLE_LIST字段,存储多个B表中的ROLE_ID值;

需要一个sql,当A表中SYSTEM_ID值为123时,找到B表和C表的关联,当B表满足SYSTEM_ID值为123时,包含其中的ROLE_ID数据,显示C表中NAME数据。例如查询结果为:james、lucy.

T_TABLE_A表

ID SYSTEM_ID
1 123
2 234

T_TABLE_B表

ID SYSTEM_ID ROLE_ID
1 222 667
2 123 555
3 123 777
4 234 567
5 234 231

T_TABLE_C表

ID NAME ROLE_LIST
1 james 667,777
2 lucy 223,555,823
3 tom 253,231
4 max 123,712
5 min 123,567

最终提供的sql如下:

1
2
3
4
5
6
7
select name from t_table_c c
where exists (
select 1 from t_table_a a
inner join t_table_b b on a.system_id = b.system_id
where a.system_id = 123
and c.role_list like '%' || b.role_id || '%'
)

上面的查询sql采用的exists子句的方式,采用连接的方式也能完成相同的功能,具体实现见文末附录Sql.

从给的脱敏数据可以推测出各个表的功能。

  • A表是权限(资源)表;
  • B表是角色权限(资源)表;
  • C表用户表;

一般使用单独的表来存储用户和角色的关联信息,这里很巧妙的采用以逗号分隔的方式存储多个角色编号。采用这种方式可以减少表连接,但处理存储和查询的复杂度更高。

单纯从数据上来看,由于采用的模糊查询,有可能出现错误的查询结果。

假设C表中存在以下数据。

ID NAME ROLE_LIST
6 kim 1777,2211
7 kenv 220,1555,800

重新执行sql,你会发现查询结果里面包含:kim和kenv. 造成这个结果的原因是,模糊查询可以匹配ROLE_LIST列中部分数据,1777和1555分别包含777和555.

通过这个简单示例,可以发现存储多个值时确实包含一些局限性。我们既想一列存储多个数值,又想消除歧义,那么怎么解决这个问题呢?

给存储的值添加一个前缀,也许是一个好办法,比如:r555,r777,r1777,r1555,前缀采用代表特定含义的单词或字母,加上实际的数值,可以构造出一个特殊的字符串。条件子语句:like ‘%r555’. 可以避免示例数据中的错误查询结果问题。

等等,好像还是有点问题。如果字符串为:r55,r555,r1555,何解。

当然如果存在这种情况,我们还有一个终极解决办法(适用于任何情况):前缀+值+后缀

1
2
3
4
前缀+值+后缀

列存储示例:[55],[555],[1555]
条件子语句:like '%[55]%',这里前缀='['、值='55'、后缀=']'

一般情况下,使用特定单词或字母作为特殊前缀加取值可以满级绝大多数情况,这种方式可读信更高。而需要严格意义上的避免误查询,可以采用:前缀+值+后缀。

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
--附录Sql

create table t_table_a
(
id int primary key,
system_id int
);

create table t_table_b
(
id int primary key,
system_id int,
role_id int
);

create table t_table_c
(
id int primary key,
name varchar2(20),
role_list varchar2(200)
);

insert into t_table_a values(1, 123);
insert into t_table_a values(2, 234);
insert into t_table_a values(3, 100);

insert into t_table_b values(1, 222, 667);
insert into t_table_b values(2, 123, 555);
insert into t_table_b values(3, 123, 777);
insert into t_table_b values(4, 234, 567);
insert into t_table_b values(5, 234, 231);

insert into t_table_c values(1, 'james', '667,777');
insert into t_table_c values(2, 'lucy', '223,555,823');
insert into t_table_c values(3, 'tom', '253,231');
insert into t_table_c values(4, 'max', '123,712');
insert into t_table_c values(5, 'min', '123,567');
insert into t_table_c values(6, 'kim', '1777,2211');
insert into t_table_c values(7, 'kenv', '220,1555,800');

select name from t_table_c c
where exists (
select 1 from t_table_a a
inner join t_table_b b on a.system_id = b.system_id
where a.system_id = 123
and c.role_list like '%' || b.role_id || '%'
);

select c.name, b.id from t_table_a a
inner join t_table_b b on a.system_id = b.system_id
inner join t_table_c c on c.role_list like '%' || b.role_id || '%'
where a.system_id = 123;

On more thing.

这sql在Oracle中跑起来,没有任何问题。但是朋友拿去改造后在mysql中运行,出现的查询结果依旧不正常。排查了很长一段时间,才找到原因:在mysql中 ‘%’ || ‘S001’ || ‘%’ 显示打印为:1. 正确的方式是使用concat函数。所以如果你在mysql中运行,需要这样构造sql.

1
2
3
4
select c.name, b.id from t_table_a a
inner join t_table_b b on a.system_id = b.system_id
inner join t_table_c c on c.role_list like concat('%', b.role_id, '%')
where a.system_id = 123;

Python核心编程

第二版学习笔记

快速入门

Python是一门解释性语言,作为对比JavaScript也是解释性语言。学习的前提需要到官网(https://www.python.org) 下载并安装最新版本。Python源文件一般用.py作为扩展名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#script.py
#井号是注释符号

#定义一个变量
aValue = 'Hello World!'

#打印一个变量
print(aValue)

#定义一个函数
def print_string(astr):
'打印一个字符串' #文档字符串(注释)
print(astr)

#调用函数
print_string(aValue)

将示例文件另存为:script.py, 在Windows命令行中执行python script.py, 可以看到如下信息。

1
2
Hello World!
Hello World!

Python基础

语句和语法

  • 井号(#)之后的字符为Python的注释;
  • 换行(\n)是标准的行分隔符,一般一行一条语句;
  • 反斜杠()表示继续上一行,一行过长的语句使用反斜杠(\)可以分解成多行;
  • 分号(;)将两个语句连接在一起,如果需要将两条语句放到同一行中,需要使用分号隔开;
  • 冒号(:)将代码块的头和体分开,像if、while、def、class复合语句,首行以关键字开始,以冒号(:)结束,该行之后的一行或多行构成代码组,首行和代码组合起来称为一个子句;
  • 语句(代码块)用缩进的方式体现,不同的缩进深度分割不同的代码块,缩进相同的一组语句构成一个代码块,建议每个缩进使用4个空格表示;
  • Python文件以模块的形式进行组织。

变量赋值

等号是主要的赋值操作符。
增量操作符和C语言类似。
Python使用下划线作为变量前缀和后缀时为特殊变量。
Python支持多元赋值,示例如下:

1
x, y, z = 1, 2, 'Hello World!' 

标识符

标识符定义规则:第一个字符必须是字母或下划线,剩下的字符可以说字符、数字和下划线。标识符大小写敏感。

1
#不要使用dict、list、file、bool、str、input、len等内置类型作为变量名。

Python对象

基本数据类型

  • 数字
  • Integer 整型
  • Boolean 布尔型
  • Long integer 长整型
  • Floating point real number 浮点型
  • Complex number 复数类型
  • String 字符串
  • List 列表
  • Tuple 元组
  • Dictionary 字典

所有类型对象的类型都是type,这个概念有点类似于C#中所有类型都是由object派生出的一样。
Python中的特殊类型是None, 表示Null对象或NoneType. None类型类似于C#中的void, 值类似于null. None的布尔值总是为False.

操作符

对象值可以使用:>、<、==、>=、<=、!=等操作符进行比较。
对象引用方式根据赋值方式,有所不同。

1
2
3
4
5
6
7
#示例
#a1和a1引用的相同对象
a1 = a2 = 123

#b1和b2引用的不同对象
b1=123
b2=123

使用is、is not可以比较是否引用的同一对象。
1
2
3
4
5
#结果:True
a1 is a1

#结果:False
b1 is b2

布尔类型操作符:not、and、or.

内置函数

函数 功能
cmp(obj1,obj2) 比较obj1和obj2,根据比较结果返回整型值
repr(obj) 返回一个对象的字符表示,类似C#中ToString方法
str(obj) 返回可读性好的对象字符串,类似C#中ToString带格式的方法
type(obj) 获取对象的类型,返回相应的type对象

类型分类

数据类型 存储类型 更新模型 访问模型
数字 标量 不可更改 直接访问
字符串 标量 不可更改 顺序访问
列表 容器 可更改 顺序访问
元组 容器 不可更改 顺序访问
字典 容器 可更改 映射访问

标量:一个保存单个字面对象的类型,类似于C#中的值类型和字符串。
容器:可存储多个对象(对象可以有不同的类型)的类型,类似C#集合。
可变类型:允许值被更新,每次修改后新值替换旧值。类似C#引用类型。
不可变类型:不允许值被更改,每次修改后使用新的值替代;旧值被丢弃,等待垃圾回收器处理回收对象。类似C#值类型。
直接访问:对数值直接进行访问,类似C#中栈。
顺序访问:可对容器按索引进行访问元素,类似C#中索引。
映射访问:元素无序存放,通过唯一Key访问,类似C#中哈希。

数字

Python的整型包含:布尔型、标准整型、长整型。布尔型只有两个取值:True和False, 对应整型的1和0. 标准整型和C# Int32和Int64表示范围相同,在32位机器上范围为Int32, 64位则为Int64. 长整型的范围超过Int64, 具体范围与(虚拟)内存大小有关,长整型一般在数值后面加一个大写的L. Python会自动转换整型和长整型。
复数由实数部分和虚数部分构成,虚数语法:real+imagj. 实数和虚数部分都是浮点类型,虚数部分后缀必须为j或J.
两个连续的星号(**)表示幂运算.

1
3 ** 2 #结果为:9

整除操作符://, 也称“地板除”,除法不管操作数为何种数值类型,总是舍去小数部分,返回数字序列总别真正商小的最接近的数字。
1
2
3
1//2       #结果为:0
1.0//2.0 #结果为:0.0
-1//2 #结果为:-1

数值工厂函数

函数 操作
bool(obj) 返回obj对象的布尔值
int(x [,base]) 将x转换为一个整型
long(x [,base] ) 将x转换为一个长整型
float(x) 将x转换到一个浮点型
complex(real [,imag]) or complex(str) 创建一个复数

仅适用于整型的内置函数

函数 操作
hex(num) 将数字转换成十六进制数并以字符串形式返回
oct(num) 将数字转换成八进制数并以字符串形式返回
chr(num) 将ASCII值的数字转换成ASCII字符,范围:0<=num<=255
ord(chr) 传入长度为1的字符串,返回相应的ASCII整数值或Unicode整数值
unichr(num) 将一个整数转换为Unicode字符

序列:字符串、列表和元组

序列

序列元素顺序

1
2
3
            0      1      2           N-2     N-1
Sequence ☐ ☐ ☐ ● ● ● ☐ ☐
-N -(N-1) -(N-2) -2 -1

序列类型操作符

序列操作符 作用
seq[index] 获取下标为index的与元素
seq[index1:index2] 获取下标从index1到index2之间的元素
seq * expr 序列重复expr次
seq1 + seq2 连接序列seq1和seq2
obj in seq 判断元素obj是否包含在seq中
obj not in seq 判断元素obj是否不包含在seq中

切片操作符:[]、[:]、[::]

1
2
3
4
5
6
7
8
9
10
11
12
#切片示例
s = 'abcdefgh'

s = 'abcdefgh'
print(s) #abcdefgh 打印原始字符串
print(s[::-1]) #hgfedcba 字符串翻转操作
print(s[::2]) #aceg 隔一个取一个的操作
print(s[1:3]) #bc 获取索引下标1到2之间的元素
print(s[3:]) #defgh 获取索引下标从3开始的所有元素
print(s[-3:]) #fgh 获取索引-3开始之后的所有元素
print(s[:3]) #abc 从右往左获取前3个元素
print(s[:-3]) #abcde 从右往左获取前5个元素(长度8 - 3)

字符串

Python中使用单引号或双引号创建字符串,使用del语句删除字符串。

字符串进行比较操作时,按照ASCII值的大小进行比较。

字符串格式化操作符:%.

字符串格式化符号

格式化符 号 转换方式
%c 格式化字符及其ASCII码
%s 格式化字符串
%d 格式化整数
%u 格式化无符号整型
%o 格式化无符号八进制数
%x 格式化无符号十六进制数
%X 格式化无符号十六进制数(大写)
%f 格式化浮点数字,可指定小数点后的精度
%e 用科学计数法格式化浮点数
%E 作用同%e,用科学计数法格式化浮点数
%g %f和%e的简写
%G %F 和 %E 的简写
%p 用十六进制数格式化变量的地址

格式化操作符辅助指令

符号 作用
* 定义宽度或者小数点精度
- 用做左对齐
+ 在正数前面显示加号( + )
在正数前面显示空格
# 在八进制数前面显示零(‘0’),在十六进制前面显示’0x’或者’0X’(取决于用的是’x’还是’X’)
0 显示的数字前面填充’0’而不是默认的空格
% ‘%%’输出一个单一的’%’
(var) 映射变量(字典参数)
m.n. m 是显示的最小总宽度,n 是小数点后的位数(如果可用的话)
1
2
3
4
5
6
#格式示例
print('%x' % 100) #64
print('%#x' % 100) #0x64
print('%c' % 100) #d
print('value is %d' % 100) #value is 100
print('value is %s' % '100') #value is 100

字符串格式化的高级用法是使用字符串模板,引入Template模块对象,调用substitute()和safe_substitute()方法进行格式化。

1
2
3
from string import Template
s = Template('Today is ${year}.${month}.${day}!')
print (s.substitute(year=2020, month=2, day=6)) #Today is 2020.2.6!

注意:字符串中的模板必须与变量一一对应,否则会出现KeyError异常。而使用safe_substitute()在缺少key时可以将字符串原封不动的打印出来。

Unicode字符串在字符串前加大写U或小写u. 加前缀u表示告诉Python后面的字符串要编码成Unicode字符串。

1
print(u'\u8881')     #袁

在控制台界面中可以使用input(raw_input()为python2中的函数python3中不存在)函数进行输入字符串,类似于C#中的Console.ReadLine().

使用反斜杠()加一个单一字符可以表示一个特殊字符。

三引号语法允许一个字符串跨多行,字符串中可以包含换行符、制表符以及其他特殊字符。语法类似C# @””.

1
2
3
4
#示例
print('''Hello

World''')

列表

列表使用方括号([])进行定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
aList = [123, 'Hello', 'abc', 123.456, ['abcd', 100]]
print(aList) #[123, 'Hello', 'abc', 123.456, ['abcd', 100]]

#访问列表中值
print(aList[0]) #123

#更新列表中值
aList[1] = 'abcdefg'
print(aList) #[123, 'abcdefg', 'abc', 123.456, ['abcd', 100]]

#删除列表元素
del aList[0]
#或使用remove()方法
aList.remove('abc')
print(aList) #['abcdefg', 123.456, ['abcd', 100]]

#删除整个列表
del aList

元组

元组的功能和列表类似,元组使用圆括号进行定义。元组属于不可变类型,类似于C#中只读集合。元组中的元素不可跟新和删除,只能删除整个元组。

1
2
3
4
5
6
7
aTuple = (123, 'Hello', 'abc', 123.456, ['abcd', 100])

#访问元组中的元素
print(aTuple[1]) #Hello

#删除元组
del aTuple

字典和集合类型

字典

字典是Python语言中唯一的映射类型,可以简单理解为Hash集合。字典初始化有两种方式:{}和dict()函数。字典和JavaScript中的对象类似,都是使用大括号({})将key和value值进行包裹。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#字典示例
dict1 = {}
dict2 = {'name': 'wenwen', 'year': '2020', 'age': 3}
dict3 = dict((['x', 1], ['y', 2]))

#循环遍历字典中的值
for key in dict2:
print('key=%s, value=%s' % (key, dict2[key]))
#key=name, value=wenwen
#key=year, value=2020
#key=age, value=3

#获取特定元素
print(dict2['name']) #wenwen

#更新字典
dict2['year'] = 2000 #更新已有key值
dict2['sex'] = 'girl' #新增key value

#删除字典元素
del dict2['year'] #删除key为"year"的项
dict2.clear() #删除dict2中所有的项
del dict2 #删除整个dict2字典

不允许一个键对应多个值,当键冲突时,取最后(最近)的赋值。键必须是可哈希的,一般使用数字、字符串作为字典键。键必须是可哈希的原因是:解释器调用哈希函数,根据键中的值来计算存储位置,如果键是可变对象,它的值可能发生变化,导致哈希函数无法映射到原有的地址,无法获取数据。

字典获取特定元素时,若key不存在则会抛出:”name ‘dict’ is not defined“异常。使用setdefault()方法可以避免key不存在的问题。

1
2
print(dict2.setdefault('name', 'yuan'))      #wenwen
print(dict2.setdefault('height', 100)) #100

集合

集合元素是一组无序排列的可哈希的值。集合有两种类型:可变集合(set)和不可变集合(frozenset)。可变集合不能作为字典的键,也不能作为其他集合的元素。创建集合只能使用集合的工厂方法set()和frozenset().

集合类型操作符包括(所有集合类型):联合(|)、交集(&)、差补/相对补集(-)、对称差分(^)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
s = set('abcdefgh')
t = frozenset('python')

print(s) #{'g', 'h', 'b', 'c', 'd', 'e', 'f', 'a'}
print(t) #frozenset({'p', 'n', 'h', 'o', 'y', 't'})

#集合类型操作符

#联合(|)
print(s | t) #{'g', 'p', 'n', 'h', 'b', 'c', 'd', 'e', 'f', 'o', 'y', 'a', 't'}

#交集(&)
print(s & t) #{'h'}

#差补/相对补集(-)
print(s - t) #{'g', 'b', 'c', 'd', 'e', 'f', 'a'}

#对称差分(^)
print(s ^ t) #{'g', 'p', 'b', 'f', 'd', 'y', 'a', 'n', 'c', 'o', 'e', 't'}

仅适用于可变集合的操作符:|=、&=、-=、^=,功能和示例代码类似,不再做演示。

条件和循环

if语句语法

1
2
3
4
if expression:
exp_true_suite
else:
exp_false_suite

Python支持elif关键字,用于表示else-if.

1
2
3
4
5
6
7
8
9
10
if expression1:
expr1_true_suite
elif expression2:
expr2_true_suite
#.
#.
elif expressionN:
exprN_true_suite
else:
none_of_the_above_suite

条件表达式(三元操作符)语法:X if C else Y

1
2
3
4
5
6
7
8
#C、C++、C#、Java 三元操作符示例
int x = 1;
int y = 2;
int smaller = x < y ? x : y;

#Python 三元操作符示例
x, y = 1, 2
smaller = x if x < y else y

while语句语法

1
2
while expression:
suite_to_repeat

for语句类似于C#中foreach,语法如下:

1
2
for iter_var in iterable:
suite_to_repeat

使用enumerate()函数可获取索引和项。

1
2
3
4
5
6
7
8
9
#enumerate()函数示例
aList = [123, 'Hello', 'abc', 123.456, 'World']
for i, item in enumerate(aList):
print('Index:%d Item:%s' % (i, item))
#Index:0 Item:123
#Index:1 Item:Hello
#Index:2 Item:abc
#Index:3 Item:123.456
#Index:4 Item:World

range()函数语法:range(start, end, step=1), range()会返回一个包含所有k的列表,start <= k < end, 从start到end, k每次递增step. step不可为0.

1
2
3
4
5
6
7
aList = range(3, 19, 5)
for item in aList:
print('Value:%d' % item)
#Value:3
#Value:8
#Value:13
#Value:18

Python中break和continue语句用法和其他语言类似。

Python提供pass语句,它不做任何事情,用于开发调试。

1
2
3
4
5
#pass语句示例
if age == 5:
pass
else:
pass

Python支持迭代器,为类序列对象提供一个类序列的接口,类似C#中IEnumerator.

1
2
3
4
5
6
7
8
#iter()函数示例
aList = [123, 'Hello', 'abc', 123.456, 'World']
item = iter(aList)
print(item.__next__()) #123
print(item.__next__()) #Hello
print(item.__next__()) #abc

#备注:原文中使用的next()方法,在Python3中不存在。

Oracle官方出的OPD.NET托管驱动对于ADO.NET的参数绑定方面,让人看不懂。
如下面这个代码,注意SAL、COMM、DEPTNO的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	const string cmdText = @"
insert into emp (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
values (:EMPNO, :ENAME, :JOB, :MGR, :HIREDATE, :SAL, :COMM, :DEPTNO)";

var connStr = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.170)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ebos)));User ID=scott;Password=tiger;";
using (var conn = new OracleConnection(connStr))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = cmdText;
cmd.Parameters.Add("EMPNO", OracleDbType.Int16, 1, ParameterDirection.Input);
cmd.Parameters.Add("ENAME", OracleDbType.Varchar2, "ENAME".ToArray(), ParameterDirection.Input);
cmd.Parameters.Add("JOB", OracleDbType.Varchar2, "JOB", ParameterDirection.Input);
cmd.Parameters.Add("MGR", OracleDbType.Int32, 1, ParameterDirection.Input);
cmd.Parameters.Add("HIREDATE", OracleDbType.Date, DateTime.Now, ParameterDirection.Input);
cmd.Parameters.Add("DEPTNO", OracleDbType.Int32, 30, ParameterDirection.Input);
cmd.Parameters.Add("COMM", OracleDbType.Decimal, 20, ParameterDirection.Input);
cmd.Parameters.Add("SAL", OracleDbType.Decimal, 10, ParameterDirection.Input);

conn.Open();
var result = cmd.ExecuteNonQuery() > 0;
}
}

你想要的结果:SAL=10、COMM=20、DEPTNO=30。但实际上结果是这样的:
默认情况下BindName为false
很奇怪是吧,OPD.NET中默认情况下,参数绑定的不是依据名称而是根据顺序来传递的。
我们设置OracleCommand属性BindByName为true,再次执行插入语句代码。
1
cmd.BindByName = true;

EMPNO=2的行是我们想要的结果。
设置OracleCommand属性BindByName为true
那么每次通过设置BindByName=true,对于开发人员而言是一件痛苦的事情。当前不设置也没有关系,前提是你必须保证参数顺序的正确性。
有没有配置或全局属性可以将BindByName默认设置为true呢,Oracle官方考虑到这种情况,提供在web.config或app.config文件中设置默认值。
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
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="oracle.manageddataaccess.client"
type="OracleInternal.Common.ODPMSectionHandler, Oracle.ManagedDataAccess, Version=4.122.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342"/>
</configSections>
<system.data>
<DbProviderFactories>
<remove invariant="Oracle.ManagedDataAccess.Client"/>
<add name="ODP.NET, Managed Driver" invariant="Oracle.ManagedDataAccess.Client" description="Oracle Data Provider for .NET, Managed Driver"
type="Oracle.ManagedDataAccess.Client.OracleClientFactory, Oracle.ManagedDataAccess, Version=4.122.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342"/>
</DbProviderFactories>
</system.data>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<publisherPolicy apply="no"/>
<assemblyIdentity name="Oracle.ManagedDataAccess" publicKeyToken="89b483f429c47342" culture="neutral"/>
<bindingRedirect oldVersion="4.121.0.0 - 4.65535.65535.65535" newVersion="4.122.1.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<oracle.manageddataaccess.client>
<version number="*">
<settings>
<setting name="BindByName" value="true"/>
</settings>
</version>
</oracle.manageddataaccess.client>
</configuration>

为了验证配置是否生效,注释代码cmd.BindByName=true, 再次运行代码,结果如下所示。
在配置文件中设置BindByName为true
需要说明的是,代码中设置BindByName的优先级高于配置。

参考链接:
https://docs.oracle.com/cd/E63277_01/win.121/e63268/InstallManagedConfig.htm#ODPNT8160
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/odpnt/CommandBindByName.html#GUID-609B7F20-2444-4CBF-AC8A-A19907A626C8

EasyUI中timespinner最大值为23:59,项目中有一个需求需要显示24:00. 调试了下源代码,发现EasyUI,在赋值时调用parser函数先将字符串转换为Date对象,然后用获取到的Date对象与最大值和最小值进行比较。小于最小值时,将当前值设置为最小值,大于最大值时,当前值等于最大值。在获取到Date对象后,调用formatter函数进行格式转换。
最终的解决方案如下。

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
$.fn.timespinner.defaults.formatter = function(_23) {
if (!_23) {
return "";
}
var _24 = $(this).timespinner("options");
var tt = [_25(_23.getDay() * 24 + _23.getHours()), _25(_23.getMinutes())];
if (_24.showSeconds) {
tt.push(_25(_23.getSeconds()));
}
return tt.join(_24.separator);
function _25(_26) {
return (_26 < 10 ? "0": "") + _26;
};
};

$.fn.timespinner.defaults.parser = function(s) {
var _27 = $(this).timespinner("options");
var _28 = _29(s);
if (_28) {
var min = _29(_27.min || "00:00");
var max = _29(_27.max || "24:00");
if (min && min > _28) {
_28 = min;
}
if (max && max < _28) {
_28 = max;
}
}
return _28;
function _29(s) {
if (!s) {
return null;
}
var tt = s.split(_27.separator);
return new Date(1900, 0, 0, parseInt(tt[0], 10) || 0, parseInt(tt[1], 10) || 0, parseInt(tt[2], 10) || 0);
};
};

项目中有这样一个需求,在通过请求第三方WebService接口的结果中,包含一个图片内容为base64的字符串。
由于经常需要查看图片内容,于是在考虑能不能在WebService的方法测试中提供传入一个图片base64的参数然后预览图片的功能。
分析了一下WebService的调试页面的请求和返回结果,返回的内容是一个xml结构。既然能够返回xml那么问题就好办了。
html也是xml。只要构造特定的html就可以了。于是第一版本的预览是这样的。构造html,body中构造一个img标签用base64显示图片。

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
	//版本1 使用html预览显示
[WebMethod]
public void Base64ToImage(string @base64)
{
var result = string.Format(@"<!DOCTYPE HTML>
<html>
<head>
<meta charset='UTF-8'>
<title></title>
</head>
<body>
<img src='data:image/png;base64,{0}'/>
</body>
", @base64);

var context = HttpContext.Current;
if (context == null || context.Response == null)
{
return;
}

context.Response.Clear();
context.Response.ClearHeaders();
context.Response.Write(result);
context.Response.End();
}

但是考虑到img标签中用base64字符串来显示图片,在低版本浏览器的兼容性问题以及渲染问题。更进一步的想到直接向http写入输出流的方式来显示图片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//版本2 直接输出image类型
[WebMethod]
public void Base64ToImage(string @base64)
{
byte[] data = Convert.FromBase64String(@base64);
var context = HttpContext.Current;

if (context == null || context.Response == null)
{
return;
}

context.Response.Clear();
context.Response.ClearHeaders();
context.Response.ContentType = "image/jpeg";
context.Response.BinaryWrite(data);
context.Response.End();
}

crsctl帮助命令
E:\app\11.2.0\grid\BIN>crsctl -h
用法: crsctl add - 添加资源, 类型或其他实体
crsctl check - 检查服务, 资源或其他实体
crsctl config - 输出自动启动配置
crsctl debug - 获取或修改调试状态
crsctl delete - 删除资源, 类型或其他实体
crsctl disable - 禁用自动启动
crsctl enable - 启用自动启动
crsctl get - 获取实体值
crsctl getperm - 获取实体权限
crsctl lsmodules - 列出调试模块
crsctl modify - 修改资源, 类型或其他实体
crsctl query - 查询服务状态
crsctl pin - 在节点列表中固定节点
crsctl relocate - 重新定位资源, 服务器或其他实体
crsctl replace - 替换表决文件的位置
crsctl setperm - 设置实体权限
crsctl set - 设置实体值
crsctl start - 启动资源, 服务器或其他实体
crsctl status - 获取资源或其他实体的状态
crsctl stop - 停止资源, 服务器或其他实体
crsctl unpin - 在节点列表中取消固定节点
crsctl unset - 取消设置实体值, 还原其默认值

开机启用Oracle High Availability Services
crsctl enable has
开机禁用
crsctl disable has
查看状态
crsctl config has

参考 http://blog.chinaunix.net/uid-20687159-id-1895010.html

Q:在命令行中执行存储过程后,存储过程中调用的dbms_output.put_line()方法中的内容未显示
A:需要执行命令: set serverout on;

Q:执行存储过程或函数的过程中报错:ORA-20000: ORU-10027: buffer overflow, limit of 2000 bytes
A:serverout的size默认为2000,需要修改size, 命令: set serveroutput on size 1000000;

Q:在Windows中登陆sqlplus时报:ORA-12560: TNS: 协议适配器错误
A:检查Oracle Net Manager中相关配置:监听程序和服务命名。测试服务命名的数据库连接是否可用。检查监听服务是否开启。
如果以上参数均正常,那么用sqlplus登陆的时候带上用户名、密码和服务名试试。eg:sqlplus scott/123456@local_instance ,这里的local_instance就是在Net Manager创建的服务命名。

Q:Oracle与表相关的视图DBA_,All_,USER_开头的视图含义
A:DBA_开头的视图显示当前数据库所有表的信息,ALL_开头的视图显示当前用户可以访问的所有视图,USER_开头的视图只显示属于当前用户的视图。

Q:在Windows安装Oracle Grid Infrastructure时报”PRCI-1113 : 目录 E:\app\11.2.0\grid 不存在”的错误提示。
A:打开目录C:\Program Files\Oracle\Inventory\ContentsXML删除inventory.xml文件。

Q:在Windows安装Oracle Grid Infrastructure时报”[INS-20802] 网格基础结构配置 失败。” 原因 - 插件的执行方法失败
A:查看Grid的安装目录下的\cfgtoollogs\crsconfig,找到目录中节点文件安装日志。查看安装日志中内容。安装过程中发现如下日志:

1
2
3
4
5
6
7
8
9
10
2017-09-25 09:56:34: ### Printing of configuration values complete ###
2017-09-25 09:56:34: HKLM/Software/Oracle/ocr/ is NOT configured

2017-09-25 09:56:34: HASH(0x4366ef8) registry key does not exist
2017-09-25 09:56:34: HASH(0x4366ef8) registry key does not exist
2017-09-25 09:56:34: Improper Oracle Clusterware configuration found on this host
2017-09-25 09:56:34: Deconfigure the existing cluster configuration before starting
2017-09-25 09:56:34: to configure a new Clusterware
2017-09-25 09:56:34: run 'E:\app\11.2.0\grid/crs/install/rootcrs.pl -deconfig'
2017-09-25 09:56:34: to deconfigure existing failed configuration and then rerun root.bat

/发现在注册表的HKLM/Software/Oracle路径中无ocr项。添加ocr项,同时在ocr中添加字符串值local_only=FALSE和ocrconfig_loc=+DATA
原因:再次安装时,提示[INS-40417] 安装程序检测到系统中有未使用的 Oracle 集群注册表 (OCR) 位置注册表键 (HKEY_LOCAL_MACHINE\Software\Oracle\Ocr)。手动删除了Ocr项。
/
update:这种情况,还是重装系统吧。

Q:Oracle RAC连接字符串设置
A:参考链接:http://www.oracle.com/technetwork/cn/articles/database-performance/oracle-rac-connection-mgmt-1650424-zhs.html

Q:Oracle RAC节点启动提示:CRS-1013:ASM 磁盘组中的 OCR 位置不可访问。
A:解决过程:日志中提示ASM磁盘不可访问,首先查看防火墙是否启用,如果启用则关闭。检查LISTENER是否正常。
最后使用命令启动:crsctl start has

公司电脑的开发环境是Vs 2010, 使用Nuget时老是安装超时,Nuget版本是:2.8.6
记得以前也遇到过这个问题,当时的解决办法是,添加多个可用程序包源。不过地址却忘记了,今天重新找了下。
地址如下:

1
2
3
4
5
https://www.nuget.org/api/v2/
http://go.microsoft.com/fwlink/?LinkID=230477
http://packages.nuget.org/v1/FeedService.svc/
http://go.microsoft.com/fwlink/?LinkID=206669
http://myget.org/F/myfeed

来源: http://stackoverflow.com/questions/5693139/what-is-the-url-for-nuget-gallery-to-access-nuget-org-from-vs2010

本文的前提是安装了Asp.Net Mvc 3.0 RTM, 同时安装Mvc 4.0
新建一个Empty Mvc3项目, 创建HomeController控制器,并创建Index视图
使用nuget安装如下package:

1
2
3
4
Install-Package Microsoft.AspNet.Razor -Version 2.0.30506
Install-Package Microsoft.AspNet.WebPages -Version 2.0.30506
Install-Package Microsoft.AspNet.Mvc -Version 4.0.40804
Install-Package Microsoft.AspNet.Mvc.zh-Hans -Version 4.0.40804

使用nuget完成安装dll后,需要修改根目录中的Web.config和Views文件夹中的Web.config
打开根目录中的Web.config,将System.Web.Helpers的版本修改为2.0.0.0, 将System.Web.Mvc的版本修改为4.0.0.1,将System.Web.WebPages的版本修改为2.0.0.0

<system.web>中的最终配置如下

1
2
3
4
5
6
7
8
9
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</assemblies>
</compilation>

节点中添加如下配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="3.0.0.0-4.0.0.1" newVersion="4.0.0.1"/>
</dependentAssembly>
</assemblyBinding>

打开Views文件夹中的Web.config,将原先的configSections节点项中Razor版本修改为2.0.0.0, 同时将这个文件中的Mvc版本修改为4.0.0.1

根目录中的Web.config最终内容

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
<?xml version="1.0" encoding="utf-8"?>
<!--
有关如何配置 ASP.NET 应用程序的详细信息,请访问
http://go.microsoft.com/fwlink/?LinkId=152368
-->

<configuration>
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>

<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Mvc, Version=4.0.0.1, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</assemblies>
</compilation>

<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>

<pages>
<namespaces>
<add namespace="System.Web.Helpers"/>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Web.WebPages"/>
</namespaces>
</pages>
</system.web>

<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="3.0.0.0-4.0.0.1" newVersion="4.0.0.1"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

Views文件夹中的Web.config中的最终内容

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
<?xml version="1.0" encoding="utf-8"?>

<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>

<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
</system.web.webPages.razor>

<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>

<system.web>
<httpHandlers>
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>

<!--
在视图页面中启用请求验证将导致验证在
控制器已对输入进行处理后发生。默认情况下,
MVC 在控制器处理输入前执行请求验证。
若要更改此行为,请对控制器或操作
应用 ValidateInputAttribute。
-->
<pages
validateRequest="false"
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<controls>
<add assembly="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
</controls>
</pages>
</system.web>

<system.webServer>
<validation validateIntegratedModeConfiguration="false" />

<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
</configuration>

重新编译后运行,自动跳转到/Home/Index

Mvc 4的最新版本为4.0.0.1, 在Nuget上可以下载, 为了方便还原packages.config如下

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
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Antlr" version="3.5.0.2" targetFramework="net40" />
<package id="jQuery" version="1.12.4" targetFramework="net40" />
<package id="Microsoft.AspNet.Mvc" version="4.0.40804.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Mvc.zh-Hans" version="4.0.40804.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Razor.zh-Hans" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR" version="1.2.2" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.Core" version="1.2.2" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.JS" version="1.2.2" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.Owin" version="1.2.2" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.SystemWeb" version="1.2.2" targetFramework="net40" />
<package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net40" />
<package id="Microsoft.AspNet.WebApi" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebApi.Client.zh-Hans" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebApi.Core.zh-Hans" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebApi.WebHost.zh-Hans" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebPages.zh-Hans" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net40" />
<package id="Microsoft.Net.Http.zh-Hans" version="2.0.20710.0" targetFramework="net40" />
<package id="Microsoft.Owin.Host.SystemWeb" version="1.0.1" targetFramework="net40" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net40" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net40" />
<package id="Owin" version="1.0" targetFramework="net40" />
<package id="WebGrease" version="1.6.0" targetFramework="net40" />
</packages>

Mvc漏洞安全公告:https://technet.microsoft.com/zh-cn/library/security/ms14-059
Mvc补丁地址:https://www.microsoft.com/zh-CN/download/details.aspx?id=44533
Mvc4发布说明:https://docs.microsoft.com/en-us/aspnet/whitepapers/mvc4-release-notes
Mvc4安装包:https://www.microsoft.com/zh-CN/download/details.aspx?id=30683

将字符串”20170504112153”转换为DateTime类型时,调用DateTime.Parse方法,发现抛出FormatException:该字符串未被识别为有效的 DateTime。
难道只能将字符串转换为特定的格式,比如下面这样?

1
2
3
4
5
6
7
8
9
10
11
var input = "20170504112153";
DateTime time;

if (input.Length > 13)
{
var timeInput = input.Substring(0, 4) + "-" + input.Substring(4, 2) + "-"
+ input.Substring(6, 2) + " " + input.Substring(8, 2) + ":"
+ input.Substring(10, 2);

time = DateTime.Parse(timeInput);
}

这种方式让人的感觉,不够优雅。有没有更好的方式?答案是有的,调用DateTime.ParseExact方法。
ParseExact方法可以指定DateTime类型的转换格式
1
2
var input = "20170504112153";
var time = DateTime.ParseExact(input, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);

但是,这个方法还是有局限性的,比如为input值加上毫秒时,再次转换时还是会抛出FormatException.
不过,ParseExact有一个重载的方法可以解决这个问题
public static DateTime ParseExact(string s, string[] formats, IFormatProvider provider, DateTimeStyles style);
参数formats表示:DateTime类型允许格式的数组,所以你可以这样写
1
2
3
4
var input = "20170504112153999";
var date = DateTime.ParseExact(input
, new string[] { "yyyyMMddHHmmss", "yyyyMMddHHmmssf", "yyyyMMddHHmmssff", "yyyyMMddHHmmssfff" }
, CultureInfo.CurrentCulture, DateTimeStyles.None);

将毫秒的每种可能格式列举出来,方法有点笨,这也不失为一种解决办法。