26
2019.2

CURD连贯操作

作者: POPASP
POPASP提供了灵活和方便的数据操作方法,对数据库操作的四个基本操作(CURD):创建、更新、读取和删除的实现是最基本的,也是必须掌握的,在这基础之上才能熟悉更多实用的数据操作方法。CURD操作通常是可以和连贯操作配合完成的。下面来分析下各自的用法: (下面的CURD操作我们均以M方法创建模型实例来说明,因为不涉及到具体的业务逻辑) ### 创建(Create) 在POPASP中使用add方法新增数据到数据库(而并不是create方法)。 #### add 写入(新增)数据到数据库 | 项目 | 说明 | | ------------- | ------------- | | 用法 | add() | | 参数 | 无 | | 返回值 | 如果数据非法或者查询错误则返回Empty;如果是自增主键 则返回主键值,否则返回1 | | 相关方法 | 通常和data、create方法配合使用 | 使用示例如下: ```brush:vb <% Class Test sub Add dim dict,id set dict = D_ dict("username") = "test" dict("password") = md5("123456") id = M_("user").db.data(dict).Add() '使用data方法连贯操作 '控制器中销毁对象变量使用that.u,property过程,返回的对象还是that end sub End Class %> ``` 在add之前须要使用data方法传入数据。 create方法的使用,请查阅本章的“自动完成”与“自动验证”两节。 使用create方法的例子: ```brush:vb Class Test sub Add dim dict,id set dict = D_ dict("username") = "test" dict("password") = md5("123456") if M_("user").db.data(dict).Create(1) then 'Create(1)中的1代表添加数据 id = M_("user").db.Add() end if call that.u(m).u(dict) end sub End Class ``` 如果你的主键是自动增长类型,并且如果插入数据成功的话,Add方法的返回值就是最新插入的主键值,可以直接获取。 用form表单进行提交并添加数据,可以将form表单的name属性设置成与数据库字段相同的名字,这样在插入数据时,可以直接插入。举例: ```brush:vb Sub insert() ' Create方法并不是添加数据,而是判断数据的合法性,自动调用model文件进行自动完成与自动验证。 if M_("article").db.Create(1) then '1对应插入操作 if M_("article").db.add() > 0 then that.success("添加成功") else that.error("添加失败") end if else that.error( array(db.error,"",1) ) end if end sub ``` #### mAdd 写入(新增)多条数据 | 项目 | 说明 | | ------------- | ------------- | | 用法 | mAdd() | | 参数 | 无 | | 返回值 | 返回没有添加进去的键名(格式为数组) | | 相关方法 | 必须配合data使用,data应该是一个二维Dictionary对象 | 关于mAdd的使用,写了较为详细的案例。请查看 [数据库批量添加](http://www.popasp.com/popasp2_4docs_demo/index.html#madd "数据库批量添加") #### copy 根据查询条件得到的数据,又插入到数据表 | 项目 | 说明 | | ------------- | ------------- | | 用法 | copy() | | 参数 | 无 | | 返回值 | 复制的个数 | | 相关方法 | 如果使用了data,则data中的值是指回定不变的值 | ```brush:vb '将查询到的数据复制到数据表,如果有主键,框架自动过滤 B_("article").where("author='admin'").copy '如果想将作者都换成test,分别都换成1 dim dict set dict = D_ dict("author") = "test" dict("sort") = 1 B_("article").where("author='admin'").data(dict).copy ``` ### 更新(Update) #### save 更换一条记录 在POPASP中使用save方法更新数据库,并且也支持连贯操作的使用。 | 项目 | 说明 | | ------------- | ------------- | | 用法 | save() | | 参数 | 无 | | 返回值 | 如果数据非法或者查询错误则返回Empty;如果更新成功返回影响的记录数(影响的记录数其实由where条件决定,并不是实际影响的记录数) | | 相关方法 | 通常配合连贯操作where、data等一起使用 | ```brush:vb <% class Test sub Save dim dict,result set dict = D_ dict("username") = "test2" dict("password") = md5("654321") result = M_("user").db.where(4).data(dict).Save() var_export result end sub end Class %> ``` 为了保证数据库的安全,避免出错更新整个数据表,如果没有任何更新条件,数据对象本身也不包含主键字段的话,save方法不会更新任何数据库的记录。 因此下面的代码不会更改数据库的任何记录 ```brush:vb result = M_("user").db.data(dict).Save() 'result的值为Empty ``` 除非使用下面的方式: ```brush:vb sub Save dim dict,result set dict = D_ dict("id") = 4 dict("username") = "test2" dict("password") = md5("654321") result = M_("user").db.data(dict).Save() var_export result end sub ``` 如果id是数据表的主键的话,系统自动会把主键的值作为更新条件来更新其他字段的值。 还有一种方法是通过create方法创建要更新的数据对象,然后进行保存操作,这样save方法的参数可以不需要传入。 ```brush:vb sub Save dim dict,result set dict = D_ dict("id") = 4 dict("username") = "test2" dict("password") = md5("654321") if M_("user").db.data(dict).Create(2) then result = M_("user").db.Save() end if var_export result end sub ``` #### setField 更新个别字段的值 如果只是更新个别字段的值,可以使用setField方法。 | 项目 | 说明 | | ------------- | ------------- | | 用法 | setField(field,value) | | 参数 | field的参数可以是String、Dictionary,如果是Dictionary,则value参数不起作用。value的参数可以是String、Numeric、Date等相应字段对象的值类型 | | 返回值 | 如果数据非法或者查询错误则返回Empty;如果更新成功返回影响的记录数 | | 相关方法 | 必须配合连贯操作where一起使用 | 使用示例: ```brush:vb '在Action中的操作 sub setField dim result result = B_("user").where(4).setField("username","test123") var_export result end sub ``` setField方法支持同时更新多个字段,只需要传入Dictionary对象即可,例如: ```brush:vb '在Action中的操作 sub setField2 dim result,dict set dict = D_ dict("username") = "test321" dict("email") = "popjb@126.cn" result = B_("user").where(4).setField(dict,"") var_export result end sub ``` #### setInc /setDec 字段增长/字段减少 而对于统计字段(通常指的是数字类型)的更新,系统还提供了setInc和setDec方法。 | 项目 | 说明 | | ------------- | ------------- | | 用法 | setInc(field)字段值增长;setDec(field)字段值减少 | | 参数 | field的参数可以是String、Array。如果为String,表示字段值增/减1,如果采用数组,如array(field,step),表示字段增/减 step | | 返回值 | 如果数据非法或者查询错误则返回Empty;如果更新成功返回影响的记录数 | | 相关方法 | 必须配合连贯操作where一起使用 | 示例: ```brush:vb 'ID为3的文章浏览次数增1 result = B_("article").where(3).SetInc("views") 'ID为3的文章浏览次数增3 result = B_("article").where(3).SetInc(array("views",3)) ``` 有一类操作,比如将文章数据表的某篇文章置顶或取消置顶,或者设置某篇文章是否允许评论,此时的字段值或为True/False,或为1/0,些时的操作我们可以使用setNot。 #### setNot 将字段值取反 | 项目 | 说明 | | ------------- | ------------- | | 用法 | setNot(fields)将一个或多个字段值取反 | | 参数 | fields的参数可以是String、Array。如果为String,多个字段须用英文逗号分隔 | | 返回值 | 如果数据非法或者查询错误则返回Empty;如果更新成功返回True | | 相关方法 | 必须配合连贯操作where一起使用 | 示例: ```brush:vb 'ID为3的文章置顶值取反 B_("article").where(3).SetNot("CMS_Top") ``` #### swap 交换两条记录的个别字段值 | 项目 | 说明 | | ------------- | ------------- | | 用法 | swap() | | 参数 | 无 | | 返回值 | 失败返回false,成功返回True | | 相关方法 | 必须配合连贯操作where一起使用,where传入两个ID值 | 示例: ```brush:vb '将文章表ID为1、3的标题进行互换 B_("article").where("1,3").field("title").swap '互换多个字段 B_("article").where("1,3").field("title,author,addtime").swap ``` #### mSave 修改多条数据 | 项目 | 说明 | | ------------- | ------------- | | 用法 | mSave() | | 参数 | 无 | | 返回值 | 返回没有成功修改的每条数据的ID值(数组),注意跟mAdd返回值不同 | | 相关方法 | 必须配合data使用,data应该是一个二维Dictionary对象 | 关于Save的使用,写了较为详细的案例。请查看 [数据库批量修改](http://www.popasp.com/popasp2_4docs_demo/index.html#msave "数据库批量修改") ### 删除(Delete) 在POPASP中使用remove方法删除数据库中的记录 | 项目 | 说明 | | ------------- | ------------- | | 用法 | remove() | | 参数 | 无 | | 返回值 | 如果数据非法或者查询错误则返回Empty;如果删除成功返回影响的记录数 | | 相关方法 | 必须配合连贯操作where一起使用 | 示例如下: ``` result = B_("user").where(11).remove() ``` remove方法可以用于删除单个或者多个数据,主要取决于删除条件,也就是where方法的参数。 ### 替换(Replace) 在POPASP中有一个重要的数据处理方法就是Replace。它结合了添加与修改二者的特性,以一组数据中的主键作为判断条件,无则添加,有则修改,此时where无效,返回结果是实际添加或修改的ID | 项目 | 说明 | | ------------- | ------------- | | 用法 | Replace() | | 参数 | 无 | | 返回值 | 如果数据中不存在该ID或其大于最大ID,则添加数据,返回自动增长的ID。如果数据中的ID在表中已被删除,则将数据插入ID所代表的位置,返回该ID;如果数据表中存在该ID,则直接进行修改,返回该ID | | 相关方法 | 必须和data方法配合使用,where方法此时失效 | ```brush:vb dim dict set dict = D_ dict("id") = 20 dict("username") = "测试Replace" result = B_("user").data(dict).Replace() ``` ### 交换(Swap) 交换两条记录的字段值,在某些场合下非常有用,这时我们可以使用Swap来快速进行交换。 该方法始于2.1版本 | 项目 | 说明 | | ------------- | ------------- | | 用法 | Swap() | | 参数 | 无 | | 返回值 | 操作成功返回True,否则返回Empty | | 相关方法 | 必须和where方法配合使用,常搭配field使用 | ```brush:vb '交换栏目数据表中两条记录的排序值 B_("nav").where("1,2").field("CMS_Sort").Swap ``` ### 搜索(Search) 从某个(些)字段中进行值搜索,使用非常频繁,POPASP2.2开始对其进行了高度封装,极大地简化了查询操作。 | 项目 | 说明 | | ------------- | ------------- | | 用法 | Search(q,fields) | | 参数 | 无 | | 返回值 | 查询错误返回Empty ;查询成功返回查询的结果集(Recordset对象) | | 相关方法 | 通常配合连贯操作where、field、order、page、leftJoin等一起使用 | ```brush:vb '从文章数据表中的文章内容与文章标题字段中查询“POPASP 模型” '空格代表逻辑AND,既含"POPASP",又含“模型” B_("article").where("CMS_Del = 0 ").field("CMS_ID,CMS_Date,CMS_Title").top(10).order("CMS_ID DESC").Search("POPASP 模型","CMS_Content,CMS_Title") ``` ### 获取随机记录(getRand) 很多网站上都有“随机阅读”的功能,POPASP提供了getRand方法来实现这个功能。 | 项目 | 说明 | | ------------- | ------------- | | 用法 | getRand | | 参数 | 如果为数字,则对应num取出N条记录,如果为数组Array(num,sort),sort值分三种,0:打乱的顺序,-1:逆序,1:顺序 | | 返回值 | 查询成功返回查询的结果集(Recordset对象) | | 相关方法 | 通常配合连贯操作where、field、order、page、leftJoin等一起使用 | ```brush:vb dim data set data = B_("content").field("ContentID,title").getRand(10) ``` ### 读取(Read) 在POPASP中读取数据的方式很多,通常分为读取数据和读取数据集。 #### select 查询数据集 读取数据集使用select方法: | 项目 | 说明 | | ------------- | ------------- | | 用法 | select() | | 参数 | 无 | | 返回值 | 查询错误返回Empty ;查询成功返回查询的结果集(Recordset对象) | | 相关方法 | 通常配合连贯操作where、field、order、page、leftJoin等一起使用 | 使用示例: ```brush:vb set rs = B_("user").field("username,email").where("id>1").select() ``` Select方法获取的是Recordset对象,可以使用POP_MVC.rs2dict或that.rs2dict转化成二维Dictionary对象,当然也可以在查询的时候直接使用getAll方法直接返回二维Dictionary对象 ```brush:vb set rs = M_("user").db.field("username,email").where("id>1").getAll() ``` Select方法配合连贯操作方法可以完成复杂的数据查询。而最复杂的连贯方法应该是where方法的使用,因为这部分涉及的内容较多,我们会在查询语言部分就如何进行组装查询条件进行详细的使用说明。基本的查询暂时不涉及关联查询部分,而是统一采用关联模型来进行数据操作,这一部分请参考关联模型部分。读取数据使用find方法: #### getAll 查询数据集 该方法使用同select,返回的是二维Dictionary对象 | 项目 | 说明 | | ------------- | ------------- | | 用法 | getAll() | | 参数 | 无 | | 返回值 | 返回二维Dictionary对象 | | 相关方法 | 通常配合连贯操作where、field、order、page、leftJoin等一起使用 | #### getArr 查询数据集 该方法使用同getArr,返回的是数组(数组内每个元素是Dictionary对象) | 项目 | 说明 | | ------------- | ------------- | | 用法 | getArr() | | 参数 | 无 | | 返回值 | 返回的是数组(数组内每个元素是Dictionary对象) | | 相关方法 | 通常配合连贯操作where、field、order、page、leftJoin等一起使用 | #### getPKAll 查询数据集 该方法使用同getAll,返回的也是二维Dictionary对象,不过键名不是单纯的递增数字,而是数据表的主键值 | 项目 | 说明 | | ------------- | ------------- | | 用法 | getPKAll() | | 参数 | 无 | | 返回值 | 返回二维Dictionary对象 | | 相关方法 | 通常配合连贯操作where、field、order、page、leftJoin等一起使用 | #### find 查询一条记录 | 项目 | 说明 | | ------------- | ------------- | | 用法 | find() | | 参数 | 无 | | 返回值 | 查询错误返回Empty ;查询成功返回查询的结果集(Recordset对象) | | 相关方法 | 通常配合连贯操作where、field、order、page、leftJoin等一起使用 | 读取数据的操作其实和数据集的类似,select可用的所有连贯操作方法也都可以用于find方法,区别在于find方法最多只会返回一条记录,因此Top方法对于find查询操作是无效的。 下面是一些查询的例子: ```brush:vb set rs = B_("user").field("username,email").where(1).find() ``` 即使满足条件的数据不止一条,find方法也只会返回第一条记录。 #### getRow 查询一条记录 getRow的使用同find,不同的是它返回的是Dictionary对象,而非Recordset对象。 | 项目 | 说明 | | ------------- | ------------- | | 用法 | getRow() | | 参数 | 无 | | 返回值 | 查询错误返回空的Dictionray对象 ;查询成功返回查询的结果集(Dictionary对象) | | 相关方法 | 通常配合连贯操作where、field、order、leftJoin等一起使用 | #### getOne 查询一个字段的值 如果要读取某个字段的值,可以使用getOne。 | 项目 | 说明 | | ------------- | ------------- | | 用法 | getOne() | | 参数 | 无 | | 返回值 | 查询错误返回Empty ;不管field是一个还是一组参数,都将返回第一个字段的值。 | | 相关方法 | 通常配合连贯操作where、order、page、leftJoin等一起使用 | 示例如下: ```brush:vb username = B_("user").field("username").getOne '一个字段只返回一个值 ``` #### getField 查询一个字段的值 | 项目 | 说明 | | ------------- | ------------- | | 用法 | getField(field) | | 参数 | fields , 等同于先field(field),参数同field()方法 | | 返回值 | 查询错误返回Empty ;不管field是一个还是一组参数,都将返回第一个字段的值。 | | 相关方法 | 通常配合连贯操作where、order、leftJoin等一起使用 | 示例如下: ```brush:vb username = B_("user").getField("username") '一个字段只返回一个值 ``` #### getFields 查询几个字段的值 | 项目 | 说明 | | ------------- | ------------- | | 用法 | getFields(fields) | | 参数 | fields , 等同于先field(fields),参数同field()方法 | | 返回值 | 查询错误返回Empty ;不管fields是一个还是一组参数,都将返回第一个Dictionary对象,键名对应字段名 | | 相关方法 | 通常配合连贯操作where、order、leftJoin等一起使用 如果传入多个字段的话,返回一个Dictionary对象: ```brush:vb set dict = B_("user").where(3).getFields("username,email") '返回Dictionary ``` #### get1arr 查询某个字段值 get1arr方法内部使用了select,它是将查询的第一个字段值单独提取出来,组成一个数组。 | 项目 | 说明 | | ------------- | ------------- | | 用法 | get1arr() | | 参数 | 无 | | 返回值 | 如果有数据返回数组,无数据返回Empty | | 相关方法 | 通常配合连贯操作where、field、order、leftJoin等一起使用 | ```brush:vb dim arr arr = B_("user").field("email").where("inStr(email,""163.com"")").get1arr ``` #### getKeyValue 获取键值对 | 项目 | 说明 | | ------------- | ------------- | | 用法 | getKeyValue() | | 参数 | 无 | | 返回值 | 结合field()使用,必须至少传入两个字段名,第1个为键,第2个为值。返回一维Dictionary对象 | | 相关方法 | 通常配合连贯操作where、order、leftJoin等一起使用 #### getObject 获取对象 不管是Dictionary还是Recordset对象,在使用的时候,都要使用`obj("field")`方式来进行数据引用。但是有些数据,比如网站的配置,如果想存到数据库,那么采用这种方式让人看着很不专业,我们更希望采用`obj.field`这种引用方式。可喜的是POPASP3.1提供了这种技术。 下面是iaspcms语言数据表,每行存储一条语言信息。 ![POPASP getObject](http://www.popasp.com/images/31/popasp_self_object01.jpg "POPASP getObject") 比如,在iaspcms中使用了 ```brush:vb set setting = B_("Language").where("IsDefault=1").getObject ``` 上面是取数据,下面是使用数据 ```brush:vb '该语言网站关闭 if setting.LanguageStatus = 0 then POP_MVC.exit( setting.LanguageName & " 网站关闭" ) end if ``` #### getObject2 获取对象 跟上面讲getObject使用的语言数据表不同,网站全站配置很多的时候,我们就不方便使用增加字段来存储配置,而需要采用以下方式存储配置信息。 下面是iaspcms的全站配置数据表。 ![POPASP getObject](http://www.popasp.com/images/31/popasp_self_object02.jpg "POPASP getObject") 跟getObject不同的是,每条记录的ConfigName值是属性,而ConfigValue是属性值。此时,我们就要使用getObject2 比如,在iaspcms中使用了 ```brush:vb '得到配置 set config = B_("Config").field("ConfigName,ConfigValue").getObject2 ``` 上面是取数据,下面是使用数据 ```brush:vb '网站禁用 if config.siteMode = 0 then POP_MVC.exit( config.siteHelp ) ``` 定位查询涉及到getN、First、Last三个方法。 ### 定位查询 #### getN 获取第N条记录 | 项目 | 说明 | | ------------- | ------------- | | 用法 | getN( num ) | | 参数 | num正数从1开始计,负数则-1表示最后1条 | | 返回值 | 返回Dictionary对象 | | 相关方法 | 通常配合连贯操作where、order、leftJoin等一起使用 | 示例如下: ```brush:vb set dict = B_("article").order("post_id asc").getN(2) ``` #### Fisrt 获取第1条记录 | 项目 | 说明 | | ------------- | ------------- | | 用法 | Fisrt | | 参数 | 无 | | 返回值 | 返回Dictionary对象 | | 相关方法 | 通常配合连贯操作where、order、leftJoin等一起使用 | 示例如下: ```brush:vb set dict = B_("article").order("post_id asc").First ``` #### Last 获取最后1条记录 | 项目 | 说明 | | ------------- | ------------- | | 用法 | Last | | 参数 | 无 | | 返回值 | 返回Dictionary对象 | | 相关方法 | 通常配合连贯操作where、order、leftJoin等一起使用 | 示例如下: ```brush:vb set dict = B_("article").order("post_id asc").Last ``` ### 统计查询 #### Count 计数 | 项目 | 说明 | | ------------- | ------------- | | 用法 | Count() | | 参数 | 无 | | 返回值 | 返回数值 | | 相关方法 | 通常配合连贯操作where等一起使用 | 示例如下: ```brush:vb cnt = B_("article").count() ``` #### Max 取最大值 | 项目 | 说明 | | ------------- | ------------- | | 用法 | Max(field) | | 参数 | field要统计的字段名(必须) | | 返回值 | 返回数值 | | 相关方法 | 通常配合连贯操作where等一起使用 | 示例如下: ```brush:vb cnt = B_("article").Max("views") ``` #### Min 取最小值 | 项目 | 说明 | | ------------- | ------------- | | 用法 | Min(field) | | 参数 | field要统计的字段名(必须) | | 返回值 | 返回数值 | | 相关方法 | 通常配合连贯操作where等一起使用 | 示例如下: ```brush:vb cnt = B_("article").Min("views") ``` #### Avg 取平均值 | 项目 | 说明 | | ------------- | ------------- | | 用法 | Avg(field) | | 参数 | field要统计的字段名(必须) | | 返回值 | 返回数值 | | 相关方法 | 通常配合连贯操作where等一起使用 | 示例如下: ```brush:vb cnt = B_("article").where("views > 0").avg("views") ``` #### Sum 求和 | 项目 | 说明 | | ------------- | ------------- | | 用法 | Sum(field) | | 参数 | field要统计的字段名(必须) | | 返回值 | 返回数值 | | 相关方法 | 通常配合连贯操作where等一起使用 | 示例如下: ```brush:vb cnt = B_("article").where("views > 0").sum("views") ```