[转载]Android自动化测试之Robotium学习(二) - Android移动开发技术文章_手机开发 - 红黑联盟

mikel阅读(927)

[转载]Android自动化测试之Robotium学习(二) – Android移动开发技术文章_手机开发 – 红黑联盟. 新建好测试程序之后,我们需要做的就是把Robotium这个包导入进你的工程里。这样才能跟Robotium关联起来。

现在最新的版本是3.4.1,不过我现在在用3.1版本,就以3.1版本为例说明。点击下载“robotium-solo-3.1.jar”选项,即为所需的robotium包,而“robotium-solo-3.1-javadoc.jar”为robotium的api文档包,下载解压之后如图:

打开index-all.html,即可看到robotium的各种方法,robotium只有一个类Solo,建立一个Solo对象,就可以用这个对象,调用所有的方法。

接着说导包的事情。下载完robotium包之后,进入eclipse,选中你的项目,右键,点击“Properties”选项,再点击“Resource”选项下的“Java Build Path”,选中“Librararies”选项:如下图:

此时,点击“Add External JARs…”选项,找到你的robotium包所在的位置,添加进去:

然后,再点击最后一个选项栏“Order and Export”选项,把下面的Android api版本和你导入的robotium包选项都勾选,如图所示:

点击“OK”即可。
不过,有时会出现这种情况:就是你进入“Java Build Path”选项后,会多出来一个“Android Dependencies”选项,如下图所示:

此时,你要做的就是,选中“Android Dependencies”,点击右侧的“Remove”选项,把这个去掉,否则你的测试程序会出现错误。
到此为止,你已经把robotium包下载并导入到你的项目中去,然后就可以写你的robotium测试程序了,至于怎么配置你的代码文件,下篇会有详细代码给出讲解。

[转载]Android自动化测试之Robotium学习(一) - Android移动开发技术文章_手机开发 - 红黑联盟

mikel阅读(849)

[转载]Android自动化测试之Robotium学习(一) – Android移动开发技术文章_手机开发 – 红黑联盟.   Robotium是Android测试中一个简单而又强大的工具。说它简单,是因为Robotium是基于Android测试框架 InstrumentationTestCase2进行的2次封装,把一些基本操作又简化了一遍,它的文档只有一个Solo类,而且配置步骤是固定的几 步。说它强大是因为,它在有源码是可以测试源码,进行白盒测试。没有源码时,只有一个应用程序的apk也可以对其进行黑盒测试。而且对程序上的每一个按钮,文本控件,图像,等都可以捕捉到,既可以使用按钮显示的名字进行点击,也可以使用坐标进行点击。
现在开始讲解怎样创建一个Robotium测试项目。
一、有源码时
先把程序源码导入到eclipse里面。然后在eclipse里面右键,点击“New”进入创建Android项目界面,如下图:(我的更新到android 4.1,里面的“Android Application Project”就是以前的“Android Project”).

然后,选择“Android Test Project”,注意不是“Android Application Project”,因为你创建的是测试项目,不是应用项目,里面的界面性的资源是用不到的,当然你误选择了“Android Application Project”,到后面通过配置还是可以进行测试的,只是会添加进很多没有用的资源。
点击“Next”,输入一个你的测试项目的名字:(比如RobotiumTest)。

点击“Next”,出现下面这个界面:

(图3)
注意:这个界面是一个分水岭,如果你有要测试的源码时,你就点击“An existing Android project”选项,然后选择你要测试的源码,比如图中的“Nothing”,然后,点击“Next”,进入你Android api版本选择界面:

然后选择你需要的api版本,点击“Finish”即可。
二、无源码时:
这是最常见的一种情况。通常我们测试人员测试时,都是只有一个程序的apk,源码很少能见到,我们也没必要一定要有源码,甚至没有程序的签名也可以进行,当然这在后面会详细介绍。
接着上面的说,没有源码时,创建程序就如图(3)中,选择“This Project”选项,点击“Next”,同样进入android api选择界面,选择好你要用到的api版本,点击“Finish”选项,即可完成项目的创建。      

如上图所示,只是一个空的android测试项目,不是应用项目,里面没有activity,布局文件等。肯定有人会问,这跟robotium有什么关 系,没有看到一点robotium的影子,这话说的没错,现在这个项目跟robotium没一点关系。只是限于篇幅,不能一言道尽,欲知详情,且看下篇详 解。

[转载]php 导入csv文件_流星飞雨_新浪博客

mikel阅读(854)

[转载]php 导入csv文件_流星飞雨_新浪博客.

 

<form enctype="multipart/form-data" action="import.php" method="POST">
    导入模板  
    <label for="文件选择">文件选择:</label><input name="csv_goods" type="file" />
    <input type="submit" value="导入" name="import" />
</form>
<?php
if (isset($_POST['import'])){
    
    $file = $_FILES['csv_goods'];
    
    $file_type = substr(strstr($file['name'],'.'),1);
    
    // 检查文件格式
    if ($file_type != 'csv'){
        echo '文件格式不对,请重新上传!';
        exit;
    }
    $handle = fopen($file['tmp_name'],"r");
    $file_encoding = mb_detect_encoding($handle);
    
    // 检查文件编码
    if ($file_encoding != 'ASCII'){
        echo '文件编码错误,请重新上传!';
        exit;
    }
    
    $row = 0;
    while ($data = fgetcsv($handle,1000,',')){
        //echo "<font color=red>$row</font>";  //可以知道总共有多少行
        $row++;
        if ($row == 1)
        continue;
        $num = count($data);
        // 这里会依次输出每行当中每个单元格的数据
        for ($i=0; $i<$num; $i++){
            echo $data[$i]."<br>";
            // 在这里对数据进行处理
        }
    }
    
    fclose($handle);
}

?> 

[转载]SQLite学习手册(内置函数) - Stephen_Liu - 博客园

mikel阅读(1073)

[转载]SQLite学习手册(内置函数) – Stephen_Liu – 博客园.

一、聚合函数:

SQLite中支持的聚合函数在很多其他的关系型数据库中也同样支持,因此我们这里将只是给出每个聚集函数的简要说明,而不在给出更多的示例了。这里还需要进一步说明的是,对于所有聚合函数而言,distinct关键字可以作为函数参数字段的前置属性,以便在进行计算时忽略到所有重复的字段值,如count(distinct x)。

函数 说明
avg(x) 该函数返回在同一组内参数字段的平均值。对于不能转换为数字值的String和BLOB类型的字段值,如’HELLO’,SQLite会将其视为0。avg函数的结果总是浮点型,唯一的例外是所有的字段值均为NULL,那样该函数的结果也为NULL。
count(x|*)   count(x)函数返回在同一组内,x字段中值不等于NULL的行数。count(*)函数返回在同一组内的数据行数。
group_concat(x[,y]) 该函数返回一个字符串,该字符串将会连接所有非NULL的x值。该函数的y参数将作为每个x值之间的分隔符,如果在调用时忽略该参数,在连接时将使用缺省分隔符”,”。再有就是各个字符串之间的连接顺序是不确定的。  
max(x) 该函数返回同一组内的x字段的最大值,如果该字段的所有值均为NULL,该函数也返回NULL。
min(x) 该函数返回同一组内的x字段的最小值,如果该字段的所有值均为NULL,该函数也返回NULL。
sum(x) 该 函数返回同一组内的x字段值的总和,如果字段值均为NULL,该函数也返回NULL。如果所有的x字段值均为整型或者NULL,该函数返回整型值,否则就 返回浮点型数值。最后需要指出的是,如果所有的数据值均为整型,一旦结果超过上限时将会抛出”integer overflow”的异常。
total(x) 该函数不属于标准SQL,其功能和sum基本相同,只是计算结果比sum更为合理。比如当所有字段值均为NULL时,和sum不同的是,该函数返回0.0。再有就是该函数始终返回浮点型数值。该函数始终都不会抛出异常。

二、核心函数:

以下函数均为SQLite缺省提供的内置函数,其声明和描述见如下列表:

函数 说明
abs(X) 该函数返回数值参数X的绝对值,如果X为NULL,则返回NULL,如果X为不能转换成数值的字符串,则返回0,如果X值超出Integer的上限,则抛出”Integer Overflow”的异常。
changes() 该函数返回最近执行的INSERT、UPDATE和DELETE语句所影响的数据行数。我们也可以通过执行C/C++函数sqlite3_changes()得到相同的结果。
coalesce(X,Y,…)   返回函数参数中第一个非NULL的参数,如果参数都是NULL,则返回NULL。该函数至少2个参数。
ifnull(X,Y) 该函数等同于两个参数的coalesce()函数,即返回第一个不为NULL的函数参数,如果两个均为NULL,则返回NULL。
length(X)   如果参数X为字符串,则返回字符的数量,如果为数值,则返回该参数的字符串表示形式的长度,如果为NULL,则返回NULL。
lower(X) 返回函数参数X的小写形式,缺省情况下,该函数只能应用于ASCII字符。
ltrim(X[,Y])   如果没有可选参数Y,该函数将移除参数X左侧的所有空格符。如果有参数Y,则移除X左侧的任意在Y中出现的字符。最后返回移除后的字符串。
max(X,Y,…) 返回函数参数中的最大值,如果有任何一个参数为NULL,则返回NULL。
min(X,Y,…) 返回函数参数中的最小值,如果有任何一个参数为NULL,则返回NULL。
nullif(X,Y) 如果函数参数相同,返回NULL,否则返回第一个参数。  
random()   返回整型的伪随机数。
replace(X,Y,Z)   将字符串类型的函数参数X中所有子字符串Y替换为字符串Z,最后返回替换后的字符串,源字符串X保持不变。
round(X[,Y]) 返回数值参数X被四舍五入到Y刻度的值,如果参数Y不存在,缺省参数值为0。  
rtrim(X[,Y])   如果没有可选参数Y,该函数将移除参数X右侧的所有空格符。如果有参数Y,则移除X右侧的任意在Y中出现的字符。最后返回移除后的字符串。
substr(X,Y[,Z]) 返回函数参数X的子字符串,从第Y位开始(X 中的第一个字符位置为1)截取Z长度的字符,如果忽略Z参数,则取第Y个字符后面的所有字符。如果Z的值为负数,则从第Y位开始,向左截取abs(Z)个 字符。如果Y值为负数,则从X字符串的尾部开始计数到第abs(Y)的位置开始。 
total_changes() 该函数返回自从该连接被打开时起,INSERT、UPDATE和DELETE语句总共影响的行数。我们也可以通过C/C++接口函数sqlite3_total_changes()得到相同的结果。
trim(x[,y]) 如果没有可选参数Y,该函数将移除参数X两侧的所有空格符。如果有参数Y,则移除X两侧的任意在Y中出现的字符。最后返回移除后的字符串。 
upper(X) 返回函数参数X的大写形式,缺省情况下,该函数只能应用于ASCII字符。
typeof(X) 返回函数参数数据类型的字符串表示形式,如”Integer、text、real、null”等。

三、日期和时间函数:

SQLite主要支持以下四种与日期和时间相关的函数,如:
    1). date(timestring, modifier, modifier, …)
    2). time(timestring, modifier, modifier, …)
    3). datetime(timestring, modifier, modifier, …)
    4). strftime(format, timestring, modifier, modifier, …)
以上所有四个函数都接受一个时间字符串作为参数,其后再跟有0个或多个修改符。其中strftime()函数还接受一个格式字符串作为其第一个参数。 strftime()和C运行时库中的同名函数完全相同。至于其他三个函数,date函数的缺省格式为:”YYYY-MM-DD”,time函数的缺省格 式为:”HH:MM:SS”,datetime函数的缺省格式为:”YYYY-MM-DD HH:MM:SS”。
1. strftime函数的格式信息:

格式 说明
%d day of month: 00
%f fractional seconds: SS.SSS
%H hour: 00-24
%j day of year: 001-366
%J Julian day number
%m month: 01-12
%M minute: 00-59
%s seconds since 1970-01-01
%S seconds: 00-59
%w day of week 0-6 with Sunday==0
%W week of year: 00-53
%Y year: 0000-9999
%% %

需要额外指出的是,其余三个时间函数均可用strftime来表示,如:
date(…)         strftime(‘%Y-%m-%d’, …)
time(…)         strftime(‘%H:%M:%S’, …)
datetime(…)   strftime(‘%Y-%m-%d %H:%M:%S’, …)

2. 时间字符串的格式:
见如下列表:
    1). YYYY-MM-DD
    2). YYYY-MM-DD HH:MM
    3). YYYY-MM-DD HH:MM:SS
    4). YYYY-MM-DD HH:MM:SS.SSS
    5). HH:MM
    6). HH:MM:SS
    7). HH:MM:SS.SSS
    8). now
5)7)中只是包含了时间部分,SQLite将假设日期为2000-01-018)表示当前时间。

   3. 修改符:
见如下列表:
    1). NNN days
    2). NNN hours
    3). NNN minutes
    4). NNN.NNNN seconds
    5). NNN months
    6). NNN years
    7). start of month
    8). start of year
    9). start of day
    10).weekday N   
1)6)将只是简单的加减指定数量的日期或时间值,如果NNN的值为负数,则减,否则加。7)9)则将时间串中的指定日期部分设置到当前月、年或日的开始。10)则将日期前进到下一个星期N,其中星期日为0。注:修改符的顺序极为重要,SQLite将会按照从左到右的顺序依次执行修改符。

4. 示例:
    –返回当前日期。
    sqlite> SELECT date(‘now’);  
    2012-01-15    
    –返回当前月的最后一天。
    sqlite> SELECT date(‘now’,’start of month’,’1 month’,’-1 day’);
    2012-01-31
    –返回从1970-01-01 00:00:00到当前时间所流经的秒数。
    sqlite> SELECT strftime(‘%s’,’now’);
    1326641166    
    –返回当前年中10月份的第一个星期二是日期。
    sqlite> SELECT date(‘now’,’start of year’,’+9 months’,’weekday 2′);
    2012-10-02  

[转载]SQLite的SQL语法 - ejzhang的专栏 - 博客频道 - CSDN.NET

mikel阅读(1136)

