[转载]自己动手--基于jQuery的表格控件

mikel阅读(1169)

[转载]自己动手—-基于jQuery的表格控件(更正DEMO下载地址) – 会动的稻草人 – 博客园.

控件效果图:

一、功能说明
1、使用简单、扩展方便。
2、表格样式可自定义,只需修改相应的CSS样式即可。
3、支持行点击事件自定义。
4、支持复选框选择行。
5、执行列排序(支持数字、日期时间、英文、汉字等)。
6、支持自定义行按钮及按钮事件(如编辑、删除按钮等)
7、支持自定义数据的显示样式(根据当前数据或当前行数据)
8、支持分页显示数据,且在插件内部实现分页处理。
二、使用说明

该插件的使用,基本只需两个步骤即可,一个是表格的声明(创建),然后就是数据的加载。1、表格的声明(创建)

            var objTable = new HyTable({
                columns: [
                    {name: '姓名', mapping: 'username', type: 'string', width: '100',
allowSort: true, renderer: function(val){return val;}}
                ],                      //(array)列信息
                emptyMsg: "暂无数据",   //(string)无数据时的提示信息
                renderTo: "",           //(string)表格容器,必须是HTML元素的ID属性值
                model: 'remote',       //(string)模式,远程(remote)or本地(local)
                url: '',                //(string)远程模式的获取数据的url地址
                allowPaging: true, (bool)//是否分页
                pageSize: 20,            //(int)每页显示的行数
                checkSelected: false,    //(bool)复选框选择
                selectedField: "",      //(string)复选框选中字段
                stripeRows: false,      //(bool)是否启用隔行变色 默认不启用
                onRowClick: null        //(function)行点击事件
            });

2、表格数据的加载

            objTable.loadData(1,data);
//第一个参数为加载页的页索引,第二个参数有两种情况,
1:如果参数内容是JSON对象,
则表示是查询的查询条件,如果是数据,则表示直接加载本地数据到表格中。
三、各参数意义(API)
columns:
参数类型:json数组

表格的列定义

    {
        name:'列头名称',
        mapping:'code',                                 //(string)映射JSON数据中的键名称
        width:'100',                                    //(string)列宽度,可数字或百分比等
        allowSort:true,                                 //(bool)是否排序
        type:'string/int/float/datetime/action',        //(string)数据类型或列类型,前四个为数据类型,为action表示为操作列,放置按钮用
        renderer:function(val,json){return "内容前加文字"+val;},
 //(function)列渲染函数function,参数为当前单元格的值及当前行的数据
        content:[{text:'修改',code:'edit',handler:function(data),renderer:function(data){return false or true;}}]
//按钮列的按钮,可为JSON数组表示多个或一个按钮
        //handler 为当前按钮的点击事件,参数为JSON格式的当前行数据。
        //renderer 为控制当前按钮是否显示的事件,可选,参数为JSON格式的当前行数据,主要用于根据当前行数据决定是否显示该按钮用。
返回false则表示按钮不显示,返回true则显示。
    }
emptyMsg:
参数类型:string

数据为空时,显示的提示文字,如:暂无数据。

renderTo:
参数类型:string

表格的父级容器,为HTML元素的ID属性值

model:
参数类型:string

(默认为“remote”)表格数据加载模式,值为“remote”则表示从远程加载表格数据,为“local”则表示从本地加载数据

url:
参数类型:string

(仅在model=”remote”时有效)远程数据加载地址

allowPaging:
参数类型:bool

(默认为“true”)是否分页显示数据,仅远程加载表格数据才支持分页。

pageSize:
参数类型:int

(默认为“20”,仅在allowPaging=true时有效)每页显示的记录数

checkSelected:
参数类型:bool

(默认为“false”)是否启用复选框选择数据,启用后,可使用getSelected()方法获取选中的数据,多条以逗号间隔

selectedField:
参数类型:string

(仅在checkSelected=true时有效)选中数据时,用以标识数据的主键字段

stripeRows:
参数类型:bool

(默认为“false”)是否启用表格隔行变色效果

onRowClick:
参数类型:function(json)

行点击时触发的事件,方法参数为JSON格式的当前点击行数据。可使用(json.字段名)获取数据

四、开发思路

主要是在开发该插件时的想法及部分注意事项,方便后期同事维护该插件。

1、表格生成过程:创建表格(主要为列头)(绑定列排序点击事件等)–> 加载数据 (绑定行点击事件、复选框选中、行中按钮定义、单元格数据显示)2、loadData(1,data)方法的第二个参数,如果是远程加载数据,则该参数内容为查询条件的JSON格式。查询数据时,将查询条件组织成JSON格式,然后调用该方法即可。3、插件在获取分页数据时,会自动往后台传入start及end两个参数,即获取数据的起始行及结束行,
如每页显示20条,则获取第5页数据时,会传入start=80,end=100这两个参数,这也方便oracle数据库获取数据。4、定义按钮操作列时,code字段不能为空,该字段主要是绑定按钮事件时,查询按钮用。
另外,在插件中所在的JS文件中,我还放置了一个简单的将JSON格式数据加载到表单中的方法$(obj).loadForm(data),其中data为JSON格式数据。
该功能与JQuery中的serializeArray()方法相反,serializeArray()方法将一个指定HTML元素中的输入项的值序列化为JSON对象,
而loadForm(data)方法则是将JSON对象赋值到指定HTML元素中的输入项。
DEMO下载地址:http://115.com/file/e7sxriym#JQuery.HyTable表格Demo.rar

[转载]SQLite学习手册(命令行工具)

mikel阅读(865)

[转载]SQLite学习手册(命令行工具) – Stephen_Liu – 博客园.

    工欲善其事,必先利其器。学好SQLite的命令行工具,对于我们学习SQLite本身而言是非常非常有帮助的。最基本的一条就是,它让我们学习 SQLite的过程更加轻松愉快。言归正传吧,在SQLite的官方下载网站,提供了支持多个平台的命令行工具,使用该工具我们可以完成大多数常用的 SQLite操作,就像sqlplus之于Oracle。以下列表给出了该工具的内置命令:

命令名 命令说明
.help 列出所有内置命令。
.backup DBNAME FILE 备份指定的数据库到指定的文件,缺省为当前连接的main数据库。
.databases 列出当前连接中所有attached数据库名和文件名。
.dump TABLENAME … 以SQL文本的格式DUMP当前连接的main数据库,如果指定了表名,则只是DUMP和表名匹配的数据表。参数TABLENAME支持LIKE表达式支持的通配符。
.echo ON|OFF 打开或关闭显示输出。
.exit 退出当前程序。
.explain ON|OFF 打开或关闭当前连接的SELECT输出到Human Readable形式。
.header(s) ON|OFF 在显示SELECT结果时,是否显示列的标题。
.import FILE TABLE 导入指定文件的数据到指定表。
.indices TABLENAME 显示所有索引的名字,如果指定表名,则仅仅显示匹配该表名的数据表的索引,参数TABLENAME支持LIKE表达式支持的通配符。
.log FILE|off  打开或关闭日志功能,FILE可以为标准输出stdout,或标准错误输出stderr。
.mode MODE TABLENAME 设置输出模式,这里最为常用的模式是column模式,使SELECT输出列左对齐显示。
.nullvalue STRING  使用指定的字符串代替NULL值的显示。
.output FILENAME  将当前命令的所有输出重定向到指定的文件。
.output stdout  将当前命令的所有输出重定向到标准输出(屏幕)。
.quit  退出当前程序。 
.read FILENAME  执行指定文件内的SQL语句。
.restore DBNAME FILE  从指定的文件还原数据库,缺省为main数据库,此时也可以指定其它数据库名,被指定的数据库成为当前连接的attached数据库。
.schema TABLENAME 显示数据表的创建语句,如果指定表名,则仅仅显示匹配该表名的数据表创建语句,参数TABLENAME支持LIKE表达式支持的通配符。
.separator STRING 改变输出模式和.import的字段间分隔符。
.show 显示各种设置的当前值。
.tables TABLENAME 列出当前连接中main数据库的所有表名,如果指定表名,则仅仅显示匹配该表名的数据表名称,参数TABLENAME支持LIKE表达式支持的通配符。
.width NUM1 NUM2 … 在MODE为column时,设置各个字段的宽度,注意:该命令的参数顺序表示字段输出的顺序。

见如下常用示例:
1). 备份和还原数据库。
    –在当前连接的main数据库中创建一个数据表,之后再通过.backup命令将main数据库备份到D:/mydb.db文件中。
    sqlite> CREATE TABLE mytable (first_col integer);
    sqlite> .backup ‘D:/mydb.db’
    sqlite> .exit
    –通过在命令行窗口下执行sqlite3.exe以重新建立和SQLite的连接。
    –从备份文件D:/mydb.db中恢复数据到当前连接的main数据库中,再通过.tables命令可以看到mytable表。
    sqlite> .restore ‘D:/mydb.db’
    sqlite> .tables
    mytable

2). DUMP数据表的创建语句到指定文件。
    –先将命令行当前的输出重定向到D:/myoutput.txt,之后在将之前创建的mytable表的声明语句输出到该文件。
    sqlite> .output D:/myoutput.txt
    sqlite> .dump mytabl%
    sqlite> .exit
    –在DOS环境下用记事本打开目标文件。
    D:\>notepad myoutput.txt

3). 显示当前连接的所有Attached数据库和main数据库。
    sqlite> ATTACH DATABASE ‘D:/mydb.db’ AS mydb;
    sqlite> .databases
    seq  name               file
    —  —————  ————————
    0    main
    2    mydb                D:\mydb.db

4). 显示main数据库中的所有数据表。
    sqlite> .tables
    mytable

5). 显示匹配表名mytabl%的数据表的所有索引。
    sqlite> CREATE INDEX myindex on mytable(first_col);
    sqlite> .indices mytabl%
    myindex  

