通用`Query`解决方案

news2024/9/25 15:28:32

文章目录

  • 通用`Query`解决方案
  • 简介
      • 什么是`Query`
      • `Query`类别
    • `Query`基本使用
      • `SQL Query`基本使用
      • 自定义`Query`基本使用
  • 现状
  • 方案
    • 通过`Json`数据或方法动态生成`Query `
    • 通过`Select Sql`语句动态生成`Query`
    • 通过`Query`生成动态`Query`
    • 支持传统的`Query`并通过参数形式生成`Query`列
    • 定义通用`Query`,只需要实现`Exceute`方法
  • 总结

通用Query解决方案

简介

什么是Query

Query是一种查询方法,用于查找满足条件的数据,将结果以数据集的形式展现出来。

Query类别

  • SQL Query,使用类 %SQLQuerySQL 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")
{
}

定义名为QueryPersonByAgeQuery类型指定为%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数组的值获取endpidindex
  • 注释2处代码,s index = $o(^CacheTemp(pid, index)) 根据解析到的pidindex开始遍历。
  • 注释3处代码,将遍历的^CacheTemp(pid, index)每行属于赋值给row,如果index为空,则一定要将end赋值为1
  • 注释4处代码,s qHandle = $lb(end, pid, index)将取到的endindex重新复制给qHandle为取下一行数据做准备。

注:Fetch方法为多次执行,有多少行数据就遍历多少遍。ExecuteClose方法为一次执行。


定义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的所有人员信息。

现状

上面2Query的基本使用示例,可能是大家最常用的两种方式。

但是经常写查询或者写报表的同学可能会面临如下几个问题:

  1. 每次写Query都需要定义列头ROWSPEC很麻烦,是否可以自己指定列头ROWSPEC
  2. 现在很多方法返回的值是JSON,如何将JSON方法快速转成Query
  3. 是否可以写一个通用Query,只需要写Execute主要逻辑即可?
  4. 是否可以优化现在的模版,例如^CacheTemp替换成^||CacheTemp

以上的问题,都是可以解决的,请继续阅读下面文章部分。

方案

如果想实现通用Query还得需要知道一个回调方法QueryNameGetInfo

ClassMethod Json2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	q $$$OK
}

其中:

  • colinfo - 此参数最关键用于定义ROWSPEC列头部分。为在 ROWSPEC 中声明的每一列包含一个列表元素。形式为 name:exttype:caption
    • name - 为列头名称。
    • exttype - 为数据类型。
    • caption - 为描述说明。
  • colinfo 类型必须是%Library.List,定义的列头的类型也是%Library.List

例如:

ClassMethod QueryPersonByAgeGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef %qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	s colinfo = $lb($lb("id", "%Integer", "ID"), $lb("age", "%String", ""), $lb("MT_Name", "%String", "name"), $lb("no", "%String", ""))
	s parminfo = ""
	s idinfo = ""
	q $$$OK
}

说明:Query执行顺序 Execute -> GetInfo -> Fetch(n) -> Close

下面分别描述以下几种解决方案:

  • 通过Json数据或方法动态生成Query
  • 通过Select Sql语句动态生成Query
  • 通过Query动态生成Query
  • 支持传统的Query并通过参数形式生成Query列
  • 定义通用Query,只需要实现Exceute方法

通过Json数据或方法动态生成Query


定义Json方法

  • Json方法可任意定义,此示例仅为了测试使用。如下方法:查询当前电脑盘符以Json结果输出。