[转载]SQLite的SQL语法 – ejzhang的专栏 – 博客频道 – CSDN.NET.

SQLite库可以解析大部分标准SQL语言。但它也省去了一些特性并且加入了一些自己的新特性。这篇文档就是试图描述那些SQLite支持/不支持的SQL语法的。查看关键字列表

如下语法表格中,纯文本用蓝色粗体显示。非终极符号为斜体红色。作为语法一部分的运算符用黑色Roman字体表示。

这篇文档只是对SQLite实现的SQL语法的综述,有所忽略。想要得到更详细的信息,参考源代码和语法文件“parse.y”。

SQLite执行如下的语法:

ALTER TABLE

sql-statement ::= ALTER TABLE [database-name .]table-namealteration
alteration ::= RENAME TO new-table-name
alteration ::= ADD [COLUMN]column-def

SQLite版本的的ALTER TABLE命令允许用户重命名或添加新的字段到已有表中,不能从表中删除字段。

RENAME TO语法用于重命名表名[database-name.]table-namenew-table-name。这一命令不能用于在附加数据库之间移动表,只能在同一个数据库中对表进行重命名。

若需要重命名的表有触发器或索引,在重命名后它们依然属于该表。但若定义了视图,或触发器执行的语句中有提到 表的名字,则它们不会被自动改为使用新的表名。若要进行这一类的修改,则需手工撤销并使用新的表名重建触发器或视图。

ADD [COLUMN]语法用于在已有表中添加新的字段。新字段总是添加到已有字段列表的末尾。Column-def可以是CREATE TABLE中允许出现的任何形式,且须符合如下限制:

  • 字段不能有主键或唯一约束。
  • 字段不能有这些缺省值:CURRENT_TIME, CURRENT_DATE或CURRENT_TIMESTAMP
  • 若定义了NOT NULL约束,则字段必须有一个非空的缺省值。

ALTER TABLE语句的执行时间与表中的数据量无关,它在操作一个有一千万行的表时的运行时间与操作仅有一行的表时是一样的。

在对数据库运行ADD COLUMN之后,该数据库将无法由SQLite 3.1.3及更早版本读取,除非运行VACUUM命令。

ANALYZE

 

sql-statement ::= ANALYZE
sql-statement ::= ANALYZE database-name
sql-statement ::= ANALYZE [database-name .]table-name

ANALYZE命令令集合关于索引的统计信息并将它们储存在数据库的一个特殊表中,查询优化器可以用该表来制作更好的索引选择。若不给出参数,所有 附加数据库中的所有索引被分析。若参数给出数据库名,该数据库中的所有索引被分析。若给出表名 作参数,则只有关联该表的索引被分析。

最初的实现将所有的统计信息储存在一个名叫sqlite_stat1的表中。未来的加强版本中可能会创建名字类似的其它表,只是把“1”改为其它数字。sqlite_stat1表不能够被撤销,但其中的所有内容可以被删除,这是与撤销该表等效的行为。

ATTACH DATABASE

ATTACH DATABASE语句将一个已存在的数据库添加到当前数据库连接。若文件名含标点符号,则应用引号引起来。数据库名’main’和’temp’代表主数据库和用于存放临时表的数据库,它们不能被拆分。拆分数据库使用DETACH DATABASE语句。

你可以读写附加数据库,或改变其结构。这是SQLite 3.0提供的新特性。在SQLite 2.8中,改变附加数据库的结构是不允许的。

在附加数据库中添加一个与已有表同名的表是不允许的。但你可以附加带有与主数据库中的表同名的表的数据库。也可以多次附加同一数据库。

使用database-name.table-name来引用附加数据库中的表。若附加数据库中的表与主数据库的表不重名,则不需加数据库名作为前缀。当数据库被附加时,它的所有不重名的表成为该名字指向的缺省表。之后附加的任意与之同名的表需要加前缀。若“缺省”表被拆分,则最后附加的同名表变为“缺省”表。

若主数据库不是“:memory:”,多附加数据库的事务是原子的。若主数据库是“:memory:”则事务在每个独立文件中依然是原子的。但若主 机在改变两个或更多数据库的COMMIT语句进行时崩溃,则可能一部分文件被改变而其他的保持原样。附加数据库的原子性的提交 是SQLite 3.0的新特性。在SQLite 2.8中,所有附加数据库的提交类似于主数据库是“:memory:”时的情况。

对附加数据库的数目有编译时的限制,最多10个附加数据库。

BEGIN TRANSACTION

 

sql-statement ::= BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ][TRANSACTION [name]]
sql-statement ::= END [TRANSACTION [name]]
sql-statement ::= COMMIT [TRANSACTION [name]]
sql-statement ::= ROLLBACK [TRANSACTION [name]]

从2.0版开始,SQLite支持带有回退和原子性的提交的事务处理。

可选的事务名称会被忽略。SQLite目前不允许嵌套事务。

在事务之外,不能对数据库进行更改。如果当前没有有效的事务,任何修改数据库的命令(基本上除了SELECT以外的所有SQL命令)会自动启动一个事务。命令结束时,自动启动的事务会被提交。

可以使用BEGIN命令手动启动事务。这样启动的事务会在下一条COMMIT或ROLLBACK命令之前一直有效。但若数据库关闭或出现错误且选用ROLLBACK冲突判定算法时,数据库也会ROLLBACK。查看ON CONFLICT子句获取更多关于ROLLBACK冲突判定算法的信息。

在SQLite 3.0.8或更高版本中,事务可以是延迟的,即时的或者独占的。“延迟的”即是说在数据库第一次被访问之前不获得锁。这样就会延迟事务,BEGIN语句本 身不做任何事情。直到初次读取或访问数据库时才获取锁。对数据库的初次读取创建一个SHARED锁 ,初次写入创建一个RESERVED锁。由于锁的获取被延迟到第一次需要时,别的线程或进程可以在当前线程执行BEGIN语句之后创建另外的事务 写入数据库。若事务是即时的,则执行BEGIN命令后立即获取RESERVED锁,而不等数据库被使用。在执行BEGIN IMMEDIATE之后,你可以确保其它的线程或进程不能写入数据库或执行BEGIN IMMEDIATE或BEGIN EXCLUSIVE,但其它进程可以读取数据库。独占事务在所有的数据库获取EXCLUSIVE锁,在执行BEGIN EXCLUSIVE之后,你可以确保在当前事务结束前没有任何其它线程或进程 能够读写数据库。

有关SHARED、RESERVED和EXCLUSIVE锁可以参见这里

SQLite 3.0.8的默认行为是创建延迟事务。SQLite 3.0.0到3.0.7中延迟事务是唯一可用的事务类型。SQLite 2.8或更早版本中,所有的事务都是独占的。

COMMIT命令在所有SQL命令完成之前并不作实际的提交工作。这样若两个或更多个SELECT语句在进程中间而执行COMMIT时,只有全部SELECT语句结束才进行提交。

执行COMMIT可能会返回SQLITE_BUSY错误代码。这就是说有另外一个线程或进程获取了数据库的读取锁,并阻止数据库被改变。当COMMIT获得该错误代码时,事务依然是活动的,并且在COMMIT可以在当前读取的线程读取结束后再次试图读取数据库。

END TRANSACTION

 

sql-statement ::= BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ][TRANSACTION [name]]
sql-statement ::= END [TRANSACTION [name]]
sql-statement ::= COMMIT [TRANSACTION [name]]
sql-statement ::= ROLLBACK [TRANSACTION [name]]

从2.0版开始,SQLite支持带有回退和原子性的提交的事务处理。

可选的事务名称会被忽略。SQLite目前不允许嵌套事务。

在事务之外,不能对数据库进行更改。如果当前没有有效的事务,任何修改数据库的命令(基本上除了SELECT以外的所有SQL命令)会自动启动一个事务。命令结束时,自动启动的事务会被提交。

可以使用BEGIN命令手动启动事务。这样启动的事务会在下一条COMMIT或ROLLBACK命令之前一直有效。但若数据库关闭或出现错误且选用ROLLBACK冲突判定算法时,数据库也会ROLLBACK。查看ON CONFLICT子句获取更多关于ROLLBACK冲突判定算法的信息。

在SQLite 3.0.8或更高版本中,事务可以是延迟的,即时的或者独占的。“延迟的”即是说在数据库第一次被访问之前不获得锁。这样就会延迟事务,BEGIN语句本 身不做任何事情。直到初次读取或访问数据库时才获取锁。对数据库的初次读取创建一个SHARED锁,初次写入创建一个RESERVED锁。由于锁的获取被 延迟到第一次需要时,别的线程或进程可以在当前线程执行BEGIN语句之后创建另外的事务写入数据库。若事务是即时的,则执行BEGIN命令后立即获取 RESERVED锁,而不等数据库被使用。在执行BEGIN IMMEDIATE之后,你可以确保其它的线程或进程不能写入数据库或执行BEGIN IMMEDIATE或BEGIN EXCLUSIVE,但其它进程可以读取数据库。独占事务在所有的数据库获取EXCLUSIVE锁,在执行BEGIN EXCLUSIVE之后,你可以确保在当前事务结束前没有任何其它线程或进程能够读写数据库。

有关SHARED、RESERVED和EXCLUSIVE锁可以参见这里

SQLite 3.0.8的默认行为是创建延迟事务。SQLite 3.0.0到3.0.7中延迟事务是唯一可用的事务类型。SQLite 2.8或更早版本中,所有的事务都是独占的。

COMMIT命令在所有SQL命令完成之前并不作实际的提交工作。这样若两个或更多个SELECT语句在进程中间而执行COMMIT时,只有全部SELECT语句结束才进行提交。

执行COMMIT可能会返回SQLITE_BUSY错误代码。这就是说有另外一个线程或进程获取了数据库的读取锁,并阻止数据库被改变。当COMMIT获得该错误代码时,事务依然是活动的,并且在COMMIT可以在当前读取的线程读取结束后再次试图读取数据库。

注释

 

comment ::= SQL-comment|C-comment
SQL-comment ::= single-line
C-comment ::= /* multiple-lines[*/]

注释不是SQL命令,但会出现在SQL查询中。它们被解释器处理为空白部分。它们可以在任何空白可能存在的地方开始 ,即使是在跨越多行的表达式中。

SQL风格的注释仅对当前行有效。

C风格的注释可以跨越多行。若没有结束符号,注释的范围将一直延伸到输入末尾,且不会引起报错。新的SQL语句可以从多行注释结束的地方开始。C风 格注释可以嵌入任何空白可以出现的地方,包括表达式内,或其他SQL语句中间, 并且C风格的注释不互相嵌套。SQL风格的注释出现在C风格注释中时将被忽略。

COPY

 

sql-statement ::= COPY [ OR conflict-algorithm][database-name .]table-name FROM filename
[ USING DELIMITERS delim]

COPY命令在SQLite 2.8及更早的版本中可用。SQLite 3.0删除了这一命令,因为在混合的UTF-8/16环境中对它进行支持是很复杂的。在3.0版本中,命令行解释器包含新的.import命令,用以替代COPY。

COPY命令是用于将大量数据插入表的一个插件。它模仿PostgreSQL中的相同命令而来。事实上,SQLite的COPY 命令就是为了能够读取PostgreSQL的备份工具pg_dump的输出从而能够将PostgreSQL的数据轻松转换到SQLite中而设计的。

table-name是将要导入数据的一个已存在的表的名字。filename是一个字符串或标识符,用于说明作为数据来源的文件。filename可以使用STDIN从标准输入流中获取数据。

输入文件的每一行被转换成一条单独的记录导入表中。字段用制表符分开。若某个字段的数据中出现制表符,则前面被添加反斜线“/”符号。数据中的反斜线则被替换为两条反斜线。可选的USING DELIMITERS子句可给出一个与制表符不同 的分界符。

若字段由“/N”组成,则被赋以空值NULL。

使用这一命令时,利用可选的ON CONFLICT子句可以定义替代的约束冲突判定算法。更多信息,参见 ON CONFLICT

当输入数据源是STDIN,输入将终止于一行仅包含一个反斜线和一个点的输入:“/.”。

CREATE INDEX

 

sql-statement ::= CREATE [UNIQUE] INDEX [IF NOT EXISTS][database-name .]index-name
ON
table-name ( column-name[, column-name]* )
column-name ::= name[ COLLATE collation-name][ ASC | DESC ]

CREATE INDEX命令由“CREATE INDEX”关键字后跟新索引的名字,关键字“ON”,待索引表的名字,以及括弧内的用于索引键的字段列表构成。每个字段名可以跟随“ASC”或 “DESC”关键字说明排序法则,但在当前版本中排序法则被忽略。排序总是按照上升序。

每个字段名后跟COLLATE子句定义文本记录的比较顺序。缺省的比较顺序是由CREATE TABLE语句说明的比较顺序。若不定义比较顺序,则使用内建的二进制比较顺序。

附加到单个表上的索引数目没有限制,索引中的字段数也没有限制。

若UNIQUE关键字出现在CREATE和INDEX之间,则不允许重名的索引记录。试图插入重名记录将会导致错误。

每条CREATE INDEX语句的文本储存于sqlite_mastersqlite_temp_master表中,取决于被索引的表是否临时表。 每次打开数据库时,所有的CREATE INDEX语句从sqlite_master表中读出,产生SQLite的索引样式的内部结构。

若使用可选的IF NOT EXISTS子句,且存在同名索引,则该命令无效。

使用DROP INDEX命令删除索引。

CREATE TABLE

 

sql-command ::= CREATE [TEMP | TEMPORARY] TABLE [IF NOT EXISTS]table-name (
    
column-def[, column-def]*
[, constraint]*
)
sql-command ::= CREATE [TEMP | TEMPORARY] TABLE [database-name.]table-name AS select-statement
column-def ::= name[type][[CONSTRAINT name]column-constraint]*
type ::= typename|
typename ( number ) |
typename ( number , number )
column-constraint ::= NOT NULL [conflict-clause]|
PRIMARY KEY
[sort-order][conflict-clause][AUTOINCREMENT]|
UNIQUE
[conflict-clause]|
CHECK (
expr ) |
DEFAULT
value|
COLLATE
collation-name
constraint ::= PRIMARY KEY ( column-list ) [conflict-clause]|
UNIQUE (
column-list ) [conflict-clause]|
CHECK (
expr )
conflict-clause ::= ON CONFLICT conflict-algorithm