6). 显示匹配表名mytable%的数据表的Schema信息。
    –依赖该表的索引信息也被输出。
    sqlite> .schema mytabl%
    CREATE TABLE mytable (first_col integer);
    CREATE INDEX myindex on mytable(first_col);

7). 格式化显示SELECT的输出信息。
    –插入测试数据
    sqlite> INSERT INTO mytable VALUES(1);
    sqlite> INSERT INTO mytable VALUES(2);
    sqlite> INSERT INTO mytable VALUES(3);    
    –请注意没有任何设置时SELECT结果集的输出格式。
    sqlite> SELECT * FROM mytable;
    1
    2
    3    
    –显示SELECT结果集的列名。
    –以列的形式显示各个字段。
    –将其后输出的第一列显示宽度设置为10.
    sqlite> .header on
    sqlite> .mode column
    sqlite> .width 10
    sqlite> SELECT * FROM mytable;
    first_col
    ———-
    1
    2
    3

[转载]jqGrid + JSON + WebService 完整示例

mikel阅读(1299)

[转载]jqGrid + JSON + WebService 完整示例 – 瑞雪年 – 博客园.

真没找到这样的例子,于是自已写了个,分享出来。

第一步,首先在WebService上,添加[System.Web.Script.Services.ScriptService]属性标签,让WebServer支持JSON.

namespace jqGrid_JSON_WebService_Sample.Services
{
///

<summary> /// Summary description for WebServiceGrid
/// </summary>

&nbsp;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class WebServiceGrid : System.Web.Services.WebService
{
}
}

接着,添加ajax调用的后端代码,获取数据,返回JSON对象:

[WebMethod]
public object Grid(bool? _search, string nd, int page, int rows, string sidx, string sord, string searchField, string searchString, string searchOper)
{
int count;
var data = dc.Query(string.IsNullOrWhiteSpace(searchField) ? null : new string[] { searchField }, new string[] { searchOper }, new object[] { searchString }, null, new string[] { string.IsNullOrWhiteSpace(sidx) ? "IssueID" : sidx }, new string[] { sord }, (page - 1) * rows, rows, out count);
return (new
{
total = Math.Ceiling((float)count / (float)rows),
page = page,
records = count,
rows = data.Select(item =&gt; new { id = item.IssueID, cell = new object[] { item.IssueID, item.Title, item.Description, item.Progress, item.CreateTime, item.Locked } })
});
}

第二步,添加前台页面,首先添加各种的js,css引用,然后添加jqGrid所需的

和js代码:



<script type="text/javascript" src="/Scripts/jquery-1.7.1.min.js"></script><script type="text/javascript" src="/Scripts/jquery-ui-1.8.11.min.js"></script>
<script type="text/javascript" src="/Scripts/i18n/grid.locale-en.js"></script><script type="text/javascript" src="/Scripts/jquery.jqGrid.min.js"></script>
<script type="text/javascript">// <![CDATA[
        $(function ()
        {
            $("#list #grid").jqGrid(
            {
                url: '/Services/WebServiceGrid.asmx/Grid',
                mtype: 'POST',
                ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
                serializeGridData: function (postData)
                {
                    if (postData.searchField === undefined) postData.searchField = null;
                    if (postData.searchString === undefined) postData.searchString = null;
                    if (postData.searchOper === undefined) postData.searchOper = null;
                    return JSON.stringify(postData);
                },
                jsonReader:
                {
                    root: "d.rows",
                    page: "d.page",
                    total: "d.total",
                    records: "d.records"
                },
                datatype: "json",
                colNames:
                [
                    'IssueID',
                    'Title',
                    'Description',
                    'Progress',
                    'CreateTime',
                    'Locked'
                ],
                colModel:
                [
                    { name: 'IssueID', width: 100, index: 'IssueID' },
                    { name: 'Title', width: 100, index: 'Title' },
                    { name: 'Description', width: 300, index: 'Description' },
                    { name: 'Progress', width: 150, index: 'Progress' },
                    { name: 'CreateTime', width: 100, index: 'CreateTime', formatter:'date',  sorttype:'datetime', datefmt:'M d h:i' },
                    { name: 'Locked', width: 100, index: 'Locked' }
                ],
                rowNum: 10,
                rowList: [10, 15, 20, 25, 40],
                pager: '#pager',
                viewrecords: true,
                sortorder: "desc",
                width: 900,
                height: 240,
            });

            $("#list #grid").jqGrid('navGrid', '#pager', { edit: false, add: false, del: false });
        });
// ]]></script>
</div>
<div id="list">
<div id="pager"></div>

注意jqGrid函数据前面的部分代码:

url: '/Services/WebServiceGrid.asmx/Grid',
mtype: 'POST',
ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },

通过url指定WebService方法,mtype指定使用POST方法,contentType指定为json,这样WebService才会返回json对象。

可是,返回的数据是放在一个属性名为d的对象里,所以还要添加 jsonReader,来作数据映射:

jsonReader:
{
root: "d.rows",
page: "d.page",
total: "d.total",
records: "d.records"
},

最后,为了保证查询时能够POST正确的参数,还要对POST的参数值进行检查:

serializeGridData: function (postData)
{
if (postData.searchField === undefined) postData.searchField = null;
if (postData.searchString === undefined) postData.searchString = null;
if (postData.searchOper === undefined) postData.searchOper = null;
return JSON.stringify(postData);
},

到此,一个完整的jqGrid示例就完成了,成果展示:


完整示例代码:jqGrid_JSON_WebService_Sample.zip

[转载]C#中值类型的装箱与拆箱浅析

mikel阅读(1010)

[转载]值类型的装箱与拆箱浅析 – 秋梧 – 博客园.

阅读目录
  • 前言
  • 值类型的装箱
  • 值类型的拆箱
  • 装箱和拆箱实例
  • 结束语

前言

         在.Net 中值类型向引用类型的转换以及从引用类型到值类型的转换是需要装箱(boxing)和拆箱(unboxing)的,这是因为值类型是比引用类型更轻型的一 种类型,因为他们不想对象那样在托管队中分配,不会被GC收集,而且不需要通过指针来引用。但是在许多情况下都需要获取对值类型的一个实例的引用。对于在 值类型与引用类型之间的互相转换,我之前一直不慎理解,在看了一些书籍之后,在本文中记录一些心得,希望各位尽量拍砖,多多指出不正确的地方,大家共同交 流,共同学习、进步。

值类型的装箱

         首先来看一个例子:

struct Point
{
    public int x,y;
}

public sealed class Program{
    public static void Main(){
      ArrayList a=new ArrayList();
Point p;                //分配一个Point(值类型,在线程堆栈上分配)
for(int i=0;i<10;i++){
          p.x=p.y=i;            //初始化值类型中的成员
          a.Add(p);            //对值类型进行装箱,并将引用添加到ArrayList中
}
}
}

每次循环迭代时,都会初始化一个Point的值类型字段。然后这个Point会被添加到ArrayList中。如果知道 大家熟悉ArrayList的话,一般都会清楚ArrayList 里面存储的是object类型,通过Reflector看一下ArrayList中Add方法所要求的参数:

public class ArrayList:IList,ICollection,IEnumerable,IConeable
{
    …//省略其他代码
public virtual int Add(object value)
{
        …
}
}

上面就是ArrayList 内Add方法的方法签名。由此可以知道Add方法是需要一个object类型的参数的。这表明Add接受对托管堆上的一个对象的引用来作为参数。但是在上 面例子中,传递给Add方法的是一个Point类型,也就是一个值类型。很明显,照这样,是不会正确执行的,为使上述代码正确工作,Point值类型必须 转换成一个真正的托管堆的对象,而且必须获取对这个转换后的对象的一个引用。为了将一个值类型转换成一个引用类型,可以使用一个名为“装箱” (boxing) 的机制。下面说一下对一个值类型的实例进行装箱操作时在内部发生的事情:

  1. 在托管堆中分配好内存。分配的内存变量是值类型的各个字段所需要的内存两加上托管对上的所有对象都有的两个额外成员(即类型对象指针和同步块索引) 所需要的内存量。(如果对类型对象指针和同步块索引不熟悉,可以看下我的另一篇文章:.Net运行时的相互关系)
  2. 值类型的字段复制到新分配的堆内存中。(原有的值类型字段不受影响,照样可以使用)
  3. 返回对象的地址,至此,这个地址是对刚才新建的对象的引用,值类型已经装箱为一个引用类型。

C#编译器会自动生成对一个值类型的实例进行装箱所需的IL代码,但理解内部发生的事情仍然很有必要,这对于代码的性能调优问题有很大帮助。

         在上述代码中,C#编译器检测到是向一个需要引用类型的方法传递一个值类型,所以会自动生成代码来对对象进行装箱操作。这样一来,在运行时,当前存在于 Point值类型实例中的字段会复制到新分配的Point对象中。已装箱的Point对象(变成引用类型了)的地址在返回之后,会传递给Add方法。 Point对象会一直存在与堆中,知道被GC收集。Point值类型变量p可以重用,在这种情况下,已装箱值类型的生存期超过了未装箱的值类型的生存期。 未装箱的值类型的生存期在所处代码段执行完毕之后,就会被删除,但是已装箱值类型还一直处于托管堆中。执行装箱操作如图1:

 值类型的拆箱操作

 在知道装箱操作具体如何进行之后,接下来谈谈“拆箱”(unboxing)操作。假定需要使用如下代码获取ArrayList的第一个元素:

Point p = (Point)a[0];

在上述代码中,获取ArrayList的第一个元素总包含的引用,并试图将其放到Point值类型的一个实例p中。为了能够正常工作,包含在已装箱的Point对象中的所有字段都必须复制到值类型变量p中,后者在线程的堆栈上。CLR按照两个步骤完成这个拆箱操作。

  1. 首先,获取已装箱的Point对象中各个Point字段(如本例的 x,y)的地址(在托管堆中的偏移地址)。
  2. 然后,这些字段包含的值从堆中复制到基于堆栈的值类型实例中。

