清除过滤器
文章
姚 鑫 · 一月 30
# 第十一章 F - H 开头的术语
#### 文件流 (file stream)
**对象(Objects)**
文件流提供了一个接口,用于在外部文件中操作和存储大量基于文本或二进制的数据。`IRIS` 的流接口可以在 `ObjectScript`、`SQL` 和 `Java` 中用于操作文件流。
#### 最终类 (final class)
**对象(Objects)**
不能被扩展或子类化的类。
#### 最终方法 (final method)
**对象(Objects)**
不能被重写的方法。
#### 最终属性 (final property)
**对象(Objects)**
不能被重写的属性。
#### 外键 (foreign key)
**InterSystems SQL**
外键约束表中的一列指向另一表中的另一列。为第一列提供的值必须存在于第二列中。
#### 基础 (foundation)
**医疗保健(Health care)**
在 `InterSystems IRIS for Health™` 和 `HealthShare®` 中,启用了医疗保健互操作性的命名空间。
# 以 G 开头的术语
#### 全局 (global)
**系统**
多维存储结构。全局在 `IRIS` 数据库中使用平衡树技术实现。
#### 全局数据库 (globals database)
**系统**
`IRIS` 的基础逻辑和物理数据存储结构,其中所有数据都存储在称为“全局”的多重下标数组系统中。
#### 全局目录 (global directory)
**系统**
包含全局数据库的目录。它包括数据库文件和目录中所有全局的列表及相关信息。
#### 全局唯一标识符 (GUID)
**系统**
`GUID` 是用于标识实体(如类的实例)的标识符,保证在所`IRIS` 实例中都是唯一的,即使跨多个系统。例如,如果两个独立的`IRIS` 实例使用包含每个实例 `GUID` 的共同类定义,那么将这两个实例的数据合并时不会产生重复的 `GUID` 值。`IRIS` 使用 `GUID` 作为对象同步的一部分。有关使用 `GUID` 的一般信息,请参见类文档 `%ExtentMgr.GUID` 和 `%Library.GlobalIdentifier`。
#### GSA 文件 (GSA file)
**系统**
`GSA` 文件是全局保存文件。`GSA` 文件扩展名不是必需的,但允许 `IRIS` 和程序员轻松识别保存的全局。除了文件扩展名外,`GSA` 文件没有其他特殊意义。
# 以 H 开头的术语
#### 主机名 (host name)
**通用**
服务器系统的名称。
#### 主机变量 (host variable)
**SQL**
在嵌入式 `SQL` 语句中,与应用程序变量关联的变量。
文章
姚 鑫 · 五月 4, 2021
# 第二章 全局变量结构(二)
# 全局变量物理结构
全局变量使用高度优化的结构存储在物理文件中。管理此数据结构的代码也针对运行InterSystems IRIS的每个平台进行了高度优化。这些优化确保全局操作具有高吞吐量(每单位时间的操作数)、高并发性(并发用户总数)、缓存的高效使用,并且不需要与性能相关的持续维护(例如频繁的重建、重新索引或压缩)。
用于存储全局变量的物理结构是完全封装的;应用程序不会以任何方式担心物理数据结构。
全局变量存储在磁盘上的一系列数据块中;每个块的大小(通常为`8KB`)是在创建物理数据库时确定的。为了提供对数据的高效访问,InterSystems IRIS维护了一种复杂的**B树状结构**,该结构使用一组指针块将相关数据块链接在一起。InterSystems IRIS维护一个缓冲池-经常引用的块的内存缓存-以降低从磁盘获取块的成本。
虽然许多数据库技术使用类似`B树`的结构来存储数据,但InterSystems IRIS在许多方面都是独一无二的:
- 存储机构通过安全、易于使用的接口显露出来。
- 压缩下标和数据以节省磁盘空间和宝贵的内存缓存空间。
- 存储引擎针对事务处理操作进行了优化:插入、更新和删除都很快。与关系系统不同,InterSystems IRIS从不需要重建索引或数据来恢复性能。
- 存储引擎针对最大并发访问进行了优化。
- 数据会自动群集,以实现高效检索。
# 引用全局变量
全局变量驻留在特定的InterSystems IRIS数据库中。如果使用适当的映射,全局变量的部分可以驻留在不同的数据库中。数据库可以在物理上位于当前系统上,也可以位于通过ECP网络访问的远程系统上。术语数据集是指包含InterSystems IRIS数据库的系统和目录。
命名空间是共同构成一组相关信息的数据集和全局映射的逻辑定义。
简单的全局变量引用适用于当前选定的命名空间。名称空间定义可能导致它物理访问本地系统或远程系统上的数据库。不同的全局变量可以映射到不同的位置或数据集(其中数据集是指包含InterSystems IRIS数据库的系统和目录)。
例如,要在当前已映射到的命名空间中创建对全局顺序的简单引用,请使用以下语法:
```java
^ORDER
```
## 设置全局变量映射
可以将全局变量和例程从一个数据库映射到相同或不同系统上的另一个数据库。这允许简单地引用可以存在于任何地方的数据,这是命名空间的主要特征。可以映射整个全局或部分全局;映射全局(或下标)的一部分称为下标级别映射(`SLM`)。因为可以映射全局下标,所以数据可以轻松地跨磁盘。
全局映射是分层应用的。例如,如果NSX命名空间有一个关联的`DBX`数据库,但将`^x`全局变量映射到`DBY`数据库,将`^x(1)`映射到`DBZ`数据库,则`^x`全局变量的任何下标形式(属于`^x(1)`层次结构的那些除外)都映射到`DBY`;属于`^x(1)`层次结构的那些全局变量映射到DBZ。下图说明了此层次结构:

在此图中,全局变量及其层次结构显示为灰色,它们映射到的数据库显示为黑色。
还可以将映射的、下标的全局的一部分映射到另一个数据库,甚至映射回初始全局映射到的数据库。假设前面的示例有`^x(1,2)`全局变量返回到`DBY`数据库的附加映射。这将如下所示:

同样,全局变量及其层次结构显示为灰色,它们映射到的数据库显示为黑色。
一旦将全局从一个命名空间映射到另一个命名空间,就可以引用映射的全局变量,就像它在当前命名空间中一样-只需一个简单的引用,如`^Order`或`^X(1)`。
重要提示:建立下标级别映射范围时,字符串下标的行为与整数下标的行为不同。对于字符串,第一个字符确定范围,而对于整数,范围使用数值。例如,下标范围`("A"):("C")` 不仅包含`AA`,还包含`AC`和`ABCDEF`;相比之下,下标范围`(1):(2)` 不包含`11`。
### 使用全局和下标的不同范围
命名空间的每个映射必须引用不同范围的全局变量或下标。映射验证可防止建立任何类型的重叠。例如,如果使用管理门户创建与现有映射重叠的新映射,则门户会阻止这种情况发生,并显示一条错误消息。
### 记录更改
通过门户对映射的成功更改也会记录在`messages.log`中;不成功的更改不会记录。通过手动编辑配置参数(CPF)文件来建立映射的任何失败尝试都会记录在`messages.log`中.
## 扩展的全局变量引用
可以引用位于当前命名空间以外的命名空间中的全局变量。这称为扩展全局变量引用或简称为扩展引用。
有两种形式的扩展引用:
- 显式命名空间引用-将全局所在命名空间的名称指定为全局变量引用语法的一部分。
- 隐含名称空间引用-指定目录和系统名称(可选)作为全局变量引用语法的一部分。在这种情况下,不适用全局变量映射,因为物理数据集(目录和系统)是作为全局变量引用的一部分提供的。
最好使用显式名称空间,因为这允许在需求更改时在外部重新定义逻辑映射,而无需更改应用程序代码。
InterSystems IRIS支持两种形式的扩展引用:
- 方括号语法,它用方括号(`[]`)将扩展引用括起来。
- 环境语法,用竖线(`||`)括起扩展引用。
注意:扩展全局引用的示例使用`Windows`目录结构。实际上,此类引用的形式取决于操作系统。
### 方括号语法
可以使用方括号语法来指定具有显式命名空间或隐含命名空间的扩展全局引用:
显式命名空间:
```java
^[nspace]glob
```
隐含命名空间:
```java
^[dir,sys]glob
```
在显式名称空间引用中,`nspace`是全局全局当前尚未映射或复制到的已定义名称空间。在隐含的名称空间引用中,`dir`是目录(其名称包括尾随反斜杠:`“\”`),`sys`是`SYSTEM`,`glob`是该目录中的全局目录。如果将`nspace`或`dir`指定为(`“^”`),则引用的是进程私有全局变量。
除非将目录和系统名称或命名空间名称指定为变量,否则必须在目录和系统名称或命名空间名称两边加上引号。目录和系统一起构成一个隐含的命名空间。隐含的命名空间可以引用以下任一项:
- 指定系统上的指定目录。
- 本地系统上的指定目录(如果未在引用中指定系统名称)。如果在隐含的命名空间引用中省略了系统名称,则必须在目录引用内提供双脱字符(`^^`)以指示省略的系统名称。
要在远程系统上指定隐式命名空间,请执行以下操作:
```java
["dir","sys"]
```
在本地系统上指定一个隐含的命名空间:
```java
["^^dir"]
```
例如,要访问名为SALES的计算机上的`C:\BUSINESS\`目录中的全局变量`ORDER`:
```java
SET x = ^["C:\BUSINESS\","SALES"]ORDER
```
要访问本地计算机上的`C:\BUSINESS\`目录中的全局`ORDER`:
```java
SET x = ^["^^C:\BUSINESS\"]ORDER
```
要访问定义的命名空间`MARKETING`中的全局`ORDER`:
```java
SET x = ^["MARKETING"]ORDER
```
要访问进程私有的全局`ORDER`:
```java
SET x = ^["^"]ORDER
```
注意:在创建涉及镜像数据库的隐含命名空间扩展引用时,可以使用镜像数据库路径,格式为`:mirror:mirror_name:mirror_DB_name`。
例如,当在镜像`CORPMIR`中引用镜像数据库名称为mirdb1的数据库时,可以形成如下的隐含引用:
```java
["^^:mirror:CORPMIR:mirdb1"]
```
镜像数据库路径既可以用于本地数据库,也可以用于远程数据库。
### 环境语法
环境语法被定义为:
```java
^|"env"|global
```
`"env"`可以有以下五种格式之一:
- 空字符串(`""`)-本地系统上的当前命名空间。
- `"namespace"` -定义的命名空间,当前没有全局映射到。
命名空间名称不区分大小写。
如果`namespace`具有特殊值`"^"`,则它是进程私有的全局变量。
- `"^^dir"` -一个隐含的命名空间,它的默认目录是本地系统上的指定目录,其中dir包含一个末尾的反斜杠(`" \ ")`。
- `"^system^dir"`——一个隐含的命名空间,默认目录是指定的远程系统上的指定目录,其中`dir`包含一个结尾的反斜杠(`" \ "`)。
- 省略-如果根本没有`"env"`,它是进程私有的全局变量。
要访问当前系统上当前命名空间中的全局`ORDER`,如果没有为`ORDER`定义映射,请使用以下语法:
```java
SET x = ^|""|ORDER
```
这与简单的全局变量引用相同:
```java
SET x = ^ORDER
```
要访问映射到定义的命名空间`MARKETING`的全局`ORDER`:
```java
SET x = ^|"MARKETING"|ORDER
```
可以使用一个隐含的命名空间来访问本地系统上`C:\BUSINESS\`目录下的全局`ORDER`:
```java
SET x = ^|"^^C:\BUSINESS\"|ORDER
```
可以使用一个隐含的命名空间来访问一个名为`SALES`的远程系统上的目录`C:\BUSINESS`中的全局`ORDER`:
```java
SET x = ^|"^SALES^C:\BUSINESS\"|ORDER
```
要访问进程私有的全局`ORDER`:
```java
SET x = ^||ORDER
SET x=^|"^"|ORDER
```
文章
姚 鑫 · 七月 3, 2021
# 第二十六章 定制 SAX解析器的使用方式
每当InterSystems IRIS读取XML文档时,它都会使用InterSystems IRIS SAX(Simple API For XML)解析器。本章介绍用于控制系统间IRIS SAX解析器的选项。
# 关于IRIS SAX解析器
每当InterSystems IRIS读取XML文档时,都会使用InterSystems IRIS SAX解析器。
它是一个事件驱动的XML解析器,读取XML文件,并在找到感兴趣的项(如XML元素的开始、DTD的开始等)时发出回调。
(更准确地说,解析器与内容处理程序协同工作,内容处理程序发出回调。只有在自定义SAX接口时,此区别才很重要,如本章后面的“创建自定义内容处理程序”中所述。)
解析器使用标准Xerces-C++库,该库符合`XML1.0`推荐标准和许多相关标准。
# 可用的解析器选项
可以通过以下方式控制SAX解析器的行为:
- 可以设置标志来指定要执行的验证和处理类型。
请注意,解析器始终检查文档是否为格式良好的XML文档。
- 可以指感兴趣的事件(即希望解析器查找的项目)。为此,需要指定一个掩码来指示感兴趣的事件。
- 可以提供验证文档所依据的架构规范。
- 可以使用特殊用途的实体解析器禁用实体解析。
- 可以指定实体解析的超时期限。
- 如果需要控制解析器如何查找文档中任何实体的定义,则可以指定更通用的自定义实体解析器。
- 如果通过URL访问源文档,则可以将发送到Web服务器的请求指定为%Net.HttpRequest的实例。
- 可以指定自定义内容处理程序。
- 可以使用HTTPS。
可用的选项取决于如何使用InterSystems IRIS SAX Parser,如下表所示:
%XML类中的SAX解析器选项
Option | %XML.Reader | %XML.TextReader |%XML.XPATH.Document | %XML.SAX.Parser
---|---|---|---|---
指定解析器标志 |supported |supported| supported| supported
指定感兴趣的解析事件(例如,元素的开始、元素的结束、注释)| not supported| supported| not supported| supported
指定模式规范| supported| supported| supported| supported
禁用实体解析或以其他方式定制实体解析| supported| supported| supported| supported
指定自定义HTTP请求(如果解析URL)| not supported| supported| not supported |supported
指定内容处理程序| not supported| not supported| not supported| supported
在HTTPS位置解析文档| supported| not supported| not supported| supported
解析HTTPS位置上的实体| not supported| not supported| not supported| supported
# 指定解析器选项
指定不同的解析器行为取决于你如何使用InterSystems IRIS SAX解析器:
- 如果使用`%XML.Reader`,可以设置阅读器实例的`Timeout`、`SAXFlags`、`SAXSchemaSpec`和`EntityResolver`属性。
例如:
```java
#include %occInclude
#include %occSAX
// set the parser options we want
Set flags = $$$SAXVALIDATION
+ $$$SAXNAMESPACES
+ $$$SAXNAMESPACEPREFIXES
+ $$$SAXVALIDATIONSCHEMA
Set reader=##class(%XML.Reader).%New()
Set reader.SAXFlags=flags
```
这些宏是在`%occSAX`中定义的。公司包含文件。
- 在其他情况下,指定所使用方法的参数。例如:
```java
#include %occInclude
#include %occSAX
//set the parser options we want
Set flags = $$$SAXVALIDATION
+ $$$SAXNAMESPACES
+ $$$SAXNAMESPACEPREFIXES
+ $$$SAXVALIDATIONSCHEMA
Set status=##class(%XML.TextReader).ParseFile(myfile,.doc,,flags)
```
# 设置解析器标志
`%occSAX.inc` include文件列出了可用于控制`Xerces`解析器执行的验证的标志。基本标志如下:
- `$$$SAXVALIDATION` -是否执行模式验证。如果此标志为开启(默认值),则报告所有验证错误。
- `$$$SAXNAMESPACES`-指定是否识别命名空间。如果此标志为ON(默认值),解析器将处理命名空间。如果此标志为OFF,InterSystems IRIS会导致`%XML.SAX.ContentHandler`的`startElement()`回调中元素的`localname`为空字符串。
- `$$$SAXNAMESPACEPREFIXES`-指定是否处理命名空间前缀。如果此标志为`ON`,解析器将报告用于名称空间声明的原始前缀名称和属性。默认情况下,此标志处于关闭状态。
- `$$$SAXVALIDATIONDYNAMIC` - 指定是否动态执行验证。如果此标志为`ON`(默认设置),则仅在指定语法时才执行验证。
- `$$$SAXVALIDATIONSCHEMA` -指定是否针对架构执行验证。如果此标志为`ON`(缺省设置),则针对给定模式(如果有的话)执行验证。
- `$$$SAXVALIDATIONSCHEMAFULLCHECKING` - 指定是否执行完整架构约束检查,包括耗时或内存密集型检查。如果此标志处于打开状态,则执行所有约束检查。默认情况下,此标志处于关闭状态。
- `$$$SAXVALIDATIONREUSEGRAMMAR` - 指定是否缓存语法以供以后在同一IRIS进程内的分析中重复使用。默认情况下,此标志处于关闭状态。
- `$$$SAXVALIDATIONPROHIBITDTDS` - 在遇到DTD时导致解析器抛出错误的特殊标志。如果需要阻止处理DTD,请使用此标志。要使用此标志,必须将值`$$$SAXVALIDATIONPROHIBITDTDS`显式添加到传递给`%XML.SAX.Parser`的各种分析方法的分析标志。
以下附加标志提供了基本标志的有用组合:
- `$$$SAXDEFAULTS` - 相当于SAX默认值。
- `$$$SAXFULLDEFAULT` - 等同于SAX默认值,外加处理名称空间前缀的选项。
- `$$$SAXNOVALIDATION` - 不执行架构验证,但可以识别命名空间和命名空间前缀。请注意,SAX解析器总是检查文档是否为格式良好的XML文档。
以下片段显示了如何组合解析器选项:
```
...
#include %occInclude
#include %occSAX
...
;; set the parser options we want
set opt = $$$SAXVALIDATION
+ $$$SAXNAMESPACES
+ $$$SAXNAMESPACEPREFIXES
+ $$$SAXVALIDATIONSCHEMA
...
set status=##class(%XML.TextReader).ParseFile(myfile,.doc,,opt)
//check status
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit}
```
# 指定事件掩码
基本标志如下:
- `$$$SAXSTARTDOCUMENT` — 指示分析器在启动文档时发出回调。
- `$$$SAXENDDOCUMENT` — 指示分析器在结束文档时发出回调。
- `$$$SAXSTARTELEMENT` — 指示分析器在找到元素开头时发出回调。
- `$$$SAXENDELEMENT` — 指示分析器在找到元素末尾时发出回调。
- `$$$SAXCHARACTERS` — 指示分析器在找到字符时发出回调。
- `$$$SAXPROCESSINGINSTRUCTION` — 指示分析器在找到处理指令时发出回调。
- `$$$SAXSTARTPREFIXMAPPING` — 指示分析器在找到前缀映射的开始时发出回调。
- `$$$SAXENDPREFIXMAPPING` — 指示分析器在找到前缀映射末尾时发出回调。
- `$$$SAXIGNORABLEWHITESPACE` — 指示分析器在发现可忽略的空格时发出回调。这仅适用于文档具有DTD并且启用了验证的情况。
- `$$$SAXSKIPPEDENTITY` — 指示分析器在找到跳过的实体时发出回调。
- `$$$SAXCOMMENT` — 指示分析器在找到注释时发出回调。
- `$$$SAXSTARTCDATA` — 指示分析器在找到`CDATA`节的开头时发出回调。
- `$$$SAXENDCDATA` —指示分析器在找到`CDATA`节末尾时发出回调。
- `$$$SAXSTARTDTD` —指示分析器在找到`DTD`的开头时发出回调。
- `$$$SAXENDDTD` —指示分析器在找到`DTD`结尾时发出回调。
- `$$$SAXSTARTENTITY` — 指示分析器在找到实体的开头时发出回调。
- `$$$SAXENDENTITY` — 指示分析器在找到实体末尾时发出回调。
## 方便的组合标志
以下附加标志提供了基本标志的有用组合:
- `$$$SAXCONTENTEVENTS` — 指示解析器对任何包含`“content”`的事件发出回调。
- `$$$SAXLEXICALEVENT` — 指示解析器向任何词汇事件发出回调。
- `$$$SAXALLEVENTS` —指示解析器对所有事件发出回调。
## 将标志组合成单个掩码
下面的片段展示了如何将多个标志组合成一个掩码:
```java
...
#include %occInclude
#include %occSAX
...
// set the mask options we want
set mask = $$$SAXSTARTDOCUMENT
+ $$$SAXENDDOCUMENT
+ $$$SAXSTARTELEMENT
+ $$$SAXENDELEMENT
+ $$$SAXCHARACTERS
...
// create a TextReader object (doc) by reference
set status = ##class(%XML.TextReader).ParseFile(myfile,.doc,,,mask)
```
# 指定模式文档
可以指定用于验证文档源的模式规范。指定一个包含逗号分隔的命名空间/URL对列表的字符串:
```java
"namespace URL,namespace URL,namespace URL,..."
```
这里的名称空间是XML名称空间(而不是名称空间前缀),URL是提供该名称空间的模式文档位置的URL。
在命名空间和URL值之间有一个空格字符。
例如,下面显示了一个具有单个命名空间的模式规范:
```java
"http://www.myapp.org http://localhost/myschemas/myapp.xsd"
```
下面是一个包含两个命名空间的模式规范:
```java
"http://www.myapp.org http://localhost/myschemas/myapp.xsd,http://www.other.org http://localhost/myschemas/other.xsd"
```
# 禁用实体解析
即使在设置SAX标志以禁用验证时,SAX解析器仍然试图解析外部实体,这可能非常耗时,具体取决于它们的位置。
类`%XML.SAX.NullEntityResolver`实现始终返回空流的实体解析器。如果要禁用实体解析,请使用此类。具体地说,在读取XML文档时,请使用`%XML.SAX.NullEntityResolver`的实例作为实体解析器。例如:
```java
Set resolver=##class(%XML.SAX.NullEntityResolver).%New()
Set reader=##class(%XML.Reader).%New()
Set reader.EntityResolver=resolver
Set status=reader.OpenFile(myfile)
...
```
重要提示:由于此更改将禁用所有外部实体解析,因此此技术还将禁用XML文档中的所有外部DTD和模式引用。
文章
姚 鑫 · 二月 18, 2021
# 第四十章 Caché 变量大全 $ZREFERENCE 变量
包含当前全局变量global引用。
# 大纲
```java
$ZREFERENCE
$ZR
```
# 描述
`$ZREFERENCE`包含上次全局引用的名称和下标。这就是所谓裸指针。
注意:最后一个全局引用是最近访问的全局节点。通常,这是对全局的最新显式引用。但是,某些命令可能在内部使用`$ORDER`函数遍历全局下标(`ZWRITE`命令就是一个例子),或者它们可能在内部引用其他全局脚本。发生这种情况时,`$ZREFERENCE`包含上次访问的全局节点,该节点可能不是为命令指定的全局节点。
最后一个全局引用可以是全局(`^myglob`)或进程专用全局(`^||myppg`)。`$ZREFERENCE`以最初用于该变量的形式返回进程专用全局前缀,而不管随后对该变量使用哪个进程专用全局前缀。在接下来的`$ZREFERENCE`描述中,单词`“global”`指的是这两种类型的变量。
最后一个全局引用是命令或函数最近引用的全局。由于ObjectScript按从左到右的顺序执行操作,因此最后一个全局引用始终是最右侧的全局引用。当命令或函数使用多个参数时,最右侧参数中指定的全局参数是最后一个全局引用。当参数包含多个全局引用时,最右侧指定的全局引用是最后一个全局引用。即使使用圆括号来定义操作顺序,从左到右的严格顺序也是正确的。
当发出显式全局引用时,InterSystems IRIS会更新`$ZREFERENCE`。调用计算结果为全局引用的表达式(如局部变量)不会更新`$ZREFERENCE`。
`$ZREFERENCE`包含最新的全局引用,即使此全局引用不成功。当命令引用未定义的全局时,会发出``错误,InterSystems IRIS会将`$ZREFERENCE`更新为该全局引用,就像定义了全局一样。此行为不受设置`%SYSTEM.Process.Unfined()`方法的影响。
`$ZREFERENCE`通常包含最新的全局引用,即使命令执行不成功。InterSystems IRIS在引用每个全局变量时更新`$ZREFERENCE`。例如,发出``错误(试图将数字除以0)的命令会将`$ZREFERENCE`更新为错误发生前命令中引用的最后一个全局变量。但是,``错误不会更新`$ZREFERENCE`。
## 长全局变量名称
如果全局名称超过31个字符(不包括全局前缀字符,如`^`),`$ZREFERENCE`将返回缩短为31个字符的全局名称。
## 裸全局变量引用
如果上一个全局引用是裸全局引用,则`$ZREFERENCE`包含当前裸全局引用的外部、可读的完整形式。下面的示例演示了这一点:
```java
/// d ##class(PHA.TEST.SpecialVariables).ZREFERENCE()
ClassMethod ZREFERENCE()
{
SET ^MyData(1)="fruit"
SET ^MyData(1,1)="apples" ; 完整的全局变量引用
SET ^(2)="oranges" ; 裸全局变量引用, 映射 ^MyData(1,2)
WRITE !,$ZREFERENCE ; Returns "^MyData(1,2)"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZREFERENCE()
^MyData(1,2)
```
## 扩展局变量引用
扩展局变量引用用于引用当前命名空间以外的命名空间中的全局。如果命令使用扩展全局引用引用全局变量,则`$ZREFERENCE`值将包含该扩展全局引用。在以下情况下,InterSystems IRIS返回扩展的全局引用:
- 如果最后一个全局引用使用扩展引用来引用另一个命名空间中的全局。
- 如果最后一个全局引用使用扩展引用来引用当前命名空间中的全局。
- 如果最后一个全局引用是远程引用(远程系统上的全局引用)。
在所有情况下,`$ZREFERENCE`都以全部大写字母返回命名空间名称,而不管它在全局引用中是如何指定的。
## 更新`$ZREFERENCE`的操作
`$ZREFERENCE`特殊变量被初始化为空字符串(`“”`)。更改当前名称空间会将`$ZREFERENCE`重置为空字符串。
以下操作将`$ZREFERENCE`设置为最近引用的`GLOBAL`:
- 使用全局变量作为参数的命令或函数。如果它使用多个全局变量,则`$ZREFERENCE`被设置为全局变量的最右侧匹配项。(但请注意,`$ORDER`除外。)
- 使用全局作为后置条件表达式的命令。
- 在`ZWRITE`之后,InterSystems IRIS将`$ZREFERENCE`设置为指定全局引用的上次访问的下标节点。
- 引用未定义的全局变量的命令或函数,它或者生成``错误,或者在`$INCREMENT`的情况下定义全局变量。
## 设置`$ZREFERENCE`
可以使用`set`命令设置此特殊变量,如下所示:
- 设置为空字符串(`“”`)。这样做会删除裸指示器。如果下一个全局引用是裸全局引用,则InterSystems IRIS会发出``错误。
- 设置为有效的全局参照(已定义或未定义)。这会导致后续的裸引用使用设置的值,就好像它是最后一个实际的全局引用一样。
不能使用`SET`命令以其他方式修改`$ZREFERENCE`。尝试这样做会导致``错误。
# 示例
下面的示例返回最后一个全局引用:
```java
/// d ##class(PHA.TEST.SpecialVariables).ZREFERENCE1()
ClassMethod ZREFERENCE1()
{
SET ^a(1,1)="Hello" ; 完整的全局变量引用
SET ^(2)=" world!" ; 裸全局变量引用
WRITE $ZREFERENCE
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZREFERENCE1()
^a(1,2)
```
下面的示例从几个不同的命令返回全局引用。请注意,`WRITE`和`ZWRITE`设置同一全局引用的不同表示形式。
```java
/// d ##class(PHA.TEST.SpecialVariables).ZREFERENCE2()
ClassMethod ZREFERENCE2()
{
SET (^barney,^betty,^wilma,^fred)="flintstone"
WRITE !,"1:"_$ZREFERENCE
KILL ^flies
WRITE !,"2:"_$ZREFERENCE
WRITE !,^fred
WRITE !,"3:"_$ZREFERENCE,!
ZWRITE ^fred
WRITE !,"4:"_$ZREFERENCE
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZREFERENCE2()
1:^fred // 按从左到右顺序设置的几个全局变量中的最后一个
2:^flies // KILL设置全局指示器,但没有全局要杀死的指示器
flintstone
3:^fred
^fred="flintstone"
4:^fred("")
```
下面的示例返回扩展的全局引用。请注意,名称空间名称始终以大写字母返回:
```java
/// d ##class(PHA.TEST.SpecialVariables).ZREFERENCE3()
ClassMethod ZREFERENCE3()
{
SET ^["samples"]a(1,1)="Hello"
SET ^(2)=" world!"
WRITE $ZREFERENCE
QUIT
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZREFERENCE3()
^["SAMPLES"]a(1,2)
```
下面的示例返回最后一个全局引用。在本例中,它是`^a(1)`,用作`$LENGTH`函数的参数:
```java
/// d ##class(PHA.TEST.SpecialVariables).ZREFERENCE4()
ClassMethod ZREFERENCE4()
{
SET ^a(1)="abcdefghijklmnopqrstuvwxyz"
SET ^b(1)="1234567890"
SET x=$LENGTH(^a(1))
WRITE $ZREFERENCE
QUIT
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZREFERENCE4()
^a(1)
```
下面的示例返回为`$ZREFERENCE`设置的值,就好像它是最后一个全局引用一样。在本例中,它是`^a(1,1)`。
```java
/// d ##class(PHA.TEST.SpecialVariables).ZREFERENCE5()
ClassMethod ZREFERENCE5()
{
SET ^a(1,1)="abcdefghijklmnopqrstuvwxyz"
SET ^b(1,1)="1234567890"
WRITE !,^(1) ; 裸全局变量引用使用上一个全局变量:
SET $ZREFERENCE="^a(1,1)"
WRITE !,$ZREFERENCE
WRITE !,^(1) ; 裸全局变量使用$ZREFERENCE值,而不是最后一个全局值。.
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZREFERENCE5()
1234567890
^a(1,1)
abcdefghijklmnopqrstuvwxyz
```
下面的示例设置扩展的全局引用。请注意双引号:
```java
/// d ##class(PHA.TEST.SpecialVariables).ZREFERENCE6()
ClassMethod ZREFERENCE6()
{
KILL ^x
WRITE !,$ZREFERENCE
SET $ZREFERENCE="^[""samples""]a(1,2)"
WRITE !,$ZREFERENCE
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZREFERENCE6()
^x
^["SAMPLES"]a(1,2)
```
文章
姚 鑫 · 六月 18, 2021
# 第十一章 重新定义读取器处理相关对象的方式
# 重新定义读取器处理相关对象的方式
当`%XML.Reader`找到与启用了XML的类相关的XML元素时,读取器会调用该类的`XMLNew()`方法,后者又会在默认情况下调用`%New()`。也就是说,当读取器找到相关元素时,它会创建相关类的新对象。新对象由从XML文档读取的数据填充。
可以通过在启用XML的类中(或在自己的自定义XML适配器中)重新定义`XMLNew()`来自定义此行为。例如,此方法可以改为打开该类的现有实例。然后,现有实例接收从XML文档读取的数据。
以下示例显示如何修改`XMLNew()`以使用XML文档中的新数据更新现有实例。
在这两个示例中,为简单起见,我们假设XML文档中的一个节点包含一个ID,我们可以将该ID与类的范围中的ID进行比较。当然,我们可以用其他方式将XML文档与现有对象进行比较。
## 当`%XML.Reader`调用`XMLNew()`时
作为参考,`%XML.Reader`在两种情况下自动调用`XMLNew()`方法:
- `%XML.Reader`在调用`%XML.Reader`的`Next()`方法调用`XMLNew()`。在将XML元素(在外部文档中)与启用了XML的类关联之后,`%XML.Reader` `Next()`方法从文档中获取下一个元素,调用`XMLNew()`创建相应对象的实例,然后将该元素导入到对象中。
- 同样,`%XML.Reader`为相关XML元素的任何对象值属性调用`XMLNew()`。
## 示例1:修改启用XML的类中的`XMLNew()`
```java
4
Quine,Maria K.
1964-11-14
Hialeah
94999
Vanzetti,Debra B.
...
```
此文件映射到以下InterSystems IRIS类(部分显示):
```java
Class GXML.PersonWithXMLNew Extends (%Persistent, %Populate, %XML.Adaptor)
{
Parameter XMLNAME = "Person";
/// make sure this is the same as the XMLNAME of the property
/// in this class that is of type %XML.Id
Parameter NAMEOFEXPORTID As %String = "IRISID";
Property IdForExport As %XML.Id(XMLNAME = "IRISID", XMLPROJECTION = "ELEMENT") [ Private, Transient ];
Property Name As %Name;
Property DOB As %Date(FORMAT = 5, MAXVAL = "+$h");
Property Address As GXML.Address;
Property Doctors As list Of GXML.Doctor;
```
在该类中,`IdForExport`属性的用途是在导出该类的对象时将InterSystems IRIS内部ID投影到元素(`IRISID`)。(在此特定示例中,这使我们能够轻松地生成适合导入的文件。类不必包含这样的属性。)
`NAMEOFEXPORTID`参数用于指示导出此类对象时用于InterSystems IRIS ID的元素。包含这一点只是为了方便自定义的`XMLNew()`方法,我们也将该方法添加到该类中。该方法定义如下:
```java
ClassMethod XMLNew(doc As %XML.Document, node As %Integer, contOref As %RegisteredObject = "") As GXML.PersonWithXMLNew
{
Set id=""
Set tmpnode=doc.GetNode(node)
Do tmpnode.MoveToFirstChild()
Do {
//将数据节点与NAMEOFEXPORTID参数提供的字符串进行比较
//指示此对象的ID的XMLNAME
If tmpnode.NodeData=..#NAMEOFEXPORTID {
//从该节点获取文本;这与数据库中的id相对应
Do tmpnode.GetText(.id)}
} While tmpnode.MoveToNextSibling()
//如果给定节点中没有id,则创建一个新对象
If id="" {
Write !, "正在创建新对象..."
Quit ..%New()}
//打开给定对象
Set result=..%OpenId(id)
//如果id与现有对象不对应,则创建一个新对象
If result=$$$NULLOREF {
Write !, "正在创建新对象..."
Quit ..%New()}
Write !, "正在更新现有对象..."
Quit result
}
```
`%XML.Reader`读取`XML`文档并将节点关联到`GXML.PersonWithXMLNew`时调用此方法。此方法查看此类中的`NAMEOFEXPORTID`参数的值,即`IRISID`。然后,它使用元素`IRISID`检查文档中的节点并获取其值。
如果此`ID`对应于此类的现有对象,则该方法将打开该实例。否则,该方法将打开此类的新实例。在这两种情况下,实例都会接收`XML`文档中指定的属性。
最后,以下实用程序类包含一个方法,该方法打开`XML`文件并在新窗口中调用`%XML.Reader`:
```java
/// w ##class(PHA.TEST.Xml).ReadFile()
ClassMethod ReadFile(filename As %String = "e:\temp\xmlnewtest.xml")
{
Set reader=##class(%XML.Reader).%New()
Set sc=reader.OpenFile(filename)
If $$$ISERR(sc) {Do $system.OBJ.DisplayError(sc) Quit }
Do reader.Correlate("Person","GXML.PersonWithXMLNew")
//loop through elements in file
While reader.Next(.person,.sc) {
Write !,person.Name,!
Set sc=person.%Save()
If $$$ISERR(sc) {Do $system.OBJ.DisplayError(sc) Quit }
}
Quit ""
}
```
运行上述方法时,文件中的每个``元素都会发生以下情况之一:
- 打开现有对象,使用文件中的详细信息进行更新,然后保存。
- 或者创建一个新对象,其中包含文件中的详细信息。
```java
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在创建新对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在创建新对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在创建新对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在更新现有对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在更新现有对象...
Quine,Maria K.
DHC-APP>w ##class(PHA.TEST.Xml).ReadFile()
正在更新现有对象...
Quine,Maria K.
```
## 示例2:在自定义XML适配器中修改XMLNew()
在第二个示例中,我们创建一个自定义XML适配器来执行与第一个示例相同的操作。适配器类如下所示:
```java
Class GXML.AdaptorWithXMLNew Extends %XML.Adaptor
{
/// 确保这与该类中属性的XMLNAME相同 它的类型为%XML.Id
Parameter NAMEOFEXPORTID As %String = "IRISID";
Property IdForExport As %XML.Id(XMLNAME = "IRISID", XMLPROJECTION = "ELEMENT") [ Private, Transient ];
ClassMethod XMLNew(document As %XML.Document, node As %Integer, containerOref As %RegisteredObject = "") As %RegisteredObject [ CodeMode = objectgenerator, GenerateAfter = %XMLGenerate, ServerOnly = 1 ]
{
If %compiledclass.Name'="GXML.AdaptorWithXMLNew" {
Do %code.WriteLine(" Set id=""""")
Do %code.WriteLine(" Set tmpnode=document.GetNode(node)")
Do %code.WriteLine(" Do tmpnode.MoveToFirstChild()")
Do %code.WriteLine(" Do {")
Do %code.WriteLine(" If tmpnode.NodeData=..#NAMEOFEXPORTID ")
Do %code.WriteLine(" {Do tmpnode.GetText(.id)}")
Do %code.WriteLine(" } While tmpnode.MoveToNextSibling() ")
Do %code.WriteLine(" If id="""" {")
Do %code.WriteLine(" Write !,""Creating new object...""")
Do %code.WriteLine(" Quit ##class("_%class.Name_").%New()}")
Do %code.WriteLine(" set result=##class("_%class.Name_").%OpenId(id)")
Do %code.WriteLine(" If result=$$$NULLOREF {")
Do %code.WriteLine(" Write !,""Creating new object...""")
Do %code.WriteLine(" Quit ##class("_%class.Name_").%New() }")
Do %code.WriteLine(" Write !,""Updating existing object ...""")
Do %code.WriteLine(" Quit result")
}
QUIT $$$OK
}
}
```
`IdForExport`属性和`NAMEOFEXPORTID`参数建立了一个约定,用于在导出子类的对象时如何将InterSystems IRIS内部`ID`投影到元素。其目的是,如果在子类中重新定义`IdForExport`,则相应地重新定义`NAMEOFEXPORTID`。
在这个类中,`XMLNew()`方法是一个方法生成器。编译该类(或任何子类)时,InterSystems IRIS会将此处显示的代码写入此方法的主体中。
以下类扩展了我们的自定义适配器:
```java
Class GXML.PersonWithXMLNew2
Extends (%Persistent, %Populate, GXML.AdaptorWithXMLNew)
{
Parameter XMLNAME = "Person";
Property Name As %Name;
Property DOB As %Date(FORMAT = 5, MAXVAL = "+$h");
Property Address As GXML.Address;
Property Doctors As list Of GXML.Doctor;
}
```
当运行前面显示的示例`ReadFile`方法时,对于文件中的每个 ``元素,该方法要么创建并保存一条新记录,要么打开并更新现有记录。
文章
Jingwei Wang · 七月 8, 2022
本片文章主要介绍两种Production debug的方式,
使用管理门户测试Production 组件
使用VS Code来对远程production代码进行debug
使用管理门户测试Production 组件
可以使用管理门户来对Production进行调试
测试组件
开启production测试: Interoperability -> 配置 -> Producton
在Production设置中,将开发与调试中的'测试开启'选中,如下图所示。
测试: Interoperability -> 测试 -> 业务主机
首先选择目标类型,业务流程或者业务操作,然后再目标名称这个下拉菜单中选择你要调试的组件名称,点击‘测试’
在请求详情中,填写测试请求的请求内容,然后点击'调用测试服务',测试结果如下。
然后,可以点击‘可视化追踪’来查看详情。
跟踪(Trace)
跟踪元素使你能够看到production中各种元素的行为,以便进行调试或诊断。
将跟踪元素添加到production的适当区域,在production运行时相应的跟踪信息就被写入,但是跟踪消息只是字符串,与Ens.Message及其子类无关。
跟踪消息可以启动和关闭,配置production以启用跟踪,意味着在运行时,跟踪元素被执行。而关闭跟踪意味着跟踪元素在production运行时会被被忽略。
可以选择将适用的业务主机配置为在前台运行,这样你就可以在生产运行时在终端看到跟踪信息。对于如何开启前台调试请参考Production前台调试。
启动跟踪
Interoperability -> 配置 -> Producton,选中要设置跟踪的组件。选中‘跟踪事件记入日志’,这一步会将追踪信息写到事件日志中。
默认情况下,所有的用户跟踪元素都被启用。你也可以通过设置^Ens.Debug来启用各种系统事件的跟踪,如果只是调试自己的代码用户跟踪就已经足够了,不需要开启其他系统事件的跟踪。
设置跟踪
业务服务 - BS ,业务操作 - BO :
在代码中添加用户跟踪
$$$TRACE("received application for "_request.CustomerName)
在代码中添加系统跟踪
$$$sysTRACE(trace_message)
在代码中添加error log
$$$LOGERROR("Awaiting connect on port "_..Port_" with timeout "_..CallInterval)
在代码中添加info log
$$$LOGINFO("Got data chunk, size="_$length(data)_"/"_tChunkSize)
业务流程 - BP :
在BPL中,点击 ‘-添加活动-' 下拉菜单,选择’跟踪‘
查看跟踪
从消息查看器中查看:
Interoperability -> 查看 -> 消息
选择你要查看的消息,点击相应的'会话'超链接,
点击 ◇,即可查看相应的trace
从事件日志中查看: Interoperability -> 查看 -> 事件日志 , 如下图所示,可以看到你所设置的所有事件日志。
使用VS Code来对远程production 代码进行debug
InterSystems支持使用VS Code连接到InterSystems IRIS服务器使用ObjectScript开发代码。如果您没有使用过VS Code连接到InterSystems IRIS服务器,请先阅读本社区文章 使用VSCode 进行IRIS 开发。
VS Code 连接远程 IRIS 请参考文章 使用VSCode 进行IRIS 开发,将VS Code连接到IRIS实例。
VS Code对本地productioin代码进行debug 如果想使用VS Code对本地代码进行debug,也请参考文章使用VSCode 进行IRIS 开发中的 ''在VSCode中调试' 部分。
VS Code对远程代码进行debug 在IRIS实例上面操作: 创建web 应用程序,在需要远程连接的IRIS实例上面建立一个名为/_vscode的web应用程序,配置如下:
在VS Code上面操作:
将VS Code连接到远程IRIS之后, 创建相应的工作区
InterSystems Tools - > 选择你要debug的命名空间 -> 点击右边笔状图标’Edit Code in Namespace‘。
进入到刚刚创建的工作区
创建workspace.json 文件 , 查看 -> command palette -> Open Workspac setting, 生成如下JSON文件, 文件名为workspace.json
{
"folders": [
{
"name": "localhcc:HCC",
"uri": "isfs://localhcc:hcc/"
}
],
"settings": {
"objectscript.showExplorer": false
}
}
创建launch.json 文件 , 点击左边菜单栏中的 'run and debug'图标,左上角会出现在launch.json文件中配置的调试名称。
配置launch.json文件
Debug普通类方法,使用如下launch.json文件,具体debug方式,请参考文章使用VSCode 进行IRIS 开发中的 ''在VSCode中调试' 部分。下图的launch.json文件表示可以调试 Test.SQL类中的SavePatientFromMsg函数,其中2为参数。
{
"version": "0.2.0",
"configurations": [
{
"type": "objectscript",
"request": "launch",
"name": "ObjectScript Debug SQL",
"program": "##class(Test.SQL).SavePatientFromMsg(2)"
}
]
}
Debug production, 使用如下launch.json文件, 其中processId为Production组件中的作业号
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "objectscript",
"request": "attach",
"name": "RetrieveCDAOperation",
"processId": 4436
}
]
}
Production中的作业号查询方式,如下图所示,在IRIS管理门户中,进入production页面。选中你想调试的组件,在右边即可以查看作业号。
调试
测试前,先增加断点,本文使用业务操作BO为例,我需要测试我的业务操作 HCC.SVR.Prod.BO.SaveCDAOperation。在我的业务操作类HCC.SVR.Prod.BO.SaveCDAOperation中增加几个断点。
选择你想调试的名称。点击左边绿色三角形的小图标’start debugging‘。
点击左边菜单栏中的 'run and debug'图标,左上角会出现在launch.json文件中配置的调试名称。选择你想调试的名称。点击左边绿色三角形的小图标’start debugging‘。
点击’start debugging‘后,最上方会出现测试功能条,但是功能条处于灰色,不可用状态。
然后触发production,可以使用postman 或者 soapUI等测试工具来触发要测试的production组件。如下图所示,相应的变量显示在左边菜单栏旁边,最上方的测试功能条处于可用状态,可以点击’step in‘ 、 ’step over‘、’step out‘ 来进行相应的调试,同时,INT - 中间源文件 Queue.1.int也会显示在调试页面上。INT 为中间源文件,是可执行的InterSystems IRIS对象代码。
如果想略过其他函数或者中间源文件,直接跳到你想调试的类或者函数中,可以将鼠标直接点击在想调试的函数中,然后右击鼠标,点击'Run to Cursor'。
点击'Run to Cursor'后,debug就会运行到鼠标指针闪烁的函数中,之后可以使用’step in‘ 、 ’step over‘、’step out‘ 来进行相应的调试。关于如何监控及查看调试过程中的变量,请参考文章使用VSCode 进行IRIS 开发中的 ''在VSCode中调试' 部分
文章
Michael Lei · 八月 6, 2024
# 数据收集
这篇分步说明指南将讲解如何创建任务来收集 InterSystems 数据库及其全局变量的相关数据(如关联的 Open Exchange App 所示,其中包含所有相关代码)
**免责声明:此软件仅用于测试/演示目的。 InterSystems 不支持将此代码作为任何发布产品的一部分。 它由 InterSystems 提供,作为特定产品和版本的演示/测试工具。 用户或客户全权负责此软件交付后的维护和测试,InterSystems 对此代码的错误或误用不承担任何责任**。
1) 首先,通过管理门户导入文件“DataCollection.xml”,并确保没有错误。 如果存在错误,则可能是版本问题,请发送电子邮件至 ari.glikman@intersystems.com 联系 Ari Glikman 获取适合你的版本的支持。 另外,确保将数据导入到你想要收集其内部数据以供后续检查的命名空间中。
2) 导入完成后,应该看到 Sample 软件包以及几个子软件包

如果服务器上已经存在 Sample 软件包,那么你仍然应该可以看到新的子软件包以及先前存在的其他文件夹。
3) 现在,运行单元测试以确保一切正常运行。
a. 创建可以被 InterSystems Terminal 读取的名为 Unit Tests 的文件夹,例如,由于我有一个本地安装,我需要在 C 盘中创建一个文件夹。

b. 我们现在将把 Sample.DBExpansion.Test.CaptureTest 类导出到此文件夹中,作为一个 xml 文件

c. 在终端中,设置 ^UnitTestRoot = “>”。 根据上面的示例,它将是(注意,你必须位于导入软件包的同一命名空间中)C:\(**注意,不是**“C:\Unit Tests”!)
```
set ^UnitTestRoot = "C:\"
```
d. d. 最后,运行单元测试。 从终端运行以下代码行:
```
do ##class(Sample.DBExpansion.Test.TestManager).RunTest("Unit Tests", "/noload/nodelete")
```
我们实际上是在告诉程序运行在 C:\Unit Tests 文件夹中找到的所有测试。 目前我们只有一个文件,也就是在 3.b 中创建的文件。
输出应如下所示

如果单元测试没有通过,程序就还不能运行。 在获得表明所有测试都通过的输出之前,不要执行后续步骤。
4) 恭喜! 现在可以构建任务了。 需要执行以下操作:
打开管理门户,转到 System Operation > Task Manager > New Task
*注意,你的用户必须有权访问 %SYS 命名空间, 否则任务将运行但不会收集任何数据*

现在,你将获得几个字段来填写想要创建的任务。 选择导入软件包的命名空间并为任务命名。 应该给出描述,以供未来参考。 最好不要选中快速复选框,虽然任务将运行得更慢,但收集的数据更完整。 如果运行时间过长(取决于数据库及其全局变量的大小),那么可以在这里勾选并选择更快的任务。 HowManyGlobals 表示应该收集多少个全局变量:-1 表示所有全局变量,这是推荐选项。 选择 Next,选择任务运行的频率,然后点击 Finish。

b. 现在,你将看到 Task Schedule,显示所有任务(包括新创建的任务)的预计运行时间。 如果你希望现在运行,选择右侧的 Run。
选择 Task History 确保其已成功创建。 运行任务后,你应该会看到它也成功运行。 否则,这里会出现错误。
此任务将创建两个表:
*Sample_DBExpansion_Data.DBAnalysisInfo*
此表将存储有关数据库本身的数据。 我们将其称为“元数据”。 可以在下图中看到它存储的信息。 快速标志将指示 4.a 中的选择。

*Sample_DBExpansion_Data.GlobalAnalysisInfo*
这将包含有关数据库中全局变量的信息。 注意,如果有与全局变量关联的类名,我们将在这里看到它以及它们的大小。 最后,注意 MetaDataID 字段与 Sample_DBExpansion_Data.DBAnalysisInfo 表的 ID 字段相对应。 这意味着在捕获数据库信息时,其对应全局变量信息也被捕获,并且它们共享这个标识号(这些是当时数据库中的全局变量信息)。 这样可以查看数据库中的全局变量以及数据库本身如何随时间演变。

5) 接下来是稍微漂亮一点的用户界面。

它以更易理解的方式显示表中的全局变量和数据库信息。 有 3 个图表:第一个显示数据的历史记录,第二个显示所选全局变量的历史大小(通过下拉菜单或搜索),最后是所有全局变量大小的概览。 在底部的表中,可以输入要显示的全局变量数量,并按大小排列。 在 %Change 列中,黄色高亮表示大小变化极小,绿色表示大小减小,红色表示大小显著增大。
在[这里](https://github.com/Ari-Glikman/DataCollection-UI)可以找到有关设置的分步说明。
如果你不喜欢图表,可以在[这里](https://github.com/Ari-Glikman/DataAnalysis)继续进行数据分析。
## Docker
### 先决条件
确保已安装 [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) 和 [Docker 桌面](https://www.docker.com/products/docker-desktop)。
### 安装
将仓库克隆/git pull 到任意本地目录
```
$ git clone https://github.com/rcemper/PR_DataCollection.git
```
```
$ docker compose up -d && docker compose logs -f
```
容器启动
创建适当的目录 «/home/irisowner/dev/Unit Tests»
设置 ^UnitTestRoot = «/home/irisowner/dev/»
打开 IRIS 终端:
```
$ docker-compose exec iris iris session iris
USER>
```
或使用 **WebTerminal**。
http://localhost:42773/terminal/
访问 IRIS 系统管理门户
http://localhost:42773/csp/sys/UtilHome.csp
访问 UnitTestPortal
http://localhost:42773/csp/sys/%25UnitTest.Portal.Indices.cls?$NAMESPACE=USUARIO 如何通过管理门户导入文件“DataCollection.xml” ? 系统资源管理器 - > 类 -> 开始
点击 ‘导入’
进入管理门户,点击菜单“系统资源管理器“>"类">"导入"。注意改命名空间为你的目标命名空间
文章
Jeff Liu · 八月 26, 2024
出于实际原因,可能需要在 Linux 服务器重启后自动启动 IRIS 实例。
下面是在 Linux 服务器重启时通过 systemd 自动启动 IRIS 的步骤:
1. 在 /etc/systemd/system/iris.service 中创建一个 iris.service 文件,其中包含以下信息
[Unit]
Description=InterSystems IRIS Data Platform
After=network.target
[Service]
Type=forking
User=irisusr
ExecStart=/usr/bin/iris start iris
ExecStop=/usr/bin/iris stop iris quietly
Restart=on-failure
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
注意:User应该填入IRIS实例的所有者。
2. 重新加载 systemd 配置管理器
sudo systemctl daemon-reload
3. 启用IRIS服务,使其自动启动
sudo systemctl enable iris
激活后将创建软链接,自动启动 IRIS:
Synchronizing state of iris.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install enable iris
Created symlink /etc/systemd/system/multi-user.target.wants/iris.service → /etc/systemd/system/iris.service.
4. 试着重启服务器
sudo reboot
5. 查看IRIS实例是否自动启动
ssh guilbaud@192.168.102.130
guilbaud@192.168.102.130's password:
Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-36-generic x86_64)
$ iris all
Instance Name Version ID Port Directory
---------------- ---------------- ----- --------------------------------
up >IRIS 2024.1.0.267.2 1972 /usr/irissys
文章
Hao Ma · 一月 15, 2021
InterSystems编程语言的错误管理技术一直在发展。接下来,我们将展示几种不同的错误管理实现方式,以及为什么要使用TRY/THROW/CATCH机制。
您可以点击这里阅读官方的错误处理建议。
为了支持遗留应用程序,InterSystems不会废弃非推荐的错误管理方法。我们建议使用objectscriptQuality等工具来检测遗留的非推荐用例以及其他可能的问题和错误。
$ZERROR
$ZERROR是一种较老的错误管理机制,支持与标准“M”不同的实现。虽然$ZERROR现在仍然有效,但我们非常不推荐使用。
如果您已经使用了$ZERROR,那么很容易对该变量进行错误的设计使用。$ZERROR是一个全局公共变量,可以被当前进程中正在执行的所有routine(宏)(来自InterSystems或自定义的)进行访问和修改。因此,它的值仅在产生错误的时候是可靠的。InterSystems不保证$ZERROR在调用系统库时会保留旧值。
我们在这里对一些案例展开分析。
案例1:自定义代码中的错误代码
Line
Code
Comments
$ZERROR value
1
Set ...
""
2
Set ...
""
...
Do ...
""
...
...
""
N-m
Do CacheMethodCall()
Call to another Caché system methods
""
...
""
N
Set VarXX = MyMethod()
The custom method generates an ObjectScript error
<UNDEFINED>B+3^InfinityMethod *varPatient
N+1
Set …
<UNDEFINED>B+3^InfinityMethod *varPatient
N+2
Do OtherCacheMethodCall()
Caché system method. $ZERROR is not updated if there is no error.
<UNDEFINED>B+3^InfinityMethod *varPatient
...
If ...
<UNDEFINED>B+3^InfinityMethod *varPatient
...
...
<UNDEFINED>B+3^InfinityMethod *varPatient
...
While ...
<UNDEFINED>B+3^InfinityMethod *varPatient
...
If $ZERROR’=”” Quit “Error”
<UNDEFINED>B+3^InfinityMethod *varPatient
N+m
Quit "OK"
<UNDEFINED>B+3^InfinityMethod *varPatient
案例2:内部Caché错误出现误报
在这种情况下,自定义代码运行良好,但内部Caché错误引发了一个错误。
Line
Code
Comments
$ZERROR value
1
Set …
""
2
Set …
""
..
Do …
""
..
…
""
N-m
Do CacheMethodCall()
Internal error but it is managed internally and decides to continue execution
<UNDEFINED>occKl+3^ MetodoInternoCache *o0bxVar
..
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N
Set VarXX = MyMethod() // OK
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+1
Set …
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+2
Do OtherCacheMethodCall() // OK
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
…
If …
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
…
…
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
…
While …
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
…
If $ZERROR’="" Quit "Error"
An error is detected while there is no error at all
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+m
Quit "OK"
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVa
案例3:在Caché内部代码中重置$ZERROR时出现误报
在这种情况下,即使不存在错误,也会直接或间接调用一个内部Caché方法或routine(宏)来重置公共变量$ZERRO
Line
Code
Comments
$ZERROR
1
Set ...
""
2
Set ...
""
...
Do ...
""
...
...
""
N-m
Do CacheMethodCall()
<UNDEFINED>occKl+3^ MetodoInternoCache *o0bxVar
...
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N
Set VarXX = MyMethod()
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+1
Set …
<UNDEFINED>occKl+3^ MetodoInterno *o0bxVar
N+2
Do OtherCacheMethodCall()
Internal method that resets $ZERROR whether there is error or not
""
...
If ...
""
...
...
""
...
While ...
""
...
If $ZERROR’="" Quit "Error"
Error not detected
""
N+m
Quit "OK"
""
$ZTRAP
$ZTRAP是在上下文中进行错误管理的,因此不存在在上下文环境之外被意外覆盖的风险。当出现错误时,控件返回到调用堆栈中的第一个错误控件。
当出现错误并处理完成后,必须清除$ZTRAP,以便在发生另一个错误时避免出现无限循环。
因此,$ZTRAP在错误管理方面比$ZERROR更先进,但开发人员在添加操作时有可能会产生更多的错误。
如果想要进一步了解此方法的使用,可以查看官方文档中的“$ZTRAP错误处理”节选内容。
%Status
该方法用于系统库中,因此是调用系统库时必须使用的机制。
可以点击这里查看用法。
TRY/THROW/CATCH
这是最现代的错误管理方法,也是目前比较推荐的方法。
您可以点击这里查看用法。
此方法可在上下文中管理错误,并且不需要开发人员管理内部错误变量。
优点
关于TRY/THROW/CATCH的文献有很多,这里列举了一些它的优点:
提供了一种清晰的方式进行异常情况处理,将错误代码与常规代码分开处理
简化了错误检测,所以无需在每次操作后检查错误
允许错误传播到上层
支持运行时错误,允许在崩溃后恢复并继续运行
缺点
最明显的缺点是轻微的性能损失,因此您必须知道需要在什么时候使用该方法。
通常,没有必要在每种方法上都使用TRY/THROW/CATCH,在很多情况下在操作前多进行几次简单的验证就可以避免很多避免错误,从而避免不必要地使用TRY/THROW/CATCH方法。
结论
避免使用$ZERROR和$ZTRAP。
只有在调用系统库时才使用%STATUS。
可以使用TRY/THROW/CATCH管理错误,但不要滥用。
文章
Michael Lei · 二月 13, 2023
在 InterSystems IRIS 2022.2 中,我们引入了列存储作为持久化 IRIS SQL 表的新选项,可以将您的分析查询性能提高一个数量级。该功能在 2022.2 和 2022.3 中标记为实验性,但将在即将发布的 2023.1 版本中“升级”为完全支持的生产能力。
产品文档和这个介绍性视频已经描述了行存储(仍然是 IRIS 上的默认设置并在我们的整个客户群中使用)与列表存储之间的区别,并提供了有关为您的用例选择合适的存储布局的高级指导。在本文中,我们将详细阐述这个主题,并根据行业实践建模原则、内部测试和抢先体验计划参与者的反馈分享一些建议。
通常,我们为您的 IRIS SQL 模式选择合适的表布局的指南如下:
如果您正在部署利用 IRIS SQL 或对象的应用程序,例如电子病历、ERP 或事务处理应用程序,则无需将其当前的行存储布局更改为列式存储布局。为最终用户应用程序或编程事务发出的大多数 SQL 查询仅检索或更新有限数量的行,结果行通常对应于表行,聚合函数的使用非常有限。在这种情况下,列式存储和矢量化查询处理提供的优势不适用。
如果在应用程序还嵌入了操作分析,如果相应的分析查询的当前性能不令人满意,请考虑添加列索引。这包括,例如,显示当前库存的仪表板或实时数据的基本财务报告。查找聚合中使用的数字字段(例如数量、货币)或范围条件中使用的高基数字段(例如时间戳)。这种机会的一个很好的指标是当前使用位图索引来加速大量行的过滤,通常是在低基数字段(例如分类或有序字段)上。无需替换这些位图索引;附加的柱状索引与它们一起工作得很好,旨在避免从主地图或常规索引地图(每行单个 gref)中过度读取。
如果您的 IRIS SQL 表包含的行数少于一百万,则无需考虑列式存储。我们不希望将自己固定在特定数字上,但矢量化查询处理的好处不太可能在这些低范围内产生影响。
如果您正在为数据仓库、商业智能或类似的分析用例部署 IRIS SQL 架构,请考虑将其更改为默认的列式存储。星型模式、雪花模式或其他非规范化表结构以及位图索引和批量摄取的广泛使用是这些用例的良好指标。从列式存储中获益最多的分析查询是那些扫描大量行并在它们之间聚合值的查询。在定义“柱状表”时,IRIS 会透明地对该表中不适合柱状存储的列采用行布局,例如流、长字符串或串行字段。 IRIS SQL 完全支持这种混合表布局,并将对查询计划中符合条件的部分使用矢量化查询处理。位图索引在柱状表上的附加值有限,可以忽略不计。
技术使用与否将根据环境和数据相关参数而有所不同。因此,我们强烈建议客户在具有代表性的设置中测试不同的场景。列索引可以很容易地添加到常规的按行组织的表中,并且会很快产生对查询性能优势的现实看法。这与混合表格布局的灵活性一起,是 InterSystems IRIS 的一个关键差异化因素,可帮助客户实现数量级的性能改进。
随着我们在完整的生产版本中获得更多的实际经验,我们打算使这些建议更加具体。显然,我们可以通过抢先体验计划和 POC 参与,根据客户的实际架构和工作量提供更具体的建议,并期待客户和社区成员的反馈。 列存储Columnar Storage 是 InterSystems IRIS Advanced Server 高级版的一部分,在 InterSystems IRIS 和 IRIS for Health 社区版中也可以试用。有关完全脚本化的演示环境,请参阅这里的 GitHub资源 。
文章
姚 鑫 · 五月 1, 2023
# 第四十五章 管理镜像 - 使用 ^MIRROR 状态监视器
# 使用 ^MIRROR 状态监视器
`^MIRROR` 例程提供基于字符的镜像状态监视器。 `^MIRROR Status Monitor` 选项显示镜像成员的状态,包括类型、状态、日志传输延迟和 `dejournal` 延迟(请参阅镜像成员日志传输和 `Dejournaling` 状态)。监视器可以在任何镜像成员上运行,但在故障转移成员上运行它会提供有关仲裁器配置和所有连接的异步成员的信息,而在异步成员上运行它则不会。
要启动状态监视器,请打开终端窗口,在 `%SYS` 命名空间中运行 `^MIRROR` 例程(请参阅使用 `^MIRROR` 例程),然后从镜像状态菜单中选择状态监视器。以下是在故障转移成员上运行时监视器的输出示例:
```java
Status of Mirror MIR25FEB at 17:17:53 on 02/27/2018
Member Name+Type Status Journal Transfer Dejournaling
-------------------------- --------- ---------------- --------------
MIR25FEB_A
Failover Primary N/A N/A
MIR25FEB_B
Failover Backup Active Caught up
MIR25FEB_C
Disaster Recovery Connected Caught up Caught up
MIR25FEB_D
Read-Only Reporting Connected Caught up Caught up
Arbiter Connection Status:
Arbiter Address: 127.0.0.1|2188
Failover Mode: Arbiter Controlled
Connection Status: Both failover members are connected to the arbiter
Press RETURN to refresh, D to toggle database display, Q to quit,
or specify new refresh interval
```
当在异步成员上运行状态监视器时,仅列出故障转移成员和该异步成员,并且还会显示异步(正在运行或已停止)上的 `dejournaling` 状态,例如:
```java
Status of Mirror MIR25FEB at 17:17:53 on 02/27/2018
Member Name+Type Status Journal Transfer Dejournaling
-------------------------- --------- ---------------- --------------
MIR25FEB_A
Failover Primary N/A N/A
MIR25FEB_B
Failover Backup Active Caught up
MIR25FEB_C
Disaster Recovery Connected Caught up Caught up
Dejournal Status: running (process id: 12256)
Press RETURN to refresh, D to toggle database display, Q to quit,
or specify new refresh interval
```
默认情况下,不显示有关镜像数据库的信息。在提示符处输入 `d` 以列出有关镜像中每个数据库的信息,包括名称、目录、状态和要取消的下一条记录,如使用镜像监视器中所述,例如:
```java
Mirror Databases:
Last Record
Name Directory path Status Dejournaled
------------- ----------------------------------- ----------- -----------
MIR25FEB_DB1 C:\InterSystems\20182209FEB25A\Mgr\MIR25FEB_DB1\
Active
Current,c:\intersystems\20182209feb25a\mgr\journal\MIRROR-MIR25FEB-20180227.001,40233316
MIR25FEB_DB2 C:\InterSystems\20182209FEB25A\Mgr\MIR25FEB_DB2\
Active
Current,c:\intersystems\20182209feb25a\mgr\journal\MIRROR-MIR25FEB-20180227.001,40233316
```
# 监控镜像通信进程
每个系统(主要和备份故障转移成员,以及每个连接的异步成员)上运行的进程负责镜像通信和同步。
## 主要故障转移成员上的镜像进程
在主要故障转移成员上运行系统状态例程 (^%SS) 会显示下表中列出的进程。
注意:本节的 `^%SS` 输出中有意省略了 `CPU`、`Glob` 和 `Pr` 列。
#### 主要故障转移成员上的镜像进程
Device| Namespace| Routine| User/Location
---|---|---|---
/dev/null| %SYS |MIRRORMGR |Mirror Master
MDB2| %SYS| MIRRORCOMM| Mirror Primary*
192.168.1.1 |%SYS |MIRRORCOMM |Mirror Svr:Rd*
这些过程定义如下:
- `Mirror Master`:该进程在系统启动时启动,负责各种镜像控制和管理任务。
- `Mirror Primary`:这是出站数据通道;这是一个单向通道。每个连接的系统有一个作业(备份故障转移或异步成员)。
- `Mirror Svr:Rd*`:这个是入站确认通道;这是一个单向通道。每个连接的系统有一个作业(备份故障转移或异步成员)。
每个连接的异步成员都会在主故障转移成员上产生一组新的 `Mirror Master`、`Mirror Primary` 和 `irror Svr:Rd*` 进程。
## 备份故障转移/异步成员上的镜像进程
在备份故障转移/异步成员上运行系统状态例程 (`^%SS`) 会显示下表中列出的进程。
#### 备份故障转移/异步成员上的镜像进程
Device| Namespace| Routine| User/Location
---|---|---|---
/dev/null| %SYS |MIRRORMGR |Mirror Master
/dev/null |%SYS|MIRRORMGR| Mirror Dejour
/dev/null| %SYS|MIRRORMGR| Mirror Prefet*
/dev/null |%SYS |MIRRORMGR| Mirror Prefet*
MDB1 |%SYS|MIRRORMGR |Mirror Backup
/dev/null| %SYS|MIRRORMGR |Mirror JrnRead
此表中标识的进程也出现在每个连接的异步成员上:
- `Mirror Master`:该进程在系统启动时启动,负责各种镜像控制和管理任务。
- `Mirror JrnRead (Mirror Journal Read)`:该过程将备份生成的日志数据读入内存,并将这些更改排队等待 `dejournal` 作业取消。
- 镜像延迟(`Mirror Dejournal`):这是备份故障转移成员上的延迟作业;它将接收到的日志数据中的 `sets` 和 `kills` 发送到镜像数据库。
- `Mirror Prefet*`(镜像预取):这些进程负责在`dejournal` 作业实际尝试使用它们之前将 `dejournal` 作业所需的磁盘块预取到内存中。这样做是为了加快 `dejournaling` 过程。系统上通常配置了多个镜像预取作业。镜像备份:这个过程是一个双向通道,将从主服务器接收到的日志记录写入备份的镜像日志文件,并向主服务器返回确认。
文章
姚 鑫 · 二月 23, 2022
# 第六十三章 SQL函数 IFNULL
测试`NULL`并返回适当表达式的函数。
# 大纲
```sql
IFNULL(expression-1,expression-2 [,expression-3])
{fn IFNULL(expression-1,expression-2)}
```
# 参数
- `expression-1` - 要计算以确定是否为`NULL`的表达式。
- `expression-2` - 如果`expression-1`为`NULL`,则返回的表达式。
- `expression-3` - 可选-如果`expression-1`不是`NULL`返回的表达式。
如果没有指定`expression-3`,则当`expression-1`不是`NULL`时返回`NULL`值。
返回的数据类型描述如下。
# 描述
支持IFNULL作为SQL通用函数和`ODBC`标量函数。
请注意,虽然这两个执行非常相似的操作,但它们在功能上是不同的。
SQL通用函数支持三个参数。
ODBC标量函数支持两个参数。
SQL通用函数和ODBC标量函数的双参数形式是不一样的;
当`expression-1`不为空时,它们返回不同的值。
SQL通用函数计算表达式1是否为NULL。
它永远不会返回expression-1:
- 如果`expression-1`为`NULL`,则返回`expression-2`。
- 如果`expression-1`不为`NULL`,则返回`expression-3`。
- 如果`expression-1`不为`NULL`,并且没有`expression-3`,则返回`NULL`。
ODBC标量函数计算`expression-1`是否为`NULL`。
它要么返回`expression-1`,要么返回`expression-2`:
- 如果`expression-1`为NULL,则返回`expression-2`。
- 如果`expression-1`不为`NULL`,则返回`expression-1`。
# 返回值数据类型
- `IFNULL(expression-1,expression-2)`:返回`expression-2`的数据类型。
如果`expression-2`是数值字面值,则字符串字面值或`NULL`返回数据类型`VARCHAR`。
- `IFNULL(expression-1,expression-2,expression-3)`:如果`expression-2`和`expression-3`具有不同的数据类型,则返回数据类型优先级更高(包容性更强)的数据类型。
如果`expression-2`或`expression-3`是数值字面值或字符串字面值,则返回数据类型`VARCHAR`。
如果`expression-2`或`expression-3`为`NULL`,则返回非`NULL`参数的数据类型。
如果`expression-2`和`expression-3`的长度、精度或比例不同,则`IFNULL`返回两个表达式的更大长度、精度或比例。
- `{fn IFNULL(expression-1,expression-2)}`:返回`expression-1`的数据类型。
如果`expression-1`是数字字面值、字符串字面值或`NULL`,则返回数据类型`VARCHAR`。
# 日期和时间显示转换
一些`expression-1`数据类型需要从逻辑模式(模式0)转换为ODBC模式(模式1)或显示模式(模式2)。例如DATE和TIME数据类型。
如果`expression-2`或`expression-3`的值不是相同的数据类型,则不能在ODBC模式或`Display`模式下转换该值,并产生一个`SQLCODE`错误:`DATE`数据类型为`-146`;
`-147`为`TIME`数据类型。
例如,`IFNULL(DOB,'nodate',DOB)`不能在ODBC模式或显示模式中执行;
它会发出一个`SQLCODE -146`错误,其中有`%msg Error: 'nodate' is an invalid ODBC/JDBC Date value or Error: 'nodate' is an invalid DISPLAY Date value.`
要在ODBC模式或`Display`模式下执行此语句,必须将该值转换为适当的数据类型:`IFNULL(DOB,CAST('nodate' as DATE),DOB)`。
这将产生日期0,显示为`1840-12-31`。
# %List显示转换
`%LIST`字段是带编码的字符串数据类型字段。如果`Expression-1`是`%List`字段,则相应的`Expression-2`或`Expression-3`值取决于选择模式:
- 在逻辑模式(模式0)或显示模式(模式2)中,`%List`值作为字符串数据类型返回,格式为`$lb("element1","element2",…)`。
因此,`expression-2`或`expression-3`的值必须指定为`%List`,示例如下:
```java
/// d ##class(PHA.TEST.SQLCommand).IfNull()
ClassMethod IfNull()
{
s myquery=3
s myquery(1)="SELECT TOP 20 Name,"
s myquery(2)="IFNULL(FavoriteColors,$LISTBUILD('No Preference'),FavoriteColors) AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New(2) // 2=Display mode
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull()
Name ColorChoice
yaoxin $lb("Red","Orange","Yellow")
xiaoli $lb("No Preference")
姚鑫 $lb("No Preference")
姚鑫 $lb("No Preference")
姚鑫 $lb("No Preference")
姚鑫 $lb("Red","Orange","Yellow","Green")
姚鑫 $lb("Red","Orange","Yellow","Green","Green")
Isaacs,Roberta Z. $lb("Red","Orange","Yellow","Green","Yellow")
Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S. $lb("White")
Fives,James D. $lb("Black")
Vonnegut,Jose P. $lb("Green","White")
Chadbourne,Barb B. $lb("Purple")
Quigley,Barb A. $lb("Yellow")
O'Rielly,Chris H. $lb("Red","Red")
Willeke,Alvin L. $lb("Black","Black")
Orwell,John V. $lb("No Preference")
Umansky,Susan C. $lb("Blue")
Kratzmann,Kirsten C. $lb("No Preference")
Ng,Josephine Z. $lb("White")
Zevon,Heloisa O. $lb("Orange")
20 Rows(s) Affected
```
- 在ODBC模式(模式1)中,`%List`值作为逗号分隔的元素字符串返回:`element1`、`element2`、....
因此,`expression-2`或`expression-3`的值可以指定为字符串,示例如下:
```java
/// d ##class(PHA.TEST.SQLCommand).IfNull1()
ClassMethod IfNull1()
{
s myquery=3
s myquery(1)="SELECT TOP 20 Name,"
s myquery(2)="IFNULL(FavoriteColors,'No Preference',FavoriteColors) AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New(1) // 1=ODBC mode
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull1()
Name ColorChoice
yaoxin $lb("Red","Orange","Yellow")
xiaoli No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 $lb("Red","Orange","Yellow","Green")
姚鑫 $lb("Red","Orange","Yellow","Green","Green")
Isaacs,Roberta Z. $lb("Red","Orange","Yellow","Green","Yellow")
Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S. $lb("White")
Fives,James D. $lb("Black")
Vonnegut,Jose P. $lb("Green","White")
Chadbourne,Barb B. $lb("Purple")
Quigley,Barb A. $lb("Yellow")
```
# NULL处理函数比较
下表显示了各种`SQL`比较函数。
如果逻辑比较测试为`True` (A与B相同),则每个函数返回一个值;如果逻辑比较测试为`False` (A与B不同),则返回另一个值。这些函数允许执行`NULL`逻辑比较。
不能在实际相等(或不相等)条件比较中指定`NULL`。
SQL Function| Comparison| Test Return Value
---|---|---
IFNULL(ex1,ex2) [two-argument form]| ex1 = NULL |True returns ex2 False returns NULL
IFNULL(ex1,ex2,ex3) [three-argument form]| ex1 = NULL | True returns ex2 False returns ex3
{fn IFNULL(ex1,ex2)}| ex1 = NULL | True returns ex2 False returns ex1
ISNULL(ex1,ex2)| ex1 = NULL | True returns ex2 False returns ex1
NVL(ex1,ex2)| ex1 = NULL | True returns ex2 False returns ex1
NULLIF(ex1,ex2)| ex1 = ex2 | True returns NULL False returns ex1
COALESCE(ex1,ex2,...)| ex = NULL for each argument | True tests next ex argument. If all ex arguments are True (NULL), returns NULL. False returns ex
# 示例
在下面的例子中,通用函数和ODBC标量函数都返回第二个表达式(`99`),因为第一个表达式是`NULL`:
```sql
SELECT IFNULL(NULL,99) AS NullGen,{fn IFNULL(NULL,99)} AS NullODBC
99 99
```
在下面的示例中,通用函数和ODBC标量函数示例返回不同的值。
通用函数返回``,因为第一个表达式不是`null`。
`ODBC`示例返回第一个表达式(33),因为第一个表达式不是`NULL`:
```sql
SELECT IFNULL(33,99) AS NullGen,{fn IFNULL(33,99)} AS NullODBC
NUll 33
```
下面的动态SQL示例返回字符串`'No Preference'`,如果`FavoriteColors`是`NULL`;
否则,返回`NULL`:
```java
/// d ##class(PHA.TEST.SQLCommand).IfNull2()
ClassMethod IfNull2()
{
s myquery=3
s myquery(1)="SELECT Name,"
s myquery(2)="IFNULL(FavoriteColors,'No Preference') AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New()
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
w !,"End of data"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull2()
Name ColorChoice
yaoxin
xiaoli No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫
姚鑫
Isaacs,Roberta Z.
```
下面的动态SQL示例返回字符串`'No Preference'`,如果`FavoriteColors`是`NULL`;
否则,它返回`FavoriteColors`的值:
```java
ClassMethod IfNull3()
{
s myquery=3
s myquery(1)="SELECT Name,"
s myquery(2)="IFNULL(FavoriteColors,'No Preference',FavoriteColors) AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New()
s tStatement.%SelectMode=2
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
w !,"End of data"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull3()
Name ColorChoice
yaoxin $lb("Red","Orange","Yellow")
xiaoli No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 $lb("Red","Orange","Yellow","Green")
姚鑫 $lb("Red","Orange","Yellow","Green","Green")
```
下面的例子返回字符串`'No Preference'`如果`FavoriteColors`是`NULL`;
否则,返回字符串`'Preference'`:
```sql
SELECT sqlName,
IFNULL(FavoriteColors,'No Preference','Preference') AS ColorPref
FROM Sample.Person
```
下面的ODBC语法示例如果FavoriteColors为NULL,则返回字符串'No Preference',否则返回FavoriteColors数据值:
```sql
SELECT Name,
{fn IFNULL(FavoriteColors,$LISTBUILD('No Preference'))} AS ColorPref
FROM Sample.Person
```
```java
/// d ##class(PHA.TEST.SQLCommand).IfNull4()
ClassMethod IfNull4()
{
s myquery=3
s myquery(1)="SELECT Name,"
s myquery(2)="{fn IFNULL(FavoriteColors,'No Preference')} AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New()
s tStatement.%SelectMode=1
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
w !,"End of data"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull4()
Name ColorChoice
yaoxin Red,Orange,Yellow
xiaoli No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 Red,Orange,Yellow,Green
姚鑫 Red,Orange,Yellow,Green,Green
Isaacs,Roberta Z. Red,Orange,Yellow,Green,Yellow
```
下面的例子在`WHERE`子句中使用了`IFNULL`。
它挑选了`21`岁以下没有最喜欢的颜色偏好的人。
如果`FavoriteColors`为`NULL`, `IFNULL`返回`Age`字段值,用于条件测试:
```sql
SELECT Name,FavoriteColors,Age
FROM Sample.Person
WHERE 21 > IFNULL(FavoriteColors,Age)
ORDER BY Age
```
类似的功能可以参考`NULL`谓词(`IS NULL`, `IS NOT NULL`)。
文章
Jingwei Wang · 七月 4, 2022
高可用性(HA)指的是使系统或应用程序在很高比例的时间内保持运行,最大限度地减少计划内和计划外的停机时间。
维持系统高可用性的主要机制被称为故障转移。在这种方法下,一个故障的主系统被一个备份系统所取代;也就是说,生产系统故障转移到备份系统上。许多HA配置还提供了灾难恢复(DR)的机制,即在HA机制无法保持系统的可用性时,也能及时恢复系统的可用性。
本文简要讨论了可用于基于InterSystems IRIS的应用程序的HA策略机制,提供了HA解决方案的功能比较,并讨论了使用分布式缓存的故障转移策略。
操作系统级别的集群HA
在操作系统层面上提供的一个常见的HA解决方案是故障转移集群,其中主要的生产系统由一个(通常是相同的)备用系统补充,共享存储和一个跟随活动成员的集群IP地址。在生产系统发生故障的情况下,备用系统承担生产工作量,接管以前在故障主系统上运行的程序和服务。备用机必须能够处理正常的生产工作负载,只要恢复故障主机所需的时间就可以了。也可以选择让备用机成为主机,一旦主机恢复,故障主机将成为备用机。
InterSystems IRIS的设计可以轻松地与所支持的平台的故障转移集群技术相结合(如InterSystems支持的平台中所述)。InterSystems IRIS实例安装在集群的共享存储设备上,以便两个集群成员都能识别它,然后添加到集群配置中,这样它将作为故障转移的一部分在备用机上自动重新启动。在故障转移后重新启动时,系统自动执行正常的启动恢复,保持结构和逻辑的完整性,就像InterSystems IRIS在故障系统上重新启动一样。如果需要,可以在一个集群上安装多个InterSystems IRIS实例。
InterSystems IRIS 镜像
具有自动故障转移功能的 InterSystems IRIS 数据库镜像为计划内和意外停机提供了经济有效的高可用性解决方案。镜像依赖于数据复制而不是共享存储,避免了存储故障导致的重大服务中断。
InterSystems IRIS 镜像由两个物理上独立的 InterSystems IRIS 系统组成,称为故障转移成员。每个故障转移成员在镜像中维护每个镜像数据库的副本;应用程序更新是在主故障转移成员上进行的,而备机故障转移用户的数据库则通过主成员的日志文件保持数据同步。
镜像会自动将主节点的角色分配给两个故障转移成员中的一个,而另一个故障转移成员则自动成为备机系统。当主 InterSystems IRIS 实例失败或变得不可用时,备机将自动快速接管并成为主机。
第三个系统称为仲裁机,它与故障转移成员保持持续的联系,在无法直接通信时安全地做出故障转移决策,为他们提供所需信息。在每个故障转移系统主机上运行的代理(称为 ISCAgents )进程也有助于自动故障转移逻辑。除非备机能够确认主节点确实处于停机状态或不可用状态,并且无法再作为主节点运行,否则备机将无法接管。在仲裁机和 ISCAgents 之间,这可以在几乎每个中断场景下完成。
当镜像配置使用 virtual IP address(虚拟 IP 地址)时,将应用程序连接重定向到新主节点是透明的。如果连接是通过 ECP,它们会自动重置到新的主节点。用于重定向应用程序连接的其他机制也是可用的。
当主机实例恢复正常时,它将自动成为备机实例。操作员启动的故障转移也可在计划的维护或升级停机期间维护可用性。
自动故障转移机制
镜像主旨在当主节点失败或变得不可用时,为备机提供安全的自动故障转移。本部分描述了允许这种情况发生的机制,包括:
• 自动故障转移的安全性要求
• 自动故障转移规则
• 对于各种中断情况的镜像响应
• 自动故障转移机制详节
自动故障转移的安全性要求
备机的 InterSystems IRIS 实例只有在能够确保满足以下两个条件时才能自动接管主节点:
• 备机实例已从主节点接收到最新的日志数据。
这一要求保证了主节点上镜像数据库的所有持久更新都已经或将要对备机的相同数据库进行,从而确保不会丢失任何数据。
• 主机实例不再作为主机实例运行,并且在没有手动干预的情况下无法这样做。
这个要求消除了两个故障转移成员同时作为主节点的可能性,这可能导致逻辑数据库性能下降和完整性损失。
自动故障转移规则
备机状态和自动故障转移
在正常镜像操作期间,备机故障转移成员的日志传输状态为活动,这意味着它已从主节点接收到所有日志数据,并与其同步。活跃的备机接收写在主机上的当前日志数据,主机等待备机确认收到日志之后才考虑该数据的持久化。因此,备机为活跃状态满足故障转移的第一个条件。
如果活跃备机在Quality of Service(QoS)(服务质量超时)内不能确定已从主节点接收新数据,主节点会撤销备机的活动状态,断开备机并暂时进入故障状态。当处于故障状态时,主节点不提交任何新的日志数据(可能导致应用程序暂停),确保充足的时间,使故障转移成员在不发生异步的情况下恢复联系或进行适当的安全故障转移决策
当备机重新连接到主节点时,它首先通过从主节点获取所有最近的日志数据来跟上,然后变为活动状态。当备机通过从主节点同步了最新的日志数据并确认其接收而跟上时,将恢复其活动状态。
备机处于活动状态时的自动故障转移
当备机处于活动状态时,如果它能够确认故障转移的第二个条件,主节点不可以再作为主节点,并且在没有人工干预的情况下不能继续成为主节点,那么它就有资格作为主机进行接管。备机可以通过以下三种方式中的任意一种来接管主节点:
• 通过接收来自主机的请求接管的通信
这发生在主机实例正常关闭期间或主机实例检测到主机实例挂起时。一旦主节点发送了这条消息,它就不能再作为主机了,活动备机可以安全地接管它。如果前一个主机被挂起,新的主机就会迫使它关机。
• 通过从仲裁机处收到信息,得知其已与主机失去联系。
当一个网络事件同时将主节点与备机和仲裁机隔离时,它将无限期地进入故障状态。因此,如果一个活跃备机与主机失去联系,并且从仲裁机那里得知它也与主机用失去了联系,那么备机可以安全地接管,因为主机用必须要么已经出现故障,要么被隔离并处于故障状态,因此不能再作为主节点运行了。当连接恢复时,如果前一个主节点挂起,新的主节点就会将之前的主节点强制关闭。
• 通过从主机的 ISCAgent 接收主机实例已关闭或挂起的信息。
当仲裁机不可用或未配置仲裁机时,与主机实例失去联系的活动备机可以尝试联系主机实例的 ISCAgent(只有在主机实例主机系统仍在运行时才可能)以确认主机实例已关闭或处于挂起状态。一旦代理确认主节点不能再作为主机,并且故障转移因此是安全的,备机就会接管。
备机不处于活动状态时的自动故障转移
不活动的备机可以尝试与主机实例的 ISCAgent 联系,以确认主机实例处于关闭状态,或在挂起时强制关闭主机实例,并从代理获取主机实例最近的日志数据。如果在这两方面都成功,那么备机可以安全地作为主机接管。
不活动且无法与主机的 ISCAgent 联系的备机无法确保主机不再作为主机,并且无法保证此时的备机拥有主机最新的日志更新,因此无法接管主机。
当备机没有活动时,仲裁机在故障转移机制中不起任何作用。
对于各种中断情况的镜像响应
自动故障转移中主机中断场景的响应
在以下几种主要的主节点中断情况下,活动的备机故障转移成员会自动接管:
主节点的计划中断(例如出于维护目的)通过关闭其 InterSystems IRIS 实例来启动。
自动故障转移发生是因为主机指示活跃备机接管。
主 InterSystems IRIS 实例由于意外情况而挂起。
自动故障转移发生是因为主节点检测到它被挂起并指示活跃备机接管。
主 InterSystems IRIS 实例由于意外情况而被迫关闭或完全失去响应。
在此情况下,主节点不能指示备机接管。然而,活动备机可以在从仲裁机那里得知它也与主节点失去联系后接管,也可以通过与主机的 ISCAgent 联系并获得主机停机的确认后接管。
主机存储的子系统失败。
存储失败的典型后果是主机实例挂起,原因是 I/O 错误,在这种情况下,主节点检测到它被挂起,并指示活动的备机接管(如场景 2)。然而,在某些情况下,场景 3 或场景 5 描述的行为可能适用。
主机的主机系统发生故障或失去响应。
如果活动的备机从仲裁机获悉它也与主机失去联系,则发生自动故障转移。
如果没有配置仲裁机,或者仲裁机在主机故障前已经不可用,则不可能进行自动故障转移;在这些情况下,可以选择手动强制备机成为主机。
一个网络问题隔离了主机。
如果配置了仲裁机,并且在网络故障时两个故障转移成员都不能连接到它,则主节点将无限地进入故障状态。
• 如果活动的备机从仲裁机获悉它也与主机失去联系,则发生自动故障转移。
• 如果备机在与主节点失去联系的同时失去与仲裁机的联系,则不可能进行自动故障转移。如果两个故障转移成员都已启动,则在还原网络时,备机将与主节点联系,主节点恢复操作。或者,可以手动指定主节点。
如果没有配置仲裁机,或者在网络故障发生前,故障转移成员之一已经与仲裁机断开,则无法进行自动故障转移,主节点将继续作为主机运行。
一个未活动的备机(例如备机正在启动或未追上主机最新日志)可以通过与主节点的 ISCAgent 联系并获得最新的日志数据,在上述场景 1 到 4 下接管。未活动的备机不能在场景 5 和 6 中接管,因为它不能与 ISCAgent 联系;在这些情况下,手动强制使备机成为主机可能是一种选择。
自动故障转移机制详解
代理控制模式
当镜像启动时,故障转移成员在代理控制模式下开始操作。如果仲裁机不可用或未配置仲裁机,则保持此模式。当处于代理控制模式时,故障转移成员响应彼此之间的联系丢失,如下所述。
主机对失联的反应
如果主节点失去了与活动备机的连接,或者超过了等待它确认数据接收的 QoS timeout (QoS 超时),则主程序撤消备机的活动状态并进入故障状态,等待备机确认其不再活动。当主节点收到来自备机的确认或故障超时(是 QoS 超时的两倍)过期时,主程序退出故障状态,恢复为主程序运行。
如果主节点失去了与非活动备机的连接,则它将继续作为主程序运行,不会进入故障状态。
备机对失联的反应
如果备机失去了与主节点的连接,或者超过了等待来自主机的消息的 QoS timeout (QoS 超时),那么它将尝试与主客户的 ISCAgent 联系。如果代理报告主机实例仍作为主机实例运行,则备机重新连接。如果代理确认主节点处于关闭状态,或者它已将其强制关闭,则备机行为如下:
• 如果备机处于活动状态,且代理确认主节点在故障超时内处于停机状态,则备机将作为主服务端接管。
• 如果备机未处于活动状态,或者超过了故障超时时间,那么如果代理确认主节点已停机,并且能够从代理获得最新的日志数据,则备机将接管。
无论是否处于活动状态,备机永远无法在代理控制模式下自动接管,除非主节点自身确认它已挂起,或者主服务的代理确认主服务已停机(可能是在强制停机之后),如果主节点已停机或网络隔离,这两种情况备机无法自动接管。
注意: 当其中一个故障转移成员重启时,它会尝试联系另一个成员的 ISCAgent,其行为与不活动备机的描述一样。
仲裁机控制模式
当故障转移成员相互连接时,两者都连接到仲裁机,并且备机是活动的,它们进入仲裁机控制模式,在该模式中,故障转移会员根据仲裁机提供的关于另一个故障转移成员的信息对它们之间的联系丢失做出响应。因为每个故障转移成员通过测试其与另一个故障转移成员的连接来响应其仲裁连接的丢失,反之亦然,由单个网络事件引起的多个连接丢失被作为单个事件处理。
在仲裁机控制模式中,如果故障转移成员仅丢失其仲裁机连接,或者备机 丢失其活动状态,则故障转移成员协调切换到代理控制模式。
如果主节点和备机节点之间的连接在仲裁机控制模式下断开,则每个故障转移成员根据仲裁机连接的状态进行响应,如下图所述。
所有三个系统连接:
镜像进入仲裁机控制模式(如果尚未进入仲裁机控制模式)
备机失去与仲裁机的连接,但仍连接到主节点:
镜像切换到代理控制模式
主节点继续作为主节点运行
备机尝试重新连接仲裁机
主节点失去与仲裁机的连接,但仍连接到备机:
镜像切换到代理控制模式
主节点继续作为主节点运行
主节点尝试重新连接仲裁程序
故障转移成员彼此失去连接,仍然连接到仲裁机:
镜像切换到代理控制模式
主节点继续作为主节点运行
备机尝试重新连接主节点
仲裁机失败或隔离-故障转移成员失去与仲裁机的连接,但仍彼此连接:
镜像切换到代理控制模式
主节点继续作为主节点运行
两个故障转移成员都尝试重新连接仲裁机
备机中断或被隔离-主节点和仲裁机失去与备机的连接,但仍相互连接:
主节点切换到代理控制模式并继续作为主节点操作
备机(如果在操作中)切换到代理控制模式并尝试重新连接到主节点
主节点中断或被隔离-备机和仲裁机失去与主节点的连接,但仍相互连接:
主节点(如果在运行中)将无限期地保持在仲裁控制模式和故障状态
备机作为主机接管,切换到代理控制模式,并在恢复连接时强制主机关闭
三个连接全部丢失:
主节点(如果在运行中)将无限期地保持在仲裁控制模式和故障状态;如果与备机设备联系,则切换到代理控制模式并恢复主设备的运行
备机(如果在操作中)切换到代理控制模式并尝试重新连接到主节点
注意: 由于单个事件(或多个同时发生的事件)而导致所有连接丢失的情况很少见。在大多数情况下,镜像在所有连接丢失之前切换到代理控制模式,在这种情况下:
主节点(如果在运行)继续作为主节点运行
备机(如果正在运行)尝试重新连接到主节点
文章
Michael Lei · 六月 26, 2022
在前一篇文章中,我已经演示了一种简单的方法来记录数据的变化。在这个时候,我改变了负责记录审计数据的 "审计抽象类 "和记录审计日志的数据结构。
我已经将数据结构改为父子结构,其中将有两个表来记录 "交易 "和在该交易中改变的 "字段的值"。
看一下新的数据模型:
看看从 "审计类 "改变的代码吧:
Class Sample.AuditBase [ Abstract ]{ Trigger SaveAuditAfter [ CodeMode = objectgenerator, Event = INSERT/UPDATE, Foreach = row/object, Order = 99999, Time = AFTER ]{ #dim %compiledclass As %Dictionary.CompiledClass #dim tProperty As %Dictionary.CompiledProperty #dim tAudit As Sample.Audit Do %code.WriteLine($Char(9)_"; get username and ip adress") Do %code.WriteLine($Char(9)_"Set tSC = $$$OK") Do %code.WriteLine($Char(9)_"Set tUsername = $USERNAME") Set tKey = "" Set tProperty = %compiledclass.Properties.GetNext(.tKey) Set tClassName = %compiledclass.Name Do %code.WriteLine($Char(9)_"Try {") Do %code.WriteLine($Char(9,9)_"; Check if the operation is an update - %oper = UPDATE") Do %code.WriteLine($Char(9,9)_"if %oper = ""UPDATE"" { ") Do %code.WriteLine($Char(9,9,9)_"Set tAudit = ##class(Sample.Audit).%New()") Do %code.WriteLine($Char(9,9,9)_"Set tAudit.Date = +$Horolog") Do %code.WriteLine($Char(9,9,9)_"Set tAudit.UserName = tUsername") Do %code.WriteLine($Char(9,9,9)_"Set tAudit.ClassName = """_tClassName_"""") Do %code.WriteLine($Char(9,9,9)_"Set tAudit.Id = {id}") Do %code.WriteLine($Char(9,9,9)_"Set tSC = tAudit.%Save()") do %code.WriteLine($Char(9,9,9)_"If $$$ISERR(tSC) $$$ThrowStatus(tSC)") Do %code.WriteLine($Char(9,9,9)_"Set tAuditId = tAudit.%Id()") While tKey '= "" { set tColumnNbr = $Get($$$EXTPROPsqlcolumnnumber($$$pEXT,%classname,tProperty.Name)) Set tColumnName = $Get($$$EXTPROPsqlcolumnname($$$pEXT,%classname,tProperty.Name)) If tColumnNbr '= "" { Do %code.WriteLine($Char(9,9,9)_";") Do %code.WriteLine($Char(9,9,9)_";") Do %code.WriteLine($Char(9,9,9)_"; Audit Field: "_tProperty.SqlFieldName) Do %code.WriteLine($Char(9,9,9)_"if {" _ tProperty.SqlFieldName _ "*C} {") Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField = ##class(Sample.AuditField).%New()") Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField.Field = """_tColumnName_"""") Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField.OldValue = {"_tProperty.SqlFieldName_"*O}") Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField.NewValue = {"_tProperty.SqlFieldName_"*N}") Do %code.WriteLine($Char(9,9,9,9)_"Do tAuditField.AuditSetObjectId(tAuditId)") Do %code.WriteLine($Char(9,9,9,9)_"Set tSC = tAuditField.%Save()") do %code.WriteLine($Char(9,9,9,9)_"If $$$ISERR(tSC) $$$ThrowStatus(tSC)") Do %code.WriteLine($Char(9,9,9)_"}") } Set tProperty = %compiledclass.Properties.GetNext(.tKey) } Do %code.WriteLine($Char(9,9)_"}") Do %code.WriteLine($Char(9)_"} Catch (tException) {") Do %code.WriteLine($Char(9,9)_"Set %msg = tException.AsStatus()") Do %code.WriteLine($Char(9,9)_"Set %ok = 0") Do %code.WriteLine($Char(9)_"}") Set %ok = 1} }
通过Test()类方法改变数据,现在你可以看到审计类(Sample.Audit)中的 "父记录 "和 "审计字段 "类中的 "子字段 "被改变。(Sample.AuditField)。
d ##class(Sample.Person).Test(1)INSERT INTO Sample.Person (Name, Age) VALUES ('TEST PARENT-CHILD', '01')SQLCODE: 0ID Age Name1 01 TEST PARENT-CHILD 1 Rows(s) AffectedUPDATE Sample.Person SET Name = 'INTERSYSTEMS DEVELOPER COMMUNITY', Age = '100' WHERE Name = 'TEST PARENT-CHILD'SQLCODE:0ID Age Name1 100 INTERSYSTEMS DEVELOPER COMMUNITY 1 Rows(s) Affected
审计类:
注意Sample.AuditField记录通过Audit字段=1对Sample.Audit类进行了引用。你可以通过使用两个类的关系来查询数据,如下所示:
这样就搞定了。这样,我们有一个不同的审计日志数据结构。
问题
jingwei lu · 三月 13, 2022
现在我们院区也遇到这样一个问题。关于cache的高可用架构现有有两个方案:
1.选择双节点的完全无共享架构的自动转移镜像集群外加一个灾难恢复镜像
2.就像你提问的那种,两台主机先做Rose HA用一套双活存储实现高可用,然后再弄一台服务器做Rose HA的单机镜像(有可能是同步也有可能是异步那种比如灾难恢复镜像)
现在想问问如果单纯考虑切换时候对业务的影响,如果切换的中断时长什么的,用哪个最好啊?第二种方案真有很多单位用么?
希望有大神能替我解答一下,谢谢。 可以参考下历史的帖子。https://community.intersystems.com/post/backup-strategy-%E5%8F%8C%E6%9C%BA%E5%A4%87%E4%BB%BD%E7%AD%96%E7%95%A5
如果是我,会选择方案1,原因:既然 intersystems 提供了完整的 mirror 高可用方案,就没必要再和第三方的方案掺和在一起,凭空增加了问题排错的复杂度。如果真出现了服务中断,这个时候要第一要务是尽快做出正确的决策,以便尽快恢复服务。架构的复杂性会给自己造成很多不必要的麻烦以及决策上的复杂性 (个人建议,仅供参考。) 我们推荐的高可用性方案是Mirror。也就是配置至少主备两个镜像成员+灾备异步镜像成员,可选配置多个异步报表镜像成员以及多个灾备异步镜像成员。
当然同时推荐的是建立健全的外部备机机制,以及使用外部备份进行恢复的演练,手动升级灾备异步镜像成员为主机的演练。
对于大型应用一般使用ECP创建多台应用服务器,进行多用户的负载分配,再将多台应用服务器连接至数据服务器,数据服务器进行高可用性配置。 感谢分享 感谢分享 常见问题系列-系统管理篇-如何进行数据库备份
InterSystems Caché系统高可用与数据库镜像