/// desc:查询盘符
ClassMethod QueryDrives(fullyQualified = 1, type = "D")
{
	s array = []
	s rs = ##class(%ResultSet).%New()
	s rs.ClassName = "%File"
	s rs.QueryName = "DriveList"
	d rs.Execute(fullyQualified)
	while (rs.Next()) {
		s drive = rs.Get("Drive")
		s drive = $zcvt(drive, "U")
		s obj = {}
		s obj.type = "D"
		continue:(type '= "D")
		s obj.drive = drive
		d array.%Push(obj)
	}
	q array
}

运行:

USER> w ##class(M.Query).QueryDrives().%ToJSON()
[{"type":"D","drive":"C:\\"},{"type":"D","drive":"D:\\"},{"type":"D","drive":"E:\\"},{"type":"D","drive":"F:\\"},{"type":"D","drive":"G:\\"}]

定义QueryName

Query Json2Query(className As %String, methodName As %String, arg...) As %Query
{
}

其中:

  • className - 类名。
  • methodName - 需要执行的Json方法名称。
  • arg... - 需要执行的方法参数。

定义QueryNameExecute

ClassMethod Json2QueryExecute(ByRef qHandle As %Binary, className As %String, methodName As %String, arg...) As %Status
{
	s array = $classmethod(className, methodName, arg...) // 注释1
	if ('$isobject(array)) { // 注释2
		s array = [].%FromJSON(array)
	}
	q:('array.%IsA("%Library.DynamicArray")) $$$ERROR($$$GeneralError, "不是数组对象") // 注释3
	q:(array.%Size() = 0) $$$ERROR($$$GeneralError, "没有数据") // 注释4
    s qHandle = array.%GetIterator() // 注释5
    q $$$OK
}
  • 注释1代码,利用反射机制调用目标方法并获取返回值。
  • 注释2代码,判断如果返回的字符串则转成Json对象。
  • 注释3代码,判断该对象不是%Library.DynamicArray抛出错误信息。
  • 注释4代码,Json数组长度为0抛出错误信息。
  • 注释5代码,获取迭代器对象用于遍历使用。

定义QueryNameGetInfo

ClassMethod Json2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	s colinfo = $lb() // 注释1
	s count = 1 
	if qHandle.%GetNext(.key, .value) { 
		s obj = value.%GetIterator() 
		while obj.%GetNext(.objKey, .objValue) { // 注释2
			s $li(colinfo, count) = $lb(objKey) 
			s count = $i(count) 
		}
	}	
	s parminfo = "" // 注释3
	s idinfo = "" // 注释4
	q $$$OK
}
  • 注释1代码,初始化colinfo数组。
  • 注释2代码,遍历Json对象获取Key,并通过$licolinfo赋值。
  • 注释3代码,初始化parminfo,否则报错。
  • 注释4代码,初始化idinfo,否则报错。

定义QueryNameFetch

ClassMethod Json2QueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = Json2QueryExecute ]
{
  	s iter = qHandle
  	q:($g(iter) = "") $$$OK
    if iter.%GetNext(.key, .value) { // 注释1
		s row = ""
  		s obj = value.%GetIterator()
  		while obj.%GetNext(.objKey, .objValue) { // 注释2
			if ( $g(row) = "" ) {
				s row = $lb(objValue)
			} else {
				s row = row _ $lb(objValue)
			}
  		}
	  	s end = 0
    } else {
		s row = "" 
		s end = 1 // 注释3
	}
    q $$$OK
}
  • 注释1代码,获取当前迭代器Json数据行。
  • 注释2代码,遍历当前Json对象并把valuerow进行$lb串联。
  • 注释3代码,如果没有数据设置end1表示遍历结束。

定义QueryNameClose

ClassMethod Json2QueryClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = Json2QueryFetch ]
{
    s qHandle = "" // 注释1
    q $$$OK
}
  • 注释1代码,将对象qHandle清空。

注:其实M有相关回收机制,实际上Close方法不声明也可以。


调用Json2Query方法

USER>d ##class(%ResultSet).RunQuery("M.Query","Json2Query","M.Query","QueryDrives","0","D")
 
type:drive:
D:D::
D:E::
D:F::
D:G::

USER>d ##class(%ResultSet).RunQuery("M.Query","Json2Query","M.Query","QueryDrives","1","D")
 
type:drive:
D:D:\:
D:E:\:
D:F:\:
D:G:\:

通过Select Sql语句动态生成Query


定义QueryName

Query Sql2Query(sql As %String, mode As %String = 1) As %Query
{
}
  • sql - 表述需要写入SQL语句的变量。
  • mode - 显示数据格式类型。
    • 0 - 逻辑格式
    • 1 - OBDC 格式
    • 2 - 显示格式

定义QueryNameExecute

