[转载]SQLite学习手册(临时文件)

mikel阅读(902)

[转载]SQLite学习手册(临时文件) – Stephen_Liu – 博客园.

一、简介:

尽管SQLite的数据库是由单一文件构成,然而事实上在SQLite运行时却存在着一些隐含的临时文件,这些临时文件是出于不同的目的而存在的,对于 开发者而言,它们是透明的,因此在开发的过程中我们并不需要关注它们的存在。尽管如此,如果能对这些临时文件的产生机制和应用场景有着很好的理解,那么对 我们今后应用程序的优化和维护都是极有帮助的。在SQLite中主要产生以下七种临时文件,如:
1). 回滚日志。
2). 主数据库日志。
3). SQL语句日志。
4). 临时数据库文件。
5). 视图和子查询的临时持久化文件。
6). 临时索引文件。
7). VACUUM命令使用的临时数据库文件。

二、具体说明:

1. 回滚日志:
SQLite为了保证事物的原子性提交和回滚,在事物开始时创建了该临时文件。此文件始终位于和数据库文件相同的目录下,其文件名格式为: 数据库文件名 + “-journal。换句话说,如果没有该临时文件的存在,当程序运行的系统出现任何故障时,SQLite将无法保证事物的完整性,以及数据状态的一致性。该文件在事物提交或回滚后将被立刻删除。
在事物运行期间,如果当前主机因电源故障而宕机,而此时由于回滚日志文件已经保存在磁盘上,那么当下一次程序启动时,SQLite在打开数据库文件的过 程中将会发现该临时文件的存在,我们称这种日志文件为”Hot Journal”。SQLite会在成功打开数据库之前先基于该文件完成数据库的恢复工作,以保证数据库的数据回复到上一个事物开始之前的状态。
在SQLite中我们可以通过修改journal_mode pragma,而使SQLite对维护该文件采用不同的策略。缺省情况下该值为DELETE, 即在事物结束后删除日志文件。而PERSIST选项值将不会删除日志文件,而是将回滚日志文件的头部清零,从而避免了文件删除所带的磁盘开销。再有就是 OFF选项值,该值将指示SQLite在开始事物时不产生回滚日志文件,这样一旦出现系统故障,SQLite也无法再保障数据库数据的一致性。

    2. 主数据库日志:
在SQLite中,如果事物的操作作用于多个数据库,即通过ATTACH命令附加到当前连接中的数据库,那么SQLite将生成主数据库日志文件以保证 事物产生的改变在多个数据库之间保持原子性。和回滚日志文件一样,主数据库日志文件也位于当前连接中主数据库文件所处的目录内,其文件名格式为:主数据库 文件名 + 随机的后缀。在该文件中,将包含所有当前事物将会改变的Attached数据库的名字。在事物被提交之后,此文件亦被SQLite随之删除。
主数据库日志文件只有在某一事物同时操作多个数据库时(主数据库和Attached数据库)才有可能被创建。通过该文件,SQLite可以实现跨多个数 据库的事物原子性,否则,只能简单的保证每个单一的数据库内的状态一致性。换句话说,如果该事物在执行的过程中出现系统崩溃或主机宕机的现象,在进行数据 恢复时,若没有该文件的存在,将会导致部分SQLite数据库处于提交状态,而另外一部分则处于回滚状态,因此该事物的一致性将被打破。

3. SQL语句日志:
在一个较大的事物中,SQLite为了保证部分数据在出现错误时可以被正常回滚,所以在事物开始时创建了SQL语句日志文件。比如,update语句修 改了前50条数据,然而在修改第51条数据时发现该操作将会破坏某字段的唯一性约束,最终SQLite将不得不通过该日志文件回滚已经修改的前50条数 据。
SQL语句日志文件只有在INSERT或UPDATE语句修改多行记录时才有可能被创建,与此同时,这些操作还极有可能会打破某些约束并引发异常。但是 如果INSERT或UPDATE语句没有被包含在BEGIN…COMMIT中,同时也没有任何其它的SQL语句正在当前的连接上运行,在这种情况 下,SQLite将不会创建SQL语句日志文件,而是简单的通过回滚日志来完成部分数据的UNDO操作。
和上面两种临时文件不同的是,SQL语句日志文件并不一定要存储在和数据库文件相同的目录下,其文件名也是随机生成。该文件所占用的磁盘空间需要视UPDATE或INSERT语句将要修改的记录数量而定。在事物结束后,该文件将被自动删除。

4. 临时数据库文件:
当使用”CREATE TEMP TABLE”语法创建临时数据表时,该数据表仅在当前连接内可见,在当前连接被关闭后,临时表也随之消失。然而在生命期内,临时表将连同其相关的索引和视 图均会被存储在一个临时的数据库文件之内。该临时文件是在第一次执行”CREATE TEMP TABLE”时即被创建的,在当前连接被关闭后,该文件亦将被自动删除。最后需要说明的是,临时数据库不能被执行DETACH命令,同时也不能被其它进程 执行ATTACH命令。

5. 视图和子查询的临时持久化文件:
在很多包含子查询的查询中,SQLite的执行器会将该查询语句拆分为多个独立的SQL语句,同时将子查询的结果持久化到临时文件中,之后在基于该临时 文件中的数据与外部查询进行关联,因此我们可以称其为物化子查询。通常而言,SQLite的优化器会尽力避免子查询的物化行为,但是在有些时候该操作是无 法避免的。该临时文件所占用的磁盘空间需要依赖子查询检索出的数据数量,在查询结束后,该文件将被自动删除。见如下示例:
SELECT * FROM ex1 WHERE ex1.a IN (SELECT b FROM ex2);
在上面的查询语句中,子查询SELECT b FROM ex2的结果将会被持久化到临时文件中,外部查询在运行时将会为每一条记录去检查该临时文件,以判断当前记录是否出现在临时文件中,如果是则输出当前记 录。显而易见的是,以上的行为将会产生大量的IO操作,从而显著的降低了查询的执行效率,为了避免临时文件的生成,我们可以将上面的查询语句改为:
SELECT * FROM ex1 WHERE EXISTS(SELECT 1 FROM ex2 WHERE ex2.b=ex1.a);
对于如下查询语句,如果SQLite不做任何智能的rewrite操作,该查询中的子查询也将会被持久化到临时文件中,如:
SELECT * FROM ex1 JOIN (SELECT b FROM ex2) AS t ON t.b=ex1.a;
在SQLite自动将其修改为下面的写法后,将不会再生成临时文件了,如:
SELECT ex1.*, ex2.b FROM ex1 JOIN ex2 ON ex2.b=ex1.a;

6. 临时索引文件:
当查询语句包含以下SQL从句时,SQLite为存储中间结果而创建了临时索引文件,如:
1). ORDER BY或GROUP BY从句。
2). 聚集查询中的DISTINCT关键字。
3). 由UNION、EXCEPT和INTERSECT连接的多个SELECT查询语句。
需要说明的是,如果在指定的字段上已经存在了索引,那么SQLite将不会再创建该临时索引文件,而是通过直接遍历索引来访问数据并提取有用信息。如果 没有索引,则需要将排序的结果存储在临时索引文件中以供后用。该临时文件所占用的磁盘空间需要依赖排序数据的数量,在查询结束后,该文件被自动删除。

7. VACUUM命令使用的临时数据库文件:
VACUUM命令在工作时将会先创建一个临时文件,然后再将重建的整个数据库写入到该临时文件中。之后再将临时文件中的内容拷贝回原有的数据库文件中,最后删除该临时文件。
该临时文件所占用的磁盘空间不会超过原有文件的尺寸。

三、相关的编译时参数和指令:

对于SQLite来说,回滚日志、主数据库日志和SQL语句日志文件在需要的时候SQLite都会将它们写入磁盘文件,但是对于其它类型的临时文 件,SQLite是可以将它们存放在内存中以取代磁盘文件的,这样在执行的过程中就可以减少大量的IO操作了。要完成该优化主要依赖于以下三个因素:
1. 编译时参数SQLITE_TEMP_STORE:
该参数是源代码中的宏定义(#define),其取值范围是0到3(缺省值为1),见如下说明:
1). 等于0时,临时文件总是存储在磁盘上,而不会考虑temp_store pragma指令的设置。
2). 等于1时,临时文件缺省存储在磁盘上,但是该值可以被temp_store pragma指令覆盖。
3). 等于2时,临时文件缺省存储在内存中,但是该值可以被temp_store pragma指令覆盖。
4). 等于3时,临时文件总是存储在内存中,而不会考虑temp_store pragma指令的设置。