CREATE TABLE语句基本上就是“CREATE TABLE”关键字后跟一个新的表名以及括号内的一堆定义和约束。表名可以是字符串或者标识符。以“sqlite_”开头的表名是留给数据库引擎使用的。

每个字段的定义是字段名后跟字段的数据类型,接着是一个或多个的字段约束。字段的数据类型并不限制字段中可以存放的数据。可以查看SQLite3的数据类型获取更多信息。UNIQUE约束为指定的字段创建索引,该索引须含有唯一键。COLLATE子句说明在比较字段的 文字记录时所使用的排序函数。缺省使用内嵌的BINARY排序函数。

DEFAULT约束说明在使用INSERT插入字段时所使用的缺省值。该值可以是NULL,字符串常量或一个数。从3.1.0版开始,缺省值也可以 是以下特殊的与事件无关的关键字CURRENT_TIME、CURRENT_DATE或CURRENT_TIMESTAMP。若缺省值为NULL、字符串 常量或数,在执行未指明字段值的INSERT语句的时候它被插入字段。若缺省值是CURRENT_TIME、CURRENT_DATE或 CURRENT_TIMESTAMP,则当前UTC日期和/或时间被插入字段。CURRENT_TIME的格式为 “HH:MM:SS”,CURRENT_DATE为“YYYY-MM-DD”,而CURRENT_TIMESTAMP是“YYYY-MM-DD HH:MM:SS”。

正常情况下定义PRIMARY KEY只是在相应字段上创建一个UNIQUE索引。然而,若主键定义在单一的INTEGER类型的字段上,则该字段在内部被用作表的B-Tree键。这即 是说字段仅能容纳唯一整数值。(在除此之外的其它情况下,SQLite忽略数据类型的说明 ,允许任何类型的数据放入字段中,不管该字段被声明为什么数据类型。)若一个表中不含一个INTEGER PRIMARY KEY字段,则B-Tree键为自动产生的整数。一行的B-Tree键可以通过如下特殊的名字“ROWID”、“OID”或“_ROWID_” 进行访问,不论是否有INTEGER PRIMARY KEY存在。INTEGER PRIMARY KEY字段可以使用关键字AUTOINCREMENT声明。AUTOINCREMENT关键字修改了B-Tree键自动产生的方式。B-Tree键的生成 的其它信息可以在这里找到。

若“TEMP”或“TEMPORARY”关键字出现在“CREATE”和“TABLE”之间,则所建立的表仅在当前数据库连接可见,并在断开连接时自动被删除。在临时表上建立的任何索引也是临时的。临时表和索引单独存储在与主数据库文件不同的文件中。

若说明了,则表在该数据库中被创建。同时声明和TEMP关键字会出错,除非 是“temp”。若不声明数据库名,也不使用TEMP关键字,则表创建于主数据库中。

在每个约束后跟可选的ON CONFLICT子句可以定义替代的约束冲突判定算法。 缺省为ABORT。同一个表中的不同约束可以使用不同的缺省冲突判定算法。若一条COPY、INSERT或UPDATE命令指定了不同的冲突判定算法,则 该算法将替代CREATE TABLE语句中说明的缺省算法。更多信息,参见ON CONFLICT

3.3.0版支持CHECK约束。在3.3.0之前,CHECK约束被解析但不执行。

表中的字段数或约束数没有任何限制。在2.8版中,单行数据的总数被限制为小于1 megabytes。而在3.0中则消除了限制。

CREATE TABLE AS形式定义表为一个查询的结果集。表的字段名字即是结果中的字段名字。

每条CREATE TABLE语句的文本都储存在sqlite_master表中。每当数据库被打开,所有的CREATE TABLE语句从 sqlite_master表中读出,构成表结构的SQLite内部实现。若原始命令为CREATE TABLE AS则合成出等效的CREATE TABLE语句并储存于sqlite_master表中代替原命令。CREATE TEMPORARY TABLE语句文本储存于sqlite_temp_master表中。

若在命令中使用可选的IF NOT EXISTS子句且存在同名的另一个表,则当前的命令无效。

删除表可以使用DROP TABLE语句。

 

CREATE TRIGGER

 

sql-statement ::= CREATE [TEMP | TEMPORARY] TRIGGER trigger-name[ BEFORE | AFTER ]
database-event ON [database-name .]table-name
trigger-action
sql-statement ::= CREATE [TEMP | TEMPORARY] TRIGGER trigger-name INSTEAD OF
database-event ON [database-name .]view-name
trigger-action
database-event ::= DELETE |
INSERT
|
UPDATE
|
UPDATE OF
column-list
trigger-action ::= [ FOR EACH ROW | FOR EACH STATEMENT ][ WHEN expression]
BEGIN
trigger-step ; [trigger-step ; ]*
END
trigger-step ::= update-statement|insert-statement|
delete-statement|select-statement

CREATE TRIGGER语句用于向数据库schema中添加触发器。触发器是一些在特定的数据库事件(database-event)发生时自动进行的数据库操作(trigger-action)。

触发器可由在特殊表上执行的DELETE、INSERT、UPDATE等语句触发,或UPDATE表中特定的字段时触发。

现在SQLite仅支持FOR EACH ROW触发器,不支持FOR EACH STATEMENT触发。因此可以不用明确说明FOR EACH ROW。FOR EACH ROW的意思是由trigger-steps说明的SQL语句可能在(由WHEN子句决定的)数据库插入,更改或删除的每一行触发trigger。

WHEN子句和trigger-steps可以使用“NEW.column-name”和“OLD.column-name”的引用形式访问正在被插入,更改或删除的行的元素,column-name是触发器关联的表中的字段名。OLD和NEW引用只在触发器与之相关的trigger-event处可用,例如:

 

INSERT NEW可用
UPDATE NEW和OLD均可用
DELETE OLD可用

当使用WHEN子句,trigger-steps只在WHEN子句为真的行执行。不使用WHEN时则在所有行执行。

trigger-time决定了trigger-steps执行的时间,它是相对于关联行的插入、删除和修改而言的。

作为的一部分trigger-step的UPDATE或INSERT可以使用ON CONFLICT子句。但若触发trigger的语句使用了ON CONFLICT子句,则覆盖前述的ON CONFLICT子句所定义的冲突处理方法。

关联表被撤销时触发器被自动删除。

不仅在表上,在视图上一样可以创建触发器,在CREATE TRIGGER语句中使用INSTEAD OF即可。若视图上定义了一个或多个ON INSERT、ON DELETE、ON UPDATE触发器,则相应地对视图执行INSERT、DELETE或UPDATE语句不会出错,而会触发关联的触发器。视图关联的表不会被修改。(除了 由触发器进行的修改操作)。

例子:

假设“customers”表存储了客户信息,“orders”表存储了订单信息,下面的触发器确保当用户改变地址时所有的关联订单地址均进行相应改变:

 
CREATE TRIGGER update_customer_address UPDATE OF address ON customers    
    BEGIN 
      UPDATE orders SET address = new.address WHERE customer_name = old.name; 
    END; 

定义了该触发器后执行如下语句:

 
UPDATE customers SET address = ’1 Main St.’ WHERE name = ’Jack Jones’; 

会使下面的语句自动执行:

 
UPDATE orders SET address = ’1 Main St.’ WHERE customer_name = ’Jack Jones’; 

注意,目前在有INTEGER PRIMARY KEY域的表上触发器可能工作不正常。若BEFORE触发器修改了一行的INTEGER PRIMARY KEY域,而该域将由触发该触发器的语句进行修改,则可能根本不会修改该域。可以用PRIMARY KEY字段代替INTEGER PRIMARY KEY字段来解决上述问题。

一个特殊的SQL函数RAISE()可用于触发器程序,使用如下语法:

 

raise-function ::= RAISE ( ABORT, error-message ) |
RAISE ( FAIL,
error-message ) |
RAISE ( ROLLBACK,
error-message ) |
RAISE ( IGNORE )

当触发器程序执行中调用了上述前三个之一的形式时,则执行指定的ON CONFLICT进程(ABORT、FAIL或者ROLLBACK)且终止当前查询,返回一个SQLITE_CONSTRAINT错误并说明错误信息。

当调用RAISE(IGNORE),当前触发器程序的余下部分,触发该触发器的语句和任何之后的触发器程序被忽略并且不恢复对数据库的已有改变。若触发触发器的语句是一个触发器程序本身的一部分,则原触发器程序从下一步起继续执行。

使用DROP TRIGGER删除触发器。

CREATE VIEW

 

sql-command ::= CREATE [TEMP | TEMPORARY] VIEW [database-name.]view-name AS select-statement

CREATE VIEW命令为一个包装好的SELECT语句命名。当创建了一个视图,它可以用于其他SELECT的FROM字句中代替表名。

若“TEMP”或“TEMPORARY”关键字出现在“CREATE”和“VIEW”之间,则创建的视图仅对打开数据库的进程可见,且在数据库关闭时自动删除。

若指定了则视图在指定的数据库中创建。同时使用和TEMP关键字会导致错误,除非是“temp”。若不声明数据库名,也不使用TEMP关键字,则视图创建于主数据库中。

你不能对视图使用COPY、DELETE、INSERT或UPDATE,视图在SQLite中是只读的。多数情况下你可以在视图上创建TRIGGER来达到相同目的。用DROP VIEW命令来删除视图。

DELETE

 

sql-statement ::= DELETE FROM [database-name .]table-name[WHERE expr]

DELETE命令用于从表中删除记录。命令包含“DELETE FROM”关键字以及需要删除的记录所在的表名。

若不使用WHERE子句,表中的所有行将全部被删除。否则仅删除符合条件的行。

DETACH DATABASE

 

sql-command ::= DETACH [DATABASE]database-name

该语句拆分一个之前使用ATTACH DATABASE语句附加的数据库连接。可以使用不同的名字多次附加同一数据库,并且拆分一个连接不会影响其他连接。

若SQLite在事务进行中,该语句不起作用。

DROP INDEX

 

sql-command ::= DROP INDEX [IF EXISTS][database-name .]index-name

DROP INDEX语句删除由CREATE INDEX语句创建的索引。索引将从数据库结构和磁盘文件中完全删除,唯一的恢复方法是重新输入相应的CREATE INDEX命令。

DROP TABLE语句在缺省模式下不减小数据库文件的大小。空间会留给后来的INSERT语句使用。要释放删除产生的空间,可以使用VACUUM命令。若AUTOVACUUM模式开启,则空间会自动被DROP INDEX释放。

DROP TABLE

 

sql-command ::= DROP TABLE [IF EXISTS][database-name.]table-name

DROP TABLE语句删除由CREATE TABLE语句创建的表。表将从数据库结构和磁盘文件中完全删除,且不能恢复。该表的所有索引也同时被删除。

DROP TABLE语句在缺省模式下不减小数据库文件的大小。空间会留给后来的INSERT语句使用。要释放删除产生的空间,可以使用VACUUM命令。若AUTOVACUUM模式开启,则空间会自动被DROP TABLE释放。

若使用可选的IF EXISTS子句,在删除的表不存在时就不会报错。

DROP TRIGGER

 

sql-statement ::= DROP TRIGGER [database-name .]trigger-name

DROP TRIGGER语句删除由CREATE TRIGGER创建的触发器。触发器从数据库的schema中删除。注意当关联的表被撤消时触发器自动被删除。

DROP VIEW

 

sql-command ::= DROP VIEW view-name

DROP VIEW语句删除由CREATE VIEW创建的视图。视图从数据库的schema中删除,表中的数据不会被更改。

EXPLAIN

 

sql-statement ::= EXPLAIN sql-statement

EXPLAIN命令修饰语是一个非标准的扩展功能,灵感来自PostgreSQL中的相同命令,但操作完全不同。

若EXPLAIN关键字出现在任何SQLite的SQL命令之前,则SQLite库返回不加EXPLAIN时执行该命令所需要使用的虚拟机指令序列,而不是真正执行该命令。关于虚拟机指令的更多信息参见系统结构描述或关于虚拟机的可用代码

表达式

 

expr ::= exprbinary-opexpr|
expr[NOT]like-opexpr[ESCAPE expr]|
unary-opexpr|
(
expr ) |
column-name|
table-name . column-name|
database-name . table-name . column-name|
literal-value|
parameter|
function-name ( expr-list| * ) |
expr ISNULL |
expr NOTNULL |
expr[NOT] BETWEEN expr AND expr|
expr[NOT] IN ( value-list ) |
expr[NOT] IN ( select-statement ) |
expr[NOT] IN [database-name .]table-name|
[EXISTS] ( select-statement ) |
CASE
[expr]( WHEN expr THEN expr)+[ELSE expr] END |
CAST (
expr AS type )
like-op ::= LIKE | GLOB | REGEXP

这一节与其它的各节有所不同。我们讨论的不是一个单一的SQL命令,而是作为其他大部分命令的一部分的表达式。

SQLite支持如下的二元运算符,按优先级由高至低排列:

||
*     /     %
+     -
<<    >>    &     |
<     <=    >     >=
=     ==    !=    <>   IN
AND    
OR

所支持的一元运算符:

-     +     !     ~

注意等号和“不等”号的两个变种。等号可以是 ===. “不等”号可以是!=<>. ||为“连接符”——它将两个字符串连接起来。 %输出左边部分以右边部分为模取模得到的余数。

二元运算符的结果均为数字,除了||连接符,它给出字符串结果。

文本值(literal value)是一个整数或浮点数。可以使用科学计数法。“.”符号总是被当作小数点即使本地设定中用“,”来表示小数点——用“,”表示小数点会造成歧 义。字符串常量由字符串加单引号“’”构成。字符串内部的单引号可像Pascal中一样用两个单引号来表示。C风格的加反斜线的表示法由于不是标准SQL 而不被支持。BLOB文本是以“x”或“X”开头的含有十六进制文本信息的文本值。例如:

X'53514697465'

文本值同样可以为“NULL”。

表达式中插入文本值占位符的参数可以使用sqlite3_bind API函数在运行时插入。参数可以是如下几种形式:

?NNN 问号跟随数字NNN为第NNN个参数占位。NNN需介于1和999之间。
? 不加数字的问号为下一个参数占位。
:AAAA 冒号带一个标识符名称为一个名为AAAA的参数占位。命名的参数同样可以使用序号占位,被赋予的参数序号为下一个尚未被使用的序号。建议不要混合使用命名代表的参数和序号代表的参数以免引起混淆。
$AAAA $符号带一个标识符名称也可以为一个名为AAAA的参数占位。在这一情况下标识符名称可以包括一个或更多的“::”以及包含任何文本的“(…)”后缀。该语法是Tcl编程语言中的一个可变形式。

不使用sqlite3_bind赋值的参数被视为NULL。

LIKE运算符进行模式匹配比较。运算符右边为进行匹配的模式而左边为需进行匹配的字符串。模式中的百分号%匹配结果中的零或任意多个字符。下划线_匹配任意单个字符。其他的任意字符匹配本身或等同的大/小写字符。(即不区分大小写的匹配)。(一个bug:SQLite仅对7-bit拉丁字符支持不区分大小写匹配。这是由于LIKE运算符对8-bit ISO8859字符或UTF-8字符是大小写敏感的。例如,表达式‘a’ LIKE ‘A’的值为真而‘æ’ LIKE ‘Æ’为假)。

如果使用可选的ESCAPE子句,则跟随ESCAPE关键字的必须是一个有一个字符的字符串。这一字符(逃逸字符)可用于LIKE模式中,以代替百 分号或下划线。逃逸字符后跟百分号,下划线或它本身代表字符串中的百分号,下划线或逃逸字符。插入的LIKE运算符功能通过调用用户函数like(X,Y)来实现。

当使用可选的ESCAPE子句,它对函数给出第三个参数,LIKE的功能可以通过重载SQL函数like()进行改变。

GLOB运算符与LIKE相似,但它使用Unix文件globbing语法作为通配符。还有一点不同是GLOB对大小写敏感。GLOB和LIKE都可以前缀NOT关键字构成相反的意思。插入的GLOB运算符功能通过调用用户函数glob(X,Y)可以通过重载函数改变GLOB的功能。

REGEXP运算符是用户函数regexp()的一个特殊的代表符号。缺省情况下regexp()函数不被定义,所以使用REGEXP运算符会报错。当运行时存在用户定义的“regexp”函数的定义,则调用该函数以实现REGEXP运算符功能。

字段名可以是CREATE TABLE语句定义的任何名字或如下几个特殊标识符之一“ROWID”、“OID”以及“_ROWID_”。 这些特殊标识符均代表每个表每一行关联的那个唯一随机整数键“row key”。仅仅在CREATE TABLE语句没有对这些特殊标识符的真实字段予以定义的情况下,它们才代表“row key”。它们与只读字段类似,可以像任何正常字段一样使用,除了在UPDATE或INSERT语句中(即是说你不能添加或更改row key)。“SELECT * …”不返回row key。

SELECT语句可以在表达式中出现,作为IN运算符的右边运算量,作为一个纯量,或作为EXISTS运算符的运算量。当作纯量或IN的运算量 时,SELECT语句的结果仅允许有一个字段,可以使用复合的SELECT(用UNION或 EXCEPT等关键字连接)。作为EXISTS运算符的运算量时,SELECT结果中的字段被忽略,在结果为空时表达式为假,反之为真。若SELECT表 达式代表的查询中不含有引用值的部分,则它将在处理其它事务之前被计算,并且结果在必要时会被重复使用。若SELECT表达式含从其它查询中得到的变量, 在每一次使用时该表达式均被重新计算。

当SELECT作为IN运算符的右运算量,在左边的运算量是SELECT产生的任意一个值时,表达式返回TRUE。IN运算符前可以加NOT构成相反的意思。

当SELECT与表达式一同出现且不在IN的右边,则SELECT结果的第一行作为表达式中使用的值。SELECT返回的结果在第一行以后的部分被忽略。返回结果为空时SELECT语句的值为NULL。

CAST表达式将的数据类型改为声明的类型。可以是CREATE TABLE语句字段定义部分定义的对该字段有效的任意非空数据类型。

表达式支持简单函数和聚集函数。简单函数直接从输入获得结果,可用于任何表达式中。聚集函数使用结果集中的所有行计算结果,仅用于SELECT语句中。

T下面这些函数是缺省可用的。可以使用C语言写出其它的函数然后使用sqlite3_create_function() API函数添加到数据库引擎中。

 

abs(X) 返回参数X的绝对值。
coalesce(X,Y,…) 返回第一个非空参数的副本。若所有的参数均为NULL,返回NULL。至少2个参数。
glob(X,Y) 用于实现SQLite的 “X GLOB Y“语法。可使用 sqlite3_create_function() 重载该函数从而改变GLOB运算符的功能。
ifnull(X,Y) 返回第一个非空参数的副本。 若两个参数均为NULL,返回NULL。与上面的 coalesce()类似。
last_insert_rowid() 返回当前数据库连接最后插入行的ROWID。sqlite_last_insert_rowid() API函数同样可用于得到该值。
length(X) 返回X的长度,以字符计。如果SQLite被配置为支持UTF-8,则返回UTF-8字符数而不是字节数。
like(X,Y [,Z]) 用于实现SQL语法”X LIKE Y [ESCAPE Z]“.若使用可选的ESCAPE子句,则函数被赋予三个参数,否则只有两个。可使用sqlite3_create_function() 重载该函数从而改变LIKE运算符的功能。
lower(X) 返回X字符串的所有字符小写化版本。这一转换使用C语言库的tolower()函数,对UTF-8字符不能提供好的支持。
max(X,Y,…) 返回最大值。参数可以不仅仅为数字,可以为字符串。大小顺序由常用的排序法则决定。注意,max()在有2个或更多参数时为简单函数,但当仅给出一个参数时它变为聚集函数。
min(X,Y,…) 返回最小值。与max()类似。
nullif(X,Y) 当两参数不同时返回X,否则返回NULL.
quote(X) 返回参数的适于插入其它SQL语句中的值。字符串会被添加单引号,在内部的引号前会加入逃逸符号。 BLOB被编码为十六进制文本。当前的VACUUM使用这一函数实现。在使用触发器实现撤销/重做功能时这一函数也很有用。
random(*) 返回介于-2147483648和 +2147483647之间的随机整数。
round(X)
round(X,Y)
X四舍五入,保留小数点后Y位。若忽略Y参数,则默认其为0。
soundex(X) 计算字符串X的soundex编码。参数为NULL时返回字符串“?000”。缺省的SQLite是不支持该函数的,当编译时选项 -DSQLITE_SOUNDEX=1 时该函数才可用。
sqlite_version(*) 返回所运行的SQLite库的版本号字符串。如 “2.8.0”。
substr(X,Y,Z) 返回输入字符串X中以第Y个字符开始,Z个字符长的子串。X最左端的字符序号为1。若Y为负,则从右至左数起。若SQLite配置支持UTF-8,则“字符”代表的是UTF-8字符而非字节。
typeof(X) 返回表达式X的类型。返回值可能为“null”、“integer”、“real”、“text”以及“blob”。SQLite的类型处理参见SQLite3的数据类型
upper(X) 返回X字符串的所有字符大写化版本。这一转换使用C语言库的toupper()函数,对UTF-8字符不能提供好的支持。

以下是缺省可用的聚集函数列表。可以使用C语言写出其它的聚集函数然后使用sqlite3_create_function() API函数添加到数据库引擎中。

在单参数聚集函数中,参数可以加前缀DISTINCT。这时重复参数会被过滤掉,然后才穿入到函数中。例如,函数“count(distinct X)”返回字段X的不重复非空值的个数,而不是字段X的全部非空值。

 

avg(X) 返回一组中非空的X的平均值。非数字值作0处理。avg()的结果总是一个浮点数,即使所有的输入变量都是整数。 
count(X)
count(*)
返回一组中X是非空值的次数的第一种形式。第二种形式(不带参数)返回该组中的行数。
max(X) 返回一组中的最大值。大小由常用排序法决定。
min(X) 返回一组中最小的非空值。大小由常用排序法决定。仅在所有值为空时返回NULL。
sum(X)
total(X)
返回一组中所有非空值的数字和。若没有非空行,sum()返回NULL而total()返回0.0。NULL通常情况 下并不是对于“没有行”的和的一个有意义的结果,但SQL标准如此要求,且大部分其它SQL数据库引擎这样定义sum(),所以SQLite 也如此定义以保证兼容。我们提供非标准的total()函数作为解决该SQL语言设计问题的一个简易方法。total()的返回值式中为浮点数。sum()可以为整数,当所有非空输入均为整数时,和是精确的。若sum()的任意一个输入既非整数也非NULL或计算中产生整数类型的溢出时,sum()返回接近真和的浮点数。

INSERT

 

sql-statement ::= INSERT [OR conflict-algorithm] INTO [database-name .]table-name[(column-list)] VALUES(value-list) |
INSERT
[OR conflict-algorithm] INTO [database-name .]table-name[(column-list)]select-statement

INSERT语句有两种基本形式。一种带有“VALUES”关键字,在已有表中插入一个新的行。若不定义字段列表,那么值的数目将与表中的字段数目相同。否则值的数目须与字段列表中的字段数目相同。不在字段列表中的字段被赋予缺省值或NULL(当未定义缺省值)。

INSERT的第二种形式从SELECT语句中获取数据。若未定义字段列表,则从SELECT得到的字段的数目必须与表中的字段数目相同,否则应与 定义的字段列表中的字段数目相同。SELECT的每一行结果在表中插入一个新的条目。SELECT可以是简单的或者复合的。如果SELECT语句带有 ORDER BY子句,ORDER BY会被忽略。

在使用这一命令时,利用可选的ON CONFLICT子句可以定义替代的约束冲突判定算法。更多信息,参见ON CONFLICT。为了兼容MySQL,可以使用REPLACE代替“INSERT OR REPLACE”。

ON CONFLICT子句

 

conflict-clause ::= ON CONFLICT conflict-algorithm
conflict-algorithm ::= ROLLBACK | ABORT | FAIL | IGNORE | REPLACE

ON CONFLICT子句不是独立的SQL命令。这是一条可以出现在许多其他SQL命令中的非标准的子句。由于它并不是标准的SQL语言,这里单独介绍它。

ON CONFLICT子句的语法在如上的CREATE TABLE命令中示出。对于INSERT和UPDATE,关键词“ON CONFLICT”由“OR”替代,这样语法显得自然。例如,不用写“INSERT ON CONFLICT IGNORE”而是“INSERT OR IGNORE”。二者表示相同的意思。

ON CONFLICT子句定义了解决约束冲突的算法。有五个选择:ROLLBACK、ABORT、FAIL、IGNORE和REPLACE,缺省方案是ABORT。选项含义如下:

ROLLBACK
当发生约束冲突,立即ROLLBACK,即结束当前事务处理,命令中止并返回SQLITE_CONSTRAINT代码。若当前无活动事务(除了每一条命令创建的默认事务以外),则该算法与ABORT相同。

ABORT
当发生约束冲突,命令收回已经引起的改变并中止返回SQLITE_CONSTRAINT。但由于不执行ROLLBACK,所以前面的命令产生的改变将予以保留。缺省采用这一行为。

FAIL
当发生约束冲突,命令中止返回SQLITE_CONSTRAINT。但遇到冲突之前的所有改变将被保留。例如,若一条UPDATE语句在100行遇到冲突100th,前99行的改变将被保留,而对100行或以后的改变将不会发生。

IGNORE
当发生约束冲突,发生冲突的行将不会被插入或改变。但命令将照常执行。在冲突行之前或之后的行将被正常的插入和改变,且不返回错误信息。

REPLACE
当发生UNIQUE约束冲突,先存在的,导致冲突的行在更改或插入发生冲突的行之前被删除。这样,更改和插入总是被执行。命令照常执行且不返回错误信息。当发生NOT NULL约束冲突,导致冲突的NULL值会被字段缺省值取代。若字段无缺省值,执行ABORT算法。

当冲突应对策略为满足约束而删除行时,它不会调用删除触发器。但在新版中这一特性可能被改变。

INSERT或UPDATE的OR子句定义的算法会覆盖CREATE TABLE所定义的。ABORT算法将在没有定义任何算法时缺省使用。

SQLite支持的编译指令(Pragma)

PRAGMA命令是用于修改SQlite库或查询SQLite库内部数据(non-table)的特殊命令。PRAGMA 命令使用与其它SQLite命令(例如:SELECT、INSERT)相同的接口,但在如下重要方面与其它命令不同:

  • 在未来的SQLite版本中部分Pragma可能被删除或添加,要小心使用。
  • 当使用未知的Pragma语句时不产生报错。未知的Pragma仅仅会被忽略,即是说若是打错了Pragma语句SQLite不会提示用户。
  • 一些Pragma在SQL编译阶段生效而非执行阶段。即是说若使用C语言的sqlite3_compile()、sqlite3_step()、 sqlite3_finalize() API(或类似的封装接口中),Pragma可能在调用sqlite3_compile()期间起作用。
  • Pragma命令不与其它SQL引擎兼容。

可用的pragma命令有如下四个基本类型:


PRAGMA命令语法

 

sql-statement ::= PRAGMA name[= value]|
PRAGMA
function(arg)

使用整数值value的pragma也可以使用符号表示,字符串“on”、“true”和“yes”等同于1,“off”、“false”和“no”等同于0。这些字符串大小写不敏感且无须进行引用。无法识别的字符串被当作1且不会报错。value返回时是整数。