拆箱并不是刚好将装箱操作逆转过来这么简单,相较于装箱操作,拆箱操作的代价要低得多,拆箱其实就是一个获取指针的过 程。该指针指向包含在一个对象中的原始值类型(实例字段)。事实上,指针只想的是已装箱实例中的未装箱部分。所以,和装箱操作不同,拆箱不涉及任何字节在 内存中的副本。在拆箱操作之后,通常会进行字段的复制操作。不过我们经常都要在一次拆箱之后,立即复制它的字段。图2:

 

很明显,装箱和拆箱操作会对应用程序的速度和内存消耗产生不利的影响,所以应该注意编译器在什么时候生成代码来自动这些操作,并尝试手工编写代码,尽可能减少自动生成的这种代码

一个已装箱的值类型实例在拆箱时,应该注意以下几点:

  1. 包含对已装箱的值类型实例的引用如果为null,就会抛出一个NullReferenceException异常。
  2. 如果引用指向的对象不是所要求的值类型的一个已装箱实例,就会抛出一个InvalidCastException异常。

上述第二项意味着下面代码不会正常工作:

public static void Main()
{
      int x=5;
      Object o=x;                //对x进行装箱,o引用已装箱的对象
      Int16 y = (Int16)o;  // 抛出一个InvalidCastException异常
}

从逻辑上来说,完全可以获取o所引用的一个已装箱的Int32,然后将其转型为一个Int6。然而,在对一个对象进行拆箱操作时,只能将其转型为未装箱时的值类型,本例即为Int32。下面是正确的写法:

public static void Main()
{
      int x=5;
      Object o=x;                         //对x进行装箱,o引用已装箱的对象
      Int16 y = (Int16)(int)o;     //先拆箱为正确的类型,再进行转型
}

前面说过,在一次拆箱操作之后,经常要紧接着执行一次字段复制。以下C#代码演示了拆箱和复制操作:

public static void Main()
{
      Point p;
      p.x=p.y=1;
      object o=p;                //对p进行装箱,o引用已装箱的实例
      p = (Point)o;               //对o进行拆箱,并将字段从已装箱的实例复制到堆栈变量中
}

最后一行,C#编译器会生成一条IL执行对o进行拆箱操作(获取已装箱实例中的各个字段的地址),并生成另一条IL指令将这些字段从对上复制到给予堆栈的变量中。

再来看看另一段代码:

public static void Main()
{
       Point p;
       p.x=p.y=1;
       object o=p;                //对p进行装箱,o引用已装箱的实例
       p = (Point)o;               //对o进行拆箱,并将字段从已装箱的实例复制到堆栈变量中
       p.x=2;                          //更改堆栈变量的状态
       o=p;                             //对p进行装箱;o引用一个新的已装箱实例
}

上述代码地步的一行唯一的目的就是将Point的x字段从1变成2。为此,首先要执行一次拆箱操作,然后执行一次字段赋值操作,接着更改堆栈上的字段x,最后执行一次装箱操作,(从而在托管对上创建一个全新的已装箱实例)。如图3:

大家知道,上述代码总共发生了多少次装箱操作,多少次拆箱操作码?

答案:装箱3次,拆箱1次。

对于这个答案是否觉得很意外呢? 下面我们仔细分析下代码,看看具体都发生了什么事情。用ILDasm工具看看所生成的IL代码是什么,就可以解释上面的答案了。IL代码如下:

.method private hidebysig static void  Main(string[] args) cil managed
{
    //入口点
  .entrypoint
  // 代码大小       53 (0x35)
  .maxstack  3
    //初始化两个变量,一个是int32类型,一个object类型的变量
  .locals init ([0] int32 v,
           [1] object o)
  IL_0000:  nop
    //将5装入堆栈
  IL_0001:  ldc.i4.5
    //把一个从堆栈中返回的值存入第0个变量(上面初始化的)
  IL_0002:  stloc.0
    //把第0个变量转入堆栈
  IL_0003:  ldloc.0
    //对v进行装箱操作
  IL_0004:  box        [mscorlib]System.Int32
    //保存装箱后的值,存入到第1个变量
  IL_0009:  stloc.1
    //将123装入堆栈
  IL_000a:  ldc.i4.s   123
    //将堆栈中的值存入第0个变量
  IL_000c:  stloc.0
    //把第0个变量转入堆栈
  IL_000d:  ldloc.0
    // 对于v进行装箱,并将指针保留在堆栈上已进行concat操作
  IL_000e:  box        [mscorlib]System.Int32
    //将字符串加载到堆栈上执行concat操作
  IL_0013:  ldstr      ", "
    //对o进行拆箱,获取一个指针,指向堆栈上的int字段
  IL_0018:  ldloc.1
  IL_0019:  unbox.any  [mscorlib]System.Int32
    //对int进行装箱,并将指针保留在堆栈以进行concat操作
  IL_001e:  box        [mscorlib]System.Int32
    //调用静态方法Concat
  IL_0023:  call       string [mscorlib]System.String::Concat(object,
                                                              object,
                                                              object)
    //将Concat连接好后的字符串传送给WriteLine方法
  IL_0028:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_002d:  nop
  IL_002e:  call       int32 [mscorlib]System.Console::Read()
  IL_0033:  pop
    //从Main返回,程序执行完毕
  IL_0034:  ret
} // end of method Program::Main

下面对以上代码进行说明:

首先在堆栈上创建一个Int32未装箱值类型的实例(v),并将其初始化为5。然后,创建一个Object类型的变 量,o,并初始化它,让它指向v装箱后的值类型。C#编译器会自动生成正确的IL代码,如上面IL代码中的box操作。接着更改v的值,但这个操作不会影 响已装箱的值类型,装箱后的值依然是5,而不会变成123。

 接着要调用WriteLine方法,WriteLine 要求向其传递一个String对象。但是当前没有字符串对象。相反,当前有三个数据项可供使用:一个未装箱的Int32 值类型实例(v),一个String类型,以及一个已装箱Int32值类型实例的引用(o),他需要转型为一个未装箱的Int32。必须采取某种方式对这 些数据项进行合并,以创建一个String。

 为了创建一个String,C#编译器会生成代码来调用String对象的静态方法Concat。该方法有几个重载的 版本,所有版本执行的操作都是一样的,唯一的区别在于参数数量。由于需要连接三个数据项来创建一个字符串,所以编译器选择的是Concat方法的下面这个 版本:

public static String Concat(Object arg0, Object arg1, Object arg2);

参数一一对应:

arg0=v;             //装箱v,并将地址给arg0;
arg1=”, ”;                   // “,” 本身就是一个String
arg2=(Int32)o; //先对o拆箱成Int32,再装箱成Object

Concat方法调用指定的每个对象的ToString方法,并连接每个对象的字符串表示。最后,传递给WriteLine方法,以显示最终的结果。

如果像下面这样写,会具有更快的执行效率

Console.WriteLint(v +”, ”+o);  //显示“123, 5”

跟之前几乎一样,指示少了o的拆箱操作,以及拆箱完之后的装箱操作,这时,o已经是一个object引用类型,它的地址可以直接传给Concat方法。所以在移除了强制转型之后,就省掉了一次拆箱和一次装箱。

我们看下改写之后的IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       43 (0x2b)
  .maxstack  3
  .locals init ([0] int32 v,
           [1] object o)
  IL_0000:  nop
  IL_0001:  ldc.i4.5
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  box        [mscorlib]System.Int32
  IL_0009:  stloc.1
  IL_000a:  ldc.i4.s   123
  IL_000c:  stloc.0
  IL_000d:  ldloc.0
  IL_000e:  box        [mscorlib]System.Int32
  IL_0013:  ldstr      ", "
  IL_0018:  ldloc.1
  IL_0019:  call       string [mscorlib]System.String::Concat(object,
                                                              object,
                                                              object)
  IL_001e:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0023:  nop
  IL_0024:  call       int32 [mscorlib]System.Console::Read()
  IL_0029:  pop
  IL_002a:  ret
} // end of method Program::Main

对比下两段IL,效果很明显,代码大小从53减少到了43,少了一次拆箱和一次装箱,内存中还少了因为装箱而产生的一个对象。以上代码较少,运行起来不会明显感觉到变化,但是假如通过一个大得循环中,这样反复的操作,就会严重损耗性能。

还可以这样调用WriteLine,进一步提升代码的性能:

Console.WriteLint(v.ToString() +”, ”+o);  //显示“123, 5”

先在v上调用ToString方法,它返回一个String,就可以直接传递给Concat,不需要任何装箱操作。

接下来再看看另一个例子:

public static void Main()
{
    int v=5;        //创建一个未装箱的值类型变量
    object o=v;     //o引用v的已装箱版本
    v=123;          //将未装箱的值类型改成123
    Console.WriteLine(v);   //显示“123”
    v=(int)o;               //拆箱并将o复制到v
    Console.WriteLine(v);   //显示”5”
}

同样的问题:上面的操作发生了多少次装箱,多少次拆箱呢?

答案: 1次装箱,1次拆箱

是不是又很意外呢?其实主要是因为WriteLine()有很多重载,如下:

public static void WriteLine(Boolean);
public static void WriteLine(Char);
public static void WriteLine(Char[]);
public static void WriteLine(Int32);
public static void WriteLine(UInt32);
public static void WriteLine(Int64);
public static void WriteLine(Single);
public static void WriteLine(Double);
public static void WriteLine(Decimal);
public static void WriteLine(Object);
public static void WriteLine(String);

在上述两个WriteLine调用中,变量是以传值方式传递给方法的。现在WriteLine方法也许会在内部对这个Int32进行装箱,但无法对此加以控制,但是,我们已经从自己的代码中消除饿了装箱操作。

在调用一个方法时,假如它没有为传给它的一种特定的值类型准备一个重载版本,那么最终肯定会调用一个接受object参数的重载版本,那么势必会产生装箱操作,从而影响性能。