2. 运行时指令temp_store pragma:
该指令的取值范围是0到2(缺省值为0),在程序运行时该指令可以被动态的设置,见如下说明:
1). 等于0时,临时文件的存储行为完全由SQLITE_TEMP_STORE编译期参数确定。
2). 等于1时,如果编译期参数SQLITE_TEMP_STORE指定使用内存存储临时文件,那么该指令将覆盖这一行为,使用磁盘存储。
2). 等于2时,如果编译期参数SQLITE_TEMP_STORE指定使用磁盘存储临时文件,那么该指令将覆盖这一行为,使用内存存储。

3. 临时文件的大小:
对于以上两个参数,都有参数值表示缺省情况是存储在内存中的,只有当临时文件的大小超过一定的阈值后才会根据一定的算法,将部分数据写入到磁盘中,以免临时文件占用过多的内存而影响其它程序的执行效率。

最后在重新赘述一遍,SQLITE_TEMP_STORE编译期参数和temp_store pragma运行时指令只会影响除回滚日志和主数据库日志之外的其它临时文件的存储策略。换句话说,回滚日志和主数据库日志将总是将数据写入磁盘,而不会 关注以上两个参数的值。

四、其它优化策略:

在SQLite中由于采用了Page Cache的缓冲优化机制,因此即便临时文件被指定存储在磁盘上,也只有当该文件的大小增长到一定的尺寸后才有可能被SQLite刷新到磁盘文件上,在此 之前它们仍将驻留在内存中。这就意味着对于大多数场景,如果临时表和临时索引的数据量相对较少,那么它们是不会被写到磁盘中的,当然也就不会有IO事件发 生。只有当它们增长到内存不能容纳的时候才会被刷新到磁盘文件中的。其中SQLITE_DEFAULT_TEMP_CACHE_SIZE编译期参数可以用 于指定临时表和索引在占用多少Cache Page时才需要被刷新到磁盘文件,该参数的缺省值为500页。

[转载]SQLite学习手册(锁和并发控制)

mikel阅读(944)

[转载]SQLite学习手册(锁和并发控制) – Stephen_Liu – 博客园.

一、概述:

SQLite中,锁和并发控制机制都是由pager_module模块负责处理的,如ACID(Atomic, Consistent, Isolated, and Durable)。在含有数据修改的事务中,该模块将确保或者所有的数据修改全部提交,或者全部回滚。与此同时,该模块还提供了一些磁盘文件的内存 Cache功能。
事实上,pager_module模块并不关心数据库存储的细节,如B-Tree、编码方式、索引等,它只是将其视为由统一大小(通常为1024字节) 的数据块构成的单一文件,其中每个块被称为一个页(page)。在该模块中页的起始编号为1,即第一个页的索引值是1,其后的页编号以此类推。

二、文件锁:

SQLite的当前版本中,主要提供了以下五种方式的文件锁状态。
1). UNLOCKED:
文件没有持有任何锁,即当前数据库不存在任何读或写的操作。其它的进程可以在该数据库上执行任意的读写操作。此状态为缺省状态。
2). SHARED:
在此状态下,该数据库可以被读取但是不能被写入。在同一时刻可以有任意数量的进程在同一个数据库上持有共享锁,因此读操作是并发的。换句话说,只要有一个或多个共享锁处于活动状态,就不再允许有数据库文件写入的操作存在。
3). RESERVED:
假如某个进程在将来的某一时刻打算在当前的数据库中执行写操作,然而此时只是从数据库中读取数据,那么我们就可以简单的理解为数据库文件此时已经拥有了 保留锁。当保留锁处于活动状态时,该数据库只能有一个或多个共享锁存在,即同一数据库的同一时刻只能存在一个保留锁和多个共享锁。在Oracle中此类锁 被称之为预写锁,不同的是Oracle中锁的粒度可以细化到表甚至到行,因此该种锁在Oracle中对并发的影响程序不像SQLite中这样大。
4). PENDING:
PENDING锁的意思是说,某个进程正打算在该数据库上执行写操作,然而此时该数据库中却存在很多共享锁(读操作),那么该写操作就必须处于等待状 态,即等待所有共享锁消失为止,与此同时,新的读操作将不再被允许,以防止写锁饥饿的现象发生。在此等待期间,该数据库文件的锁状态为PENDING,在 等到所有共享锁消失以后,PENDING锁状态的数据库文件将在获取排他锁之后进入EXCLUSIVE状态。
5). EXCLUSIVE:
在执行写操作之前,该进程必须先获取该数据库的排他锁。然而一旦拥有了排他锁,任何其它锁类型都不能与之共存。因此,为了最大化并发效率,SQLite将会最小化排他锁被持有的时间总量。

最后需要说明的是,和其它关系型数据库相比,如MySQL、Oracle等,SQLite数据库中所有的数据都存储在同一文件中,与此同时,它却仅仅提 供了粗粒度的文件锁,因此,SQLite在并发性和伸缩性等方面和其它关系型数据库还是无法比拟的。由此可见,SQLite有其自身的适用场景,就如在本 系列开篇中所说,它和其它关系型数据库之间的互换性还是非常有限的。

三、回滚日志:

当一个进程要改变数据库文件的时候,它首先将未改变之前的内容记录到回滚日志文件中。如果SQLite中的某一事务正在试图修改多个数据库中的数据,那 么此时每一个数据库都将生成一个属于自己的回滚日志文件,用于分别记录属于自己的数据改变,与此同时还要生成一个用于协调多个数据库操作的主数据库日志文 件,在主数据库日志文件中将包含各个数据库回滚日志文件的文件名,在每个回滚日志文件中也同样包含了主数据库日志文件的文件名信息。然而对于无需主数据库 日志文件的回滚日志文件,其中也会保留主数据库日志文件的信息,只是此时该信息的值为空。
我们可以将回滚日志视为”HOT”日志文件,因为它的存在就是为了恢复数据库的一致性状态。当某一进程正在更新数据库时,应用程序或OS突然崩溃,这样 更新操作就不能顺利完成。因此我们可以说”HOT”日志只有在异常条件下才会生成,如果一切都非常顺利的话,该文件将永远不会存在。

四、数据写入:

如果某一进程要想在数据库上执行写操作,那么必须先获取共享锁,在共享锁获取之后再获取保留锁。因为保留锁则预示着在将来某一时刻该进程将会执行写操 作,所以在同一时刻只有一个进程可以持有一把保留锁,但是其它进程可以继续持有共享锁以完成数据读取的操作。如果要执行写操作的进程不能获取保留锁,那么 这将说明另一进程已经获取了保留锁。在此种情况下,写操作将失败,并立即返回SQLITE_BUSY错误。在成功获取保留锁之后,该写进程将创建回滚日 志。
在对任何数据作出改变之前,写进程会将待修改页中的原有内容先行写入回滚日志文件中,然而,这些数据发生变化的页起初并不会直接写入磁盘文件,而是保留在内存中,这样其它进程就可以继续读取该数据库中的数据了。
或者是因为内存中的cache已满,或者是应用程序已经提交了事务,最终,写进程将数据更新到数据库文件中。然而在此之前,写进程必须确保没有其它的进程正在读取数据库,同时回滚日志中的数据确实被物理的写入到磁盘文件中,其步骤如下:
1). 确保所有的回滚日志数据被物理的写入磁盘文件,以便在出现系统崩溃时可以将数据库恢复到一致的状态。
2). 获取PENDING锁,再获取排他锁,如果此时其它的进程仍然持有共享锁,写入线程将不得不被挂起并等待直到那些共享锁消失之后,才能进而得到排他锁。
3). 将内存中持有的修改页写出到原有的磁盘文件中。
如果写入到数据库文件的原因是因为cache已满,那么写入进程将不会立刻提交,而是继续对其它页进行修改。但是在接下来的修改被写入到数据库文件之 前,回滚日志必须被再一次写到磁盘中。还要注意的是,写入进程获取到的排他锁必须被一直持有,直到所有的改变被提交时为止。这也意味着,从数据第一次被刷 新到磁盘文件开始,直到事务被提交之前,其它的进程不能访问该数据库。
当写入进程准备提交时,将遵循以下步骤:
4). 获取排他锁,同时确保所有内存中的变化数据都被写入到磁盘文件中。
5). 将所有数据库文件的变化数据物理的写入到磁盘中。
6). 删除日志文件。如果在删除之前出现系统故障,进程在下一次打开该数据库时仍将基于该HOT日志进行恢复操作。因此只有在成功删除日志文件之后,我们才可以认为该事务成功完成。
7). 从数据库文件中删除所有的排他锁和PENDING锁。
一旦PENDING锁被释放,其它的进程就可以开始再次读取数据库了。
如果一个事务中包含多个数据库的修改,那么它的提交逻辑将更为复杂,见如下步骤:
4). 确保每个数据库文件都已经持有了排他锁和一个有效的日志文件。
5). 创建主数据库日志文件,同时将每个数据库的回滚日志文件的文件名写入到该主数据库日志文件中。
6). 再将主数据库日志文件的文件名分别写入到每个数据库回滚日志文件的指定位置中。
7). 将所有的数据库变化持久化到数据库磁盘文件中。
8). 删除主日志文件,如果在删除之前出现系统故障,进程在下一次打开该数据库时仍将基于该HOT日志进行恢复操作。因此只有在成功删除主日志文件之后,我们才可以认为该事务成功完成。
9). 删除每个数据库各自的日志文件。
10).从所有数据库中删除掉排他锁和PENDING锁。

