通用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