关于装箱的最后一点注意事项:假如知道自己写的代码会造成编译器反复对一个值类型进行装箱,那么换用手动方式对值类型进行装箱,代码会变得更小、更快。如下例子:

public class Program
{
      public static void Main(){
          int v=5;
          Console.WriteLine(“{0},{1},{2}”,v,v,v);      //三次装箱操作
          //Object o=v;                                //一次装箱
           //Console.WriteLine(“{0},{1},{2}”,o,o,o);
   }
}

未注释代码不止会装箱三次,而且会在堆上分配3个对象。如果执行注释行的代码,则只会产生一个对象,一次装箱。

只要想要对值类型的一个实例的引用,该实例就必须装箱。假如有一个值类型的实例,而且想把他传给需要引用类型的一个方法,就必须装箱。

值类型轻型的原因在于:

  • 他们不在托管堆上分配
  • 他们没有堆上的每个对象的额外成员

结束语

至此,我们分析了值类型实例在装箱和拆箱中CLR所做的事,平时写代码,多注意一点,能够减少很多不必要的内存消耗。

[转载]SQL Server中灾难时备份结尾日志(Tail of log)的两种方法

mikel阅读(900)

[转载]SQL Server中灾难时备份结尾日志(Tail of log)的两种方法 – CareySon – 博客园.

简介

在数据库数据文件因各种原因发生损坏时,如果日志文件没有损坏。可以通过备份结尾日志(Tail of log)使得数据库可以恢复到灾难发生时的状态。

例如:

grid.ai

上图中。在DB_1中做了完整备份,在Log_1,Log_2处做了日志备份。在Log_2备份之后不久,发生了故障。从Log_2备份到灾难发生时之间 的日志。就是结尾日志(Tail of log)。如果不能备份尾端日志,则数据库只能恢复到Log_2备份的点。尾端日志期间所做的改动全部丢失。更详细的概念可以查看我之前关于日志的博文。

下面我们分别来看在SQL Server实例运行良好和SQL Server实例崩溃状态下,备份结尾日志方法。

SQL Server实例运行正常时,结尾日志的备份

下面来模拟一次灾难下结尾日志的备份:

1

现在数据库TestBackUp有了一个完整备份和一个日志备份,而最后那条”日志备份后的测试数据”是在上次日志备份之后的,被结尾日志所包含。

接下来模拟数据库文件所在磁盘损坏(日志文件完好)

1.停掉Server SQL服务

2.删除数据库文件(MDF文件)

此时在SSMS中访问数据库TestBackUp会出现不可用:

2

此时,因为SQL Server实例可用,通过在T-SQL语句指定NO_TRUNCATE选项(必须指定,否则无法备份尾端日志),备份尾端日志:

3

依次进行完整备份恢复,和两次事务日志恢复,可以看到数据已经成功恢复到灾难点:

4

 

当SQL Server实例崩溃时,结尾日志的备份

此时由于各种原因,所处的SQL Server实例也崩溃,无法通过T-SQL来备份结尾日志。此时数据库文件损坏,而事务日志文件保持正确。

假设情况和上面例子一样,此时我手里有一个完整备份(TestBackUp_FULL.bak)和一个日志备份(TestBackUp_log1.bak),还有一个日志文件(ldf)。

这时我将这几个文件拷贝到其他拥有SQL Server实例的机器上。

新建一个和原数据库名一样的数据库。设置为脱机:

5

删除新建数据库的MDF文件。

将需要备份的数据库的日志文件替换掉原有的LDF文件。

此时直接备份结尾日志,成功:

6

原有Sql server实例恢复后一次恢复完整备份和两个日志备份。成功恢复到灾难发生点。

 

总结

我相信看到这篇文章的人都不希望碰到用到上面两种方法的情况。但是,墨菲定律(事情如果有变坏的可能,无论这种可能性有多小,它总会发生)是残酷的,事先练习一下总是比真正遇到情况用生产数据练习惬意的多:-)

[转载]建立完善的日期定义表

mikel阅读(881)

[转载]建立完善的日期定义表 – vvian – 博客园.

随着公司业务给规模越来越大,各级领导和销售部门、业务主管对各类业务数据报表的需求也越来越多,越来越复杂,针对这种情况,为了更好的处理好各类数据的简单分析,我建立了一张较为完善的日期定义表,现将过程与大家共同分享,还请大家提出不足和意见。

在这张表中,我将每一个日期划分出不同的年度、季度、月度、星期等内容,这样在对业务数据进行统计时,只要业务日期匹配,再来选择相应的统计时段就 可以很快的得到数据,而不需要在SQL语句中再进行year/month/day/quarter等计算,加快了运算速度。如果以此表为主表,与其他表进 行外接连时,还可以直观的显示出无销售收入的日期,避免日期跳行。同时,我还在表中加入了农历日期,方便进行传统农历时段的销售统计(春节、中秋等),而 且在开发前端程序的时候,可以直接引用表中的农历日期,不需要再找其他的算法。

首先,建一张基础数据表,内容如下:

CREATE TABLE [dbo].[date_def](
    [count_id] [int] IDENTITY(1,1) NOT NULL,
    [count_date] [datetime] NOT NULL,
    [count_year] [smallint] NULL,
    [count_quarter] [tinyint] NULL,
    [count_month] [tinyint] NULL,
    [count_week] [tinyint] NULL,
    [count_weekday] [tinyint] NULL,
    [count_day] [smallint] NULL,
    [chinese_year] [varchar](10) NULL,
    [lunar_year] [varchar](10) NULL,
    [lunar_date] [varchar](10) NULL,
    [work_day] [bit] NULL,
 CONSTRAINT [PK_date_def] PRIMARY KEY CLUSTERED
(
    [count_id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE UNIQUE INDEX Idx_date_def ON date_def(count_date)

接下来,在基础数据表中插入我们需要的日期值,我们先插入从1999年到2012年的日期数据。

declare @startdate datetime,@enddate datetime
set @startdate='1999-01-01'
set @enddate='2003-12-31'

insert into date_def (count_date)
select convert(varchar(10),dateadd(day,number,@startdate),120)
from
    master..spt_values
where
    datediff(day,dateadd(day,number,@startdate), @enddate)>=0
    and number>=0
    and type='p'

大家可能会发现一个问题,我上次说的是插入的是从1999年到2012年的数据,但代码只是从1999到2003年,实际上,我们插入日期的时候, 利用了master..spt_values表中的顺序数,但这个表中只有2048个顺序号,因此我们只能每5年一个单位的进行插入,从1999到 2003,然后2004到2008,最后2009到2012,完成插入的过程。

下面这一步就很简单了,将每一个日期值的年度、季度、月份、星期、星期几、第几天这些信息分离出来。此处要注意的一定是,西方人习惯于星期天做为一 周的第一天,而中国更习惯以星期一来做为一周的开始,因此就需要调整一个参数。如果你不需要调整这个参数的话,就要注意下面的工作日的设定了。

-- 设定星期一为每周的第一天,默认为星期天为每周第一天
SET   DATEFIRST   1

update date_def
set count_year = year(count_date),count_quarter = datepart(qq,count_date),
count_month = month(count_date),count_week = datepart(week,count_date),
count_day = datepart(dy,count_date),count_weekday = datepart(weekday,count_date)
-- 默认除周六周日外都是工作日,如你不需要修改每周第一天,此处就要修改
update date_def
set work_day = case when count_weekday between 6 and 7 then 0 else 1 end
实际上这只是一个很简单的工作日设定,每年底还要根据国务院发布的次年休假时间表来确认第二年的工作日情况,
有个默认值也是为了方便其他不需要修改的地方。点击下载1999年到2012年的真实工作日的数据

下一步就是填充三个农历字段的内容了,一些代码是从网上找到少许修改的,在此要谢谢提供代码的各位前辈了。

先建立一张农历的基础数据表:

 CREATE TABLE SolarData (
  yearid decimal(16, 0),
  data char(7),
  dataint decimal(16, 0));
INSERT INTO SolarData VALUES (1900, '0x04bd8', 19416);
INSERT INTO SolarData VALUES (1901, '0x04ae0', 19168);
INSERT INTO SolarData VALUES (1902, '0x0a570', 42352);
INSERT INTO SolarData VALUES (1903, '0x054d5', 21717);
INSERT INTO SolarData VALUES (1904, '0x0d260', 53856);
INSERT INTO SolarData VALUES (1905, '0x0d950', 55632);
INSERT INTO SolarData VALUES (1906, '0x16554', 91476);
INSERT INTO SolarData VALUES (1907, '0x056a0', 22176);
INSERT INTO SolarData VALUES (1908, '0x09ad0', 39632);
INSERT INTO SolarData VALUES (1909, '0x055d2', 21970);
INSERT INTO SolarData VALUES (1910, '0x04ae0', 19168);
INSERT INTO SolarData VALUES (1911, '0x0a5b6', 42422);
INSERT INTO SolarData VALUES (1912, '0x0a4d0', 42192);
INSERT INTO SolarData VALUES (1913, '0x0d250', 53840);
INSERT INTO SolarData VALUES (1914, '0x1d255', 119381);
INSERT INTO SolarData VALUES (1915, '0x0b540', 46400);
INSERT INTO SolarData VALUES (1916, '0x0d6a0', 54944);
INSERT INTO SolarData VALUES (1917, '0x0ada2', 44450);
INSERT INTO SolarData VALUES (1918, '0x095b0', 38320);
INSERT INTO SolarData VALUES (1919, '0x14977', 84343);
INSERT INTO SolarData VALUES (1920, '0x04970', 18800);
INSERT INTO SolarData VALUES (1921, '0x0a4b0', 42160);
INSERT INTO SolarData VALUES (1922, '0x0b4b5', 46261);
INSERT INTO SolarData VALUES (1923, '0x06a50', 27216);
INSERT INTO SolarData VALUES (1924, '0x06d40', 27968);
INSERT INTO SolarData VALUES (1925, '0x1ab54', 109396);
INSERT INTO SolarData VALUES (1926, '0x02b60', 11104);
INSERT INTO SolarData VALUES (1927, '0x09570', 38256);
INSERT INTO SolarData VALUES (1928, '0x052f2', 21234);
INSERT INTO SolarData VALUES (1929, '0x04970', 18800);
INSERT INTO SolarData VALUES (1930, '0x06566', 25958);
INSERT INTO SolarData VALUES (1931, '0x0d4a0', 54432);
INSERT INTO SolarData VALUES (1932, '0x0ea50', 59984);
INSERT INTO SolarData VALUES (1933, '0x06e95', 28309);
INSERT INTO SolarData VALUES (1934, '0x05ad0', 23248);
INSERT INTO SolarData VALUES (1935, '0x02b60', 11104);
INSERT INTO SolarData VALUES (1936, '0x186e3', 100067);
INSERT INTO SolarData VALUES (1937, '0x092e0', 37600);
INSERT INTO SolarData VALUES (1938, '0x1c8d7', 116951);
INSERT INTO SolarData VALUES (1939, '0x0c950', 51536);
INSERT INTO SolarData VALUES (1940, '0x0d4a0', 54432);
INSERT INTO SolarData VALUES (1941, '0x1d8a6', 120998);
INSERT INTO SolarData VALUES (1942, '0x0b550', 46416);
INSERT INTO SolarData VALUES (1943, '0x056a0', 22176);
INSERT INTO SolarData VALUES (1944, '0x1a5b4', 107956);
INSERT INTO SolarData VALUES (1945, '0x025d0', 9680);
INSERT INTO SolarData VALUES (1946, '0x092d0', 37584);
INSERT INTO SolarData VALUES (1947, '0x0d2b2', 53938);
INSERT INTO SolarData VALUES (1948, '0x0a950', 43344);
INSERT INTO SolarData VALUES (1949, '0x0b557', 46423);
INSERT INTO SolarData VALUES (1950, '0x06ca0', 27808);
INSERT INTO SolarData VALUES (1951, '0x0b550', 46416);
INSERT INTO SolarData VALUES (1952, '0x15355', 86869);
INSERT INTO SolarData VALUES (1953, '0x04da0', 19872);
INSERT INTO SolarData VALUES (1954, '0x0a5d0', 42448);
INSERT INTO SolarData VALUES (1955, '0x14573', 83315);
INSERT INTO SolarData VALUES (1956, '0x052d0', 21200);
INSERT INTO SolarData VALUES (1957, '0x0a9a8', 43432);
INSERT INTO SolarData VALUES (1958, '0x0e950', 59728);
INSERT INTO SolarData VALUES (1959, '0x06aa0', 27296);
INSERT INTO SolarData VALUES (1960, '0x0aea6', 44710);
INSERT INTO SolarData VALUES (1961, '0x0ab50', 43856);
INSERT INTO SolarData VALUES (1962, '0x04b60', 19296);
INSERT INTO SolarData VALUES (1963, '0x0aae4', 43748);
INSERT INTO SolarData VALUES (1964, '0x0a570', 42352);
INSERT INTO SolarData VALUES (1965, '0x05260', 21088);
INSERT INTO SolarData VALUES (1966, '0x0f263', 62051);
INSERT INTO SolarData VALUES (1967, '0x0d950', 55632);
INSERT INTO SolarData VALUES (1968, '0x05b57', 23383);
INSERT INTO SolarData VALUES (1969, '0x056a0', 22176);
INSERT INTO SolarData VALUES (1970, '0x096d0', 38608);
INSERT INTO SolarData VALUES (1971, '0x04dd5', 19925);
INSERT INTO SolarData VALUES (1972, '0x04ad0', 19152);
INSERT INTO SolarData VALUES (1973, '0x0a4d0', 42192);
INSERT INTO SolarData VALUES (1974, '0x0d4d4', 54484);
INSERT INTO SolarData VALUES (1975, '0x0d250', 53840);
INSERT INTO SolarData VALUES (1976, '0x0d558', 54616);
INSERT INTO SolarData VALUES (1977, '0x0b540', 46400);
INSERT INTO SolarData VALUES (1978, '0x0b5a0', 46496);
INSERT INTO SolarData VALUES (1979, '0x195a6', 103846);
INSERT INTO SolarData VALUES (1980, '0x095b0', 38320);
INSERT INTO SolarData VALUES (1981, '0x049b0', 18864);
INSERT INTO SolarData VALUES (1982, '0x0a974', 43380);
INSERT INTO SolarData VALUES (1983, '0x0a4b0', 42160);
INSERT INTO SolarData VALUES (1984, '0x0b27a', 45690);
INSERT INTO SolarData VALUES (1985, '0x06a50', 27216);
INSERT INTO SolarData VALUES (1986, '0x06d40', 27968);
INSERT INTO SolarData VALUES (1987, '0x0af46', 44870);
INSERT INTO SolarData VALUES (1988, '0x0ab60', 43872);
INSERT INTO SolarData VALUES (1989, '0x09570', 38256);
INSERT INTO SolarData VALUES (1990, '0x04af5', 19189);
INSERT INTO SolarData VALUES (1991, '0x04970', 18800);
INSERT INTO SolarData VALUES (1992, '0x064b0', 25776);
INSERT INTO SolarData VALUES (1993, '0x074a3', 29859);
INSERT INTO SolarData VALUES (1994, '0x0ea50', 59984);
INSERT INTO SolarData VALUES (1995, '0x06b58', 27480);
INSERT INTO SolarData VALUES (1996, '0x055c0', 21952);
INSERT INTO SolarData VALUES (1997, '0x0ab60', 43872);
INSERT INTO SolarData VALUES (1998, '0x096d5', 38613);
INSERT INTO SolarData VALUES (1999, '0x092e0', 37600);
INSERT INTO SolarData VALUES (2000, '0x0c960', 51552);
INSERT INTO SolarData VALUES (2001, '0x0d954', 55636);
INSERT INTO SolarData VALUES (2002, '0x0d4a0', 54432);
INSERT INTO SolarData VALUES (2003, '0x0da50', 55888);
INSERT INTO SolarData VALUES (2004, '0x07552', 30034);
INSERT INTO SolarData VALUES (2005, '0x056a0', 22176);
INSERT INTO SolarData VALUES (2006, '0x0abb7', 43959);
INSERT INTO SolarData VALUES (2007, '0x025d0', 9680);
INSERT INTO SolarData VALUES (2008, '0x092d0', 37584);
INSERT INTO SolarData VALUES (2009, '0x0cab5', 51893);
INSERT INTO SolarData VALUES (2010, '0x0a950', 43344);
INSERT INTO SolarData VALUES (2011, '0x0b4a0', 46240);
INSERT INTO SolarData VALUES (2012, '0x0baa4', 47780);
INSERT INTO SolarData VALUES (2013, '0x0ad50', 44368);
INSERT INTO SolarData VALUES (2014, '0x055d9', 21977);
INSERT INTO SolarData VALUES (2015, '0x04ba0', 19360);
INSERT INTO SolarData VALUES (2016, '0x0a5b0', 42416);
INSERT INTO SolarData VALUES (2017, '0x15176', 86390);
INSERT INTO SolarData VALUES (2018, '0x052b0', 21168);
INSERT INTO SolarData VALUES (2019, '0x0a930', 43312);
INSERT INTO SolarData VALUES (2020, '0x07954', 31060);
INSERT INTO SolarData VALUES (2021, '0x06aa0', 27296);
INSERT INTO SolarData VALUES (2022, '0x0ad50', 44368);
INSERT INTO SolarData VALUES (2023, '0x05b52', 23378);
INSERT INTO SolarData VALUES (2024, '0x04b60', 19296);
INSERT INTO SolarData VALUES (2025, '0x0a6e6', 42726);
INSERT INTO SolarData VALUES (2026, '0x0a4e0', 42208);
INSERT INTO SolarData VALUES (2027, '0x0d260', 53856);
INSERT INTO SolarData VALUES (2028, '0x0ea65', 60005);
INSERT INTO SolarData VALUES (2029, '0x0d530', 54576);
INSERT INTO SolarData VALUES (2030, '0x05aa0', 23200);
INSERT INTO SolarData VALUES (2031, '0x076a3', 30371);
INSERT INTO SolarData VALUES (2032, '0x096d0', 38608);
INSERT INTO SolarData VALUES (2033, '0x04bd7', 19415);
INSERT INTO SolarData VALUES (2034, '0x04ad0', 19152);
INSERT INTO SolarData VALUES (2035, '0x0a4d0', 42192);
INSERT INTO SolarData VALUES (2036, '0x1d0b6', 118966);
INSERT INTO SolarData VALUES (2037, '0x0d250', 53840);
INSERT INTO SolarData VALUES (2038, '0x0d520', 54560);
INSERT INTO SolarData VALUES (2039, '0x0dd45', 56645);
INSERT INTO SolarData VALUES (2040, '0x0b5a0', 46496);
INSERT INTO SolarData VALUES (2041, '0x056d0', 22224);
INSERT INTO SolarData VALUES (2042, '0x055b2', 21938);
INSERT INTO SolarData VALUES (2043, '0x049b0', 18864);
INSERT INTO SolarData VALUES (2044, '0x0a577', 42359);
INSERT INTO SolarData VALUES (2045, '0x0a4b0', 42160);
INSERT INTO SolarData VALUES (2046, '0x0aa50', 43600);
INSERT INTO SolarData VALUES (2047, '0x1b255', 111189);
INSERT INTO SolarData VALUES (2048, '0x06d20', 27936);
INSERT INTO SolarData VALUES (2049, '0x0ada0', 44448);
然后加入一个函数,农历日期就用这个函数来求出,对于原始的代码,我修改了一个地方,把“十一月”、“十二月”分别改成了“冬月”和“腊月”,对齐一点,好看,哈哈。
-- 功能:计算阳历1900/01/31 - 2050/01/22间某一天对应的阴历是多少
-- 算法:在一张表中用10进制格式保存某个农历年每月大小,有无闰月,闰月大小信息
-- 1.用12个2进制位来表示某个农历年每月的大小,大月记为1,否则为0
-- 2.用低4位来表示闰月的月份,没有闰月记为0
-- 3.用一个高位表示闰月的大小,闰月大记为0,闰月小或无闰月记为0
-- 4.再将该2进制数转化为10进制,存入表中
-- 农历2000年: 0 110010010110 0000 -> 0x0c960 -> 51552
-- 农历2001年: 0 110110010101 0100 -> 0x0d954 -> 55636
-- 采用查表的方式计算出农历日期
-- 作者:Angel_XJW

create function dbo.fn_cnyear(@solarday datetime)
returns nvarchar(30)
as
begin
  declare @soldata int
  declare @offset int
  declare @ilunar int
  declare @i int
  declare @j int
  declare @ydays int
  declare @mdays int
  declare @mleap int
  declare @mleap1 int
  declare @mleapnum int
  declare @bleap smallint
  declare @temp int
  declare @year nvarchar(10)
  declare @month nvarchar(10)
  declare @day nvarchar(10)
  declare @chinesenum nvarchar(10)
  declare @outputdate nvarchar(30)
  set @offset=datediff(day,'1900-01-30',@solarday)
  --确定农历年开始
  set @i=1900
  --set @offset=@soldata
  while @i<2050 and @offset>0
  begin
    set @ydays=348
    set @mleapnum=0
    select @ilunar=dataint from solardata where yearid=@i      

    --传回农历年的总天数
    set @j=32768
    while @j>8
    begin
      if @ilunar & @j >0
        set @ydays=@ydays+1
      set @j=@j/2
    end
    --传回农历年闰哪个月 1-12 , 没闰传回 0
    set @mleap = @ilunar & 15
    --传回农历年闰月的天数 ,加在年的总天数上
    if @mleap > 0
    begin
      if @ilunar & 65536 > 0
        set @mleapnum=30
      else
        set @mleapnum=29
      set @ydays=@ydays+@mleapnum
    end
    set @offset=@offset-@ydays
    set @i=@i+1
  end
  if @offset <= 0
  begin
    set @offset=@offset+@ydays
    set @i=@i-1
  end
  --确定农历年结束
  set @year=@i
  --确定农历月开始
  set @i = 1
  select @ilunar=dataint from solardata where yearid=@year
  --判断那个月是润月
  set @mleap = @ilunar & 15
  set @bleap = 0
  while @i < 13 and @offset > 0
  begin
    --判断润月
    set @mdays=0
    if (@mleap > 0 and @i = (@mleap+1) and @bleap=0)
    begin--是润月
      set @i=@i-1
      set @bleap=1
      set @mleap1= @mleap
      --传回农历年闰月的天数
      if @ilunar & 65536 > 0
        set @mdays = 30
      else
        set @mdays = 29
    end
    else
    --不是润月
    begin
      set @j=1
      set @temp = 65536
      while @j<=@i
      begin
        set @temp=@temp/2
        set @j=@j+1
      end      

      if @ilunar & @temp > 0
        set @mdays = 30
      else
        set @mdays = 29
    end      

    --解除润月
    if @bleap=1 and @i= (@mleap+1)
      set @bleap=0    

    set @offset=@offset-@mdays
    set @i=@i+1
  end      

  if @offset <= 0
  begin
    set @offset=@offset+@mdays
    set @i=@i-1
  end      

  --确定农历月结束
  set @month=@i    

  --确定农历日结束
  set @day=ltrim(@offset)
  --输出日期
  set @chinesenum= N'〇一二三四五六七八九十'
  while len(@year)>0
  select @outputdate=isnull(@outputdate,'')
         + substring(@chinesenum,left(@year,1)+1,1)
         , @year=stuff(@year,1,1,'')
  set @outputdate=@outputdate+ N'年'
         + case @mleap1 when @month then N'润' else '' end
  if cast(@month as int)<10
    set @outputdate=@outputdate
         + case @month when 1 then N'正'
             else substring(@chinesenum,left(@month,1)+1,1)
           end
  else if cast(@month as int)>=10
    set @outputdate=@outputdate
         + case @month when '10' then N'十' when 11 then N'冬'
           else N'腊' end
  set @outputdate=@outputdate + N'月'
  if cast(@day as int)<10
    set @outputdate=@outputdate + N'初'
         + substring(@chinesenum,left(@day,1)+1,1)
  else if @day between '10' and '19'
    set @outputdate=@outputdate
         + case @day when '10' then N'初十' else N'十'+
           substring(@chinesenum,right(@day,1)+1,1) end
  else if @day between '20' and '29'
    set @outputdate=@outputdate
         + case @day when '20' then N'二十' else N'廿' end
         + case @day when '20' then N'' else
           substring(@chinesenum,right(@day,1)+1,1) end
  else
    set @outputdate=@outputdate+N'三十'
  return @outputdate
END
go

现在我们就可以利用这个函数填充二个农历字段了,分别是chinese_year和lunar_date,因为lunar_year我是准备留用生肖年份(天干地支)用的。
-- 计算农历年份、日期
update date_def
set chinese_year = substring(dbo.fn_cnyear(count_date),1,4),
lunar_date = substring(dbo.fn_cnyear(count_date),6,4)

这一步是最慢的,我的服务器上跑了1分15秒才出来,可能是因为每一个日期都要进行二次函数计算吧,这也是为什么我们需要一张日期定义表的原因了。 最后一步,填充生肖年份,这个代码好像没有现成的,我自己google了一下,写了个小算法,正确是正确了,但可能效率不怎么样,大家就将就点看吧,希望 大家提出更好的方法。

create table #temp1
(flag char(1),
ys smallint,
tgdz varchar(2)
)
insert into #temp1
select 'T',4,'甲'
union all select 'T',5,'乙'
union all select 'T',6,'丙'
union all select 'T',7,'丁'
union all select 'T',8,'戊'
union all select 'T',9,'已'
union all select 'T',0,'庚'
union all select 'T',1,'辛'
union all select 'T',2,'壬'
union all select 'T',3,'癸'
union all select 'D',4,'子'
union all select 'D',5,'丑'
union all select 'D',6,'寅'
union all select 'D',7,'卯'
union all select 'D',8,'辰'
union all select 'D',9,'巳'
union all select 'D',10,'午'
union all select 'D',11,'未'
union all select 'D',0,'申'
union all select 'D',1,'酉'
union all select 'D',2,'戍'
union all select 'D',3,'亥'

declare @year_begin int 
declare @year_end int
declare @lunar_newyear datetime
select @year_begin = min(count_year) from date_def
select @year_end = max(count_year) from date_def

while @year_end >= @year_begin
begin
  select @lunar_newyear = count_date 
   from date_def
   where count_year = @year_begin
    and lunar_date = '正月初一'

  update date_def set lunar_year = b.tgdz+c.tgdz+'年'
  from #temp1 b,#temp1 c
  where count_year%12 = c.ys
  and c.flag = 'D'
  and count_year%10 = b.ys
  and b.flag = 'T'
  and count_year = @year_begin
  and count_date >= @lunar_newyear

  update date_def set lunar_year = b.tgdz+c.tgdz+'年'
  from #temp1 b,#temp1 c  
  where (count_year-1)%12 = c.ys
  and c.flag = 'D'
  and (count_year-1)%10 = b.ys
  and b.flag = 'T'
  and count_year = @year_begin
  and count_date < @lunar_newyear  

  set @year_begin = @year_begin + 1
end
drop table #temp1
注意了,农历的年份和公历可不一定相同的,公历2012年1月9日对应的农历可是二〇一一年辛卯年腊月十六,2012年2月9日对应的农历才是二〇一二年壬辰年正月十八。
好了,这个日期定义表到这里除了需要每年手工修改一次的工作日定义外,就已经全部完工了。我们就可以在以后的日子里,利用这张表去统计一大堆业务部门喜闻乐见,但不知道他们会不会真正用的上的数据吧。

[原创]KiWing框架中存储过程Datetime返回类型的问题

mikel阅读(1049)

最近项目中出现了,通过存储过程查询出来的实体类的Datetime类型的属性没有获得到值,但是执行存储过程有返回值,原存储过程如下:

 

ALTER Procedure [dbo].[Client_TrackMonthSelect]
@PageSize int=0,
@CurrentPage int=1,
@Identifier int=NULL,
@Tracker nvarchar(200)=NULL,
@Create_Date Datetime=NULL,
@StartDate Datetime=null,
@EndDate Datetime=null,
@TrackNum int=null
AS
select identity(int,1,1) as Identifier,convert(char(10),Create_Date,120) as Create_Date,
COUNT(DISTINCT Client_ID) as TrackNum 
into #temp_tails
from Client_TrackRecord as t
where 
(@Tracker is NULL OR Tracker=@Tracker) AND
(convert(char(10),Create_Date,120) between DATEADD(MONTH,DATEDIFF(MONTH,0,@StartDate),0) and DATEADD(DAY,-1,DATEADD(MONTH,1+DATEDIFF(MONTH,0,@StartDate),0)))
group by convert(char(10),Create_Date,120)

select identifier,Create_Date,@Tracker as Tracker,TrackNum,@StartDate as StartDate,@EndDate as EndDate,0 as totalPage,0 as SumCount
from #temp_tails order by Create_Date desc

问题原因是因为已经用Convert将表中的Datetimne转换成了char类型导致直接返回Create_Date是char类型和实体类中声明的datetime的值不一样,于是需要转换下类型为datetime,修改后的存储过程如下:

ALTER Procedure [dbo].[Client_TrackMonthSelect]
@PageSize int=0,
@CurrentPage int=1,
@Identifier int=NULL,
@Tracker nvarchar(200)=NULL,
@Create_Date Datetime=NULL,
@StartDate Datetime=null,
@EndDate Datetime=null,
@TrackNum int=null
AS
select identity(int,1,1) as Identifier,convert(char(10),Create_Date,120) as Create_Date,
COUNT(DISTINCT Client_ID) as TrackNum 
into #temp_tails
from Client_TrackRecord as t
where 
(@Tracker is NULL OR Tracker=@Tracker) AND
(convert(char(10),Create_Date,120) between DATEADD(MONTH,DATEDIFF(MONTH,0,@StartDate),0) and DATEADD(DAY,-1,DATEADD(MONTH,1+DATEDIFF(MONTH,0,@StartDate),0)))
group by convert(char(10),Create_Date,120)

select identifier,CAST(Create_Date as DateTime) as Create_Date,@Tracker as Tracker,TrackNum,@StartDate as StartDate,@EndDate as EndDate,0 as totalPage,0 as SumCount
from #temp_tails order by Create_Date desc

[转载]突破限制开发Windows Phone手电筒(三种方案)

mikel阅读(950)

[转载]突破限制开发Windows Phone手电筒(三种方案) – 豆浆咖啡 – 博客园.

现在的Windows Phone开发实在是太封闭了,很多功能都无法实现,只有三星、LG、戴尔、Nokia这些厂家能利用Native代码开发应用,目前就HTC开发了手电 筒软件发布到了微软市场,但是普通开发者利用native代码就无法提交大市场,当然有很多聪明的开发者不用LED相关的API照样开发出了手电筒应用, 下面来讨论几种常用方法。

一、在应用中创建了一个死循环,无限调用摄像头的Focus()方法,即自动对焦,而摄像头处于对焦状态时LED是亮着的,间接实现了手电筒功能。

优点:能发布到微软市场.

缺点:对摄像头损伤大

二、人类的脑子是无比强大的,有人发现在windows phone有一个Microsoft.Phone.Media.Extended.dll,通过他能调用手机的摄像头,通过录像让闪光灯开启。但是打开 vs2010发现根本没有Microsoft.Phone.Media.Extended这个程序集。顺藤摸瓜找到了Den Delimarsky写的两篇文章Not your regular photo and video camera on Windows Phone 7和How VideoCamera/PhotoCamera content is saved on Windows Phone 7,前一篇介绍了如何使用PhotoCamera和VideoCamera进行摄像头图像实时显示,后一篇则是介绍怎么取到拍照后的数据,写得很棒,通俗易懂。文章中提到:

通过反射调用摄像头比较慢而且增加多余的代码。所以我转而使用GAC程序集 “GAC_Microsoft.Phone.Media.Extended_v7_0_0_0_cneutral_1.dll”,由于是托管代码,所以就 不用加上WMInteropManifest.xml文件(或者<Capability Name=”ID_CAP_INTEROPSERVICES”/>)了。

并且提供了GAC Dump(可以看作未开放API程序集的集合)下载。不过没有详细教程,于是搜了一下实现GAC Dump的牛人Thomas Hounsell的Blog,找到了方法 Avoiding Reflection: Adding the InteropServices library to the WP7 SDK。总结起来很简单:

首先下载Hounsell那篇文章里面的7z文件。

捡自己需要的放到C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone,记得改 名,把前后下划线前的内容都去掉。比如对于原生摄像头,我们需要Microsoft.Phone.Media.Extended.dll。

打开RedistList文件夹里面的FrameworkList.xml文件,记得用管理员权限。新增一个File标签(复制之前的就行),程序集名改成你刚刚加入的那个dll的,去掉publicKeyToken属性。保存。

当然还必须执行sn.exe -Vr [dll path]对DLL进行签名.这样就可以使用Microsoft.Phone.Media.Extended.dll了。

优点:对摄像头损伤小

缺点:原来是可以提交到市场,但是现在微软发现了,此类调用已经无法提交到市场了。

 

三、俗话说的好你有张良记,我有过墙梯,其实Microsoft.Phone.Media.Extended.dll在windows phone全局缓存中,可以通过反射来调用此程序集,当然这样的速度稍微慢点,不过是可以接受的。

优点:可以提交到市场,对摄像头损伤小

缺点:速度稍微慢点。

[转载]Android上利用Facebook SDK的开发—上传图文到涂鸦墙

mikel阅读(993)

[转载]Android上利用Facebook SDK的开发——上传图文到涂鸦墙 – lingyun1120 – 博客园.

由于众所周知的原因,国内不能上脸书,但是由于公司产品需要在M国发布,所以在Facebook的共享功能成了我最近的主要任务。写下这篇文章,希望帮助到大家。

 

一、如何登录到的Facebook方便开发?

 

登录FB当然是进行翻墙,在进行开发之前,我先介绍一个比较好的工具——VPN。这个东西的强大之处是无论PC,还是手机都可以十分方便地进行翻墙。国内较好的VPN是greenvpn,它的地址是:https://www.green002.com/index.php?option=com_user&task=register&affid=98670。它的优势有免费的VPN可以使用,而且速度不错,但是由于免费的,每20分钟就会需要重新登录VPN一次。注册之后里面有详细的教程(PC有客户端,Android手机有设置教程),这次不再赘述,开始进入正题。

 

二、Facebook API简介

 

在介绍 Facebook SDK for Android 之前,有必要了解一下 Facebook Platform 及其 API。据 Facebook 声称,Facebook Platform 允许任何人 “在 Facebook 和 Web 上构建社交应用程序”。为了允许您构建此类应用程序,Facebook 提供广泛的一组核心且高级的 API 和 SDK。
核心 Facebook Platform API 是 Graph API,它允许您从 Facebook 读写数据。Facebook 也具有 Old Rest API。新的 Graph API 将 API 范型从面向方法的                        从 Facebook 读写数据的方式更改为一种新的方式,即使用对象(比如说用户简介、好友、帖子、照片,诸如此类)及其相互之间的关系或连接。该方法简化了 Facebook API,使之处理对象时更加一致。注意,尽管 Graph API 是首选的 Facebook API,但是 Old REST API 仍然是活跃且受支持的。Graph 和 REST API 都适用于移动应用程序(包括原生和移动 web 应用程序),它们通过使用 WebViews 在原生应用程序中包含移动 web 内容。
Graph API 对象被分配一个惟一的 ID,很容易使用一个 URL 访问它,此 URL 可被进一步限定,以寻址一个特定的对象/连接。对象 URL 的一般结构类似如下: https://graph.facebook.com/OBJECT_ID/CONNECTION_TYPE,其中 OBJECT_ID 是对象的惟一 ID,CONNECTION_TYPE 是对象支持的一种连接类型。例如,一个页面支持以下连接:feed/wall、photos、notes、posts、members,等等。
利用 Graph API,您可以检索对象,删除对象和发布对象。您可以搜索、更新对象、过滤结果,甚至动态地发现对象的连接/关系。
默认情况下,应用程序对用户的公共数据具有访问权限。要访问私有数据,应用程序必须首先请求用户的权限(被称之为扩展权限)。Facebook 定义了大量权限,您可以在 Extended Permissions 页面了解它们。

 

三、导入Facebook SDK 

 

  把facebook sdk所提供的工程导入到Eclipse了,所以要让我们的工程拥有与facebook沟通的能力,就必须将该工程以library的方式参考到新建工程 当中,在工程名称上点击右键,选择”Properties”,选择第二项”Android”,并於library框中利用”Add”加进 com_facebook_android该工程。

 

  

 


四、Facebook的登录与发布文字消息

 

 1 //这里的APPID是你在Facebook申请的APPKEY
 2 private Facebook mFacebook = new Facebook(FACEBOOK_APP_ID);
 3 private static final String[] PERMISSIONS = new String[] {
 4 "publish_stream", "read_stream", "offline_access","user_photos" };
 5 
 6 
 7 AsyncFacebookRunner mAsyncRunner = new AsyncFacebookRunner(mFacebook);
 8                         mFacebook.authorize(AAuthTestActivity.this,
 9                                 PERMISSIONS, new LoginDialogListener());
10 
11 
12 //我是在完成登录之后马上就发布文字消息,这点大家可以灵活修改
13 private final class LoginDialogListener implements
14             com.facebook.android.Facebook.DialogListener {
15         public void onComplete(Bundle values) {
16             Log.e("FB Sample App", "LoginDialogListener.onComplete()");
17             refreshListView();
18             postToWall(values.getString(Facebook.TOKEN));
19         }
20 
21         public void onFacebookError(FacebookError error) {
22             Log.d("FB Sample App", "LoginDialogListener.onFacebookError()");
23         }
24 
25         public void onError(DialogError error) {
26             Log.d("FB Sample App", "LoginDialogListener.onError()");
27         }
28 
29         public void onCancel() {
30             Log.d("FB Sample App", "LoginDialogListener.onCancel()");
31         }
32 
33         private void postToWall(String string) {
34             Bundle params = new Bundle();
35             params.putString(Facebook.TOKEN, accessToken);
36             try {
37                 params.putString("message", "Test!!!!");
38                 mFacebook.request("me/feed", params, "POST");
39             } catch (FileNotFoundException e) {
40                 e.printStackTrace();
41             } catch (MalformedURLException e) {
42                 e.printStackTrace();
43             } catch (IOException e) {
44                 e.printStackTrace();
45             }
46         }
47     }

 

  这里需要注意的是:PERMISSIONS 字符串,这里是在登录授权时把哪些权限进行了说明,其中比如publish_stream为是否可以发布消息到涂鸦墙,read_stream为读取涂鸦墙,user_photos为图片相关的权限。(官方文件的詳細權限列表)

 

  五、 发布图片到涂鸦墙(Wall)

 

  这是困扰我最久的问题。传送图片都不是问题,主要是如何把照片发布到涂鸦墙上去,如同新浪微博一样——发送带图的微博,我们需要的是如何发送带图的文字信息到涂鸦墙上去。这里简述一下上传图片几个方法以及我最终的解决方法,主要是参考了网上的几个帖子。

 

  我想要实现的效果:

 

  

 

  效果应该是:1这儿的文字消息用来告诉朋友我的这个软件在哪儿可以找到。

 

        2这儿的图片就是展示这个软件最后做到了怎样一个效果,用来吸引其他的用户。

 

  第一种:

 

1 Bundle params = new Bundle();
2 params.putString("message", "Uploaded on!!!!!!");
3 params.putByteArray("picture", bytes); //bytes contains photo bytes, no problem here
4 asyncRunner.request("me/photos", params, "POST", new PostPhotoRequestListener(), null);

 

  产生的效果:我只是把图片发送到了自己的相册,涂鸦墙上也显示了“某某把若干图片加入到XX相册”。但我想分享的文字消息在涂鸦墙上看不到,这就大大削弱了分享的效果。

 

  第二种:

 

1 Bundle params = new Bundle();
2 params.putString("message", "Uploaded on " + now());                
3 params.putString("link", "http://i1114.photobucket.com/albums/k538/tom_rada/bota2.jpg");
4 asyncRunner.request("me/feed", params, "POST", new PostPhotoRequestListener(), null);

 

  产生的效果:可以在涂鸦墙看到文字与图片,可是由于项目希望是本地上传显示,而不是利用连接显示某一图片,所以这个方案放弃。

 

  第三种:

 

1 Bundle params = new Bundle();
2 params.putString("message", "Uploaded on " + now());                
3 params.putByteArray("picture", bytes); //bytes contains photo bytes, no problem here
4 asyncRunner.request("me/feed", params, "POST", new PostPhotoRequestListener(), null);

 

  产生的效果:利用me/feed接口其实只能展示文字,所以图片完全不能上传,失败

 

  解决办法:

 

 

 

 1 String wallAlbumID = null;
 2 String response = facebook.request("me/albums");
 3 JSONObject json = Util.parseJson(response);
 4 JSONArray albums = json.getJSONArray("data");
 5 for (int i =0; i < albums.length(); i++) {
 6     JSONObject album = albums.getJSONObject(i);                     
 7     if (album.getString("type").equalsIgnoreCase("wall")) {
 8         wallAlbumID = album.getString("id");
 9         Log.d("JSON", wallAlbumID);
10         break;
11     }
12 }
13 
14 if (wallAlbumID != null) {
15     Bundle params = new Bundle();
16     params.putString("message", "Uploaded on " + now());                
17     params.putByteArray("source", bytes);
18     asyncRunner.request(wallAlbumID+"/photos", params, "POST", new PostPhotoRequestListener(), null);
19 }

 

  产生的效果:其实我们在facebook网页上直接发送带图文字消息,FB将图片放在了一个type属性是wall的 特定相册中,所以我们的思路是找的这个相册,然后上传图片加文字消息,这样涂鸦墙上就可以完整地显示我们的文字加图片的消息。但是这里也存在一个隐患,如 果用户从来没有发不过带图文字消息的话,相册中就不会有wall这个相册,所以接下来要做的就是新建这么一个相册,在传入图片,这里就不再赘述。

 

  最后希望对大家有所帮助,虽然知道国内开发这个FB的不多,重在分享么。有什么问题在下面留言啊。

  P.S.本来是有一篇关于利用(新浪、腾讯、开心、空间、豆瓣……)API发送消息这么一篇文章的,但是由于本人实在懒惰,所以一直放在草稿箱,没有完成#_#,最近尽可能发布。

[转载]SQLite学习手册(数据类型)

mikel阅读(885)

[转载]SQLite学习手册(数据类型) – Stephen_Liu – 博客园.

一、存储种类和数据类型:

SQLite将数据值的存储划分为以下几种存储类型:
     NULL: 表示该值为NULL值。
     INTEGER: 无符号整型值。
     REAL: 浮点值。
     TEXT: 文本字符串,存储使用的编码方式为UTF-8、UTF-16BE、UTF-16LE。
     BLOB: 存储Blob数据,该类型数据和输入数据完全相同。
由于SQLite采用的是动态数据类型,而其他传统的关系型数据库使用的是静态数据类型,即字段可以存储的数据类型是在表声明时即以确定的,因此它们之间 在数据存储方面还是存在着很大的差异。在SQLite中,存储分类和数据类型也有一定的差别,如INTEGER存储类别可以包含6种不同长度的 Integer数据类型,然而这些INTEGER数据一旦被读入到内存后,SQLite会将其全部视为占用8个字节无符号整型。因此对于SQLite而 言,即使在表声明中明确了字段类型,我们仍然可以在该字段中存储其它类型的数据。然而需要特别说明的是,尽管SQLite为我们提供了这种方便,但是一旦 考虑到数据库平台的可移植性问题,我们在实际的开发中还是应该尽可能的保证数据类型的存储和声明的一致性。除非你有极为充分的理由,同时又不再考虑数据库 平台的移植问题,在此种情况下确实可以使用SQLite提供的此种特征。
   1. 布尔数据类型:
SQLite并没有提供专门的布尔存储类型,取而代之的是存储整型1表示true,0表示false。

   2. 日期和时间数据类型:
和布尔类型一样,SQLite也同样没有提供专门的日期时间存储类型,而是以TEXT、REAL和INTEGER类型分别不同的格式表示该类型,如:
TEXT: “YYYY-MM-DD HH:MM:SS.SSS”
REAL: 以Julian日期格式存储
INTEGER: 以Unix时间形式保存数据值,即从1970-01-01 00:00:00到当前时间所流经的秒数。

二、类型亲缘性:

为了最大化SQLite和其它数据库引擎之间的数据类型兼容性,SQLite提出了”类型亲缘性(Type Affinity)”的概念。我们可以这样理解”类型亲缘性 “,在表字段被声明之后,SQLite都会根据该字段声明时的类型为其选择一种亲缘类型,当数据插入时,该字段的数据将会优先采用亲缘类型作为该值的存储 方式,除非亲缘类型不匹配或无法转换当前数据到该亲缘类型,这样SQLite才会考虑其它更适合该值的类型存储该值。SQLite目前的版本支持以下五种 亲缘类型:

亲缘类型 描述  
TEXT 数值型数据在被插入之前,需要先被转换为文本格式,之后再插入到目标字段中。
NUMERIC 当文本数据被插入到亲缘性为NUMERIC的 字段中时,如果转换操作不会导致数据信息丢失以及完全可逆,那么SQLite就会将该文本数据转换为INTEGER或REAL类型的数据,如果转换失 败,SQLite仍会以TEXT方式存储该数据。对于NULL或BLOB类型的新数据,SQLite将不做任何转换,直接以NULL或BLOB的方式存储 该数据。需要额外说明的是,对于浮点格式的常量文本,如”30000.0″,如果该值可以转换为INTEGER同时又不会丢失数值信息,那么SQLite 就会将其转换为INTEGER的存储方式。
INTEGER 对于亲缘类型为INTEGER的字段,其规则等同于NUMERIC,唯一差别是在执行CAST表达式时。
REAL 其规则基本等同于NUMERIC,唯一的差别是不会将”30000.0″这样的文本数据转换为INTEGER存储方式。
NONE 不做任何的转换,直接以该数据所属的数据类型进行存储。  

   1. 决定字段亲缘性的规则:
字段的亲缘性是根据该字段在声明时被定义的类型来决定的,具体的规则可以参照以下列表。需要注意的是以下列表的顺序,即如果某一字段类型同时符合两种亲缘性,那么排在前面的规则将先产生作用。
1). 如果类型字符串中包含”INT”,那么该字段的亲缘类型是INTEGER。
2). 如果类型字符串中包含”CHAR”、”CLOB”或”TEXT”,那么该字段的亲缘类型是TEXT,如VARCHAR。
3). 如果类型字符串中包含”BLOB”,那么该字段的亲缘类型是NONE。
4). 如果类型字符串中包含”REAL”、”FLOA”或”DOUB”,那么该字段的亲缘类型是REAL。
5). 其余情况下,字段的亲缘类型为NUMERIC。

    2. 具体示例:

声明类型 亲缘类型 应用规则
INT
INTEGER
TINYINT
SMALLINT
MEDIUMINT
BIGINT
UNSIGNED BIG INT
INT2
INT8
INTEGER 1
CHARACTER(20)
VARCHAR(255)
VARYING CHARACTER(255)
NCHAR(55)
NATIVE CHARACTER(70)
NVARCHAR(100)
TEXT
CLOB
TEXT 2
BLOB NONE 3
REAL
DOUBLE
DOUBLE PRECISION
FLOAT
REAL 4
NUMERIC
DECIMAL(10,5)
BOOLEAN
DATE
DATETIME
NUMERIC 5

注:在SQLite中,类型VARCHAR(255)的长度信息255没有任何实际意义,仅仅是为了保证与其它数据库的声明一致性。

三、比较表达式:

在SQLite3中支持的比较表达式有:”=”, “==”, “<“, “<=”, “>”, “>=”, “!=”, “<>”, “IN”, “NOT IN”, “BETWEEN”, “IS” and “IS NOT”。
数据的比较结果主要依赖于操作数的存储方式,其规则为:
1). 存储方式为NULL的数值小于其它存储类型的值。
2). 存储方式为INTEGER和REAL的数值小于TEXT或BLOB类型的值,如果同为INTEGER或REAL,则基于数值规则进行比较。
3). 存储方式为TEXT的数值小于BLOB类型的值,如果同为TEXT,则基于文本规则(ASCII值)进行比较。
4). 如果是两个BLOB类型的数值进行比较,其结果为C运行时函数memcmp()的结果。

四、操作符:

所有的数学操作符(+, -, *, /, %, <<, >>, &, and |)在执行之前都会先将操作数转换为NUMERIC存储类型,即使在转换过程中可能会造成数据信息的丢失。此外,如果其中一个操作数为NULL,那么它们 的结果亦为NULL。在数学操作符中,如果其中一个操作数看上去并不像数值类型,那么它们结果为0或0.0。