最后需要说明的是,在SQLite2中,如果多个进程正在从数据库中读取数据,也就是说该数据库始终都有读操作发生,即在每一时刻该数据库都持有至少一 把共享锁,这样将会导致没有任何进程可以执行写操作,因为在数据库持有读锁的时候是无法获取写锁的,我们将这种情形称为”写饥饿”。在SQLite3中, 通过使用PENDING锁则有效的避免了”写饥饿”情形的发生。当某一进程持有PENDING锁时,已经存在的读操作可以继续进行,直到其正常结束,但是 新的读操作将不会再被SQLite接受,所以在已有的读操作全部结束后,持有PENDING锁的进程就可以被激活并试图进一步获取排他锁以完成数据的修改 操作。

五、SQL级别的事务控制:

SQLite3在实现上确实针对锁和并发控制做出了一些精巧的变化,特别是对于事务这一SQL语言级别的特征。在缺省情况下,SQLite3会将所有的 SQL操作置于antocommit模式下,这样所有针对数据库的修改操作都会在SQL命令执行结束后被自动提交。在SQLite中,SQL命 令”BEGIN TRANSACTION”用于显式的声明一个事务,即其后的SQL语句在执行后都不会自动提交,而是需要等到SQL命令”COMMIT” 或”ROLLBACK”被执行时,才考虑提交还是回滚。由此可以推断出,在BEGIN命令被执行后并没有立即获得任何类型的锁,而是在执行第一个 SELECT语句时才得到一个共享锁,或者是在执行第一个DML语句时才获得一个保留锁。至于排它锁,只有在数据从内存写入磁盘时开始,直到事务提交或回 滚之前才能持有排它锁。
如果多个SQL命令在同一个时刻同一个数据库连接中被执行,autocommit将会被延迟执行,直到最后一个命令完成。比如,如果一个SELECT语 句正在被执行,在这个命令执行期间,需要返回所有检索出来的行记录,如果此时处理结果集的线程因为业务逻辑的需要被暂时挂起并处于等待状态,而其它的线程 此时或许正在该连接上对该数据库执行INSERT、UPDATE或DELETE命令,那么所有这些命令作出的数据修改都必须等到SELECT检索结束后才 能被提交。

[转载]Windows 8 傻瓜式安装教程

mikel阅读(723)

[转载]Windows 8 傻瓜式安装教程 – 刘水镜 – 博客园.

29号晚上将Windows8-ConsumerPreview下载下来了,由于太晚了所以没有安装,所以今天晚上将win8安装一次!

首先去微软官网将ISO文件下载下来,分为32bit跟64bit两个版本,因人而异,根据自己的需要下载。传送门Windows8镜像下载



下载完以后解压(解压到一个盘的根目录下,而且这个盘必须是非Win8安装盘,就是说你不打算把Win8安到这个盘里),然后下载一个引导文件Win6Ins_v1.2.0.62 ,然后将此引导文件跟解压出的放到之前Win8解压的目录下。然后双击,如下图



点击“安装”按钮,如下图



按照提示,重启电脑。之后系统会提示你是进入原来的系统还是从硬盘启动另一个系统。大概写的是“windows xp,vista,7 from hard disk”之类的话。总之一看就明白了!

接下来我们可以做的主要就是等待了…


 

下面的界面我们很熟悉了,跟Win7相差不大… 直接下一步!


输入密钥——WNP7G-QRYW7-RPCJP-P234C-W2JX8


下面该选择将要安装Win8的磁盘了,这一步跟WIn7几乎一样,我就不过多说了

 

 

静静的等待…


重启后准备桌面!


设置背景跟计算机名


一些常规设置,自己看着来


欢迎界面,激动人心的时刻!


终于来到了我们期盼已久的Metro界面了!好了可以尽情享受了!


当然如果你不适应Win8的Metro界面还以按“Win”键切换回传统界面

当然传统界面也有了一些变化,例如取消了“开始”按钮,资源管理器做了一些整合等等,自己慢慢去探索吧!相信Win8不会让你失望的!


PS:安装过程中会重启数次,等着就行了…

[转载]查看sql修改痕迹(SQL Change Tracking on Table)

mikel阅读(943)

[转载]查看sql修改痕迹(SQL Change Tracking on Table) – MR_ke – 博客园.

申明本博客转至http://www.codeproject.com/Articles/338724/SQL-Change-Tracking-on-Table

在阅读这篇博客之前,我想告诉你我并不是经常写博客,这只是一个尝试,如果写的不好请见谅,我仅仅想告诉你们一些SQL内在机制。

凭经验,很多时候我们都人事为触发器不是最好查找一个表修改痕迹的方法,触发器跟插入 修改 删除等动作紧紧的联系再一次,可能在我们

未来的修改发布中产生很多问题。那我现在告诉你一个及时查看修改表记录的方法。

1.修改数据库配置,允许数据库进行修改跟踪。

Alter Database RDCC
set change_tracking = on
(change_retention = 2 days, auto_cleanup = on);

数据库修改跟踪权限开启了,不是所有表的跟踪权限都开启了,每个表还必须单独开启。

2.首先我们建一个简单的表然后插入几条记录。

 Create table Employee 
(
    EmployeeID nvarchar(10) primary key,
    FirstName nvarchar(100),
    LastName nvarchar(100),
    Phone1 nvarchar(10)
)

insert into Employee (EmployeeID,FirstName,LastName,Phone1) Values ('E001','Santosh','Poojari','1111111111')
insert into Employee (EmployeeID,FirstName,LastName,Phone1) Values ('E002','Karan','Shah','2222222222')
insert into Employee (EmployeeID,FirstName,LastName,Phone1) Values ('E003','Vineesh','Padmanabhan','3333333333')
insert into Employee (EmployeeID,FirstName,LastName,Phone1) Values ('E004','Gautam','Sharma','4444444444')

在数据库允许跟踪之后,我们还必须把把表设置成可跟踪。

ALTER TABLE Employee ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON);

那么现在我们开始跟踪表的修改

update employee  set Phone1 ='12121212' where EmployeeID ='E001'
update employee  set Phone1 ='21212121' where EmployeeID ='E002'

然后我们查看修改记录:

SELECT  ISNUll(pn.EmployeeID,0) as EmployeeID 
from changetable(changes employee, 1) as ct
INNER JOIN employee pn on pn.EmployeeID = CT.EmployeeID
WHERE SYS_CHANGE_VERSION > 1 and  CT.Sys_Change_Operation <>'D'

 


我们看到的是employee表修改过的EmployeeID对应的行值。

下面我们看看整个修改记录:

SELECT  *
from changetable(changes employee, 1) as ct
INNER JOIN employee pn on pn.EmployeeID = CT.EmployeeID
WHERE SYS_CHANGE_VERSION > 1 and  CT.Sys_Change_Operation <> 'D'


列SYS_CHANGE_OPERATION显示的是操作类型,那么我们再做一条插入操作并查看结果:

insert into Employee (EmployeeID,FirstName,LastName,Phone1) Values ('E006','S','G','555555')

GO

SELECT  *
from changetable(changes employee, 1) as ct
INNER JOIN employee pn on pn.EmployeeID = CT.EmployeeID
WHERE SYS_CHANGE_VERSION > 1 and  CT.Sys_Change_Operation <> 'D'