ClassMethod Sql2QueryExecute(ByRef qHandle As %Binary, sql As %String, mode As %String = 1) As %Status
{
 	s sqlStatement = ##class(%SQL.Statement).%New()
    s sqlStatement.%SelectMode = mode // 注释1
    s sqlStatus = sqlStatement.%Prepare(.sql) // 注释2
    q:$$$ISERR(sqlStatus) sqlStatus
    s sqlResult = sqlStatement.%Execute() 
	s stateType = sqlStatement.%Metadata.statementType
	q:('stateType = 1 ) $$$ERROR($$$GeneralError, "不是select语句") // 注释3
	s qHandle = {}
	s qHandle.sqlResult = sqlResult // 注释4
	s qHandle.sqlStatement = sqlStatement 
    q $$$OK
}
  • 注释1代码,设置SQL的数据显示格式。
  • 注释2代码,传入SQL语句得到sqlStatementsqlResult对象。
  • 注释3代码,传入的SQLSelect语句,抛出错误信息。
  • 注释4代码,将qHandle传入两个对象分别是sqlResultsqlStatement
    • sqlResult用于遍历数据使用。
    • sqlStatement用于得到数据列头信息。

定义QueryNameGetInfo

ClassMethod Sql2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	s colinfo = $lb()
	s sqlStatement = qHandle.sqlStatement // 注释1
	s count = 1
    s column = ""
   	for {
		s column = $o(sqlStatement.%Metadata.columnIndex(column)) 
		q:(column = "")
		s data = sqlStatement.%Metadata.columnIndex(column)
		s $li(colinfo, count) = $lb($lg(data, 2)) // 注释2
		s count = $i(count)
	}
	s parminfo = ""
	s idinfo = ""
	q $$$OK
}
  • 注释1代码,通过qHandle得到sqlStatement对象。
  • 注释2代码,给colinfo列表进行循环赋值列头信息,

定义QueryNameFetch

ClassMethod Sql2QueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = Sql2QueryExecute ]
{
	s sqlStatement = qHandle.sqlStatement // 注释1
	s sqlResult =  qHandle.sqlResult 
	s colCount = sqlResult.%ResultColumnCount // 注释2
    if (sqlResult.%Next()) {
        for i = 1 : 1 : colCount{
            s val = sqlResult.%GetData(i)
			if ( $g(row) = "" ) { // 注释3
				s row = $lb(val)
			} else {
				s row = row _ $lb(val)
			}
        }
        s end = 0 
    } else {
	   s row = ""
	   s end = 1
	}
	s qHandle.sqlResult = sqlResult // 注释4
    q $$$OK
}
  • 注释1代码,通过qHandle得到sqlStatementsqlResult对象。
  • 注释2代码,得到列数,相当于得到一行数据有多少项。
  • 注释3代码,遍历数据给row赋值。
  • 注释4代码,将qHandle.sqlResult对象,赋值给循环当前对象。

定义QueryNameClose

此处省略。

注:其实M有相关回收机制,实际上Close方法不声明也可以。


调用Sql2Query方法

USER>d ##class(%ResultSet).RunQuery("M.Query","Sql2Query","select * from M_T.Person", 1)
 
id:MT_Age:MT_Name:MT_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:
...
100:24:Nathanson,Jocelyn A.:147578:
USER>d ##class(%ResultSet).RunQuery("M.Query","Sql2Query","select ID,MT_Name from M_T.Person")
 
id:MT_Name:
1:yaoxin:
2:yx:
3:Umansky,Josephine Q.:
4:Pape,Ted F.:
5:Russell,Howard T.:
6:Xenia,Ashley U.:
7:Rotterman,Martin O.:
...
100:Nathanson,Jocelyn A.:
USER>d ##class(%ResultSet).RunQuery("M.Query","Sql2Query","select top 10 ID as id from M_T.Person")
 
id:
1:
2:
3:
4:
5:
6:
7:
8:
9:
11:

通过Query生成动态Query


定义QueryName

Query Query2Query(className As %String, queryName As %String, arg...) As %Query
{
}
  • className - 类名。
  • queryName - 需要执行的Query方法名称。
  • arg... - 需要执行的Query方法参数。

定义QueryNameExecute

ClassMethod Query2QueryExecute(ByRef qHandle As %Binary, className As %String, queryName As %String, arg...) As %Status
{
 	s sqlStatement = ##class(%SQL.Statement).%New()
 	s sqlStatus = sqlStatement.%PrepareClassQuery(className, queryName)
    q:$$$ISERR(sqlStatus) sqlStatus
    s sqlResult = sqlStatement.%Execute() 
	s qHandle = {}
	s qHandle.sqlResult = sqlResult
	s qHandle.sqlStatement = sqlStatement
    q $$$OK
}
  • Sql2Query类似。

定义QueryNameGetInfo