用于修改SQLite库的操作的Pragma

  • PRAGMA auto_vacuum;
    PRAGMA auto_vacuum =
    0 | 1;

    查询或设置数据库的auto-vacuum标记。

    正常情况下,当提交一个从数据库中删除数据的事务时,数据库文件不改变大小。未使用的文件页被标记并在以后的添加操作中再次使用。这种情况下使用VACUUM命令释放删除得到的空间。

    当开启auto-vacuum,当提交一个从数据库中删除数据的事务时,数据库文件自动收缩,(VACUUM命令在auto-vacuum开启的数据库中不起作用)。数据库会在内部存储一些信息以便支持这一功能,这使得数据库文件比不开启该选项时稍微大一些。

    只有在数据库中未建任何表时才能改变auto-vacuum标记。试图在已有表的情况下修改不会导致报错。

  • PRAGMA cache_size;
    PRAGMA cache_size =
    Number-of-pages;

    查询或修改SQLite一次存储在内存中的数据库文件页数。每页使用约1.5K内存,缺省的缓存大小是2000。若需要使用改变大量多行的UPDATE或DELETE命令,并且不介意SQLite使用更多的内存的话,可以增大缓存以提高性能。

    当使用cache_size pragma改变缓存大小时,改变仅对当前对话有效,当数据库关闭重新打开时缓存大小恢复到缺省大小。要想永久改变缓存大小,使用default_cache_size pragma。

  • PRAGMA case_sensitive_like;
    PRAGMA case_sensitive_like =
    0 | 1;

    LIKE运算符的缺省行为是忽略latin1字符的大小写。因此在缺省情况下‘a’ LIKE ‘A’的值为真。可以通过打开case_sensitive_like pragma来改变这一缺省行为。当启用case_sensitive_like,‘a’ LIKE ‘A’为假而 ‘a’ LIKE ‘a’依然为真。

  • PRAGMA count_changes;
    PRAGMA count_changes =
    0 | 1;

    查询或更改count-changes标记。正常情况下INSERT, UPDATE和DELETE语句不返回数据。当开启count-changes,以上语句返回一行含一个整数值的数据——该语句插入、修改或删除的行数。 返回的行数不包括由触发器产生的插入、修改或删除等改变的行数。

  • PRAGMA default_cache_size;
    PRAGMA default_cache_size =
    Number-of-pages;

    查询或修改SQLite一次存储在内存中的数据库文件页数。每页使用约1.5K内存,它与cache_size pragma类似,只是它永久性地改变缓存大小。利用该pragma,你可以设定一次缓存大小,并且每次重新打开数据库时都继续使用该值。

  • PRAGMA default_synchronous;

    该语句在2.8版本中可用,但在3.0版中被去掉了。这条pragma很危险且不推荐使用,安全起见在该文档中不涉及此pragma的用法。

  • PRAGMA empty_result_callbacks;
    PRAGMA empty_result_callbacks =
    0 | 1;

    查询或更改empty-result-callbacks标记。

    empty-result-callbacks标记仅仅影响sqlite3_exec API函数。正常情况下,empty-result-callbacks标记清空,则对返回0行数据的命令不调用sqlite3_exec()的回叫函 数,当设置了empty-result-callbacks,则调用回叫 函数一次,置第三个参数为0(NULL)。这使得使用sqlite3_exec() API的程序即使在一条查询不返回数据时依然检索字段名。

  • PRAGMA encoding;
    PRAGMA encoding = “UTF-8”;
    PRAGMA encoding = “UTF-16”;
    PRAGMA encoding = “UTF-16le”;
    PRAGMA encoding = “UTF-16be”;

    在第一种形式中,若主数据库已创建,这条pragma返回主数据库使用得文本编码格式,为“UTF-8”、“UTF-16le”(little- endian UTF-16 encoding)或者“UTF-16be”(big-endian UTF-16 encoding)中的一种。 若主数据库未创建,返回值为当前会话创建的主数据库将要使用的文本编码格式。

    第二种及以后几种形式只在主数据库未创建时有效。这时该pragma设置当前会话创建的主数据库将要使用的文本编码格式。“UTF-16”表示“使用本机字节顺序的UTF-16编码”。若这些形式在主数据库创建后使用,将被忽略且不产生任何效果。

    数据库的编码格式设置后不能够被改变。

    ATTACH命令创建的数据库使用与主数据库相同的编码格式。

  • PRAGMA full_column_names;
    PRAGMA full_column_names =
    0 | 1;

    查询或更改the full-column-names标记。该标记影响SQLite命名SELECT语句(当字段表达式为表-字段或通配符”*”时)返回的字段名的方式。 正常情况下,当SELECT语句将两个或多个表连接时,这类结果字段的返回名为,当SELECT语句查询一个单独的表时,返回字段名为。当设置了 full-column-names标记,返回的字段名将统一为 不管是否对表进行了连接。

    若short-column-names和full-column-names标记同时被设置,则使用full-column-names方式。

  • PRAGMA fullfsync
    PRAGMA fullfsync =
    0 | 1;

    查询或更改fullfsync标记。该标记决定是否在支持的系统上使用F_FULLFSYNC同步模式。缺省值为off。截至目前(2006-02-10)只有Mac OS X系统支持F_FULLFSYNC。

  • PRAGMA page_size;
    PRAGMA page_size =
    bytes;

    查询或设置page-size值。只有在未创建数据库时才能设置page-size。页面大小必须是2的整数倍且大于等于512小于等于8192。上限可以通过在编译时修改宏定义SQLITE_MAX_PAGE_SIZE的值来改变。上限的上限是32768。

  • PRAGMA read_uncommitted;
    PRAGMA read_uncommitted =
    0 | 1;

    查询,设置或清除READ UNCOMMITTED isolation(读取未授权的分隔符)。缺省的SQLite分隔符等级是SERIALIZABLE。任何线程或进程可选用READ UNCOMMITTED isolation,但除了共享公共页和schema缓存的连接之间以外的地方也会使用SERIALIZABLE。缓存共享通过sqlite3_enable_shared_cache() API开启,且只在运行同一线程的连接间有效。缺省情况下缓存共享是关闭的。

  • PRAGMA short_column_names;
    PRAGMA short_column_names =
    0 | 1;

    查询或更改the short-column-names标记。该标记影响SQLite命名SELECT语句(当字段表达式为表-字段或通配符”*”时)返回的字段名的方 式。正常情况下,当SELECT语句将两个或多个表连接时,这类结果字段的返回名为,当SELECT语句查询一个单独的表时,返回字段名为。当设置了 full-column-names标记,返回的字段名将统一为不管是否对表进行了连接。

    若short-column-names和full-column-names标记同时被设置,则使用full-column-names方式。

  • PRAGMA synchronous;
    PRAGMA synchronous = FULL;
    (2)
    PRAGMA synchronous = NORMAL;
    (1)
    PRAGMA synchronous = OFF;
    (0)

    查询或更改“synchronous”标记的设定。第一种形式(查询)返回整数值。 当synchronous设置为FULL(2),SQLite数据库引擎在紧急时刻会暂停以确定数据已经写入磁盘。这使系统崩溃或电源出问题时能确保数据 库在重起后不会损坏。FULL synchronous很安全但很慢。当synchronous设置为NORMAL,SQLite数据库引擎在大部分紧急时刻会暂停,但不像FULL模式 下那么频繁。NORMAL模式下有很小的几率(但不是不存在)发生电源故障导致数据库损坏的情况。但实际上,在这种情况下很可能你的硬盘已经不能使用,或 者发生了其他的不可恢复的硬件错误。设置为synchronous OFF(0)时,SQLite在传递数据给系统以后直接继续而不暂停。若运行SQLite的应用程序崩溃,数据不会损伤,但在系统崩溃或写入数据时意外断 电的情况下数据库可能会损坏。另一方面,在synchronous OFF时一些操作可能会快50倍甚至更多。

    在SQLite2中,缺省值为NORMAL,而在3中修改为FULL。

  • PRAGMA temp_store;
    PRAGMA temp_store = DEFAULT;
    (0)
    PRAGMA temp_store = FILE;
    (1)
    PRAGMA temp_store = MEMORY;
    (2)

    查询或更改“temp_store”参数的设置。当temp_store设置为DEFAULT(0),使用编译 时的C预处理宏TEMP_STORE来定义储存临时表和临时索引的位置。当设置为MEMORY(2)临时表和索引存放于内存中。当设置为FILE(1)则 存放于文件中。temp_store_directory pragma可用于指定存放该文件的目录。当改变temp_store设置,所有已存在的临时表、索引、触发器及视图将被立即删除。

    库中的编译时C预处理标志TEMP_STORE可以覆盖该pragma设置。下面的表给出TEMP_STORE预处理宏和temp_store pragma交互作用的总结:

    TEMP_STORE PRAGMA
    temp_store
    临时表和索引
    使用的存储方式
    0 any 文件
    1 0 文件
    1 1 文件
    1 2 内存
    2 0 内存
    2 1 文件
    2 2 内存
    3 any 内存

  • PRAGMA temp_store_directory;
    PRAGMA temp_store_directory = ‘directory-name’;

    查询或更改“temp_store_directory”设置——存储临时表和索引的文件所在的目录。仅在当前连接有效,在建立新连接时重置为缺省值。

    当改变了temp_store_directory设置,所有已有的临时表、索引、触发器、视图会被直接删除。建议在数据库一打开时就设置好temp_store_directory。

    directory-name需用单引号引起来。要想恢复缺省目录,把directory-name设为空字符串。例如 PRAGMA temp_store_directory = ”。若directory-name未找到或不可写会引发错误。

    临时文件的缺省目录与主机的系统有关,使用Unix/Linux/OSX系统的主机,缺省目录是如下序列之中第一个可写的/var/tmp、/usr/tmp、/tmp、current-directory。对于Windows NT,缺省目录由Windows决定,一般为C:/Documents and Settings/user-name/Local Settings/Temp/。SQLite创建的临时文件在使用完毕时就被unlink,所以操作系统可以在SQLite进程进行中自动删除临时文件。于是,正常情况下不能通过lsdir命令看到临时文件。


用于查询数据库的schema的Pragma

  • PRAGMA database_list;

    对每个打开的数据库,使用该数据库的信息调用一次回叫函数。使用包括附加的数据库名和索引名在内的参数。第一行用于主数据库,第二行用于存放临时表的临时数据库。

  • PRAGMA foreign_key_list(table-name);

    对于参数表中每个涉及到字段的外键,使用该外键的信息调用一次回叫函数。每个外键中的每个字段都将调用一次回叫函数。

  • PRAGMA index_info(index-name);

    对该索引涉及到的每个字段,使用字段信息(字段名、字段号)调用一次回叫函数。

  • PRAGMA index_list(table-name);

    对表中的每个索引,使用索引信息调用回叫函数。参数包括索引名和一个指示索引是否唯一的标志。

  • PRAGMA table_info(table-name);

    对于表中的每个字段,使用字段信息(字段名、数据类型、可否为空、缺省值)调用回叫函数。


用于查询/更改版本信息的Pragma

  • PRAGMA [database.]schema_version;
    PRAGMA [database.]schema_version =
    integer ;
    PRAGMA [database.]user_version;
    PRAGMA [database.]user_version =
    integer ;

    这两条pragma分别用于设置schema-version和user-version的值。schema-version和user-version均为32位有符号整数,存放于数据库头中。

    schema-version通常只由SQLite内部操作。每当数据库的Schema改变时(创建或撤消表或索引),SQLite将这个值增大。 Schema版本在每一次Query被执行时被SQLite所使用,以确定编译SQL Query时内部Cache的Schema与编译后的Query实际执行时数据库的Schema相匹配。使用“PRAGMA schema_version”更改schema-version会破坏这一机制,有导致程序崩溃或数据库损坏的潜在危险。请小心使用!

    user-version不在SQLite内部使用,任何程序可以用它来做任何事。


用于库Debug的Pragma

  • PRAGMA integrity_check;

    该命令对整个数据库进行完整性检查,查找次序颠倒的记录,丢失的页,残缺的记录以及损坏的索引。若发现任何问题则返回一形容问题所在的字符串,若一切正常返回“ok”。

  • PRAGMA parser_trace = ON; (1)
    PRAGMA parser_trace = OFF;
    (0)

    打开或关闭SQLite库中的SQL语法分析追踪,用于Debug。只有当SQLite不使用NDebug宏进行编译时该pragma才可用。

  • PRAGMA vdbe_trace = ON; (1)
    PRAGMA vdbe_trace = OFF;
    (0)

    打开或关闭SQLite库中的虚拟数据库引擎追踪,用于Debug。更多信息,查看 VDBE文档

  • PRAGMA vdbe_listing = ON; (1)
    PRAGMA vdbe_listing = OFF;
    (0)

    打开或关闭虚拟机程序列表,当开启列表功能,整个程序的内容在执行前被打印出来,就像在每条语句之前自动执行EXPLAIN。语句在打印列表之后正常执行。用于Debug。更多信息,查看VDBE文档

REINDEX

 

sql-statement ::= REINDEX collation name
sql-statement ::= REINDEX [database-name .]table/index-name

REINDEX命令用于删除并从草稿重建索引。当比较顺序改变时该命令显得很有效。

在第一种形式中,所有附加数据库中使用该比较顺序的索引均被重建。在第二种形式中,[database-name.]table/index-name标识出一个表,所有关联该表的索引被重建。若标识出索引,则仅仅该索引被删除并重建。

若不指定database-name而指定表/索引名以及比较顺序,只有关联该比较顺序的索引被重建。在重建索引时总是指定database-name可以消除这一歧义。

REPLACE

 

sql-statement ::= REPLACE INTO [database-name .]table-name[( column-list )] VALUES ( value-list ) |
REPLACE INTO
[database-name .]table-name[( column-list )]select-statement

REPLACE命令用于替代INSERT的“INSERT OR REPLACE”变体,以更好的兼容MySQL。查看INSERT命令文档获取更多信息。

SELECT

 