那么我们看到SYS_CHANGE_OPERATION多了一条插入记录类型为I.那么数据库表的任意改动我们都可以通过代码跟踪到。而且一个修改记录的即(sys_change_version)从创建开始,会在系统表里保存两天。

有错误的地方欢迎大家拍砖,希望交流和共享。

[转载]ASP.NET MVC经典项目ProDinner项目解析(1)

mikel阅读(936)

[转载]ASP.NET MVC经典项目ProDinner项目解析(1) – aspnetdream – 博客园.

前面的话:

由于最近在设计一款Web产品,我希望找到一成熟的耦合度低的产品架构,并且是建立在ASP.NET mvc的框架基础上的,对此找到ProDinner开源项目,但是网上的资料少得可怜,好,没有范例,那么就自己来做处理,所以将自己的所学与各位分享, 多有不足,就算是抛砖引玉,望高手指点。

 

一、整体系统分析

系统采用了Entity Framework Code First+ASP.NET MVC Razor+Castle.Core+Castle.Windsor,可以说整体设计是个非常轻量级别的框架,但却是做到了或者说惯承了整体架构分层明 显,模块耦合度低的架构思想。

Core 层实现了本地化Models和Entity Fremework模型化需要的models数据,另该层的另一个职能是,为低耦合的业务逻辑和低耦合的数据访问做好了接口准备,所以我说这个项目惯撤了 低耦合的架构思想,如果要设计一个更大型的项目,那么这层可以继续将业务逻辑和数据访问,以及一些公共运用的功能进行更近一层的接口化。

Model

—DelEntity.cs

该类文件做了模型实体的定义,基本可以说与将要运用的数据库形成了完成的模型映射关系。

public class Entity
{
public int Id { get; set; }
}

public interface IDel
{
bool IsDeleted { get; set; }
}

Entity为所有模型的公共基类,这其实是非常好的设计思想,我想我们一般在做数据库表结构设计的时候,表与表都会有些共同的字段,如操作人,操作时间,操作机器,操作程序接入模块名。这里其实只是一个范例,根据各自的需要自己调整需要的类字段设计。

public class DelEntity : Entity, IDel
     {
         public bool IsDeleted { get; set; }
     }
 
     public class Country : DelEntity
     {
         public string Name { get; set; }
     }
 
     public class Meal : DelEntity
     {
         public string Name { get; set; }
         public string Comments { get; set; }
         public virtual ICollection<Dinner> Dinners { get; set; }
         public string Picture { get; set; }
     }
 
     public class Chef : DelEntity
     {
         public string FirstName { get; set; }
         public string LastName { get; set; }
         public int CountryId { get; set; }
         public virtual Country Country { get; set; }
     }
 
     public class Dinner : DelEntity
     {
         public string Name { get; set; }
         public int CountryId { get; set; }
         public virtual Country Country { get; set; }
         public int ChefId { get; set; }
         public virtual Chef Chef { get; set; }
         public string Address { get; set; }
         public DateTime Start { get; set; }
         public DateTime End { get; set; }
         public virtual ICollection<Meal> Meals { get; set; }
     }
 
     public class User : DelEntity
     {
         public string Login { get; set; }
         public string Password { get; set; }
         public virtual ICollection<Role> Roles { get; set; }
     }
 
     public class Role : Entity
     {
         public string Name { get; set; }
         public virtual ICollection<User> Users { get; set; }
     }

接下来其实就没什么特别的,建立程序需要使用的业务类,这个时候其实针对于Code First而言,还不存在数据库这个概念,只是根据我们的业务需要设计相应的模

类在涉及到表关联的时候,我们看到,这里统一使用了ICollection的集合,并且都是Virtual类型,这非常明确的表示了该层都是接口和基类,注定是要被重写。这里稍微提下,如果需要再抽象点,再低耦合点,那么我想再定义一个ICollection,而不是具体指定那个类,这样就更抽象了。

Repository

—IRepo.cs

顾名思义,这是一个仓库,业务操作的仓库,我们更多的听到的是数据仓库,那确有操作仓库。

public interface IRepo<T>
 {
 }

public interface IDelRepo<T>
{
}

public interface IUniRepo
{
}

观察每个接口的名字我们就会发现,这里基本是攘括了所有的数据操作方式和数据业务逻辑的函数原型,其后Service所有具体实现的实体数据操作都会基于这些接口

Security

—IFormsAuthentication.cs

定义用户登录的业务处理过程。同样也是抽象的规范接口。

Service

—ICrudService.cs

该层定义了一些业务操作得的函数接口,如果说IRepo是标准的接口规范,那么ICrudService就是个性化定制接口最后ProDinnerException.cs定义

项目自身的异常处理类,如果要建立自己的异常规范文档,那么像这样来处理异常显然是不可以的,我们可以看看国产化的淘宝接口,每个异常对应着编号,

以及自身的异常描述。我的建议是依据程序和模块来处理异常。这样形成完整的层次关系。Core层暂时解析这么多。

[转载]排序算法 快速排序l两种算法和堆排序

mikel阅读(1033)

[转载]排序算法 快速排序l两种算法和堆排序(原创) – 北冥茶花开 – 博客园.

快排算法有两种 一种是算法导论里的改进的快排算法
另一种是清华那本数据结构中的古典快排算法。这里我们会看到他们在运行时间上的不同,而且古典快排竟然优于改进的快排
。呵呵 好了不多说  上代码吧  。

