通用Query解决方案
简介
什么是Query
Query是一种查询方法,用于查找满足条件的数据,将结果以数据集的形式展现出来。
Query类别
SQL Query,使用类%SQLQuery和SQL SELECT语句。- 自定义
Query,使用类%Query和自定义逻辑生成查询数据。
说明:在讲通用Query解决方案之前,我们先了解一下Query的基础和基础使用,有助于理解实现原理。如果读者了解Query基本使用,可跳过此章节,直接阅读“现状”。
Query基本使用
SQL Query基本使用
Query QueryPersonByName(name As %String = "") As %SQLQuery(COMPILEMODE = "IMMEDIATE", CONTAINID = 1, ROWSPEC = "id:%Integer:ID,MT_Name:%String:name,age:%String,no:%String", SELECTMODE = "RUNTIME") [ SqlName = QueryPersonByName, SqlProc ] { SELECT top 10 ID, MT_Age, MT_Name, MT_No FROM M_T.Person WHERE (MT_Name %STARTSWITH :name) ORDER BY id }
说明:
Query- 声明Query方法关键字。QueryPersonByName-Query方法的名称。name As %String = ""-Query方法的参数。
%SQLQuery-Query类型为%SQLQuery。
%SQLQuery为%Query的子类,使用Query的简单的形式,可在方法体内直接编写Select SQL语句。
COMPILEMODE- 为%SQLQuery的参数,表示编译方式。
IMMEDIATE- 立即编译,当检测当前SQL语句是否正确。DYNAMIC- 动态编译 ,在运行时在编译SQL语句。
CONTAINID- 置为返回ID的列的编号。
1- 返回ID列。0- 不返回。
SELECTMODE- 表示显示方式。
RUNTIME- 无ODBC- 以ODBC方式显示数据。DISPLAY- 以显示方式显示数据。
LOGICAL- 以逻辑方式显示数据。
ROWSPEC- 提供数据列名称、数据类型、描述。用引号和逗号分隔的变量名和数据类型列表。格式如下:
ROWSPEC = "id:%Integer:ID,age:%String,MT_Name:%String:name,no:%String"
id- 表示数据列名称。%Integer- 表示数据类型。ID- 数据描述。
SqlProc- 表示该方法可作为存储过程调用。SqlName- 调用的存储过程名称。
- 无声明调用方式 -
call M.Query_QueryPersonByName() - 声明调用方式 -
call M.QueryPersonByName()
- 运行
Query方法 -d ##class(%ResultSet).RunQuery(className, queryName, arg...)
USER>d ##class(%ResultSet).RunQuery("M.Query", "QueryPersonByName") ID:age:name:no: 1:21:yaoxin:314629: 2:29:yx:685381: 3:18:Umansky,Josephine Q.:419268: 4:27:Pape,Ted F.:241661: 5:25:Russell,Howard T.:873214: 6:30:Xenia,Ashley U.:420471: 7:24:Rotterman,Martin O.:578867: 8:18:Drabek,Hannah X.:662167: 9:19:Eno,Mark U.:913628: 11:18:Tsatsulin,Dan Z.:920134:
自定义Query基本使用
在使用自定义Query时,一般都遵循固定的模版。在同一个类中定义以下类方法:
QueryName- 在Query方法类型指定%Query。QueryNameExecute— 此方法主要编写获取数据的业务逻辑,得到数据集。QueryNameFetch— 此方法遍历数据集。QueryNameClose— 此方法删除临时数据或对象。
说明:下面示例展示常用“套路”的自定义Query模版,此模版仅仅是常用的一种,并非是固定写法。
定义QueryName
Query QueryPersonByAge(pAge As %String = "", count As %Integer = "10") As %Query(ROWSPEC = "id:%Integer:ID,MT_Name:%String:name,age:%String,no:%String") { }
定义名为QueryPersonByAge的Query类型指定为%Query。并将查询定义的主体留空。
定义QueryNameExecute
ClassMethod QueryPersonByAgeExecute(ByRef qHandle As %Binary, pAge As %String = "", count As %Integer = "10") As %Status { s pid = $i(^CacheTemp) // 注释1 s qHandle = $lb(0, pid, 0) // 注释2 s index = 1 // 注释3 /* 业务逻辑代码 注释4 */ s id = "" for { s id = $o(^M.T.PersonD(id)) q:(id = "") q:(id > count) s data = ^M.T.PersonD(id) s i = 1 s name = $lg(data, $i(i)) s age = $lg(data, $i(i)) continue:(age < pAge) s no = $lg(data, $i(i)) d output } /* 业务逻辑代码 */ q $$$OK output s ^CacheTemp(pid, index) = $lb(id, age, name, no) // 注释6 s index = index + 1 // 注释7 }
QueryNameExecute() 方法提供所有需要的业务逻辑。方法的名称必须是 QueryNameExecute() ,其中 QueryName是定义Query的名称。
其中:
qHandle- 用于与实现此查询的其他方法进行通信。qHandle可以为任何类型。默认为%Binary。pAge As %String = "", count As %Integer = "10"为Query传入参数,可作为业务逻辑的条件使用。- 注释
1处代码,s pid = $i(^CacheTemp)- 获取pid。 - 注释
2处代码,s qHandle = $lb(0, pid, 0)- 数组内第一个元素0表示循环的开始,第二个元素pid用于获取^CacheTemp数据,第三个元素0用于遍历^CacheTemp起始节点。 - 业务逻辑代码 - 为获取数据集的主要实现逻辑。
- 注释
3处代码与注释7处代码,为^CacheTemp增加索引节点。
注释6处代码,s ^CacheTemp(pid, index) = $lb(id, name, age, no) - 为^CacheTemp赋值为后续遍历使用。
- 这里数据格式为
%Library.List形式,这样Fetch方法就不用转类型了,否则Fetch方法还需要将数据转为内部列表格式。
定义QueryNameFetch
ClassMethod QueryPersonByAgeFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = QueryPersonByAgeExecute ] { s end = $li(qHandle, 1) // 注释1 s pid = $li(qHandle, 2) s index = $li(qHandle, 3) s index = $o(^CacheTemp(pid, index)) // 注释2 if index = "" { // 注释3 s end = 1 s row = "" } else { s row = ^CacheTemp(pid, index) } s qHandle = $lb(end, pid, index) // 注释4 q $$$OK }
QueryNameFetch() 方法必须以 %Library.List 格式返回单行数据。方法的名称必须是 QueryNameFetch,其中 QueryName是定义Query的名称。
其中:
qHandle- 用于与实现此查询的其他方法进行通信。它的值应该是Execute定义的值。row- 表示要返回的一行数据的值类型为%Library.List,如果没有返回数据则为空字符串。end- 当到达最后一行数据时,end必须为1。如果不指定为1,则会无限循环。PlaceAfter-PlaceAfter方法关键字控制此方法在生成代码中顺序。这里表示在方法QueryPersonByAgeExecute生成之后在生成QueryPersonByAgeFetch方法。- 注释
1处代码,1~3行,解析qHandle数组的值获取end、pid、index。 - 注释
2处代码,s index = $o(^CacheTemp(pid, index))根据解析到的pid,index开始遍历。 - 注释
3处代码,将遍历的^CacheTemp(pid, index)每行属于赋值给row,如果index为空,则一定要将end赋值为1。 - 注释
4处代码,s qHandle = $lb(end, pid, index)将取到的end、index重新复制给qHandle为取下一行数据做准备。
注:Fetch方法为多次执行,有多少行数据就遍历多少遍。Execute、Close方法为一次执行。
定义QueryNameClose
ClassMethod QueryPersonByAgeClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = QueryPersonByAgeExecute ] { s pid = $li(qHandle, 2) // 注释1 k ^CacheTemp(pid) // 注释2 q $$$OK }
QueryNameClose() 方法在数据检索完成后删除清理临时数据或对象等结束收尾工作。方法的名称必须是 QueryNameClose() ,其中 QueryName是定义Query的名称。
qHandle- 用于与实现此查询的其他方法进行通信。- 注释
1处代码,获取qHandle保存的pid。 - 注释
2处代码,清除临时生成的^CacheTemp。
调用自定义Query
USER> d ##class(%ResultSet).RunQuery("M.Query", "QueryPersonByAge","20") ID:name:age:no: 1:yaoxin:21:314629: 2:yx:29:685381: 4:Pape,Ted F.:27:241661: 5:Russell,Howard T.:25:873214: 6:Xenia,Ashley U.:30:420471: 7:Rotterman,Martin O.:24:578867:
- 这里查询是年龄大于
20岁并且id小于10的所有人员信息。
使用Query
使用%SQL.Statement对象调用Query
可以在如下示例代码中使用类查询:
ClassMethod UseStatement() { s statement = ##class(%SQL.Statement).%New() s sc = statement.%PrepareClassQuery("M.Query", "QueryPersonByName") if $$$ISERR(sc) { q $system.Status.GetErrorText(sc) } s rs = statement.%Execute("Y") while rs.%Next() { w rs.%Get("MT_Name"),! } q $$$OK }
USER>w ##class(M.Query).UseStatement() Young,Ralph P. Yang,Thelma A. Yang,Aviel X. Yu,Brendan G. Yezek,Terry K. Yezek,Barbara L. Yoders,Christine D. Yezek,Debra I. Yeats,Patrick J. Yancik,Debra R. 1
- 使用
%New()创建%SQL.Statement的对象。 - 调用该对象的
%PrepareClassQuery(pClassName As %String = "", pQueryName As %String = "") As %Library.Status方法。
pClassName- 定义要使用的Query类的全限定名称。pQueryName- 该类中Query名称。- 此方法返回
%Status值。
调用%SQL.Statement实例的%Execute(%parm...) As %SQL.StatementResult方法,
%parm...- 定义Query的参数,该参数为可变参数。
- 此方法返回
%SQL.StatementResult对象。
- 使用
%SQL.StatementResult的方法从结果集中检索数据。
使用%ResultSet对象调用Query
可以在如下示例代码中使用类查询:
ClassMethod UseResultSet() { s rs = ##class(%ResultSet).%New() s rs.ClassName = "M.Query" s rs.QueryName = "QueryPersonByName" d rs.Execute("X") while (rs.Next()) { w rs.%Get("MT_Name"),! } q $$$OK }
USER>w ##class(M.Query).UseResultSet() Xenia,Fred H. Ximines,Alfred F. Xerxes,Ted S. Ximines,Debra R. Xander,Mary I. Ximines,Terry B. Xenia,Yan J. Xerxes,Rhonda C. Ximines,Sally B. Xander,Howard T. 1
- 使用
%New()创建%ResultSet的对象。
- 可以在
%New(pClassName _ "||" _ pQueryName )方法传入类的全限定名称与Query名称。
- 设置该对象的
ClassName属性为类的全限定名称。
- 设置该对象的
QueryName属性为类的Query名称。 - 调用
%ResultSet实例的Execute(args...) As %Status方法,
args...- 定义Query的参数,该参数为可变参数。
Execute执行成功后根据%ResultSet对象检索数据。
注:%ResultSet方式使用Query在IRIS中被废弃,不再推荐使用。
通用`Query`解决方案(中):https://developer.aliyun.com/article/1516551