ClassMethod Query2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	s colinfo = $lb()
	s sqlStatement = qHandle.sqlStatement
	s count = 1
    s column = ""
   	for {
		s column = $o(sqlStatement.%Metadata.columnIndex(column)) 
		q:(column = "")
		s data = sqlStatement.%Metadata.columnIndex(column)
		s $li(colinfo, count) = $lb($lg(data, 2))
		s count = $i(count)
	}
	s parminfo = ""
	s idinfo = ""
	q $$$OK
}
  • Sql2Query类似。

定义QueryNameFetch

ClassMethod Query2QueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = Query2QueryExecute ]
{
	s sqlStatement = qHandle.sqlStatement
	s sqlResult =  qHandle.sqlResult
	s colCount = sqlResult.%ResultColumnCount
    if (sqlResult.%Next()) {
        for i = 1 : 1 : colCount{
            s val = sqlResult.%GetData(i)
			if ( $g(row) = "" ) {
				s row = $lb(val)
			} else {
				s row = row _ $lb(val)
			}
        }
        s end = 0 
    } else {
	   s row = ""
	   s end = 1
	}
	s qHandle.sqlResult = sqlResult
    q $$$OK
}
  • Sql2Query类似。

调用Query2Query

USER>d ##class(%ResultSet).RunQuery("M.Query","Query2Query","M.Query","QueryPersonByName")
 
age:id:MT_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

  • 支持传统的Query形式。
  • 支持通过参数形式定义列,不需要指定ROWSPEC参数。
  • 优化将^CacheTemp^||CacheTemp。

定义M.CommonQuery

Class M.CommonQuery Extends %Query
{

ClassMethod Close(ByRef qHandle As %Binary) As %Status [ CodeMode = generator, PlaceAfter = Execute, ProcedureBlock = 1, ServerOnly = 1 ]
{
		s %code($i(%code))= (" s pid = $li(qHandle, 2)")
 		s %code($i(%code))= (" k ^||GlobalTemp(pid)")
 		s %code($i(%code))= (" q $$$OK")
		q $$$OK
}

ClassMethod Fetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ CodeMode = generator, PlaceAfter = Execute, ProcedureBlock = 1, ServerOnly = 1 ]
{
		s %code($i(%code))= (" s end = $li(qHandle, 1)")
		s %code($i(%code))= (" s pid = $li(qHandle, 2)")
		s %code($i(%code))= (" s ind = $li(qHandle, 3)")
		s %code($i(%code))= (" s ind = $o(^||GlobalTemp(pid, ind))")
		s %code($i(%code))= (" if (ind = """") { ")
		s %code($i(%code))= (" 	s end = 1")
		s %code($i(%code))= (" 	s row = """"")
		s %code($i(%code))= (" } else { ")
		s %code($i(%code))= (" 	s row = ^||GlobalTemp(pid, ind)")
		s %code($i(%code))= (" }")
		s %code($i(%code))= (" s qHandle = $lb(end, pid, ind)")
		s %code($i(%code))= (" q $$$OK")
		q $$$OK
}

ClassMethod GetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, ByRef extinfo As %List) As %Status [ CodeMode = generator, ServerOnly = 1 ]
{
		s %code($i(%code))= (" s colinfo = $lb()")
		s %code($i(%code))= (" s column = $lg(qHandle, 4)")
		s %code($i(%code))= (" s len = $l(column, "","")")
		s %code($i(%code))= (" for i = 1 : 1 : len {")
		s %code($i(%code))= (" 	s $li(colinfo, i) = $lb($p(column, "","", i))")
		s %code($i(%code))= (" }")
		s %code($i(%code))= (" s parminfo = """"")
		s %code($i(%code))= (" s idinfo = """"")
		s %code($i(%code))= (" q $$$OK")
		q $$$OK
}

}

定义QueryName

Query CustomColumnQuery(column As %List) As M.CommonQuery
{
}
  • column - 表示要自定义参数列的变量。
  • M.CommonQuery - 自定义Query类型,不需要写GetInfoFetchClose方法。

定义QueryNameExecute