#include<stdio.h>
 #include<stdlib.h>
 #include<time.h>               //时间计数头文件
 
 
 int  left(int i)
 {
         return 2*i;
 }
 
 int right(int i)
 {
         return 2*i+1;
 }
 
 
 void MAX_HEAPIFY(int arr[],int i,int heapsize) //调堆根函数,这名字自己YY的,基本原理就是让不合理的角色合理化,
 {                                                //能力小的往下降,能力大的做老大。
         int l,r;
         int largest;
         int temp;
         l= left(i);
         r=right(i);
         if(l<=heapsize&&arr[l]>arr[i])                  //左孩子比较大那么暂记录左孩子 选他做准太子。
                 largest=l;
         else
                 largest=i;
         if(r<=heapsize&&arr[r]>arr[largest])        //右孩子更有实力,选他做准太子
                 largest=r;
         if(largest!=i)                                 //当权者arr【i】不是实力最大,则交换二者
         {
                 temp  =arr[largest];
                 arr[largest]=arr[i];
                 arr[i]   =temp;
         MAX_HEAPIFY(arr,largest,heapsize);          //给以前的当权者找个归路
         }
 
 }
 
 
 void BUILD_MAX_HEAP(int arr[],int heapsize)             //建堆
 {
         int contrasize=int (heapsize/2);                  //数组建堆是下标为n/2+1,...,n 是叶子节点。
         for(int i=contrasize;i>=1;i--)
                 MAX_HEAPIFY(arr,i,heapsize);
 
 }
 
 void HEAP_SORT(int arr[],int heapsize)                //堆排序算法
 {
         clock_t start=clock();                             //计时开始时钟变量
         double duration;                                    //程序消耗时钟个数。
         int temp;
         int equalsize=heapsize;
         BUILD_MAX_HEAP(arr,heapsize);                      //首建堆,但是实际上为了初始化。仔细体味能感觉到
                 for(int i=heapsize;i>=2;i--)
                 {
                         temp  =arr[i];
                         arr[i]=arr[1];
             arr[1]=temp;
                         equalsize=equalsize-1;
             MAX_HEAPIFY(arr,1,equalsize);
                 }
                  printf("排序后的结果是:\n");
      for(int i=1;i<=heapsize;i++)
                  printf(" %d ",arr[i]);
      clock_t finish=clock();
          duration=(double)(finish-start)/CLOCKS_PER_SEC;
          printf("堆排序的耗费时间为: %f\n",duration);
 }
 
 /*上面是堆排序,下面是快速排序的两个的版本*/
 
 
 void exchange(int arr[],int i,int j)                  //交换数组里的两个元素,为了后面方便我故意挪出来的。
 {                                                     //也算是减少内聚低耦合吧 自己都觉得自己太自恋了。
         int temp;
         temp  =arr[i];
         arr[i]=arr[j];
         arr[j]=temp;
         return ;
 }
 
 int partition(int arr[],int p,int r)                   //这是改进版的 就是圣经上的那个快排选分段标记的函数
 {
      int contra=arr[r];
          int i,j;
          i=p-1;
          for( j=p;j<=r-1;j++)                    //核心代码 这个实际上下面的是区别是这个固定一头,后面的小于标准
          {                                       //则往这边靠,正是这个原因导致他不如古典的快排耗时短,后面运行结果将验证
                  if(arr[j]<=contra)
                  {
                          i=i+1;
                          exchange(arr,i,j);
                  }
          }
          exchange(arr,i+1,r);
          return i+1;
 }
 
 int quicksort(int arr[],int p,int r)                //快排函数主体  没什么好说的 
 {
         int q;
         if(p < r)
         {
                 q=partition(arr,p,r);
                 quicksort(arr,p,q-1);
                 quicksort(arr,q+1,r);
 
         }
         
         return 0;
 }
 
 
 /* 上面是改进后快速排序算法,下面这个是快速排序的最早版本  实际上清华的那本什么数据结构上就这个*/
 
 int hoarepartition(int arr[],int p,int r)                       //这是古典的快排
 {
   int contra=arr[p];
   int i=p;
   int j=r;
   while(true)
   {
           for(;arr[j]>contra;j--);                               //注意哦 有个小分号看见没?
           for(;arr[i]<contra;i++);                              //选择contra的左大与右小交换 这个是快排的核心两头动
                   if(i<j)
                         exchange( arr, i, j);
                   else
                           return j;
 
   }
 
 }
 
 
 int hoarequicksort(int arr[],int p,int r)               //快排主体没什么好说的 呵呵
 {
         int q;
         if(p < r)
         {
                 q=hoarepartition(arr,p,r);
                 quicksort(arr,p,q-1);
                 quicksort(arr,q+1,r);
 
         }
         
         return 0;
 }
 
 
 int initarr(int arr1[],int arr2[],int arrcol)        //这个函数自己构思的 主要是为了便于后面对数组排序时
 {                                                      //前一次排序算法对数组的影响不会殃及下一次别的算法对数组调用。
    for(int i=1;i<=arrcol;i++)
         {
                 arr1[i]=arr2[i];
         }
    printf("排序前的数组为:\n");
    for(int i=1;i<=arrcol;i++)
            printf(" %d ",arr1[i]);
    printf("\n");
    return 0;
 }
 
 int _tmain(int argc, _TCHAR* argv[])                      //主函数没什么好说的 呵呵 
 {
         time_t timer;
         int number;
         int choice;
         clock_t start,finish;
         double duration;
         srand((unsigned) time(&timer));
         printf("请输入你想排序的的随机数个数:");
         scanf("%d",&number);
         int *arraytest=new int[number+1];                       //这个地方是为了克服VC的局限,不能申明
         arraytest[0]=0;                                         //未定义大小的数组,所以采用指针的形式先申明在转换数组
         printf("待排序数组为:\n");
         for(int i=1;i<=number;i++)
         {
                 arraytest[i]=rand()%1000;                          //记得上次Cydelovy 大神给我说过随机生成,这次我就自己YY啦
                 printf(" %d ",arraytest[i]);
         }
         printf("\n");
     int *arraycpy=new int[number+1];
 
         for(int i=0;i<=number;i++)
         {
                 arraycpy[i]=arraytest[i];                         //保存数组副本。便于适合不用排序方法不影响时间。
         }
     printf("请输入您的操作 输入CTRL+c则推出:\n");
         printf("1  堆排序         2 古典快速排序        3 改进的快速排序\n");
         while(scanf("%d",&choice)!=EOF)
         {
        switch(choice)
            {
            case 1:
                       initarr(arraytest,arraycpy,number);           //你永远不知道第几次进入这个case,进入前数组是什么样
                       HEAP_SORT(arraytest,number);                  //那你就有义务进行数组同一,对,不是统一。
                           break;
            case 2:                                              //下面同了啊 不讲了啊 
                       initarr(arraytest,arraycpy,number);
               start=clock();
                   hoarequicksort(arraytest,1,number);
                           printf("排序后的结果是:\n");
               for(int i=1;i<=number;i++)
                       printf(" %d ",arraytest[i]);
                           finish=clock();
                   duration=(double)(finish-start)/CLOCKS_PER_SEC;
                   printf("\n古典快速排序的耗费时间为: %f\n",duration);
                           break;
            case 3:
                       initarr(arraytest,arraycpy,number);
               start=clock();
                           hoarequicksort(arraytest,1,number);
                           printf("排序后的结果是:\n");
               for(int i=1;i<=number;i++)
                       printf(" %d ",arraytest[i]);
                           finish=clock();
                   duration=(double)(finish-start)/CLOCKS_PER_SEC;
                   printf("\n改进的快速排序的耗费时间为: %f\n",duration);
                           break;
            default:
                       printf("\n输入错误 ");
                           break;
            }
 
 
 
 
         }
         return 0;
 }

[转载]先睹为快:Visual Studio 11测试版已于2.29在微软官方网站正式发布

mikel阅读(860)

[转载]先睹为快:Visual Studio 11测试版已于2.29在微软官方网站正式发布 – 风尘浪子 – 博客园.

先睹为快:Visual Studio 11测试版已于2.29在微软官方网站正式发布

在2011的下半年,Visual Studio 11就已经呼之欲出,在2012年的2月29日,微软官方网站正式发布了VS11的测试版。
VS11中使用了Framework4.5,与Framework4.0相比,没有发生太大的改变,只是对功能进行了优化。

下载地址:http://www.microsoft.com/visualstudio/11/zh-cn/downloads

单单一个安装包就需要1.7G左右,安装旗舰版大约需要3.6G的空间,安装时间可能比较长,需要耐心地等待喔。


一、支持触屏管理与Metro

VS11明显是面向Windows 8开发的,与Windows 8相同,它支持触屏管理与Metro 风格的应用开发,并支持本地或者远程调试。

 

二、ASP.NET

ASP.NET方面,在数据绑定上增加了新ItemType属性来定义绑定类型, 以SelectMethod来绑定获取数据源的方法。
在显示类型属性的时候,只需要使用<%#:Item.Id%>即可代替原来的<%#Eval(“Id”)%>,使用起来会更加简单。

1       <asp:Repeater ID="repeater1" runat="server" SelectMethod="GetPersonList" ItemType="Person">
2           <ItemTemplate>
3               ID <%#:Item.ID%>
4               Age <%#:Item.Age%>
5               Name <%#:Item.Name%>
6           </ItemTemplate>
7       </asp:Repeater>

绑定数据源

 1     public IList<Person> GetPersonList()
 2     {
 3         var personList = new List<Person>();
 4         var person1 = new Person();
 5         person1.ID = 1;
 6         person1.Name = "Leslie";
 7         person1.Age = 29;
 8         personList.Add(person1);
 9         return personList;
10     }

 

在编写ASP.NET页面代码的时候,开发人员编辑控件件会出现Smart Tasks提示,使开发更加简单:

在页面也能看到属性设置工具,非常地方便

 

 

三、支持HTML 5和CSS 3

从 VS10起,系统就开始支持 HTML 5 与 CSS 3 , 而在VS11,更是增强了这方面的功能。
在视觉方面,它支持了开张(Expand)、闭合功能(Collapse),使用起来更加方便。

 

CSS有父子关系的设定,只要启用这功能,CSS就可以有伸缩的效果,阅读CSS与阅读代码一样井井有条。
如图,在菜单选择Tools -> Options -> Text Editor -> CSS -> Formatting,选择 Indentation 中的 Hierarchical Indentation

 

以往无论是页面开发人员还是程序员,都不太喜欢用VS来开发CSS,因为使用不太方便。
有见及此,VS11为CSS代码添加了感知能力,无论在普通属性或者是色彩设置方面,使用都非常地方便。

 

对audio这些HTML 5中的控件,VS 11无不支持

1 <audio controls="controls">
2     <source src="mylove.mp3"/>
3 </audio>

四、JavaScript

在VS2010中,已经增加了对JavaScript的支持,使用也比较方便,但唯一欠缺的就是对JSON的支持。
特别是在使用数组型的JSON数据时候,往往需要使用第三方插件才会比较方便,本人更多的是使用Newtonsoft.Json工具包。
在VS11中,明显是正视了这个问题,它支持了ECMAScript 5,而且为JSON提供了更方便的转换方式。

使用 parse 方法,可以将JSON数组转换为对象集,使用alert可以显示数组中Leslie的Age。

1 window.onload = function () {
2      var obj = JSON.parse('[{"ID":"1","Age":"23","Name":"Rose"},{"ID":"2","Age":"29","Name":"Leslie"}]');
3      alert(obj[1].Age);
4 }