sql-statement ::= SELECT [ALL | DISTINCT]result[FROM table-list]
[WHERE expr]
[GROUP BY expr-list]
[HAVING expr]
[compound-opselect]*
[ORDER BY sort-expr-list]
[LIMIT integer[( OFFSET | , )integer]]
result ::= result-column[, result-column]*
result-column ::= * |table-name . * |expr[[AS]string]
table-list ::= table[join-optablejoin-args]*
table ::= table-name[AS alias]|
(
select ) [AS alias]
join-op ::= , |[NATURAL][LEFT | RIGHT | FULL][OUTER | INNER | CROSS] JOIN
join-args ::= [ON expr][USING ( id-list )]
sort-expr-list ::= expr[sort-order][, expr[sort-order]]*
sort-order ::= [ COLLATE collation-name][ ASC | DESC ]
compound_op ::= UNION | UNION ALL | INTERSECT | EXCEPT

SELECT语句用于查询数据库。一条SELECT命令的返回结果是零或多行每行有固定字段数的数据。字段的数目由在SELECT和FROM之间的表达式列表定义。任意的表达式都可以被用作结果。若表达式是 *则表示所有表的所有字段。若表达式是表的名字后接.*则结果为该表中的所有字段。

DISTINCT关键字的使用会使返回的结果是原结果的一个不含相同行的子集。NULL值被认为是相同的。缺省行为是返回所有的行,为清楚起见可以使用关键字ALL。

查询对FROM之后定义的一个或多个表进行。若多个表用逗号连接,则查询针对它们的交叉连接。所有的SQL-92连接语法均可以用于定义连接。圆括 号中的副查询可能被FROM子句中的任意表名替代。当结果中仅有一行包含表达式列表中的结果的行时,整个的FROM子句会被忽略。

WHERE子句可以限定查询操作的行数目。

GROUP BY子句将一行或多行结果合成单行输出。当结果有聚集函数时这将尤其有用。GROUP BY子句的表达式不须是出现在结果中的表达式。HAVING子句与WHERE相似,只是HAVING用于过滤分组创建的行。HAVING子句可能包含值,甚至是不出现在结果中的聚集函数。

ORDER BY子句对所得结果根据表达式排序。表达式无须是简单SELECT的结果,但在复合SELECT中每个表达式必须精确对应一个结果字段。每个表达式可能跟 随一个可选的COLLATE关键字以及用于排序文本的比较函数名称和/或关键字ASC或DESC,用于说明排序规则。

LIMIT子句限定行数的最大值。负的LIMIT表示无上限。后跟可选的OFFSET说明跳过结果集中的前多少行。在一个复合查询中,LIMIT子 句只允许出现在最终SELECT语句中。限定对于所有的查询均适用,而不仅仅是添加了LIMIT子句的那一行。注意OFFSET关键字用于LIMIT子句 中,则限制值是第一个数字,而偏移量(offset)是第二个数字。若用逗号替代OFFSET关键字,则偏移量是第一个数字而限制值是第二个数字。这是为 了加强对遗留的SQL数据库的兼容而有意造成的矛盾。

复合的SELECT由两个或更多简单SELECT经由UNION、UNION ALL、INTERSECT、EXCEPT中的一个运算符连接而成。在一个复合SELECT中,各个SELECT需指定相同个数的结果字段。仅允许一个 ORDER BY子句出现在SELECT的末尾。UNION和UNION ALL运算符从左至右将所有SELECT的结果合成一个大的表。二者的区别在于UNION的所有结果行是不相同的而UNION ALL允许重复行。INTERSECT运算符取左右两个SELECT结果的交。EXCEPT从左边SELECT的结果中除掉右边SELECT的结果。三个 或更多SELECT复合时,它们从左至右结合。

UPDATE

 

sql-statement ::= UPDATE [ OR conflict-algorithm][database-name .]table-name
SET
assignment[, assignment]*
[WHERE expr]
assignment ::= column-name = expr

UPDATE语句用于改变表中所选行的字段值。每个UPDATE的赋值的等号左边为字段名而右边为任意表达式。表达式可以使用其它字段的值。所有的表达式将在赋值之前求出结果。可以使用WHERE子句限定需要改变的行。

在使用这一命令时,利用可选的ON CONFLICT子句可以定义替代的约束冲突判定算法。更多信息,参见ON CONFLICT

VACUUM

 

sql-statement ::= VACUUM [index-or-table-name]

VACUUM命令是SQLite的一个扩展功能,模仿PostgreSQL中的相同命令而来。若调用VACUUM带一个表名或索引名,则将整理该表或索引。在SQLite 1.0中,VACUUM命令调用gdbm_reorganize()整理后端数据库文件。

SQLITE 2.0.0中去掉了GDBM后端,VACUUM无效。在2.8.1版中,VACUUM被重新实现。现在索引名或表名被忽略。

当数据库中的一个对象(表、索引或触发器)被撤销,会留下空白的空间。它使数据库比需要的大小更大,但能加快插入速度。实时的插入和删除会使得数据 库文件结构混乱,减慢对数据库内容访问的速度。VACUUM命令复制主数据库文件到临时数据库并从临时数据库重新载入主数据库,以整理数据库文件。这将除 去空白页,使表数据彼此相邻排列,并整理数据库文件结构。不能对附加数据库文件进行以上操作。

若当前有活动事务,该命令无法起作用。对于In-Memory数据库,该命令无效。

SQLite 3.1中,可以通过使用auto-vacuum模式取代VACUUM命令,使用auto_vacuum pragma开启该模式。

[转载]NoSQL初探之人人都爱Redis:(1)Redis简介与简单安装 - Edison Chou - 博客园

mikel阅读(1036)

[转载]NoSQL初探之人人都爱Redis:(1)Redis简介与简单安装 – Edison Chou – 博客园.

一、NoSQL的风生水起

 1.1 后Web2.0时代的发展要求

随着互联网Web2.0网站的兴起,传统的关系数据库在应付Web2.0网站,特别是超大规模和高并发的SNS类型的Web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题:

(1)对数据库高并发读写的需求

网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以基本上无法使用动态页面静态化技术,因此数据库并发负载非常高,往往要达到每 秒上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。

(2)对海量数据的高效率存储和访问的需求

对于大型的SNS网站,每天用户产生海量的用户动态,以国外的Friendfeed为例,一个月就达到了2.5亿条用户动态,对于关系数据库来说,在一张2.5亿条记录的表里面进行SQL查询,效率是极其低下乃至不可忍受的。

(3)对数据库的高可扩展性和高可用性的需求

在基于Web的架构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,你的数据库却没有办法像Web服务器和 应用服务器那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供7*24小时不间断服务的网站来说,对数据库系统进行升级和扩 展是非常痛苦的事情,往往需要停机维护和数据迁移,为什么数据库不能通过不断的添加服务器节点来实现扩展呢?

在上面提到的“三高”的需求面前,关系数据库遇到了难以克服的障碍,而对于Web2.0网站来说,关系数据库的很多主要特性却往往无用武之地,例如:

(1)数据库事务一致性需求

很多Web实时系统并不要求严格的数据库事务,对读一致性的要求很低,有些场合对写一致性要求也不高。因此数据库事务管理成了数据库高负载下一个沉重的负担。

(2)数据库的写实时性和读实时性需求

对关系数据库来说,插入一条数据之后立刻查询,是肯定可以读出来这条数据的。并不要求这么高的实时性。

(3)对复杂的SQL查询,特别是多表关联查询的需求

任何大数据量的Web系统,都非常忌讳多个大表的关联查询,以及复杂的数据分析类型的复杂SQL报表查询,特别是SNS类型的网站,从需求以及 产品设计角度,就避免了这种情况的产生。往往更多的只是单表的主键查询,以及单表的简单条件分页查询,SQL的功能被极大的弱化了。

因此,关系数据库在这些越来越多的应用场景下显得不那么合适了,为了解决这类问题的非关系数据库应运而生。NoSQL 是非关系型数据存储的广义定义。它打破了长久以来关系型数据库与ACID理论大一统的局面。NoSQL 数据存储不需要固定的表结构(例如以键值对存储,它的结构不固定,每一个元组可以有不一样的字段,每个元组可以根据需要增加一些自己的键值对,这样就不会局限于固定的结构,可以减少一些时间和空间的开销),通常也不存在连接操作。

 1.2 NoSQL无与伦比的特点

在大数据存取上具备关系型数据库无法比拟的性能优势例如:

(1)易扩展

NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力。

(2)大数据量,高性能

NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。

(3)灵活的数据模型

NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的Web2.0时代尤其明显。

(4)高可用

NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如Cassandra,HBase模型,通过复制模型也能实现高可用。

综上所述,NoSQL的非关系特性使其成为了后Web2.0时代的宠儿,助力大型Web2.0网站的再次起飞,是一项全新的数据库革命性运动。

二、Redis的脱颖而出

随着应用对高性能需求的增加,NoSQL逐渐在各大名企的系统架构中生根发芽。时至今日,涌现出的NoSQL产品已经有很多种了,例如Membase、MongoDB、Apache Cassandra、CouchDB等。不过,在国内外互联网巨头例如社交巨头新浪微博、传媒巨头Viacom及图片分享领域佼佼者Pinterest等名企都不约而同地采用了Redis作为其NoSQL数据库的选择,到底Redis是何方神圣呢?能让如此多的名企为它而痴狂。