ClassMethod CustomColumnQueryExecute(ByRef qHandle As %Binary, column As %List) As %Status
{
	s pid = $i(^||GlobalTemp)
	s qHandle = $lb(0, pid, 0)
	s $li(qHandle, 4) = column
	s ind = 1
	
	s id = ""
	for {
		s id = $o(^M.T.PersonD(id))
		q:(id = "")
		s data = ^M.T.PersonD(id)
		s i = 1
		s name = $lg(data, $i(i))
		s age = $lg(data, $i(i))
		s no = $lg(data, $i(i))
		d output
	}	
	
    q $$$OK
    
output
	s data = $lb(id, name)
	s ^||GlobalTemp(pid, ind)=data	
	s ind = ind + 1
}

调用CustomColumnQuery

USER>d ##class(%ResultSet).RunQuery("M.Query","CustomColumnQuery","ID,Name")
 
ID:Name:
1:yaoxin:
2:yx:
3:Umansky,Josephine Q.:
4:Pape,Ted F.:
5:Russell,Howard T.:
6:Xenia,Ashley U.:

定义通用Query,只需要实现Exceute方法

实现通用Query,需要通过抽象方法,子类去重写的方式去实现。所以首先定义父类。

定义M.CommonQuery

Class M.BaseQuery Extends %RegisteredObject
{

/// d ##class(%ResultSet).RunQuery("M.BaseQuery","CustomQuery","id,name")
Query CustomQuery(column As %List, arg...) As %Query
{
}

ClassMethod CustomQueryExecute(ByRef qHandle As %Binary, column As %List, arg...) As %Status
{
	s qHandle = $lb(0, 0) // 注释1
	s $li(qHandle, 3) = column // 注释2
	d ..QueryLogic(arg...) // 注释3
    q $$$OK
}

ClassMethod CustomQueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, ByRef extinfo As %List) As %Status
{
	s colinfo = $lb()
	s column = $lg(qHandle ,3)
	s len = $l(column, ",") 
	for i = 1 : 1 : len {
	 	s $li(colinfo, i) = $lb($p(column, ",", i)) // 注释5
	}
	s parminfo = ""
	s idinfo = ""
	q $$$OK
}

ClassMethod CustomQueryClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = CustomQueryExecute ]
{
	k %zQueryList // 注释7
	q $$$OK
}

ClassMethod CustomQueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = CustomQueryExecute ]
{
	s end = $li(qHandle,1)
	s index = $li(qHandle,2)
	s index = $o(%zQueryList(index))
	if index = "" {  // 注释6
		s end = 1
		s row = ""
	} else      { 
		s row = %zQueryList(index)
	}
	s qHandle = $lb(end, index)
	Quit $$$OK
}

ClassMethod QueryLogic(arg...) [ Abstract ]
{
	// 注释4
}

}

  • column - 表示要自定义参数列的变量。
  • arg... - 传入的参数。
  • 注释1代码,这里做了一些改变,qHandle只记录了endindex。因为这里如果是全局变量或者进程私有Global只针对当前进程有效,所以pid可省略。
  • 注释2代码,将qHandle第三个位置传入列头名称。
  • 注释3代码,调用待实现的业务逻辑方法,此方法为抽象方法,需要子类去实现。
  • 注释4代码,子类需要实现的具体业务逻辑,得到数据集。
  • 注释5代码,获取到column动态设置列头。
  • 注释6代码,遍历全局变量。
  • 注释7代码,遍历结束后,将全局变量清空。

定义子类M.PersonQuery继承M.BaseQuery实现QueryLogic方法

  • 这里只需要给%zQueryList($i(count))全局变量赋值即可。固定模版已经抽象到父类。
ClassMethod QueryLogic(arg...)
{
	s pName = arg(1)
	s id = ""
	for {
		s id = $o(^M.T.PersonD(id))
		q:(id = "")
		s data = ^M.T.PersonD(id)
		s i = 1
		s name = $lg(data, $i(i))
		continue:(pName '= "")&&(name '= pName)
		s age = $lg(data, $i(i))
		s no = $lg(data, $i(i))
		s %zQueryList($i(count)) = $lb(id, name, age)
	}
}

调用CustomQuery方法

USER>d ##class(%ResultSet).RunQuery("M.PersonQuery","CustomQuery","ID,Name,Age", "yaoxin")
 
ID:Name:Age:
1:yaoxin:21:

注:这里是用的是全局变量作为数据传递,如果数据过大,则可能会出现内存泄漏问题。改成进程私有Global即可。由读者基于此逻辑自行实现。

注:这种方式一个类只能声明一个Query,如果想一个类声明多个Query,则考虑换成支持传统的Query方式。