反过来,使用 stringify 方法可以把对象转换为JSON

1 obj.ID = "1";
2 obj.Name = "Leslie";
3 obj.Age="29";
4 alert(JSON.stringify(obj));

 
五、异步操作

在异步操作方面,VS11也作出了一点调整,比如在异步获取上,它在原基础上增加一个await操作。
功能与PLINQ相似,系统能自动判断操作是否能提高系统的性能,如果通过判定,将在在后台线程中运行,不会影响主线程的运作。

1 try
2 {
3   var response = await new HttpClient().GetAsync("http://msdn.microsoft.com"); 
4   string result = response.EnsureSuccessStatusCode().Content.ReadAsString();
5 
6   this.textBox1.Text = result;
7   .......
8 }

若要使用异步页面,注意把页面的异步属性设置为true

1 <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
2     CodeBehind="ProductDetails.aspx.cs" Inherits="WebFormsLab.ProductDetails"
3     Async="true" %>
4 
5 <asp:Label ID="threadsMessageLabel" runat="server" />

同样地与原本的异步页面比较一下,只需要在异步调用时加上 await  的执行方式

 1 private void UpdateProductImage(Product product)
 2 {
 3   string imageUrl = product.ImagePath;
 4 
 5   if (!string.IsNullOrEmpty(imageUrl) && !VirtualPathUtility.IsAbsolute(imageUrl))
 6   {
 7     product.ImagePath = string.Format("/Images/{0}{1}", product.ProductId, Path.GetExtension(imageUrl));
 8 
 9     RegisterAsyncTask(new PageAsyncTask(async(o,a,ct) =>
10     {
11       var startThread = Thread.CurrentThread.ManagedThreadId;
12 
13       using (var wc = new WebClient())
14       {
15         await wc.DownloadFileTaskAsync(imageUrl, Server.MapPath(product.ImagePath));
16       }
17 
18       var endThread = Thread.CurrentThread.ManagedThreadId;
19 
20       threadsMessageLabel.Text = string.Format("Started on thread: {0}<br /> Finished on thread: {1}", startThread, endThread);
21     }));
22   }
23 }

 

 

六、代码测试

VS11新增了一个重复代码分析功能,使代码的修改更加简单快节。
它还有一个调试工具,叫做 “Page Inspector”,专门为ASP.NET开发而设,能加强调试的功能。
还有带有一个 Metro 风格模拟器,允许开发者模拟测试 Metro 应用的触摸和旋转事件。

 


结束语
据说在WF与F#上,VS11也进行了不少的优化,这需要更多时间进行更深入的了解。
相信VS11能更有效地帮助代码的编写,减少开发时间。
Visual Studio 11视频介绍:http://v.youku.com/v_show/id_XMzU2NzU3NzU2.html
对.NET开发有兴趣的朋友请加入博客园讨论小组“.NET高级编程” 一起探讨!

[转载]SQLite学习手册(临时文件)

mikel阅读(905)

[转载]SQLite学习手册(临时文件) – Stephen_Liu – 博客园.

一、简介:

尽管SQLite的数据库是由单一文件构成,然而事实上在SQLite运行时却存在着一些隐含的临时文件,这些临时文件是出于不同的目的而存在的,对于 开发者而言,它们是透明的,因此在开发的过程中我们并不需要关注它们的存在。尽管如此,如果能对这些临时文件的产生机制和应用场景有着很好的理解,那么对 我们今后应用程序的优化和维护都是极有帮助的。在SQLite中主要产生以下七种临时文件,如:
1). 回滚日志。
2). 主数据库日志。
3). SQL语句日志。
4). 临时数据库文件。
5). 视图和子查询的临时持久化文件。
6). 临时索引文件。
7). VACUUM命令使用的临时数据库文件。

二、具体说明:

1. 回滚日志:
SQLite为了保证事物的原子性提交和回滚,在事物开始时创建了该临时文件。此文件始终位于和数据库文件相同的目录下,其文件名格式为: 数据库文件名 + “-journal。换句话说,如果没有该临时文件的存在,当程序运行的系统出现任何故障时,SQLite将无法保证事物的完整性,以及数据状态的一致性。该文件在事物提交或回滚后将被立刻删除。
在事物运行期间,如果当前主机因电源故障而宕机,而此时由于回滚日志文件已经保存在磁盘上,那么当下一次程序启动时,SQLite在打开数据库文件的过 程中将会发现该临时文件的存在,我们称这种日志文件为”Hot Journal”。SQLite会在成功打开数据库之前先基于该文件完成数据库的恢复工作,以保证数据库的数据回复到上一个事物开始之前的状态。
在SQLite中我们可以通过修改journal_mode pragma,而使SQLite对维护该文件采用不同的策略。缺省情况下该值为DELETE, 即在事物结束后删除日志文件。而PERSIST选项值将不会删除日志文件,而是将回滚日志文件的头部清零,从而避免了文件删除所带的磁盘开销。再有就是 OFF选项值,该值将指示SQLite在开始事物时不产生回滚日志文件,这样一旦出现系统故障,SQLite也无法再保障数据库数据的一致性。

    2. 主数据库日志:
在SQLite中,如果事物的操作作用于多个数据库,即通过ATTACH命令附加到当前连接中的数据库,那么SQLite将生成主数据库日志文件以保证 事物产生的改变在多个数据库之间保持原子性。和回滚日志文件一样,主数据库日志文件也位于当前连接中主数据库文件所处的目录内,其文件名格式为:主数据库 文件名 + 随机的后缀。在该文件中,将包含所有当前事物将会改变的Attached数据库的名字。在事物被提交之后,此文件亦被SQLite随之删除。
主数据库日志文件只有在某一事物同时操作多个数据库时(主数据库和Attached数据库)才有可能被创建。通过该文件,SQLite可以实现跨多个数 据库的事物原子性,否则,只能简单的保证每个单一的数据库内的状态一致性。换句话说,如果该事物在执行的过程中出现系统崩溃或主机宕机的现象,在进行数据 恢复时,若没有该文件的存在,将会导致部分SQLite数据库处于提交状态,而另外一部分则处于回滚状态,因此该事物的一致性将被打破。

3. SQL语句日志:
在一个较大的事物中,SQLite为了保证部分数据在出现错误时可以被正常回滚,所以在事物开始时创建了SQL语句日志文件。比如,update语句修 改了前50条数据,然而在修改第51条数据时发现该操作将会破坏某字段的唯一性约束,最终SQLite将不得不通过该日志文件回滚已经修改的前50条数 据。
SQL语句日志文件只有在INSERT或UPDATE语句修改多行记录时才有可能被创建,与此同时,这些操作还极有可能会打破某些约束并引发异常。但是 如果INSERT或UPDATE语句没有被包含在BEGIN…COMMIT中,同时也没有任何其它的SQL语句正在当前的连接上运行,在这种情况 下,SQLite将不会创建SQL语句日志文件,而是简单的通过回滚日志来完成部分数据的UNDO操作。
和上面两种临时文件不同的是,SQL语句日志文件并不一定要存储在和数据库文件相同的目录下,其文件名也是随机生成。该文件所占用的磁盘空间需要视UPDATE或INSERT语句将要修改的记录数量而定。在事物结束后,该文件将被自动删除。

4. 临时数据库文件:
当使用”CREATE TEMP TABLE”语法创建临时数据表时,该数据表仅在当前连接内可见,在当前连接被关闭后,临时表也随之消失。然而在生命期内,临时表将连同其相关的索引和视 图均会被存储在一个临时的数据库文件之内。该临时文件是在第一次执行”CREATE TEMP TABLE”时即被创建的,在当前连接被关闭后,该文件亦将被自动删除。最后需要说明的是,临时数据库不能被执行DETACH命令,同时也不能被其它进程 执行ATTACH命令。

