一、CQ简介
类查询(Class Queries
) ,简称CQ
是一种工具,包含在类中,用于动态 SQL
,用于查找满足指定条件的记录。使用类查询,您可以为应用程序创建预定义的查找。例如,您可以按名称
查找记录,或提供满足一组特定条件的记录列表。
通过创建类查询,可以避免按内部 ID
查找特定对象。相反,您可以创建一个基于所需的任何类属性
进行查找的查询。这些甚至可以在运行时从用户输入中指定。
如果定义自定义类查询,则查找逻辑可以使用 Caché ObjectScript
,并且可以任意复杂。
有两种类型的类查询:
%SQLQuery
和 SQL SELECT
语句。%Query
和自定义逻辑来执行、提取和关闭查询。请注意:您可以在任何类中定义类查询;不需要将它们包含在持久类中。
二、使用CQ
在了解如何定义类查询之前,了解如何使用它们很有用。在服务器端代码中,可以使用类查询,如下所示:
%New()
创建 %SQL.Statement
的实例。%PrepareClassQuery()
方法。作为参数,按顺序使用以下内容:
%Status
值,您应该检查该值。%SQL.Statement
的 %Execute()
方法。这将返回 %SQL.StatementResult.
的实例。%SQL.StatementResult
的方法, 从结果集中检索数据。#include %occInclude
set statement=##class(%SQL.Statement).%New()
set status=statement.%PrepareClassQuery("Sample.Person","ByName")
if $$$ISERR(status) { do $system.OBJ.DisplayError(status) }
set resultset=statement.%Execute()
while resultset.%Next() {
write !, resultset.%Get("Name")
}
SqlProc
(将其定义为 ODBC
或 JDBC
存储过程),则可以从 SQL
上下文中将其作为存储过程调用。三、基本CQ
若要定义基本类查询,请按如下方式定义查询:
%SQLQuery
。SQL SELECT
语句。:
)。INTO
子句。ROWSPEC
参数(在括号中,在查询类型之后)。此参数提供有关查询结果集每行中字段的名称、数据类型、标题和顺序的信息。CONTAINID
参数(在括号中,在查询类型之后)。此参数指定包含特定行的 ID 的字段的列号(如果有);默认值为 1。ROWSPEC
和 CONTAINID
参数统称为查询规范。SqlProc
关键字。%ResultSet
调用查询,并且不需要将查询作为存储过程调用,则可以省略此步骤。如果您计划使用 %SQL.Statement
调用查询,必须指定 SqlProc
关键字。SqlName
关键字。%SQLQuery
) 之后。/// d ##Class(%ResultSet).RunQuery("DHCAnt.Auth.Test","ListEmployees","D")
Query ListEmployees(City As %String = "D") As %SQLQuery(CONTAINID = 1, ROWSPEC = "ID:%Integer,TATitleDR:%String,TAControlType:%String") [ SqlName = MyProcedureName, SqlProc ]
{
SELECT ID,TATitleDR,TAControlType FROM CF_DOC_ANT.TitleAuth
WHERE (TAControlType %STARTSWITH :City)
ORDER BY ID
}
查询的 ROWSPEC
参数提供有关每行中字段的名称、数据类型、标题和顺序的信息。它是变量名称和数据类型的带引号和逗号分隔的列表,其形式为:
ROWSPEC = "Var1:%Type1,Var2:%Type2[:OptionalDescription],Var3"
ROWSPEC
以逗号
分隔列表的形式指定字段的顺序。每个字段的信息由以冒号
分隔的名称列表、数据类型(如果与相应属性的数据类型不同)和可选标题
组成。要编辑 ROWSPEC
,选项包括:
Studio Inspector
窗口中显示该查询,展开其参数列表,然后使用可用的对话框。ROWSPEC
参数中的元素数必须与查询中的字段数匹配。否则,Caché 将返回“Cardinality Mismatch”
错误。
Query ByName(name As %String = "")
As %SQLQuery(CONTAINID = 1, ROWSPEC = "ID:%Integer,Name,DOB,SSN", SELECTMODE = "RUNTIME")
[ SqlName = SP_Sample_By_Name, SqlProc ]
{
SELECT ID, Name, DOB, SSN
FROM Sample.Person
WHERE (Name %STARTSWITH :name)
ORDER BY Name
}
此处,CONTAINID
参数指定行ID
是第一个字段(默认值); 请注意,SELECT
语句中指定的第一个字段是 ID
。ROWSPEC
参数指定字段为 ID
(视为整数)、Name、DOB 和 SSN;同样,SELECT 语句按该顺序包含字段 ID、Name、DOB 和 SSN。
CONTAINID
应设置为返回 ID
的列的编号(默认为 1)或 0(如果没有列返回 ID)。如果您使用新建查询向导创建查询,则 Studio
会根据您在该向导中指定的顺序自动为 CONTAINID
分配适当的值。
注意:
Caché
不验证 CONTAINID
的值。如果为此参数指定无效值,则 Caché
不会引发错误。这意味着,如果查询处理逻辑依赖于此信息,则如果 CONTAINID
参数设置不正确,则可能会遇到不一致的情况。
除了 ROWSPEC
和 CONTAINID
之外,还可以指定查询的以下参数。以下是 %SQLQuery
的类参数:
%Library.SQLQuery
和 %Library.Query
(其超类)的类引用。四、自定义CQ
尽管简单的 %SQLQuery
查询会为您执行所有结果集管理,但对于某些应用程序来说,这还不够。对于这种情况,Caché
允许您编写自定义查询,这些查询在方法中定义(默认情况下是用 Caché ObjectScript
编写的)。若要定义自定义查询,请使用本章前面给出的说明,并进行以下更改:
%Query
作为查询类型。Query AllPersons() As %Query(CONTAINID = 1,ROWSPEC = "ID:%String,Name:%String,DOB:%String,SSN:%String")
{
}
querynameExecute
— 此方法必须执行任何一次性设置。querynameFetch
— 此方法必须返回结果集的一行;每个后续调用都返回下一行。querynameClose
— 此方法必须执行任何清理操作。queryname
是查询的名称。这些方法中的每一个都接受一个参数 (qHandle
),该参数通过引用传递。可以使用此参数在这些方法之间传递信息。
querynameExecute()
方法必须提供所需的所有设置逻辑。方法的名称必须是 querynameExecute,其中 queryname
是查询的名称。此方法必须具有以下签名:
ClassMethod queryNameExecute(ByRef qHandle As %Binary,
additional_arguments) As %Status
qHandle
用于与实现此查询的其他方法进行通信。querynameFetch
方法的需要设置 qHandle
。qHandle
在形式上属于 %Binary
类型,但它可以保存任何值,包括 OREF
或多维数组。additional_arguments
是查询可以使用的任何运行时参数。在此方法实现中,使用以下常规逻辑:
querynameFetch
方法的需要设置 qHandle
。ClassMethod AllPersonsExecute(ByRef qHandle As %Binary) As %Status
{
set statement=##class(%SQL.Statement).%New()
set status=statement.%PrepareClassQuery("Sample.Person","ByName")
if $$$ISERR(status) { quit status }
set resultset=statement.%Execute()
set qHandle=resultset
Quit $$$OK
}
在此方案中,该方法将 qHandle
设置为等于 OREF
,是 %SQL.StatementResult,
的实例,这是 %Execute()
方法返回的值。
querynameFetch()
方法必须以 $List
格式返回单行数据。方法的名称必须是 querynameFetch,其中 queryname
是查询的名称。此方法必须具有以下签名:
ClassMethod queryNameFetch(ByRef qHandle As %Binary,
ByRef Row As %List,
ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = querynameExecute ]
qHandle
用于与实现此查询的其他方法进行通信。Caché
开始执行此方法时,qHandle
具有由 querynameExecute
方法或此方法的上一次调用(如果有)建立的值。此方法应根据后续逻辑的需要设置 qHandle
。qHandle
在形式上属于 %Binary
类型,但它可以保存任何值,包括 OREF
或多维数组。Row
必须是表示要返回的数据行的值的 %List
,如果未返回任何数据,则必须为 null
字符串。AtEnd
必须为 1。PlaceAfter
方法关键字控制此方法在生成的例程代码中的位置。对于 querynameExecute,替换特定 querynameExecute() 方法的名称。如果您的查询使用 SQL
游标,请确保包含此游标。(控制此顺序的能力是一项高级功能,应谨慎使用。InterSystems
不建议一般使用此关键字。在此方法实现中,使用以下常规逻辑:
%List
对象,并将其放在 Row
变量中。querynameClose
() 方法需要设置 qHandle
。Row
设置为 null
字符串,并将 AtEnd
设置为 1。ClassMethod AllPersonsFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status
[ PlaceAfter = AllPersonsExecute ]
{
set rs=$get(qHandle)
if rs="" quit $$$OK
if rs.%Next() {
set Row=$lb(rs.%GetData(1),rs.%GetData(2),rs.%GetData(3),rs.%GetData(4))
set AtEnd=0
} else {
set Row=""
set AtEnd=1
}
Quit $$$OK
}
querynameClose()
方法必须在数据检索完成后执行任何需要的清理。方法的名称必须是 querynameClose,其中 queryname
是查询的名称。此方法必须具有以下签名:
ClassMethod queryNameClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = querynameFetch ]
qHandle
用于与实现此查询的其他方法进行通信。Caché
开始执行此方法时,qHandle
具有由上次调用 querynameFetch
方法建立的值。在此方法实现中
ClassMethod AllPersonsClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = AllPersonsFetch ]
{
Set qHandle=""
Quit $$$OK
}
系统自动生成 querynameGetInfo()
和 querynameFetchRows()
。应用程序不直接调用这些方法中的任何一个 — %Library.ResultSet
对象使用它们来处理查询请求。
五、自定义CQ的参数
如果自定义查询应接受参数,请执行以下操作:
MyParm
的参数:Query All(MyParm As %String) As %Query(CONTAINID = 1, ROWSPEC = "Title:%String,Author:%String")
{
}
querynameExecute
方法的参数列表中包含相同的参数,其顺序与查询类成员中的顺序相同。querynameExecute
方法的实现中,根据需要使用参数。六、其他自定义CQ示例
上一节提供了自定义类查询的简单示例,该示例可以很容易地作为基本类查询实现。本节显示了 Caché 类库中一个更典型的示例。
提示:此示例旨在演示可以使用的方法,而不是记录类库如何实现特定功能。
Query ByServer() As %Query(ROWSPEC = "Name,Port,PingPort,Renderer,State,StateEx") [ SqlProc ]
{
}
querynameExecute()
方法,请注意,此方法将数据保存到 process-private
全局变量中,而不是保存到 qHandle
变量中。另请注意,此方法使用较旧的动态 SQL
类 (%ResultSet
)。ClassMethod ByServerExecute(ByRef qHandle As %Binary) As %Status [ Internal ]
{
Set tSC = $$$OK
Try {
Set tRS = ##class(%ResultSet).%New("%ZEN.Report.RenderServer:ByName")
Kill ^||%ISC.ZRS
Set tSC = tRS.Execute()
For {
Quit:'tRS.Next()
Set tType = tRS.Get("ServerType")
If (tType'=0) && (tType'="") Continue // Not a Render Server
Set name = tRS.Get("Name")
Set ^||%ISC.ZRS(name) = $LB(name,tRS.Get("Port"),tRS.Get("PingPort"),tRS.Get("Renderer"))
}
}
Catch (ex) {
Set tSC = ex.AsStatus()
}
Set qHandle = $LB("")
Quit tSC
}
querynameFetch()
方法如下:ClassMethod ByServerFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0)
As %Status [ Internal, PlaceAfter = ByServerExecute ]
{
Set index = $List(qHandle,1)
Set index = $O(^||%ISC.ZRS(index))
If index="" {
Set Row = ""
Set AtEnd = 1
}
Else {
Set Row = ^||%ISC.ZRS(index)
Set stInt = ..GetState($List(Row,2),$List(Row,3),$List(Row,4))
Set stExt = $Case(stInt,0:$$$Text("Inactive"),1:$$$Text("Active"),
2:$$$Text("Unresponsive"),3:$$$Text("Troubled"),4:$$$Text("Error"),
5:$$$Text("Mismatch"),:"")
Set $List(Row,5) = stInt, $List(Row,6) = stExt
}
Set qHandle = $LB(index)
Quit $$$OK
}
最后,querynameClose()
方法如下:
ClassMethod ByServerClose(ByRef qHandle As %Binary) As %Status [ Internal, PlaceAfter = ByServerExecute ]
{
Set qHandle = ""
Kill ^||%ISC.ZRS
Quit $$$OK
}
七、何时使用自定义CQ
以下列表建议了自定义查询适用的一些方案:
querynameFetch()
方法可以包含任意复杂的逻辑。querynameFetch()
方法,以便根据 Row
变量的需要将该格式的数据转换为$List
。querynameExecute()
方法中执行角色升级。querynameExecute()
方法中执行标注,然后将结果存储在 qHandle
或全局
中。querynameExecute()
方法中执行此类工作。八、SQL 游标和CQ
如果类查询使用 SQL
游标,请注意以下几点:
%SQLQuery
类型的查询生成的游标自动名称,例如具有 Q14
等名称。DECLARE
语句(通常在 querynameExecute()
方法中)必须与 Close
和 Fetch
位于同一 MAC
例程中,并且必须位于它们中的任何一个之前。如本章前面所示,在 querynameFetch() 和 querynameClose() 方法定义中使用方法关键字 PlaceAfter
来确保发生这种情况。