总结

  • 理解qHandle参数与GetInfo方法是实现通用Query的关键。
  • 使用通用Query可以提升开发效率。
  • 使用通用Query可以解决数据适配问题。

以上是个人对基于Query的一些理解,由于个人能力有限,欢迎大家提出意见,共同交流。

如果一个好点子,只是因为某个人先到想到就禁止后人使用,这会让整个人类社会多走很多弯路,这也是自由软件精神一直以来所表达的内容。
                                                                                                                                                                                                             - 理查德·马修·斯托曼

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/187351.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nacos区分权限

背景 nacos的默认是不进行分配权限的&#xff0c;那么这样就带来了一个问题&#xff0c;如果多项目共同使用一个nacos&#xff0c;可以带了一个情况是开发人员误操作&#xff0c;把其他项目的nacos配置文件更改或者删除。那么如何解决这个问题呢&#xff1f;就是把nacos进行分…

TF-A源码移植

1.对tf-a源码进行解压$> tar xfz tf-a-stm32mp-2.2.r2-r0.tar.gz 2.打补丁进入/home/ubuntu/FSMP1A/tf-a-stm32mp-2.2.r2-r0/tf-a-stm32mp-2.2.r2-r0/tf-a-stm32mp-2.2.r2目录执行for p in ls -1 ../../*.patch; do patch -p1 < $p; done3.配置工具链1)进入/home/ubuntu/…

GJB 5000B二级-ESM外部供方管理

一、主要变化情况 新增3项(红色)、删除1项(黄色)、合并2项(绿色)、修订4项(蓝色) 将原标准过程域名称“供方协议管理”改为“外部供方管理”,其适应范围包括所以形式的外部提供过程、产品和服务协议,同时增加对外部供方按协议提供过程、产品和服务能力评价的内容,…

小偷和抢劫是被怎么遏制的?

小偷和抢劫是怎么被消灭的&#xff1f; 是被摄像头消灭的&#xff01; 一切土地和实物都会被安装传感设备监控 农业生产用地也会被物联检测 趣讲大白话&#xff1a;万物互联时代稳步实现 *********** 全国有5000万亩茶园 20年内一定会被物联网化 进入精耕农业时代 20年后&…

一篇文章带你了解自动化测试开发

都讲自动化测试开发&#xff0c;当然要把开发自动化测试框架也当做一个项目来做。这时候&#xff0c;就需要考虑应该选择何种类型的自动化测试框架&#xff1a;数据驱动、关键字驱动、还是Junit ,TestNG ? 抑或直接利用现有的开源自动化测试框架&#xff0c;如Robot Framework…

Mysql内核查询成本计算实战(一)

目录 Mysql内核查询成本计算实战&#xff08;一&#xff09; Optimizer Trace 什么是成本 I/O成本 CPU成本 单表查询的成本 MySQL查询成本计算实战 1.根据搜索条件&#xff0c;找出所有可能使用的索引 2. 计算全表扫描的代价 3. 计算使用不同索引执行查询的代价 4. 对…

CAD中怎么局部升降桥架?CAD局部升降操作技巧

在使用浩辰CAD电气软件绘制电气图纸的时候&#xff0c;常常会用到三维桥架中的一些功能来进行桥架的CAD设计工作&#xff0c;为了让大家对此有更深入的了解&#xff0c;接下来的CAD设计教程就和小编一起来看看正版CAD软件——浩辰CAD电气软件的三维桥架中局部升降功能的相关使用…

Magisk模块开发指南

BusyBox Magisk整合了功能完整的BusyBox二进制文件(包括对SELinux的完整支持)。执行文件位于/data/adb/magisk/busybox。Magisk的BusyBox支持运行时可切换的“ASH Standalone Shell Mode(ASH独立Shell模式)”。这种独立模式的意思是,在ashshell的中的BusyBox运行时,无论PATH…

MySQL核心参数优化文件my.ini详解

一.数据库服务器配置 CPU&#xff1a;48C 内存&#xff1a;128G DISK&#xff1a;3.2TSSD 二.CPU的优化 innodb_thread_concurrency32 表示SQL经过解析后&#xff0c;允许同时有32个线程去innodb引擎取数据&#xff0c;如果超过32个&#xff0c;则需要排队&#xff1b; 值太…

spring系列 SpringMVC-拦截器

拦截器&#xff08;Interceptor&#xff09;是在SpringMVC中动态拦截控制器方法的执行。 拦截器执行流程&#xff1a; 拦截器与过滤器区别 归属不同&#xff1a;Filter属于Servlet技术&#xff0c;Interceptor属于SpringMVC技术 拦截内容不同&#xff1a;Filter对所有访问进…

【Mysql第二期 MySQL环境搭建】

文章目录01.为什么要安装新版本&#xff1f;02.官网下载mysql03.安装配置初始化mysql04.查看 MySQL服务05.验证是否安装成功06.修改root密码07.如果有navicat工具可以在测试一下&#xff1a;01.为什么要安装新版本&#xff1f; mysql8.x版本和msyql5.x版本zip安装的方式大同小…

证券交易金融知识学习(1)

学习目标&#xff1a; 需要做一些关于投资交易软件的测试&#xff0c;需要了解操作背后的交易意义&#xff0c;需要学习一些金融基础知识。本人是金融证券交易的小白&#xff0c;从0开始学习。故记录一些金融知识学习的笔记&#xff0c;比较零散&#xff0c;目的是为了让自己复…

Spring-DI相关内容

Spring-DI相关内容 5&#xff0c;DI相关内容 前面我们已经完成了bean相关操作的讲解&#xff0c;接下来就进入第二个大的模块DI依赖注入&#xff0c;首先来介绍下Spring中有哪些注入方式? 我们先来思考 向一个类中传递数据的方式有几种? 普通方法(set方法)构造方法 依赖注…

【数据结构】8.4 选择排序

文章目录1. 简单选择排序简单选择排序算法简单排序算法分析2. 堆排序堆的定义堆的调整堆的建立堆排序算法堆排序算法分析1. 简单选择排序 基本思想 在待排序的数据中选出最大&#xff08;小&#xff09;的元素放在其最终的位置。 基本操作 首先通过 n - 1 次关键字比较&…

计算机SCI期刊能一稿多投吗? - 易智编译EaseEditing

首先建议不要一稿多投&#xff0c;投稿前要对目标期刊了解清楚&#xff0c;是什么方向&#xff0c;什么水平的。 可以看看期刊近期发表的文章&#xff0c;是什么方向的&#xff0c;这样会更精准。 一稿多投就是广撒网嘛&#xff0c;还不如做好功课&#xff0c;找到对应期刊&a…

网络知识详解之:HTTPS通信原理剖析(对称、非对称加密、数字签名、数字证书)

网络知识详解之&#xff1a;HTTPS通信原理剖析&#xff08;对称、非对称加密、数字签名、数字证书&#xff09; 计算机网络相关知识体系详解 网络知识详解之&#xff1a;TCP连接原理详解网络知识详解之&#xff1a;HTTP协议基础网络知识详解之&#xff1a;HTTPS通信原理剖析&…

Es6 扩展运算符... ,以及rest和arguments

扩展运算符... … 扩展运算符能将 数组 转换为逗号分隔的 参数序列 应用场景&#xff1a; 多个数组的合并 var arr4 [1, 2, 3];var arr5 [4, 5, 6];var arr6 [...arr4, ...arr5];//合并数组 也可以为数组的一部分arr6的值应为[1,2,3,4,5,6] 数组的克隆&#xff0…

C语言--指针与数组

目录指针运算&#xff08;补&#xff09;指针指针指针的关系运算&#xff08;补&#xff09;指针与数组数组名二级指针指针数组指针运算&#xff08;补&#xff09; 指针指针 上一篇博客我们介绍了指针运算中的三种常见运算&#xff1a;指针整数&#xff0c;指针关系运算&…

关于android studio安装篇

前言&#xff1a;本文安装环境为windows系统&#xff0c;调试环境AVD&#xff08;电脑上运行的“虚拟手机”&#xff09;&#xff0c;安装android studio之前需安装jdk&#xff0c;配置好jdk的环境变量。解释&#xff1a;android运行调试环境有三种方式&#xff0c;真机、AVD&a…

基于uboot的truested安全启动模式进行TF-A源码移植

步骤 1. 对源码进行解压 tar -xvf tf-a-stm32mp-2.2.r2-r0.tar.gz2. 将补丁文件全打上 for p in ls -1 ../*.patch; do patch -p1 < $p; done3. 配置交叉编译工具链&#xff0c;在TF-A顶层目录下打开Makefile.sdk文件修改&#xff0c;搜索cross_compile&#xff0c;然后进…