5. 视图和子查询的临时持久化文件:
在很多包含子查询的查询中,SQLite的执行器会将该查询语句拆分为多个独立的SQL语句,同时将子查询的结果持久化到临时文件中,之后在基于该临时 文件中的数据与外部查询进行关联,因此我们可以称其为物化子查询。通常而言,SQLite的优化器会尽力避免子查询的物化行为,但是在有些时候该操作是无 法避免的。该临时文件所占用的磁盘空间需要依赖子查询检索出的数据数量,在查询结束后,该文件将被自动删除。见如下示例:
SELECT * FROM ex1 WHERE ex1.a IN (SELECT b FROM ex2);
在上面的查询语句中,子查询SELECT b FROM ex2的结果将会被持久化到临时文件中,外部查询在运行时将会为每一条记录去检查该临时文件,以判断当前记录是否出现在临时文件中,如果是则输出当前记 录。显而易见的是,以上的行为将会产生大量的IO操作,从而显著的降低了查询的执行效率,为了避免临时文件的生成,我们可以将上面的查询语句改为:
SELECT * FROM ex1 WHERE EXISTS(SELECT 1 FROM ex2 WHERE ex2.b=ex1.a);
对于如下查询语句,如果SQLite不做任何智能的rewrite操作,该查询中的子查询也将会被持久化到临时文件中,如:
SELECT * FROM ex1 JOIN (SELECT b FROM ex2) AS t ON t.b=ex1.a;
在SQLite自动将其修改为下面的写法后,将不会再生成临时文件了,如:
SELECT ex1.*, ex2.b FROM ex1 JOIN ex2 ON ex2.b=ex1.a;

6. 临时索引文件:
当查询语句包含以下SQL从句时,SQLite为存储中间结果而创建了临时索引文件,如:
1). ORDER BY或GROUP BY从句。
2). 聚集查询中的DISTINCT关键字。
3). 由UNION、EXCEPT和INTERSECT连接的多个SELECT查询语句。
需要说明的是,如果在指定的字段上已经存在了索引,那么SQLite将不会再创建该临时索引文件,而是通过直接遍历索引来访问数据并提取有用信息。如果 没有索引,则需要将排序的结果存储在临时索引文件中以供后用。该临时文件所占用的磁盘空间需要依赖排序数据的数量,在查询结束后,该文件被自动删除。

7. VACUUM命令使用的临时数据库文件:
VACUUM命令在工作时将会先创建一个临时文件,然后再将重建的整个数据库写入到该临时文件中。之后再将临时文件中的内容拷贝回原有的数据库文件中,最后删除该临时文件。
该临时文件所占用的磁盘空间不会超过原有文件的尺寸。

三、相关的编译时参数和指令:

对于SQLite来说,回滚日志、主数据库日志和SQL语句日志文件在需要的时候SQLite都会将它们写入磁盘文件,但是对于其它类型的临时文 件,SQLite是可以将它们存放在内存中以取代磁盘文件的,这样在执行的过程中就可以减少大量的IO操作了。要完成该优化主要依赖于以下三个因素:
1. 编译时参数SQLITE_TEMP_STORE:
该参数是源代码中的宏定义(#define),其取值范围是0到3(缺省值为1),见如下说明:
1). 等于0时,临时文件总是存储在磁盘上,而不会考虑temp_store pragma指令的设置。
2). 等于1时,临时文件缺省存储在磁盘上,但是该值可以被temp_store pragma指令覆盖。
3). 等于2时,临时文件缺省存储在内存中,但是该值可以被temp_store pragma指令覆盖。
4). 等于3时,临时文件总是存储在内存中,而不会考虑temp_store pragma指令的设置。

2. 运行时指令temp_store pragma:
该指令的取值范围是0到2(缺省值为0),在程序运行时该指令可以被动态的设置,见如下说明:
1). 等于0时,临时文件的存储行为完全由SQLITE_TEMP_STORE编译期参数确定。
2). 等于1时,如果编译期参数SQLITE_TEMP_STORE指定使用内存存储临时文件,那么该指令将覆盖这一行为,使用磁盘存储。
2). 等于2时,如果编译期参数SQLITE_TEMP_STORE指定使用磁盘存储临时文件,那么该指令将覆盖这一行为,使用内存存储。

3. 临时文件的大小:
对于以上两个参数,都有参数值表示缺省情况是存储在内存中的,只有当临时文件的大小超过一定的阈值后才会根据一定的算法,将部分数据写入到磁盘中,以免临时文件占用过多的内存而影响其它程序的执行效率。

最后在重新赘述一遍,SQLITE_TEMP_STORE编译期参数和temp_store pragma运行时指令只会影响除回滚日志和主数据库日志之外的其它临时文件的存储策略。换句话说,回滚日志和主数据库日志将总是将数据写入磁盘,而不会 关注以上两个参数的值。

四、其它优化策略:

在SQLite中由于采用了Page Cache的缓冲优化机制,因此即便临时文件被指定存储在磁盘上,也只有当该文件的大小增长到一定的尺寸后才有可能被SQLite刷新到磁盘文件上,在此 之前它们仍将驻留在内存中。这就意味着对于大多数场景,如果临时表和临时索引的数据量相对较少,那么它们是不会被写到磁盘中的,当然也就不会有IO事件发 生。只有当它们增长到内存不能容纳的时候才会被刷新到磁盘文件中的。其中SQLITE_DEFAULT_TEMP_CACHE_SIZE编译期参数可以用 于指定临时表和索引在占用多少Cache Page时才需要被刷新到磁盘文件,该参数的缺省值为500页。

[转载]13个ASP.NET MVC的扩展

mikel阅读(914)

[转载]13个MVC的扩展 – 潇湘〃细雨 – 博客园.

ASP.NET MVC设计的主要原则之一是可扩展性。处理管线(processing pipeline)上的所有(或大多数)东西都是可替换的。因此,如果您不喜欢ASP.NET MVC所使用的约定(或缺乏某些约定),您可以创建自己的服务来支持您的约定,并将其注入到主管线中。

 

在本文中,我们将从管线开始直到视图呈现,逐一向您展示每个ASP.NET MVC开发者都必须了解13个扩展点。

 

1.ASP.NET MVC扩展之RouteConstraint

 

通常情况下你可以使用正则表达式对url参数进行约束,但如果您的约束不仅仅取决于单一参数,您可以实现IRouteConstrains的方法,并在其中添加你的验证逻辑。

 

比如对日期的验证,url中可能会包含年、月、日,而你需要验证这三者是否可以组合成一个有效的日期。

 

2.ASP.NET MVC扩展之RouteHandler

 

RouteHandler是在路由选择之后进行处理的组件,它并不仅仅针对ASP.NET MVC。显然,如果您改变了RouteHandler,那么对请求的处理将不再使用ASP.NET MVC,但这在您使用其他HttpHandler或经典的WebForm进行路由处理时却是非常有用的。

 

3.ASP.NET MVC扩展之ControllerFactory

 

ControllerFactory是基于路由的组件,它选择正确的controller并对其实例化。default factory会查找实现了IController并且以Controller结尾的类,然后通过反射使用无参构造函数进行实例化。

 

但如果您希望使用依赖注入,就不能再使用default factory,而必须使用支持IoC的controller factory。MvcContrib和Ninject Controller Factory都包含支持IoC容器的controller factory。

 

4.ASP.NET MVC扩展之ActionInvoker

 

ActionInvoker顾名思义是负责调用(invoke)action的。默认的action invoker通过方法名、action名或其他可能的selector attribute来查找action,然后调用action方法以及定义的filter,最终执行得到action result。

 

你会发现大部分执行管线存在于ControllerActionInvoker类的逻辑之中。因此,如果希望改变这些约定,如action方法的选择逻辑、http参数映射到action参数的方式、选择和执行filter的方式等,您需要扩展该类并重写需要修改的方法。

 

可以参阅NinjectActionInvoker I developed to allow injection of dependencies inside filters。

 

5.ASP.NET MVC扩展之ActionMethodSelectorAttribute

 

使用默认的action invoker时,action的选择是基于名称的。您也可以实现自己的Method Selector以改善对于action的选择。在框架中已经包含了AcceptVerbs特性,它允许您指定使用哪一个HTTP Verb来处理action的响应。

 

例如,您也许会希望基于浏览器所支持的语言或浏览器类型(如移动设备的浏览器或桌面浏览器)来进行action的选取。

 

6.ASP.NET MVC扩展之AuthorizationFilter

 

这种过滤器是在action执行之前执行的,用来确保请求是有效的。

 

框架中已经包含了一些autorization过滤器,最有名的莫过于Authorize特性,它用来检查当前用户是否允许执行该action。另 一个是用来阻止CSRF攻击的ValidateAntiForgeryToken。如果您希望实现自己的authorization,那么必须实现接口。 例如,日期中的小时。

 

7.ASP.NET MVC扩展之ActionFilter

 

Action Filters在action执行前后执行。OutputCache过滤器是几个核心过滤器之一。这可能是您最有可能使用的扩展点,并且在我看 来,controller只关心它的主要工作,而view所需要的所有其他数据都必须从action过滤器内部获取,这样的实现对于一个组织良好的 view来说,是十分关键的。

 

