通用`Query`解决方案(上)

简介: 通用`Query`解决方案

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

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


  1. 使用%New()创建%SQL.Statement的对象。
  2. 调用该对象的%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对象。
  1. 使用%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
  1. 使用%New()创建%ResultSet的对象。
  • 可以在%New(pClassName _ "||" _ pQueryName )方法传入类的全限定名称与Query名称。
  1. 设置该对象的ClassName属性为类的全限定名称。
  1. 设置该对象的QueryName属性为类的Query名称。
  2. 调用%ResultSet实例的Execute(args...) As %Status 方法,
  • args... - 定义Query的参数,该参数为可变参数。
  1. Execute执行成功后根据%ResultSet对象检索数据。

注:%ResultSet方式使用QueryIRIS中被废弃,不再推荐使用。

通用`Query`解决方案(中):https://developer.aliyun.com/article/1516551

相关文章
|
SQL JSON BI
通用`Query`解决方案(中)
通用`Query`解决方案
183 0
|
9月前
|
人工智能 安全 搜索推荐
宜搭AIOA:AI+低代码重构OA生态,打通人财物事数据流,解锁办公效能新高度
AIOA协同办公解决方案基于钉钉+低代码+AI构建,以钉钉为底座,提供快速迭代的OA应用。方案内置三大主线应用(销售、采购、财务费用闭环),支持独立或关联使用,并预置高频OA场景如固定资产、车辆管理等,实现开箱即用。面对传统办公模式挑战,AIOA从单一到协同、人工到智能、人治到数治、分散到聚合全面升级。工作空间角色化、流程管理一体化、专属AI助理智能化,覆盖营销、采购、费控、资产管理等全生命周期,助力企业高效数字化转型。
693 8
|
JavaScript Java 应用服务中间件
用白薅来的gitlab自带的gitlab-runner部署一个vue项目
用白薅来的gitlab自带的gitlab-runner部署一个vue项目
405 2
|
缓存 网络协议 Shell
ADB各种操作指令详解大汇总
这篇文章提供了ADB(Android Debug Bridge)的详细操作指令汇总,包括设备管理、应用操作、日志查看、文件操作、屏幕截取与录制、Shell命令使用等。
2701 2
|
移动开发 JavaScript 前端开发
HTML5 Web Workers详解
HTML5 Web Workers 允许在后台线程中运行 JavaScript,实现复杂计算而不影响用户界面,提升应用性能。其主要特性包括并行处理、异步通信、独立作用域及多数据类型支持。通过创建和使用 Worker 文件,如 `worker.js`,可执行后台任务,并与主线程通过消息传递机制通信。适用于数据处理、图像处理、复杂计算及网络请求并行等场景。需要注意的是,Web Workers 在浏览器兼容性、安全性限制、调试及资源消耗方面需特别关注。合理利用 Web Workers 可显著增强 Web 应用的流畅度和响应速度。
|
网络协议 Ubuntu 前端开发
好好的容器突然起不来,经定位是容器内无法访问外网了?测试又说没改网络配置,该如何定位网络问题
本文记录了一次解决前端应用集成到主应用后出现502错误的问题。通过与测试人员的沟通,最终发现是DNS配置问题导致的。文章详细描述了问题的背景、沟通过程、解决方案,并总结了相关知识点和经验教训,帮助读者学习如何分析和定位网络问题。
731 1
|
JSON 数据格式
通用`Query`解决方案(下)
通用`Query`解决方案
162 0
通用`Query`解决方案(下)
|
机器学习/深度学习 TensorFlow 语音技术
使用Python实现深度学习模型:语音合成与语音转换
【7月更文挑战第19天】 使用Python实现深度学习模型:语音合成与语音转换
625 1
|
负载均衡 Java 开发者
如何在Spring Boot项目中实现微服务架构?
如何在Spring Boot项目中实现微服务架构?
1141 1