按照官方的说法,Redis是一个开源的,使用C语言编写,面向“键/值”(Key/Value)对类型数据的分布式NoSQL数据库系统,特点是高性能持久存储适应高并发的应用场景。因此,可以说Redis纯粹为应用而产生,它是一个高性能的key-value数据库,并且还提供了多种语言的API(包括我们的大C#)。那么,也许我们会问:到底性能如何呢?以下是官方的bench-mark数据:

  测试完成了50个并发执行100000个请求
  设置和获取的值是一个256字节字符串。
  Linux box是运行Linux 2.6,这是X3320 Xeon 2.5 ghz。
  文本执行使用loopback接口(127.0.0.1)。
  结果:读的速度是110000次/s,写的速度是81000次/s 。(当然不同的服务器配置性能也有所不同)。

和Memcached类似,Redis支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、 zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是 原子性的。在此基础上,Redis支持各种不同方式的排序。与Memcached一样,为了保证效率,数据都是缓存在内存中。区别的是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步(数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。)。

因此,Redis的出现,很大程度补偿了Memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用

三、Redis在Windows上的安装与配置

前面介绍了一大堆,现在开始真刀实干!首先,肯定是安装Redis,这里我们选择比较熟悉的Windows平台来进行安装。既然是 Windows平台,那么肯定要选择一个Windows版本的Redis安装包(其实NoSQL大部分都是部署在Linux服务器上得,什么原因?大家都 懂得,开源+免费=成熟的服务器方案)。

(1)进入GitHub的Redis相关包下载页面:https://github.com/MSOpenTech/redis

Of course,这个地址也是可以的:https://github.com/dmajkic/redis/downloads

(2)选择相应版本,我这里选择的是2.6的版本,点击Download ZIP按钮进行下载;

(3)打开压缩包,可以看到我们下载的其实是一个完整的Redis-2.6的包,包含了bin、src等文件夹,src是源码,而bin则是编 译好的执行文件,也是我们主要使用的东东。进入bin目录,可以看到里面又有两个目录,一个是32位操作系统使用的,另一个则是64位操作系统使用的。

(4)我所使用的是64位系统,所以我将redisbin64.zip拷贝出来,解压后移动至我的D:/Redis目录中,可以看到解压后的内容包含以下的一些可执行exe文件:

下面来看看这几个可执行exe文件都是干嘛用的?

redis-server.exe:服务程序,也是最最最核心的一个程序。说到底,Redis也就是我们服务器系统上一直运行的一个服务甚至就是一个进程而已。

②redis-check-dump.exe:本地数据库检查

③redis-check-aof.exe:更新日志检查

④redis-benchmark.exe:性能测试,用以模拟同时由N个客户端发送M个 SETs/GETs 查询。上面所提到的测试结果就是使用的该程序来进行的。

⑤redis-cli.exe: 服务端开启后,我们的客户端就可以输入各种命令测试了,例如GET、SET等操作;

另外,将刚刚下载的包里边的redis.conf默认配置文件拷贝到工作目录中(我的是D:/Redis),redis.conf主要是一些Redis的默认服务配置,包括默认端口号(一般默认6379)啊之类的配置。

(5)既然我们知道了redis-server.exe是最核心的一个程序,那么我们就首先来将它开启。这里需要在Windows的命令行界面 中来开启,首先在运行窗口输入cmd进入命令窗口,使用cd命令切换到指定的目录(我是将刚刚解压的文件放在了D:/Redis文件夹下)

(6)最后就是惊心动魄地开启Redis的服务了,输入一句简单的命令:redis-server.exe redis.conf

这里需要注意的是:开启Redis服务后,命令行窗口不要关闭,一旦关闭,Redis服务也会相应关闭。因此,我们一般会将其改为Windows服务,并且设置为开机自动启动,就像我们数据库服务器中的SQL Server服务和Web服务器中的IIS服务一样。

(7)究竟我们的Redis安装好了没呢?我们可以通过新打开(记得是新打开一个,而不是将原来那个关闭了)一个cmd窗口使用redis-cli.exe来测试一下:redis-cli.exe -h 服务器IP –p 端口

(8)既然每次都需要在命令窗口中开启Redis服务不爽,那我们就动手将其改为Windows服务,让它自动启动。通过在网上查找,我在CSDN找到了一个批处理文件将拷贝到指定的Redis目录(我的是D:/Redis),其代码如下:

@echo off
set cur_path=%cd%
sc create redis-instance binpath= "\"%cur_path%\redis-server.exe\" %cur_path%\redis.conf" start= "auto" DisplayName= "Redis"

意思是在我们的Windows中创建一个Redis的服务实例,指定要启动的程序路径与配置文件路径,启动模式设置为auto代表自动启动,显 示的服务名称为Redis。这样,一个bat文件就做好了,点击运行之后,一个Redis的Windows服务也出现在了我们的Windows服务列表 中,如下图所示:

四、小结

随着新浪、赶集这些大型网站同一时刻的访问量逐步飙升,但它们却能做到让用户顺利的访问,运用的就是Redis的数据优化处理技术。因 此,Redis经过了各大名企的考验,是一个高性能的NoSQL数据库,具有多种实用的用途。那么,我们.Net开发者是否可以借助Redis解决日常开 发中遇到的高并发的问题呢,答案是肯定的。下一篇,我们会熟悉一下Redis为我们.Net开发者提供的API,以及Redis中提供的常见的数据类型 (多种)等等。最后,衷心感谢本文的参考文献作者,特别是传智播客的王承伟老师,本文是基于其发布的公开课视频学习整理而成,感谢其提供了这么好的公开课 教程!

参考文献

(1)传智播客Redis公开课,王承伟主讲,http://bbs.itcast.cn/thread-26525-1-1.html

(2)NoSQL百度百科,http://baike.baidu.com/view/2677528.htm

(3)孙立,《NoSQL开篇—为什么使用NoSQL》,http://www.infoq.com/cn/news/2011/01/nosql-why/

(4)Redis百度百科,http://baike.baidu.com/view/4595959.htm

(5)Ruthless,《Windows下安装Redis》,http://www.cnblogs.com/linjiqin/archive/2013/05/27/3101694.html

(6)张善友,《在Windows上以服务方式运行Redis》,http://www.cnblogs.com/shanyou/archive/2013/01/17/redis-on-windows.html

附件下载

(1)Redis-2.6服务包:http://pan.baidu.com/s/1dDEKojJ

(2)Redis注册Windows服务的批处理文件:http://pan.baidu.com/s/1jGJtNXs

 

[转载]php 模拟用户自动在qq空间发表文章

mikel阅读(1248)

[转载]php 模拟用户自动在qq空间发表文章.

我们这里是一个简单的利用php来php 模拟登录后再到QQ空间发送文章的一个简单的程序,有需要的朋友可以参考,或改进可以给我意见哦。

<?php

//模拟get post请求函数
/*
函数说明:
功能:请求方式可以get,post,可以发送的cookie,保存的cookiefile文件
参数:$url-----请求url    $referer---来源url    $postdata----------用于post请求的数据,''为get请求
$cookie---------发送的cookie     $cookiefile-----保存的cookiefile文件
返回值:返回获取的源码
*/
function request($url,$referer='',$postdata='',$cookie='',$cookiefile=''){
//header设置
$header='';
$header.="Content-Type: application/x-www-form-urlencodedrn";//内容请求类型
$header.="User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)rn";//浏览器字段
$header.="Referer:".$referer."rn";//设置来源地址
$header .= "Cookie:".$cookie ; //设置cookie,默认空
//请求方法get post,通过$postdata空---get,非空----post
if($postdata=='')$method='GET';
else $method='POST';
//定义用于创建流的数组
$opts=array();
$opts['http']=array('method'=>$method,'header'=>$header,'content'=>$postdata);
//生成流
$context=stream_context_create($opts);
//发送请求,获取源码
$yuanma=file_get_contents($url,false,$context);
//是否需要保存cookie到文件,$cookiefile不空时
if($cookiefile!=''){
echo '需要保存cookie<br>';
//判断保存文件存在,不存在创建
if(!file_exists($cookiefile)){
file_put_contents($cookiefile,'');
}
//获取cookie,保存起来
$response=implode("rn",$http_response_header);
//用正则匹配cookie
$zengze="/Set-Cookie:(.*?)rn/";
preg_match_all($zengze,$response,$cookie_arr);
//存在匹配,保存
if(!empty($cookie_arr[1])){
$cookiestr=implode(';',$cookie_arr[1]);
file_put_contents($cookiefile,$cookiestr);
echo '成功保存cookie<br>';
}
else echo '没有匹配到cookie<br>';
}//end if($cookiefile!='')
//返回源码
return $yuanma;
}//end function request($url,$referer,$postdata,$cookie,$cookiefile)


//获得当前的脚本网址
function GetCurUrl()
{
if(!empty($_SERVER["REQUEST_URI"]))
{
$scriptName = $_SERVER["REQUEST_URI"];
$nowurl = $scriptName;
}
else
{
$scriptName = $_SERVER["PHP_SELF"];
if(empty($_SERVER["QUERY_STRING"]))
{
$nowurl = $scriptName;
}
else
{
$nowurl = $scriptName."?".$_SERVER["QUERY_STRING"];
}
}
return $nowurl;
}


//获得当前文件名
$nowurl=GetCurUrl();
//echo $nowurl;

//表单输出,没有提交时
if(!isset($_POST['qq'])){
echo '<form method="post" action="'.$nowurl.'">
qq号码:<input type="text" name="qq"><br>
g_tk:<input type="text" name="g_tk"><br>
标题:<input type="text" name="title"><br>
内容:<input type="text" name="content"><br>
<input type="submit" value="发表文章">
</form>';
die();
}


/*
提交参数说明:
$_POST['qq']---用户QQ
$_POST['g_tk']--这个参数很关键,获得这个参数,需要抓下发表时提交的post地址后面调用的g_tk=1276354485,
路POST http://b1.qzone.qq.com/cgi-bin/blognew/blog_add?g_tk=1276354485里的g_tk=1276354485
$_POST['title']---文章标题,不得空
$_POST['content']---文章内容,不得空
*/

header('Content-Type:text/html;charset=gb2312');
set_time_limit(0);
//ob_end_clean();
//ob_start();

//获取cookie文件,不存在创建,并退出程序
$cookiefile=dirname(__FILE__).'qq_cookie.txt';
if(!file_exists($cookiefile)){
echo 'qq_cookie.txt不存在,自动创建,请填写抓包的cookie<br>';
file_put_contents($cookiefile,'');
die('程序退出');
}
//存在,读取cookie
else{
$cookie=file_get_contents($cookiefile);//登录cookie
//$cookie=urlencode($cookie);
}
//echo 'cookie:'.$cookie.'<br>';

//构成发表页,post数据等的重要信息
//qq号码
if(empty($_POST['qq'])||preg_match('/[^0-9]/is',$_POST['qq']))die('qq号码有误,必须数字');
else $qq=$_POST['qq'];//qq号
if(empty($_POST['g_tk'])||preg_match('/[^0-9]/is',$_POST['g_tk']))die('post重要参数g_tk不合法,必须数字,请使用抓包的值');
$g_tk=$_POST['g_tk'];

$title=empty($_POST['title'])?die('标题不得空'):$_POST['title'];//文章标题
$content=empty($_POST['content'])?die('内容不得空'):$_POST['content'];//内容

$category='个人日记';//分类
$fabiao='http://b1.qzone.qq.com/cgi-bin/blognew/blog_add?g_tk='.$g_tk;//发表处理页
$referer='http://ctc.qzs.qq.com/qzone/v5/toolpages/fp_gbk.html';//来源页
$r1='http://user.qzone.qq.com/'.$qq.'/infocenter';//列表访问来源页
$postdata='uin='.$qq.'&category='.urlencode($category).'&title=".urlencode($title)."&content='.urlencode($content).'&html='.urlencode('<div class="blog_details_20110920">'.$content.'</div>').'&tweetflag=0&cb_autograph=1&topflag=0&needfeed=0&g_tk='.$g_tk.'&_fp_refer=http%3A%2F%2Fctc.qzs.qq.com%2Fqzone%2Fnewblog%2Fv5%2Feditor.html%3Fsource%3D1%7Chttp%3A%2F%2Fctc.qzs.qq.com%2Fqzone%2Fnewblog%2Fv5%2Feditor.html%3Fsource%3D1%3Chttp%3A%2F%2Fuser.qzone.qq.com%2F'.$qq.'%2Fmain';//post数据
//$postdata=urlencode($postdata);
//echo $postdata;
//发送请求,获取源码
$yuanma=request($fabiao,$r1,$postdata,$cookie,'');
if(strpos($yuanma,'发表成功'))echo $title.'  发表成功<br>';
else echo '发表失败:右键查看源码,可以看到具体错误'.$yuanma;

 


?>


php脚本:注意需要保存命名随意已经自动识别,我是命名为qq_fabiao.php,然后设置提交地址,cookie文件qq_cookie.txt需要填写抓包获取的空间登录cookie,以通过登录验证,qq_cookie.txt与php文件同目录

 

 

 

 


/*
提交参数说明:
$_POST['qq']---用户QQ
$_POST['g_tk']--这个参数很关键,获得这个参数,需要抓下发表时提交的post地址后面调用的g_tk=1276354485,
路POST http://b1.qzone.qq.com/cgi-bin/blognew/blog_add?g_tk=1276354485里的g_tk=1276354485

 

 

$_POST['title']---文章标题,不得空
$_POST['content']---文章内容,不得空

[转载]ASP.NET MVC扩展之HtmlHelper辅助方法 - 书洞里的猫 - 博客园

mikel阅读(965)

[转载]MVC扩展之HtmlHelper辅助方法 – 书洞里的猫 – 博客园.

1、什么是HtmlHelper辅助方法?其实就是HtmlHelper类的扩展方法,如下所示:

namespace System.Web.Mvc.Html
{
    public static class FormExtensions//表单相关扩展方法,例如创建表单标签等。

    public static class InputExtensions//这里包含了所有input,例如:text,button,readiobutton等等。

    public static class LinkExtensions//链接相关方法

    public class MvcForm : IDisposable//与客户端控件无关

    public static class RenderPartialExtensions//这是输出PartialView

    public static class SelectExtensions//输出下拉框

    public static class TextAreaExtensions//输出多行文本框

    public static class ValidationExtensions//输出相关表单元素验证。
}

比如对于扩展类InputExtensions,MVC框架本身对此已有扩展:

namespace System.Web.Mvc.Html
{
    // Summary:
    //     Represents support for HTML input controls in an application.
    public static class InputExtensions
    {
         public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name);
    }
}

2、通过对HtmlHelper进行扩展来构建自己的HtmlHelper辅助方法

System.Web.Mvc.Html下的HtmlHelper只能完成大部分html控件的输出,有一些我们经常用到的东东它却没有,怎么办?自己动手吧~

在我们扩展之前,有个叫TagBuilder的类(生成标签)比较好用,你不必纠结于它的细节,只要大概知道他有那些方法就行:

public TagBuilder(string tagName);
   public void AddCssClass(string value);//增加样式
   public void GenerateId(string name);//设置控件ID
   private string GetAttributesString();
   public void MergeAttribute(string key, string value);//设置属性值
   public void MergeAttribute(string key, string value, bool replaceExisting);
   public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes);
   public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes, bool replaceExisting);
   public void SetInnerText(string innerText);//设置显示文本
   public override string ToString();
   public string ToString(TagRenderMode renderMode);//输出控件html

现在可以开始扩展了!

A、扩展img标签

namespace System.Web.Mvc
{
    public static class ImageExtensions
    {
        public static string Image(this HtmlHelper helper, string id, string url, string alternateText)
        {
            return Image(helper, id, url, alternateText, null);
        }
        public static string Image(this HtmlHelper helper, string id, string url, string alternateText, object htmlAttributes)
        {
            // 创建IMG标签
            var builder = new TagBuilder("img");

            // 增加ID属性
            builder.GenerateId(id);

            // 增加属性
            builder.MergeAttribute("src", url);
            builder.MergeAttribute("alt", alternateText);
            builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));

            // 输出完整的img标签
            return builder.ToString(TagRenderMode.SelfClosing);
        }

    }
}

调用:@Html.Image(“img1”, http://img/111.jpg, “这是一张图片”, new {border=”4px”})

输出:这是一张图片

B、扩展div标签

namespace System.Web.Mvc
{
    public static class DivExtensions
    {
        public static String Div(this HtmlHelper helper, String id, String content, String cssStyle, object htmlAttrs)
        {
            TagBuilder builder = new TagBuilder("div");
            //替换“.”为“_”
            builder.IdAttributeDotReplacement = "_";
            builder.GenerateId(id);       
            builder.MergeAttributes(new RouteValueDictionary(htmlAttrs));
            builder.AddCssClass(cssStyle);
            builder.InnerHtml=content;
            return builder.ToString(TagRenderMode.Normal);    //代表是双面标签
        }
    }
}

调用:

@Html.Div(“MyDiv.1”, “扩展方法”, “MyClassStyle”, new { style=”border:solid red 1px;” })

输出:

扩展方法

C、扩展Span标签

namespace System.Web.Mvc
{
    public static class SpanExtensions
    {
        public static string Span(this HtmlHelper helper, string id, string text, string css, object htmlAttributes)
        {
            //创意某一个Tag的TagBuilder
            var builder = new TagBuilder("span");

            //创建Id,注意要先设置IdAttributeDotReplacement属性后再执行GenerateId方法.
            builder.IdAttributeDotReplacement = "-";
            builder.GenerateId(id);
            

            //添加属性            
            builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));

            //添加样式
            builder.AddCssClass(css);
            //或者用下面这句的形式也可以: builder.MergeAttribute("class", css);

            //添加内容,以下两种方式均可
            //builder.InnerHtml = text;
            builder.SetInnerText(text);

            //输出控件
            return builder.ToString(TagRenderMode.Normal);

        }
    }
}

调用:

@Html.Span(“span.test”, “使用TagBuilder帮助构建扩展方法”, “ColorRed”, new { style=”font-size:15px;” })

输出:

使用TagBuilder帮助构建扩展方法

D、扩展ul、li标签

namespace System.Web.Mvc
{
    public static class UlLiExtensions
    {
        public static MvcHtmlString UlLi(this HtmlHelper helper, string[] listItems)

        {

            TagBuilder ulTag = new TagBuilder("ul");

            foreach (string item in listItems)

            {

                TagBuilder liTag = new TagBuilder("li");

                liTag.SetInnerText(item);

                ulTag.InnerHtml += liTag.ToString();

            }

            return new MvcHtmlString(ulTag.ToString());

        }

    }
}

调用:

@Html.List(new string[]{“上海”,”深圳”,”北京”,”广州”})

输出:

<ul> 
<li>上海</li> 
<li>深圳</li> 
<li>北京</li> 
<li>广州</li>  
</ul>

E、扩展截取字符串方法(当我们在显示某一个字段时,如果太长,显示的时候最好截取一下,最好是做成扩展方法来用)

namespace System.Web.Mvc
{
    public static class CutStringExtensions
    {
        public static string CutString(this  System.Web.Mvc.HtmlHelper helper, string content, int length)
        {
            if (content.Length > length)
            {
                return content.Substring(0, length) + "...";
            }
            else
            {
                return content;
            }
        }


    }
}

此处只是抛砖引玉,更多的用法要根据实际需求来进行开发~

[转载]架构师修练 I - 超级代码控 - Ray Liang - 博客园

mikel阅读(941)

[转载]架构师修练 I – 超级代码控 – Ray Liang – 博客园.

 可实现的是架构,空谈是概念 So don’t tell me the concepts show me the code!  “不懂编码的架构师不是好架构师” 好架构师都是超级代码控。

代码是最好的老师

从代码中学习设计的思想、方法是提升类库设计能力、印证你所了解的概念与理论这就是架构师看代码的观点。

基本准备

一个类库可能有数千个类上万个方法,应该如何去看呢? 在看代码前我们需要进行一些什么样的准备呢 ?
  • 设计模式 – 最标准的23种设计模式基本上要有一个了解,可能一下子不能理解他们的用法,但一定要记下他们的英文名字和基本的用途,如:Factory, Wrapper (Decorator), Command,  Builder等 。
  • 语言规范 – 熟读语言本身的官方编码规范与命名规则,这是共同的标准,也是从官方得到写代码的第一指导。
  • 要看懂UML中对类的图形表示方法(类、接口、抽象类、继承关系、使用关系)

看代码的方法

这里所提供的方法就先以C#作为语言基础,因为C#有极为规范的的语法规则。.net 的文档在类库方面的文档是最完整也是最易读的。以.net framework作为起点会是一个很好的练习入口。在开始前我还推荐一下大家需要有一个反编译工具我用的是.NET Reflector, 用反编译工具不是让你去抄代码(代码本身是没有多大价值的,价值的核心在于设计)而是可以更深入地了解到代码是怎样实现的。用反编译工具看微软的代码会看 到很多的不同的,你会发现大最设计得非常有意思的内部类 (Internal) 。从中大至可以推断出微软的开发方法,“在外部接口完全一至的情况下,让程序员编写的自由度最大边界就是内部类与内部方法” 你可以马上动手先看看 System.Web.Mvc.dll 的实现
要点:多问为什么,带着问题看代码——为什么这样写(存在理由)?为什么这样设计(出发点)? 由你来写又将如何实现?

看命名

    以面向对象的语言为例,大多会在名字内带有具体的用法信息,从名称推算可能使用的模式及实现
带有模式印记的类:
  • TagBuilder – 以Builder模式实现的Html标记的构建器
  • StringBuilder – 以Builder模式实现的字符串构造建器
  • XXXXWriter – 以构建器模式实现的各种写入器
  • ConnectionFactory – 数据库连接对象构造工厂
XXX+模式名 是惯用的对模式类的一种命名规则
找到了模式实现如果你看不懂,那恭喜你这就是学习的机会到了,马上翻开设计模式与实现类进行对照印证,这个过程可以加速模式在你大脑中的印象与加深理解。(模式是架构师的大杀招,能不能上档次就看对模式的理解与认识了,一有机会就应该学)
带有家族印记的类:
XXX+Base – 抽象类
基类名+XXX – 某抽象类的子类
是继承关系一种常见的命名规则。
注:每一种基础语言会有自身的命名规则,所以必须对官方提供的命名规则烂熟于胸,既可以学习别人怎么写代码也可以规范自己的代码写法。
 做完这两个练习基本上你可以在不看 Classes References(类手册)的情况下一下子了解一大堆类的存在与用法。

看接口

   接口在设计中有着极为重要的地位,结构再复杂的系统到了接口级别基本上都会很简单。而且也是判定这个类库设计是否成熟的一种标准。接口与接口间的定义就定义整个系统的基本框架。看接口的最基本意义就是深入理解类库设计者的设计思路与了解类库最核心的能力。
这里我们以 IRepository 为例来讲讲怎么去看接口 (如果想深入了解IRepostiory的朋友可以阅读我之前的文章:“Repository模式与UnitOfWorks模式的运用” )
我以 IRepository 为例是因为它的共识度很大,而实现起来可以很庞大也可以很小,众所周知IRepository提供的就是对某个实体的CURD(增加、更新、读取、删除) 的一个接口。那么当我们看到它的存在时,应该可以推断出另一个接口:IUnitOfWork 因为它们往往会是孪生兄弟般的存在,再进一步推断是否会存在IRepositoryFactory 和 IUnitOfWorkBuilder 呢? 那么就可以带着这些问题在类库中找答案。
通过IRepository我会可能会发现一大堆的Repository类,
如抽象类:EntityRepositoryBase, FiledRepositoryBase ,JSONRepositoryBase 等
具体类:BlogRepository, PostRepository, UserRepository 等
注:作为练习大家可以去下载我在NuGet上发布的一个名为 DotNetAge Document Storage 类库,里面就有Repository的实现
一但掌握了从接口看结构的方法,就可以快速地在无文档的情况下理解类库的核心与设计理念。

参考“最佳实践”

      一般上来说,流行的语言都会提供官方的“最佳实践”提供下载学习。这是一个必修项目,同一个需求,我们可以采用各种的设计方法来实现,但哪一种最好的? “最佳实践”就提供了方法选择的指导。“最佳实践”会有大量文档辅助讲解,在此时使用上述两个方法去学习那将会更大地提高你在设计上的提升。
随着不断的积累与大量的代码阅历,你可能就会得到这样一种能力:随便拿个Dll,在一个短时间内你可以如数家珍般说出整个Dll中的特点与功能。
这,就是“看”的练习方法,与练习后的效果

 要成为架构师就需要突破语言的障壁,不同的语言有不同的优势,设计应该是因势利导,好的架构可用任何语言实现,反过来优秀的架构则应尽可能地发挥语言的特性。应用此方法前首先你至少已掌握或精通一门语言。

学习多种语言的动机

  • 开拓视野,从不同语言中学习特有的设计理念
  • 寻找与更新自己的 “最佳实践”
  • 规避语言被淘汰的风险

开拓视野

    我最近在不少网站上看到这样的一种论调:“学语言只学一门就好,不需要多只需要精”。咋一看,似乎是对的,而我认为仅限于初学者或不求进取者。这是一种语言同质论,是一种误导与限制!如果学习语言能“窥一斑而见全豹”那就不需要有那么多的其它语言存在了,对吗?
    当对某一门语言精通(我指的是精通类库而不是语法)后,这个时间大概也得几年,很容易进入到一种“思维定式”,以某种固定语言为基点想问题,或是设 计。一旦进入这种状态也就意味着局限性的出现,程序员或是架构师就被限制在了一个局部的小范围,而且还是风险极高的范围内。
    IT的发展是飞速的,今天的宠儿明天的乞丐这种剧目屡屡上演着,你愿意被大浪淘沙吗?吊在一颗树上真是一条死路,这是技术发展的风险层面。又,你的设 计工作对你还有挑战性吗?你是否仍然对设计和开发充满激情?你的设计是否在当下可以有什么让你自豪的特色或创建呢? 被同质化后这些答案都会被否定掉。

   我的论点是:不断学习各种语言,体验各种语言所带来的开发与设计的激情,开拓自我的视野才是一个架构师应走的路。架构师不单单是技术的选择者,而更应该是技术的整合者。

选择“最佳实践”

   我们长期会在某一领域内工作,自然而然会诞生出对此领域内的软件的设计理念与实现方法。但这仅是一种,举一个最简单的例子,同一个网站我们可以用 ASP.NET MVC , Java, PHP 或是NodeJS来实现,固然实现方法与代码量就截然不同了。随便写个博客网站就能体验他们的区别所在:
  • ASP.NET 和 Java的思维方式与代码量差异不大,学习曲线最长、 Hosting 资源成本中等,但数据库Hosting成本高
  • Php 相对前两者简单而且资源众多,Hosting 资源最多,成本最低
  • NodeJS性能最高、学习曲线最短、代码量最少、资源也最多但Hosting 成本最高
从这个比较中可见,作为架构师不单要考虑设计方法与实现,还得考虑部署环境。如果只是某一方面的“能手”那只能干瞪眼,无可选择。
我不是要选择“最好”的而是要选择“最合适”的,因时制宜,因地制宜才是我们所需要的“最佳实践”

成为 “O” 型架构师

 开发是一个团队共同完成的,与真实社会一样,活在哪里就说哪里的话才会深入了解对方的文化。 架构师不是一个独立的个体,而是团队中不可或缺的成员,在行政与地位上与其它成员是对等的。开发队团就像是一个人,流着共同的血液。一个架构师,一份设计 就应该是一种“O”型血,无论在哪个队团内都能融合,使用哪种语言都能实现与优化这才是一个好架构师的目标。

学习多种语言的方法

 学习语言的方法大家都会有各自的路径与方法,在这里我只是介绍一下我自己的学习方法仅供大家参考与给我建议。我从业也10多年了,经历了不少语言以下这些是一些记忆与状态:
  • Delphi (1-5) (Object pascal) – 这是初恋 , 拥有最多的界面组件和最简单的可视化开发环境,VCL算是当时最好的选择。
  • VB (2-6) – 最容易调用COM的语言也是做面向对象很苦B的一个了。
  • C++ – 学得最差的
  • java – 用来学面向对象的
  • C# – 算是我最擅长的,也是做项目最多的
  • Php – 只能算是懂一点
  • JavaScript – 我最喜欢的动态弱类型语言。
  • css/less – 最让我头疼 (有了Less会好一点)
  • html/xml/xslt – 最容易建立方法论的标记性语言是最容易建立发散性思维与抽象思维的工具
  • Objective-C 和 Swift – 现在在学的
每一种语言就像自己的女朋友一般天天在身边陪伴着我们,所以想学得快首先是爱上她。即使她不再受宠也她会给我们留下很多美好的记忆。

感性认知

   我学语言的第一步是不看语法的,因为面向对象的语言语法上基本是相似的,而且语法参考会很长读起来慢(这跟找老婆不要光看外貌是一个理)。我是从类库入 手的,从主打的类库中可基本上快速了解整个语言的重要特色和常用的内容。就如iOS吧,一入手我会先看Cococa Layer中的UIKit,花两小时看完就知道XCode怎么用了然后就可以做个简单的移动应用跑一下,从感性上互相认识一下。

相互印证

   没有哪个语言是没有参照物凭空发明出来的,就像学java时如果学过c++会很快因为java就是在极大层面上改进C++而来的,学C#的时候学过 java就一下能上手,因为C#是微软没买到java自己搞出来的同时也去改进了java。所以语言之间会有互通性存在,在学习的路径上可以先了前他们的 前辈是谁,有什么特色通过双向的印证了解可以很快速地去掌握与深入理解一门语言。

长年的积累

    掌握一门语法很快,精通一门语言就是硬功了。这和我们学自然语言一样,词汇量是日积月累的成果。开发语言的词汇量就在于类库了,每一门语言的标准类库也是 够我们喝一壶的,需要实践、学习与理解结合经验沉淀成我们的成果。在这方面我给出的建议就是要培养自己的耐心与毅力,罗马不是一天建成的,高手也不是一天 就能修炼出来的。除了掌握官方类库还得将业界流行的类库和框架都能了解与熟悉这才算是“精通”。

写 – 疯狂的编码

“实践是检验真理的唯一标准” 能说不如能写。学到的知识是别人看不到的内容,作为程序员或是架构师将脑中的精华程序化呈现,然后成为产品这才是我们的终极目标。
学会一门语言并不代表写得好,作为一名架构师写的代码要求更是不同:
  • 易读 – 命名是否符合代码规范,所有接口是否全部代码都有注释
  • 易用 – 每一个类,每一个方法都是架构师与程序员的UI,少参数,容易理解的设计可以大大减少沟通成本。
  • 框架化 – 一个一个类写是很慢的事,要活用模式于代码中能同时构建出10几个或几十个类。
  • 参考性 – 架构师不是程序员,写代码为的是固定核心功能与公共用法,便于成员开发。面对复杂的场景需要多写示例同时也是测试设计的易用性的方法。
怎么才能达到这样的标准呢?
  • 为自己立项从现在起为自己而编码
  • 多写代码片 – 对局部的理论进行实践,多写一些小的代码片段或实验程序,而按正式项目一样来对待。完整的记录,共享源码获得Feedback,有良好的注释。
  • 模仿是学习与理解新事物的最佳捷径 — 可以去仿造某些项目,当深入其中可以更直接地理解设计者的最初设计想法,同时也可以得到一个仿造品(不是抄,仿造的目的是获得编码经验)
  • 尝试使用模式并控制类的规模
最后也就是时间的积累,疯狂地编码。同样的时间一个架构师的编码能力至少需要同等于五个以上程序员同时编码。

小结

架构师之路是一条很漫长而且需要不断学习、思考与实践积累的道路。我只是走了这条路的一小段,以此总结与更多的朋友分享共勉。在下一篇文章中我将会从另一个角度来谈架构师的修改项目:表达力。希望有兴趣的朋友能给予更多的关注与反馈。

[转载]数据库的版本为661,无法打开。此服务器支持655版及更低版本。不支持降级路径 - 其他综合 - 红黑联盟

mikel阅读(1923)

[转载]数据库的版本为661,无法打开。此服务器支持655版及更低版本。不支持降级路径 – 其他综合 – 红黑联盟.

数据库 的版本为 661,无法打开。此服务器支持 655 版及更低版本。不支持降级路径”

 

出现这样的问题,一般是因为数据库版本不同造成的。

 

我们可以用下面的语句查询数据库的版本

 

use master

select @@VERSION

 

(1)661是SQL2008 R2的版本号

 

Microsoft SQL Server 2008 R2 (RTM) – 10.50.1600.1 (Intel X86) Apr 2 2010 15:53:02 Copyright (c) Microsoft

 

(2)655版本也就是SQL2008 sp1版本号

 

Microsoft SQL Server 2008 (SP1) – 10.0.2531.0 (Intel X86) Mar 29 2009 10:27:29 Copyright (c) 1988-2008

\

如果后面是1600,则表明只安装了sql2008r2,还要装一个sql2008sp1。这样才能保证后面是2500.