8.ASP.NET MVC扩展之ModelBinder

 

默认的model binder使用参数名称进行HTTP参数到action方法参数的映射。例如,http参数user.address.city将映射到方法参数 user的Address属性的City属性。DefaultModelBinder也同样适用于数组和其他列表类型。

 

更进一步来说,例如,您可能希望从数据库中进行检索,直接根据person的id将其转换为Person对象。Timothy Khouri(网名SingingEels)在他的文章Model Binders in ASP.NET MVC中更好的阐述了这种方法。他的代码基于Preview 5,但其理念是一样的。

 

9.ASP.NET MVC扩展之ControllerBase

 

所有的Controller均继承自基类Controller。要想在action中封装自己的逻辑和约定,创建自己的父类使所有Controller继承自该类,是一种很好的方式。

 

10.ASP.NET MVC扩展之ResultFilter

 

与ActionFilter类似,ResultFilters在ActionResult前后执行。OutputCache过滤器也可以作为 ResultFilter的示例。另外,比较常用的诠释这种过滤器的示例是日志记录。如果您希望在页面返回给用户时记录日志,可以编写自定义的 RenderFilter,在ActionResult执行之后记录日志。

 

11.ASP.NET MVC扩展之ActionResult

 

ASP.NET MVC提供了很多result用来呈现视图、JSON、纯文本、文件并重定向到其他action。如果您需要其他类型的result,可以自定义 ActionResult,并实现ExecuteResult方法。例如,如果您希望将PDF文件作为结果发送,您需要使用PDF库编写能够生成PDF的 ActionResult。又如RSS feed,可参见how to write a RssResult in this post。

 

12.ASP.NET MVC扩展之ViewEngine

 

您可能不需要编写自己的view engine,但您也许可以考虑使用其他引擎来替代默认的WebForm view engine。在我看来,最有趣的引擎就是Spark。

 

如果您确实希望编写自己的view engine,可以看一下Brad Wilson的文章: Partial Rendering & View Engines in ASP.NET MVC。

 

13.ASP.NET MVC扩展之HtmlHelper

 

视图必须十分简单整洁,它们只能包含html标记并调用HtmlHelper的辅助方法。视图中不能包含任何代码,所以辅助方法必须十分方便,使您 可以将代码从视图中提取出来,放到一个可测试的环境中去。正如Rob Conery所说:如果有if,就构造辅助方法(If there’s an IF, make a Helper)。

 

什么是HtmlHelper辅助方法?其实就是HtmlHelper类的扩展方法,这是唯一的要求。

 

你可以从Rob的文章Avoiding Tag Soup中了解到为什么说HtmlHelper是封装视图中代码的好方法。

 

在您的应用中该使用哪个呢?

 

正如您所猜测的那样,并不是所有的应用都需要扩展以上的13个扩展点。最可能在所有应用中进行扩展的是ActionFilter和 HtmlHelper。另外,您很可能会使用其他人编写的扩展,如使用了IoC容器的ControllerFactory或用来摆脱WebForm的 ViewEngine。

但是,学习这些扩展点并进行尝试是十分重要的,这样您才会做出选择,并随时准备在必要的时候使用这些强大的扩展点。下周我将发表一些文章来阐述如何使用这些扩展点

[转载]ASP .NET MVC 之Entity Framework- code first

mikel阅读(1020)

[转载]ASP .NET MVC 之Entity Framework- code first – tomin – 博客园.

最近,用到了ASP.NET  MVC Entity Framework  开发了一些项目,感觉还是非常好用了,这让我见证了微软技术的发展:

 

通过这个图,我们很清晰的看到,数据访问方式的改变。

 

如果,你想了解,ADO.NET Entity Framework, 你可以直接访问这个网站  Entity Framework

 

如果,你想了解ASP.NET MVC , 你可以直接访问这个网站MVC

 

下面我介绍一下Entity Framework 的一些运用:

 

 

 

 

在园子里,我看到很多关于MVC的讲解,很多的用到的是Model First, or Schema First . 在这里,我将结合ASP.NET
MVC 3.0 ,
做一个Entity Framework CODE FIRST的演示,希望大家能有所帮助。特别是,对你想架构小型项目的时候,这个运用是非常有帮助的.

 

实验环境:

 

OS: Windows Server 2008R2, Windows 7

 

DE: VS2010 + MVC 3.0+Entity Framework+ SQL Server 2008 R2

 

一、创建MVC
3.0
的网站项目,这和大家创建其他的MVC项目是一样的。注意:在创建之前,记得先安装MVC 3.0, Entity Framework. 创建完之后,你将会看到经典的MVC 模式,Model->View->Control

 

二、在Model中添加2个类,一个是Restaurant,一个是OdeToFoodDB,
定义如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MVCET.Models
{
public class Restaurant
{
public int ID { get; set; }
public string Name { get; set; }
public string State { get; set; }
public Adress Adress { get; set; }

}
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;

namespace MVCET.Models
{
public class OdeToFoodDB:DbContext
{
public DbSet&lt;Restaurant&gt; Restaurants { get; set; }
public DbSet&lt;Reviewers&gt; Reviewer { get; set; }
}
}

OdeToFoodDB, 注意这个类必须继承 DbContext

然后,在webConfig中添加一个数据库连接字符串,如下:

 <configuration>
<connectionStrings>
<add name=”ApplicationServices”
connectionString=”data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true”
providerName=”System.Data.SQLClient” />
<add name=”OdeToFoodDB”
         connectionString=”data source=.\SQLEXPRESS;Integrated Security=SSPI;initial catalog=OdeToFoodDB;”
    providerName=”System.Data.SqlClient”/>
  </connectionStrings>

三、在Control 中添加RestaurantControl 类,再为Index()函数添加View. 并且选择强类型去显示数据。这时候,view 会自动绑上数据,显示数据。这时候,肯定没有数据,如果要有数据怎么弄呢?这时候就到根据代码生成数据库。
四、在VS 中选择服务器浏览器窗口,在数据库的节点上选择”Add Connection ….” 中文版的就是“添加新的连接”。写上数据库的服务器名称,在填上数据库的名字,如下图所示:

点击ok,然后创建数据库,如果,有对话框弹出来,选择”Yes” 就行了。这时候,系统就会自动生成数据库了,如下图所示

我们,看到Restaurants 表中的字段和我们定义的Restaurant中字段的名字基本类似的
到现在为止,我们已经根据代码结构生成了数据库.但是,如果Restaurant 中的字段有所改变,怎办办呢?这时候,就要到Drop以前的数据库,然后,重现建立数据库
五、类发生了改变,数据库也发生对应的改变:
在Global.asax 中的 Application_Start() 函数中,添加以下的代码

protected void Application_Start()
        {
           // Database.SetInitializer(new DropCreateDatabaseIfModelChanges<OdeToFoodDB>()); 
            Database.SetInitializer(new OdeToFoodDBInitializer()); 
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }

你也可以写一个类继承DropCreateDatabaseIfModelChanges, 如下代码所示,你就可以不用注释的哪一行代码了

public class OdeToFoodDBInitializer :
        DropCreateDatabaseIfModelChanges<OdeToFoodDB>
    {
        protected override void Seed(OdeToFoodDB context)
        {
            base.Seed(context);

            context.Restaurants.Add(
                new Restaurant()
                {
                    Name = "qitian",
                    Adress = new Adress() { Street = " Nanjiang RD, MD", City = "Shanghai" }
                });

            context.Restaurants.Add(
                new Restaurant()
                {
                    Name = "haiyun",
                    Adress = new Adress() { Street = " Dongchuan RD, MD", City = "Shanghai" }
                });
            context.Restaurants.Add(
                new Restaurant()
                {
                    Name = "qitian",
                    Adress = new Adress() { Street = " Beijing RD, HF", City = "Guangdong" }
                });

            context.Restaurants.Add(
                new Restaurant()
                {
                    Name = "Lantian",
                    Adress = new Adress() { Street = " Wuhan RD, HF", City = "Guangdong" }
                });

            context.Reviewer.Add(new Reviewers() { 
            Name="Tomin", Sex="Female"});
            context.Reviewer.Add(new Reviewers()
            {
              
                Name = "Tony",
                Sex = "Male"
            });

            context.SaveChanges();
                
        }
    }

重新运行运用程序,会给你一个意想不到的结果,你会惊呆的。