搜索​​​​

清除过滤器
文章
姚 鑫 · 三月 31, 2021

第十四章 使用SQL Shell界面(二)

# 第十四章 使用SQL Shell界面(二) # 存储和调用SQL语句 ## 通过数据回调 SQL Shell自动将在终端会话期间发出的每个成功的SQL语句存储在本地缓存中,并为其分配一个顺序号。这些数字用于在当前Terminal过程中重新调用以前的SQL语句。 SQL Shell仅将数字分配给成功的SQL语句。如果在准备SQL语句期间发生错误,则不会分配任何编号。这些数字分配不是特定于名称空间的。以下是可用的数字调用命令: - `#`:可以使用#列出所有先前缓存的SQL语句及其分配的编号。 - `#n`:可以通过在SQL Shell提示符下指定#n来调用并执行先前的SQL语句,其中`n`是SQL Shell分配给该语句的整数。 - `#0`:可以通过在SQL Shell提示符下指定`#0`来调用并执行最近准备的SQL语句。 `#0`调用最近准备的SQL语句,而不必调用最近执行的SQL语句。因此,调用和执行SQL语句对#0调用哪个SQL语句没有影响。 通过数字调用SQL语句不会为该语句分配新的数字。 SQL Shell在终端会话的持续时间内顺序分配数字;退出并重新进入SQL Shell或更改名称空间不会影响数字分配或先前分配的数字的有效性。 要删除所有号码分配,请使用`#CLEAR`并在显示的提示符下确认此操作。这将删除所有先前的号码分配,并从1重新开始号码分配。 ## 通过名字回调 可以选择为SQL语句分配名称,然后按名称重新调用该语句。这些名称用于重新调用从任何当前用户的Terminal进程发出的SQL语句。通过名称保存和调用SQL语句有两种方法: - 使用`SAVEGLOBAL`保存到全局;使用`OPEN`从全局调用。 - 使用`SAVE`保存到文件;使用`LOAD`从文件中调用。 ### 保存到全局变量 要将全局名称分配给最新的SQL语句,请使用sql shell命令`saveglobal`名称,该名称可以缩写为`SG`名称。然后,可以使用SQL Shell命令打开名称来调用全局的SQL语句。如果`Executemode`是立即的,则SQL shell都会调用并执行该语句。如果延迟了`executemode`,则将准备该语句,但在指定`GO`命令之前,不会执行该语句。 每次使用打开名称以全局名称调用SQL语句时,SQL shell会为语句分配新号码。旧的和新数字都对调用数字仍然有效。 名称可以包含除空白字符之外的任何可打印字符。名称中的字母区分大小写。名称可以是任何长度。名称特定于当前命名空间。可以多次使用不同名称保存相同的SQL语句;所有已保存的名称都保持有效。如果使用已分配的名称保存SQL语句,则SQL Shell会提示是否希望覆盖现有名称,将其重新分配给新的SQL语句。 为当前命名空间分配全局名称。可以使用SQL Shell `L`(或列表)命令列出当前命名空间的所有分配的全局名称。分配后,所有当前用户的终端进程都可以使用名称。在创建它结束的终端进程后,分配的名称仍然存在。如果没有名称分配,则列表返回“保存”消息的“无语句”。 要删除全局名称分配,请使用清除名称。要删除当前命名空间的所有全局名称分配,请在显示的PROMP下使用清除并确认此操作 ### 保存到文件 要将文件名分配给最新的SQL语句,请使用SQL Shell命令保存名称。然后,可以使用SQL Shell命令加载名称来调用SQL语句。如果`Executemode`是立即的,则SQL shell都会调用并执行该语句。每次使用`Load Name`按文件名调用SQL语句时,SQL Shell会将新号码分配给语句。旧的和新数字都对召回数字仍然有效。 名称可以包含除空白字符之外的任何可打印字符。名称中的字母区分大小写。名称可以是任何长度。名称特定于当前命名空间。可以多次使用不同名称保存相同的SQL语句;所有已保存的名称都保持有效。如果尝试使用已分配的名称保存SQL语句,则SQL Shell会提示是否希望覆盖现有名称,将其重新分配给新的SQL语句。 为当前命名空间分配名称。分配后,所有当前用户的终端进程都可以使用名称。在创建它结束的终端进程后,分配的名称仍然存在。 # 清除缓存查询Query SQL shell提供了清除(缩写`p`)命令,以清除当前命名空间中的所有缓存查询。此命令清除名称空间中的所有缓存查询,而不仅仅是使用SQL Shell生成的查询。 `$SYSTEM.SQL.Purge()`方法和管理门户操作下拉列表选项为提供了更具体的选项,仅清除所选择的缓存查询或清除命名空间中的所有缓存查询。 # 配置SQL shell - 可以使用Management Portal配置SQL Shell默认值。 - 可以使用SQL Shell参数配置单个SQL shell。更改SQL Shell参数覆盖SQL shell的当前调用的系统范围默认值;它不会更改系统范围的SQL shell默认值。 以下是可用的SQL Shell配置选项,相应的shell参数和默认设置: 管理门户shell配置| Shell 参数| 默认 ---|---|--- Select Mode| selectmode| Logical SQL Dialect (TSQL) |dialect (TSQL) |IRIS Schema Search Path| path| none Result Column Alignment| colalign |Delimiter Command Prefix (TSQL)| commandprefix (TSQL)| none Result Output Display Mode |displaymode |Current Device Display Path| displaypath|none Display File| displayfile| none Display File Translate Table| displaytranslatetable|none Echo Mode |echo| On Execute Mode| executemode|| Immediate Messages Mode| messages| On IF condition to allow execution || 1 | |log| Off 标记为(TSQL)的参数主要用于从SQL Shell执行`Sybase`或`MSSQL` `Transact-SQL`代码。 ## 配置SQL Shell系统范围默认值 转到管理门户,选择系统管理,配置,SQL和对象设置,SQL。选择SQL Shell选项卡。查看并设置SQL Shell系统范围的当前默认设置。 如果更改一个或多个配置设置,则在管理门户路径之后立即由屏幕的左上角的星号(`*`)表示。例如,系统>配置> SQL *。按SAVE按钮接受更改。激活更改,星号消失。 ## 为SQL shell配置参数 SQL Shell配置参数特定于当前终端进程上的当前SQL Shell调用。设置跨名称空间应用。但是,如果退出SQL Shell,则所有SQL Shell参数都会重置为系统宽的默认值。 Intersystems Iris提供系统默认值;您可以使用Set Save建立当前进程的不同默认值,如下所述。 SQL shell set命令(没有参数)显示当前shell配置参数,如以下示例所示。在此示例中,该组显示系统默认值,这些值是调用SQL Shell时建立的值: ```java [SQL]USER>>SET commandprefix = "" dialect = IRIS displayfile = displaymode = currentdevice displaypath = displaytranslatetable = echo = on executemode = immediate log = off messages = on path = SQLUser selectmode = logical [SQL]USER>> ``` 要显示单个配置参数的当前设置,请指定`set param`。例如,`SET SelectMode`返回当前选择介绍设置。 可以使用`SQL Shell Set`命令设置shell配置参数。 SQL Shell调用的持续时间持续一个设定值;每次调用SQL shell时,参数都会重置为默认值。设置可以使用以下任一语法表单: ```java SET param value SET param = value ``` 参数和值都不区分大小写。允许空间,但不需要,之前和之后。 SQL Shell `Set Save`命令将当前shell配置参数设置保存为用户默认值。这些默认值应用于当前进程的所有后续SQL Shell调用。它们也被应用于SQL Shell默认值,以在该用户调用的终端过程中的任何后续调用的SQL Shell。它们仍然有效,直到特别重置。使用`Set`保存不会影响当前正在运行的SQL Shell调用。 SQL Shell `Set Clear`命令清除(重置为系统默认值)当前进程的当前shell配置参数设置。 Intersystems IRIS将此重置应用于当前进程的后续SQL Shell调用,或者当前用户调用的任何新终端进程。设置清除不会影响当前运行的SQL Shell调用。 既不设定保存也没有设置清除更改系统范围的SQL Shell Shell默认设置,使用管理门户进行配置和显示。 ## Setting COLALIGN 可以使用`Set Colalign`来指定用于显示查询`ResultSet`数据和列标题的空格格式。可用选项包括: - 分隔符:`ResultSet`标题/数据列将基于标准分隔符(标签)对齐。这是默认值。 - 标题:`ResultSet`标题/数据列将基于列标题的长度和标准分隔符(标签)对齐。 - 数据:`ResultSet`标题/数据列将基于列数据属性的精度/长度和标准分隔符(标签)对齐。 ## 设置displaymode和displaytranslatetable 可以使用`Set DisplayMode`指定用于显示查询数据的格式,如以下示例所示: ```java DHC-APP>DO $SYSTEM.SQL.Shell() SQL Command Line Shell ---------------------------------------------------- The command prefix is currently set to: . Enter q to quit, ? for help. DHC-APP>>SET DISPLAYMODE XML displaymode = xml DHC-APP>> ``` `DisplayMode`默认值是`CurrentDevice`,其在TXT格式中显示终端上的查询数据。可以指定`set displaymode = cur`恢复`CurrentDevice`默认值。 其他可用选项有`TXT`、`HTML`、`PDF`、`XML`和`CSV`。 格式的选择决定了文件类型。 InterSystems IRIS创建这种类型的文件,将查询数据写入该文件,并在可能的情况下启动适当的程序来显示该查询数据文件。 对于除TXT之外的所有选项,将创建第二个文件来记录结果集消息。 默认情况下,SQL Shell在InterSystems IRIS mgr\Temp\目录中创建这些文件,并分配一个随机生成的带有适当文件类型后缀的文件名。 生成的消息文件名与数据文件名相同,除了附加的字符串`“Messages”`。 对于`HTML`、`PDF`和`XML`选项,消息文件具有与查询数据文件相同的文件类型后缀。 对于`CSV`选项,消息文件具有`TXT`文件类型后缀。 以下是`DisplayMode = TXT`时创建的文件的示例: ``` C:\InterSystems\IRIS\mgr\Temp\sGm7qLdVZn5VbA.txt C:\InterSystems\IRIS\mgr\Temp\sGm7qLdVZn5VbAMessages.txt ``` 每次运行查询时,SQL shell都会创建一个具有随机生成的文件名的新文件。 如果显示屏是`txt`或`csv`,则可以选择在执行格式转换时指定要应用的翻译表的名称可以指定`SET DISPLAYTRANSLATE`或`SET DISPLAYTRANSLATERATE`。转换表名称值区分大小写。 如果`DisplayMode`被设置为除`CurrentDevice`以外的值,则任何查询结果集包含控制字符的数据会导致生成的警告消息。通常,控制字符仅在逻辑模式下出现在查询结果集数据中。例如,列表结构中的数据包含在逻辑模式下显示的控制字符。因此,建议将`DisplayMode`设置为`CurrentDevice`以外的值时,还将`SelectMode`设置为显示或ODBC。 ### 设置displayfile和displaypath 如果`DisplayMode`设置为`“CurrentDevice以外的值”`,则可以使用`DisplayFile`和`DisplayPath`参数指定目标文件位置: - `DISPLAYFILE:`设置为一个没有后缀的简单文件名; 例如:`SET DISPLAYFILE = myfile`。 也可以将该参数设置为部分限定路径,系统间的IRIS将该路径追加到`DISPLAYPATH`值或默认目录中,根据需要创建子目录; 例如:`SET DISPLAYFILE = mydir\myfile`。 如果设置了`DISPLAYPATH`,系统将在指定的目录中创建一个以该文件名命名的文件; 如果没有设置`DISPLAYPATH`,系统将在InterSystems IRIS mgr\Temp\目录下创建一个以该文件名命名的文件。 - `DISPLAYPATH:`根据操作系统平台的不同,设置为以斜杠(`“/”`)或反斜杠(`“\”`)结尾的现有的全限定目录路径结构。 如果设置了`DISPLAYFILE`,系统将在此目录下创建一个名为`DISPLAYFILE`的文件; 如果没有设置`DISPLAYFILE`,系统将在该目录下创建一个随机生成的文件名文件。 如果目录`“DISPLAYPATH”`不存在,InterSystems IRIS将忽略`“DISPLAYPATH”`和`“DISPLAYFILE”`的设置,使用默认目录和随机生成的默认文件名。 必要时,系统自动在`DISPLAYPATH`值的末尾添加斜杠(或反斜杠)和/或从`DISPLAYFILE`值的开始删除斜杠(或反斜杠),以创建有效的完全限定目录路径。 设置`DISPLAYMODE`、`DISPLAYFILE`和`DISPLAYPATH`: ```java DHC-APP>>SET DISPLAYMODE XML displaymode = xml DHC-APP>>SET DISPLAYFILE = myfile displayfile = myfile DHC-APP>>SET DISPLAYPATH = C:\temp\mydir\ displaypath = C:\temp\mydir\ DHC-APP>> ``` 执行查询时,SQL shell将生成以下文件。第一个包含查询数据。第二个包含Query执行产生的任何消息: ```java C:\temp\mydir\myfile.xml C:\temp\mydir\myfileMessages.xml ``` 如果既不指定`DISPLAYFILE`也不指定`DISPLAYPATH`,系统将在Mgr\Temp\目录下为InterSystems IRIS安装(例如,`C:\InterSystems\IRIS\Mgr\Temp\`)创建一个随机生成的文件名。 如果显示屏未设置为`CurrentDevice`,则每次使用`displayfile`集运行查询时,命名文件中的任何现有数据都会被新查询数据替换为新的查询数据。每次使用`displayfile`未设置查询时,SQL shell都会使用随机生成的文件名和新的相应邮件文件创建一个新文件。 如果`displaymode`设置为`currentDevice`,则`DisplayFile`和`DisplayPath`参数无效。 ## 设置executemode SQL Shell支持立即和延迟的SQL语句执行。立即执行准备并在按Enter键时执行指定的SQL语句。延迟执行准备在输入Enter时,但在指定转到SQL提示符之前,不会执行它。 可用选项已立即设置`ExecuteMode`(默认值),设置`ExecuteMode`延迟和设置`ExecuteMode`以显示当前模式设置。以下示例设置执行模式: ```java DHC-APP>>SET EXECUTEMODE DEFERRED Executemode = deferred ``` 延迟执行允许准备多个SQL查询,然后按名称或编号调用它们以进行执行。要执行准备好的SQL语句,请调用所需的语句(来自适当的命名空间),然后指定`Go`。 以下示例显示了在延迟模式下准备三个查询。前两个保存并分配了调用名称;第三个未分配一个名称,但可以通过数字来调用: ```java DHC-APP>>SELECT TOP 5 Name,Home_State FROM Sample.Person 1. SELECT TOP 5 Name,Home_State FROM Sample.Person --------------------------------------------------------------------------- DHC-APP>>SAVE 5sample ...statement saved as: 5sample DHC-APP>>SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State 2. SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State --------------------------------------------------------------------------- DHC-APP>>SAVE 5ordered ...statement saved as: 5ordered DHC-APP>>SELECT Name,Home_State FROM Sample.Person ORDER BY Home_State 3. SELECT Name,Home_State FROM Sample.Person ORDER BY Home_State --------------------------------------------------------------------------- ``` 以下示例显示了延迟模式执行前一个示例中定义的两个查询的执行。请注意,此示例通过名称调用一个查询(在调用SQL Shell提供新号码时,并按编号调用一个查询: ```java DHC-APP>>go C:\InterSystems\Cache\mgr\Temp\ffQlXfFdbGnOxA.xml Messages.xml statement prepare time(s)/globals/lines/disk: 0.0526s/45464/263430/5ms execute time(s)/globals/lines/disk: 0.2948s/153553/1042318/75ms --------------------------------------------------------------------------- ``` ## Setting ECHO 可以使用`Set Echo`来指定是否将查询结果恢复到SQL Shell。如果指定`SET Echo = OFF`,则准备查询,定义缓存查询,并执行查询。终端没有查询结果。这在以下示例中显示: ```java DHC-APP>>set echo=off echo = off DHC-APP>>SELECT Name,Age FROM Sample.Person 4. SELECT Name,Age FROM Sample.Person -------------------------------------------------------------------------- ``` 如果指定`SET Echo = ON`(默认值),则将查询结果显示给终端。这在以下示例中显示: ```java DHC-APP>>set echo=on echo = on DHC-APP>>SELECT Name,Age FROM Sample.Person 5. SELECT Name,Age FROM Sample.Person --------------------------------------------------------------------------- DHC-APP>>go C:\InterSystems\Cache\mgr\Temp\LVZpPfjfxXXJBg.xml Messages.xml statement prepare time(s)/globals/lines/disk: 0.0001s/5/187/0ms execute time(s)/globals/lines/disk: 0.1613s/152365/1040157/0ms --------------------------------------------------------------------------- ``` `SET Echo`如果`displaymode = currentDevice`(默认值)仅有意义。 `SET ECHO`和`SET MESSAGES`指定终端显示的内容; 它们不会影响查询的准备或执行。 如果`SET MESSAGES=OFF和SET ECHO=OFF`,则查询准备好了,一个缓存的查询被创建,查询执行创建一个查询结果集,但是没有返回给终端。 ## Setting MESSAGES 可以使用`SET MESSAGES`来指定是否显示查询错误消息(如果不成功)或查询执行信息(如果成功): - 如果查询执行不成功:如果指定`SET MESSAGES=OFF`,则终端不会显示任何信息。 如果指定`SET MESSAGES=ON`(默认值),则返回查询错误提示,如下所示`:error #5540: SQLCODE: -30 message: Table 'SAMPLE`。 值得注意的没有找到。 - 如果查询执行成功:如果指定`SET MESSAGES=OFF`,则只显示查询结果和受影响的`n`行。 如果指定`SET MESSAGES=ON`(默认值),则查询结果和受影响的`n`行(`s`行)后面紧跟着语句准备度量、语句执行度量和生成的缓存查询的名称。 准备和执行指标以运行时间(以毫秒为单位)、全局引用总数、执行的命令总数和磁盘读取延迟(以毫秒为单位)来衡量。 设置`DISPLAYMODE`不会改变`SET MESSAGES=ON`时显示的信息。 一些`DISPLAYMODE`选项同时创建一个查询结果集文件和一个消息文件。 该消息文件包含结果集消息,而不是`set messages =ON`时显示到终端的查询准备和执行消息。 设置消息并设置`echo`指定终端上显示的内容;它们不会影响查询的准备或执行。如果`SET MESSAGENT = OFF`和`SET ECHO = OFF`,则准备成功的查询,创建缓存的查询,查询执行创建查询结果集,但没有返回到终端。 ## Setting LOG 可以使用`Set`日志指定是否将SQL Shell活动记录到文件。可用选项包括: - `SET LOG OFF`: 默认值。 Intersystems IRIS不会为当前SQL Shell记录活动。 - `SET LOG ON`: Intersystems Iris将SQL Shell活动记录到默认日志文件。 - `SET LOG pathname`:Intersystems Iris将SQL Shell活动记录到`Pathname`指定的文件。 `SET LOG ON`在IRIS\mgr\namespace中创建一个日志文件,其中namespace是进程当前命名空间的名称。 这个默认日志文件名为xsqlnnnn。 其中nnnn是当前进程的进程ID (pid)号。 日志文件可以挂起并恢复。 创建日志文件后,`SET log OFF`会挂起对该日志文件的写入。 设置`LOG ON`恢复写入默认日志文件。 日志重新启动:日志恢复时,将日期时间写入日志文件。 设置`LOG ON`总是激活默认日志文件。 因此,如果暂停写入指定的路径名日志文件,则在恢复时必须指定`SET log pathname`。 激活日志文件创建终端上显示的SQL Shell活动的副本;它不会重定向SQL Shell终端输出。 SQL Shell Log为失败的SQL执行和SQL代码记录SQL错误,并为成功的SQL执行而导致的行计数。 SQL Shell日志不会记录结果集数据。 如果日志已处于活动状态,则指定“设置”登录无效。如果日志已处于活动状态,则指定设置日志路径名暂停当前日志并激活路径名指定的日志。、 ## Setting PATH 可以使用SET路径架构来设置Schema Search Path,SQL用于提供不合格表名的正确架构名称。架构可以是单个架构名称,或者逗号分隔的架构名称列表,如下例所示: ```java DHC-APP>>SET PATH cinema,sample,user path = cinema,sample,user ``` 没有任何参数的设置路径删除了当前架构搜索路径,恢复系统范围的默认模式名称。 如果未指定`SET`路径架构,或者在指定的模式中找不到表,则SQL Shell使用系统范围的默认模式名称。 ## Setting SELECTMODE 可以使用`SetSeliteMode`指定用于显示查询数据的模式。 ```java DHC-APP>>SET SELECTMODE DISPLAY selectmode = display ``` 可用选项显示,逻辑和ODBC。逻辑是默认值。要确定当前模式,请指定`SETELESMODE`,无需值: ```java DHC-APP>>SET SELECTMODE logical selectmode = logical ``` `%List`数据使用非打印字符进行编码。因此,当`SelectMode =逻辑`时,SQL shell将`%List`数据值显示为`$listbuild`语句,例如以下`$lb("White","Green")`。时间数据类型数据支持分数秒。因此,当`SelectMode = ODBC`时,SQL Shell显示分数秒,这与ODBC标准不对应。实际的ODBC时间数据类型截断分数秒。 还可以使用`SET SELECTMODE`指定输入数据是否将从显示格式转换为逻辑存储格式。 要进行此数据转换,必须使用`select`运行时模式编译SQL代码。 在执行时,`SET SELECTMODE`必须设置为`LOGICAL`(默认值)。
问题
Yufeng Li · 十二月 23, 2021

在IRIS 上怎么实现读写分离

在IRIS 上怎么实现读写分离? IRIS的镜像(Mirroring)支持多个报告类型(Reporting Asyncs)的异步成员,这些异步成员可以用于查询、报表运行、BI等多种场景。 我们现在是用前端调用rest api,这种情况下要怎么区分调用读服务器还是写服务器? 可以封装一个REST服务用于前端api调用,这个REST服务根据不同api操作和路径,在后台调用不同服务器上的REST API或SQL操作。或者也可以考虑使用InterSystems API管理器做这个事。 相关文章请参考:https://cn.community.intersystems.com/smartsearch?search=API 如果是指数据库层的读写分离,可以使用Sharding技术,利用Sharding技术中的计算结点和数据结点,搭建负载均衡+读写分离的数据库集群,参考: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSCALE_sharding#GSCALE_sharding_reference_plan_qs 如果服务器/实例资源有限,又想实现读负载与写负载的分离,那么基于@Qiao Peng指出的镜像异步成员,在API层(即应用程序层)通过业务流程来控制查询/写入操作的分发则是成本较低的方案。 请参考这个文档链接[Deploy Compute Nodes for Workload Separation and Increased Query Throughput](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSCALE_sharding#GSCALE_sharding_options_qs)
文章
Michael Lei · 三月 12, 2022

Linux TZ环境变量未被设置以及对Caché的影响

在最近的大规模基准测试活动中,我们看到过多的%sys CPU时间,对应用程序的扩展产生了负面影响。 问题 我们发现,由于TZ环境变量没有被设置,很多时间都花在了localtime()系统调用上。 我们创建了一个简单的测试程序来证实这一观察结果,设置了TZ与未设置TZ的时间差和所需的CPU资源都是惊人的。 我们发现,当TZ没有设置时,从localtime()继承使用stat()系统调用到/etc/local_time是成本很高。 建议 InterSystems强烈建议在任何Linux安装中,无论是x86还是Linux on Power,都要确认TZ环境变量的设置是否适当,以获得最佳性能。 更多详情请见。"man tzset"。 目前的Caché 2016.1现场测试包含了对日期和时间功能的优化,初步测试表明有很大的改进。 下面是测试新的内部函数调用的样本输出,在这两种情况下,在Power上的Linux都没有设置TZ。 在Caché 2016.1 FT之前: real 0m22.60s user 0m1.64s sys 0m20.89s 带有Caché 2016.1 FT: real 0m0.40s user 0m0.37s sys 0m0.00s 请在评论区说明你的应用程序在TZ环境变量方面的任何经验,以及Caché 2016.1现场测试在设置或不设置TZ时的影响。谢谢!
文章
Qiao Peng · 一月 24, 2021

解决SQL适配器连接到字符集为US7ASCII的Oracle数据库的中文乱码问题

在使用xDBC连接到字符集为US7ASCII的Oracle数据库时,大家可能遇到过中文的乱码问题,尤其是使用Oracle自己的xDBC驱动的时候。 字符集为US7ASCII的Oracle数据库虽然可以保存中文数据,但给客户端带来了很多麻烦,需要对获取和提交的数据进行转码。 在Ensemble/Health Connect/InterSystems IRIS 中使用SQL适配器连接到这样的Oracle数据库时,可以使用$ZCVT函数进行转码。 1. $ZCVT函数 $ZCVT函数是广泛使用的字符串转换函数,可以做大小写转换、编码转换、URL 和 URI 转换等。我们用其编码转换能力来解决字符集转码问题。 2. 获取的SQL结果集数据有中文时 这时,Oracle的驱动返回的中文数据通常是GB码,而不是Unicode或UTF码。可以通过$ZCVT函数对GB码的数据进行转码,转换为Unicode: Set tCorrectData = $ZCVT(tOriginalData,"I","GB18030")其中$ZCVT函数的第一个参数tOriginalData是获取到到结果集字段值;第二个参数“I”说明tOriginalData是输入字符串;第三个参数“GB18030”是说明输入字符串的字符集编码是GB18030。 上面的代码会将tOriginalValue按GB18030编码转换为Unicode编码,并将转换结果赋给变量tCorrectData。 3. 发送中文数据或中文查询条件时 首先,需要将本地Unicode的中文数据转码为GB码。还是使用$ZCVT函数: Set tSendingData = $ZCVT(tOriginalData,"O","GB18030") 其中$ZCVT函数的第一个参数tOriginalData是输出的含中文数据;第二个参数“O”说明要转换输出数据;第三个参数“GB18030”是说明输出时要转换为GB18030。 上面的代码会将tOriginalValue按Unicode编码转换为GB18030编码,并将转换结果赋给变量tSendingData。 做完这一步,Oracle驱动很可能依然不认识其中的GB码中文数据。这时,需要将GB码的中文数据转换成GB明码字符串,例如你要将查询条件 中文 传给Oracle,你需要传递 D6D0CEC4过去(中的GB码为D6D0,文的GB码为CEC4),然后再使用Oracle的函数UTL_RAW.CAST_TO_VARCHAR2,将GB明码字符串转为内部使用的中文数据。所以修改后代码如下: Set tSendingData = $ZCVT(tOriginalData,"O","GB18030") Set tTmpDataHex = "" //转换为GB明码 For i=1:1:$length(tSendingData) { Set tTmpDataHex = tTmpDataHex_$zhex($ASCII(tSendingData,i)) } //使用Oracle的UTL_RAW.CAST_TO_VARCHAR2的函数 Set tSQL = "update dept_dict set dept_name = UTL_RAW.CAST_TO_VARCHAR2('"_ tTmpDataHex_"') where dept_code = '1'" 这里用到了$ZHEX和$ASCII函数,将数据转换为其编码,并转为16进制值,从而得到GB明码。关于更多的Ensemble/Health Connect/InterSystems IRIS函数,可以参考文档。 自此,应该可以解决从Ensemble和Health Connect连接到字符集为US7ASCII的Oracle数据库所遇到的各种中文乱码问题了。 如果您遇到更多的问题,欢迎在社区提问。 点赞
文章
姚 鑫 · 七月 1, 2021

第二十四章 执行XSLT转换

# 第二十四章 执行XSLT转换 # 执行XSLT转换 要执行`XSLT`转换,请执行以下操作: - 如果使用的是`Xalan`处理器(对于`XSLT 1.0`),请使用`%XML.XSLT.Transformer`的以下类方法之一: - `TransformFile()`——转换给定XSLT样式表的文件。 - `TransformFileWithCompiledXSL()`——转换一个文件,给定一个已编译的XSLT样式表。 - `TransformStream()`——转换给定XSLT样式表的流。 - `TransformStreamWithCompiledXSL()`——转换一个流,给定一个已编译的XSLT样式表。 - `TransformStringWithCompiledXSL()`——转换给定已编译XSLT样式表的字符串。 - 如果使用`Saxon`处理器(用于XSLT 2.0),请使用`%XML.XSLT2.Transformer`的以下类方法之一: - `TransformFile()`——转换给定XSLT样式表的文件。 - `TransformFileWithCompiledXSL()`——转换一个文件,给定一个已编译的XSLT样式表。 - `TransformStream()`——转换给定XSLT样式表的流。 - `TransformStreamWithCompiledXSL()`——转换一个流,给定一个已编译的XSLT样式表。 这些方法具有相似的签名。这些方法的参数列表按顺序如下: - pSource—要转换的源XML。请参见此列表后面的表。 - pXSL -样式表或编译样式表。请参阅此列表后面的表格。 - pOutput -作为输出参数返回的结果XML。请参阅此列表后面的表格。 - pErrorHandler -一个可选的自定义错误处理程序。请参阅本章后面的“自定义错误处理”。如果不指定自定义错误处理程序,该方法将使用%XML.XSLT.ErrorHandler的新实例(对于两个类)。 - pParms -一个可选的InterSystems IRIS多维数组,包含要传递给样式表的参数。 - pCallbackHandler -定义`XSLT`扩展函数的可选回调处理程序。 - pResolver -一个可选的实体解析器。 8. (仅适用于`%XML.XSLT2.Transformer`)网关-`%Net.Remote.Gateway`的可选实例。如果要重用XSLT网关连接以获得更好的性能,请指定此参数; 作为参考,下表显示了这些方法的前三个参数,并进行了对比: XSLT变换方法的比较 Method | pSource (Input XML) |pXSL (Stylesheet) |pOutput(Output XML) ---|---|---|--- TransformFile() |String that gives a file name| String that gives a file name |String that gives a file name TransformFileWithCompiledXSL()| String that gives a file name |Compiled style sheet| String that gives a file name TransformStream()| Stream| Stream |Stream, returned by reference TransformStreamWithCompiledXSL()| Stream |Compiled style sheet |Stream, returned by reference TransformStringWithCompiledXSL()| String |Compiled style sheet| String that gives a file name # 示例 本节使用以下代码(但不同的输入文件)展示了几种转换: ```java Set in="c:\0test\xslt-example-input.xml" Set xsl="c:\0test\xslt-example-stylesheet.xsl" Set out="c:\0test\xslt-example-output.xml" Set tSC=##class(%XML.XSLT.Transformer).TransformFile(in,xsl,.out) Write tSC ``` ## 示例1:简单替换 在本例中,我们从以下输入XML开始: ```xml Content ``` 我们使用以下样式表: ```xml Content Replaced ``` 在这种情况下,输出文件将如下所示: ```xml Content Replaced ``` ## 示例2:内容提取 在本例中,我们从以下输入XML开始: ```xml Some text Some more text ``` 我们使用以下样式表: ```xml : ``` 在这种情况下,输出文件将如下所示: ```java 13: Some text 14: Some more text ``` ## 其他示例 InterSystems IRIS提供了以下附加示例: - 对于`XSLT 1.0`,请参阅`%XML.XSLT.Transformer`中的`Example()`、`Example2()`和其它方法。 - 对于`XSLT 2.0`,请参见`Samples`命名空间中的类`XSLT2.Examples`。
文章
王喆 👀 · 九月 24, 2022

使用Global进行数据可视化---商业智能(BI)

在医院但凡接触“数据”和“指标”的人,对以下场景应该是深有感触。同样的指标、同样的时间,有可能是同一个部门出的,最后“数据不一致”。除了“匪夷所思”,更有“深恶痛绝”。那么,如何解决这个问题?我的答案是商业智能(BI)。随着技术和市场的发展,有很多公司开始研发直接面向业务用户的敏捷BI工具,FineBI就是这样的一款BI工具。这个也是我接触的第一款国产BI。 项目的前提条件和设置 操作系统:Windows 64 IRIS产品:HealthConnect 2020 BI工具:FineBI JDK: JDK1.8 FineBI、JDK、HealthConnect、FineBI的相关安装步骤网上有很多,在此就暂时略过。要在FineBI上创建仪表板,我们需要一点数据,在此我使用的是开发过程中的一些测试数据,相关测试数据和互联互通的69个交互服务息息相关,有需要可以自己模拟的造。 将FineBI和IRIS连接 我们首先找到HealthConnect的安装文件,我们需要找到intersystems-jdbc-3.2.0.jar这样一个文件。大概在【安装目录】\InterSystems\HealthConnect\dev\java\lib\JDK18,如图所示: 将文件复制到FineBI的安装目录\webapps\webroot\WEB-INF\lib,如图所示: 这个时候我们启动FineBI 看到此说明FineBI启动成功,如果启动失败重启一下电脑或者检查以下JDK的环境变量是否配置 在浏览器上打开控制面板 点击【管理系统】--【数据连接】-【新建数据连接】-【数据连接管理】--【其他】--【其他JDBC】 填入内容 连接成功 创建仪表板 通过编写SQL的方式在FineBI中建立业务表: 按照此方式在【数据准备】-【数据列表】中建立多个业务表,如图所示: 新建仪表板 通过添加组件,选择表,选择样式 最终编辑成如图所示仪表板 总结 从上面演示的例子,我们可以看出,极短的时间我们就把FineBI连接到了IRIS的HealthConnect上,然后创建一些数据上的可视化的功能和东西。作为开发人员来说这很便捷,我们可以很快的完成一些数据展示的工作。同时如果把这些东西开放给一些精通业务但是对技术很生疏的人,就可以十分快速的把数据按照业务上需要的方式展示出来,一个部门乃至多个部分对出具的数据也有了更清晰的认识,在可视化的帮助下,那些乱起八糟的数据也开始变的更接近真实。 本文只展示了FineBI的使用,相信一些可以使用JDBC或者ODBC作为连接的BI工具都可以,比如最经典的PowerBI。
文章
姚 鑫 · 七月 27, 2023

第四章 HL7 架构和可用工具 - 查看数据结构

# 第四章 HL7 架构和可用工具 - 查看数据结构 ## 查看数据结构 当单击“数据结构”列中的名称时,`InterSystems` 会显示该数据结构中的所有字段。这是 `HL7` 数据结构页面。显示的以下列是最有用的: - 组件列列出了可用于访问段内字段的数字。 - 属性名称列列出了可用于访问段内字段的名称。 - 单击“数据结构”列中的条目可深入了解详细信息。 - 单击“代码表”列(如果有)中的条目可查看可在此字段中输入的有效代码。 当单击上面段结构页面中名为 `2.3:XCN` 的数据结构项时,将出现以下示例页面。该页面指出类别 `2.3` 数据结构 `XCN` 描述“扩展复合 `ID` 号和名称”并由 `14` 个字段组成。其中,有些是简单值,有些是数据结构,有些是代码。 有了这些信息,就可以为消息结构 `2.3:ADT_A01` 中的复杂 `PR1grp().PR1:Surgeon` 字段创建虚拟属性路径,如下所示: ``` PR1grp().PR1:Surgeon.familyname PR1grp().PR1:Surgeon.degree ``` ## 查看代码表 当单击“代码表”列中的名称时,它会列出并解释该字段的有效代码。这是 `HL7` 代码表页面。当单击上一节中显示的数据结构页面中名为 `2.3:200` 的代码表项时,将出现以下示例页面。 上面的示例示出类别`2.3`代码表`200`描述可以具有值`L`、`O`、`M`、`A`、`C`或`D`的“名称类型”。 这意味着,如果有一条 `DocType` 为 `2.3:ADT_A01` 的 `HL7` 消息,则它具有一个可选虚拟属性,路径为 `PR1grp().PR1:Anesthesiograph.nametype`,可以包含以下值之一:`L`、`O`、`M`、 `A`、`C` 或 `D`。 ## 使用自定义架构编辑器 自定义架构编辑器允许创建新的自定义 `HL7` 架构或编辑现有的自定义 `HL7` 架构。通常,自定义模式具有基本模式,它是标准模式或其他自定义模式。当 `InterSystems` 产品使用自定义架构来解析消息时,如果自定义架构中未定义消息类型、段或其他元素,它将使用基本架构中的定义。因此,只需在自定义架构中定义基本架构中不存在的元素,或者需要与基本架构中的定义不同的元素。无法编辑标准架构。 定义自定义架构的最常见原因是能够解析带有尾部 `Z` 段的 `HL7` 消息。 `InterSystems` 产品可以处理带有架构中未定义的尾部 `Z` 段的消息,但要执行以下任一操作,需要定义自定义架构: - 访问路由规则、数据转换或 `ObjectScript` 代码中尾部 `Z` 段中的字段路径。 - 验证尾部 `Z` 段。 如果`production`当前正在使用标准模式,并且需要访问数据转换或路由规则中的尾部 `Z` 段字段路径,则应执行以下操作: 1. 使用管理门户中的自定义架构编辑器创建新的 `HL7` 架构。输入自定义架构的名称并指定基本架构。请参阅创建新的自定义架构。 2. 定义可以出现在消息中的 `Z` 段。如果 `Z` 段与基础架构中的现有段具有相似的字段,可以从基础复制定义,然后根据需要进行修改。否则,可以创建一个新段。可以添加字段、删除字段或更改字段的顺序。请参阅定义新段。 3. 对于包含尾随 `Z` 段的每个消息类型,在从基础架构复制的自定义架构中创建消息类型和结构类型。将 `Z` 段添加到结构类型的末尾。请参阅定义新消息类型和结构类型 4. 修改`production` 中的业务服务以使用新的自定义架构而不是基本架构。 5. **通过向`production`的业务服务提供带有尾部 `Z` 段的新消息来测试`production`。如果在消息查看器中查看消息,则 `Z` 段(如果它们在架构中定义)将显示为蓝色。无法识别的段显示为黑色。**
文章
Michael Lei · 五月 8, 2021

将 Python JDBC 连接到 IRIS 数据库 - 快速笔记

关键字:Python,JDBC,SQL,IRIS,Jupyter Notebook,Pandas,Numpy ,机器学习  ## 1. 目的 这是一个用于演示的 5 分钟快速笔记,通过 Jupyter Notebook 中的 Python 3 调用 IRIS JDBC 驱动程序,以经由 SQL 语法从 IRIS 数据库实例读取数据和向 IRIS 数据库实例写入数据。  去年,我发表了关于[将 Python 绑定到 Cache 数据库](https://community.intersystems.com/post/deep-learning-demo-kit-python3-binding-healthshare-part-i)的简要笔记(第 4.7 节)。 如何使用 Python 挂入 IRIS 数据库以将其数据读入 Pandas 数据框和 NumPy 数组进行常规分析,然后再将一些经过预处理或标准化的数据写回 IRIS 中,准备进一步用于 ML/DL 管道,现在可能是时候回顾一些选项和讨论了。 一些立即浮现的快速**选项**: 1.    **ODBC**:Python 3 和原生 SQL 的 PyODBC? 2.    **JDBC**:Pyhton 3 和原生 SQL 的 JayDeBeApi? 3.    **Spark**:PySpark 和 SQL? 4.    **Python Native API for IRIS**:超越先前的 Python Binding for Cache? 5.   ** IPtyhon Magic SQL %%sql**?  [它](https://github.com/catherinedevlin/ipython-sql)可以支持 IRIS 了吗?  这里有漏掉其他选项吗?  我有兴趣尝试任何选项。  ## 2. 范围  我们是不是应该从普通的 JDBC 方法开始? 下一个快速笔记将总结 ODBC、Spark 和 Python Native API。  ### 范围内: 此快速演示涉及以下常见组件: Anaconda Jupyter Notebook  Python 3 JayDeBeApi JPyPe Pandas NumPy 一个 IRIS 2019.x 实例 ###  范围外: 本快速笔记不会涉及以下内容,但它们也很重要,可以使用特定的站点解决方案、部署和服务单独解决:  安全端到端。 非功能性能等。 问题排查和支持。 许可。    ## 3. 演示 ### 3.1 运行 IRIS 实例: 我只运行了一个 IRIS 2019.4 容器,作为“远程”数据库服务器。 您可以使用任何您有权利访问的 IRIS 实例。 zhongli@UKM5530ZHONGLI MINGW64 /c/Program Files/Docker Toolbox$ docker psCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                PORTS                                              NAMESd86be69a03ab        quickml-demo        "/iris-main"        3 days ago          Up 3 days (healthy)   0.0.0.0:9091->51773/tcp, 0.0.0.0:9092->52773/tcp   quickml ### 3.2 Anaconda 和 Jupyter Notebook:  我们将在笔记本电脑中重用相同的设置方法,[这里](https://community.intersystems.com/post/deep-learning-demo-kit-python3-binding-healthshare-part-i)对应 Anaconda(第 4.1 节),[这里](https://community.intersystems.com/post/run-deep-learning-demo-python3-binding-healthshare-part-ii)对应 Jupyter Notebook(第 4 节)。  Python 3.x 在这一步安装。 ### 3.3 安装 JayDeBeApi 和 JPyPe: 启动 JupyterNotebook,然后在其单元格中运行以下内容设置 Python-to-JDBC/Java 桥:     !conda install --yes -c conda-forge jaydebeapi JayDeBeApi 在撰写本文时(2020 年 1 月)使用 JPype 0.7,该版本由于一个已知错误无法运行,必须降级为 0.6.3 !conda install --yes -c conda-forge JPype1=0.6.3 --force-reinstall ### 3.4 通过 JDBC 连接到 IRIS 数据库  这里有一个正式的[使用 JDBC 连接到 IRIS 的文档](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_jdbc)。  对于通过 JDBC 执行 Python SQL,我以下面的代码为例。 它连接到此 IRIS 实例的“USER”命名空间内的数据表“`DataMining.IrisDataset`”。  ### 1. Set environment variables, if necessary<br>#import os<br>#os.environ['JAVA_HOME']='C:\Progra~1\Java\jdk1.8.0_241'<br>#os.environ['CLASSPATH'] = 'C:\interSystems\IRIS20194\dev\java\lib\JDK18\intersystems-jdbc-3.0.0.jar'<br>#os.environ['HADOOP_HOME']='C:\hadoop\bin'  #winutil binary must be in Hadoop's Home ### 2. Get jdbc connection and cursor<br><strong>import jaydebeapi<br>url = "jdbc:IRIS://192.168.99.101:9091/USER"<br>driver = 'com.intersystems.jdbc.IRISDriver'<br>user = "SUPERUSER"<br>password = "SYS"</strong><br>#libx = "C:/InterSystems/IRIS20194/dev/java/lib/JDK18"<br><strong>jarfile = "C:/InterSystems/IRIS20194/dev/java/lib/JDK18/intersystems-jdbc-3.0.0.jar"</strong> conn = jaydebeapi.connect(driver, url, [user, password], jarfile)<br>curs = conn.cursor() ### 3. specify the source data table<br><strong>dataTable = 'DataMining.IrisDataset'</strong>  ### 4. Get the result and display<br><strong>curs.execute("select TOP 20 * from %s" % dataTable)<br>result = curs.fetchall()<br>print("Total records: " + str(len(result)))<br>for i in range(len(result)):<br>    print(result[i])</strong> ### 5. CLose and clean - I keep them open for next accesses.<br><strong>#curs.close()<br>#conn.close()</strong>   Total records: 150 (1, 1.4, 0.2, 5.1, 3.5, 'Iris-setosa') (2, 1.4, 0.2, 4.9, 3.0, 'Iris-setosa') (3, 1.3, 0.2, 4.7, 3.2, 'Iris-setosa') ... ... (49, 1.5, 0.2, 5.3, 3.7, 'Iris-setosa') (50, 1.4, 0.2, 5.0, 3.3, 'Iris-setosa') (51, 4.7, 1.4, 7.0, 3.2, 'Iris-versicolor') ... ... (145, 5.7, 2.5, 6.7, 3.3, 'Iris-virginica') ... ... (148, 5.2, 2.0, 6.5, 3.0, 'Iris-virginica') (149, 5.4, 2.3, 6.2, 3.4, 'Iris-virginica') (150, 5.1, 1.8, 5.9, 3.0, 'Iris-virginica')   测试表明 JDBC 上的 Python 可以正常运行。 以下只是常规 ML 管道的一些常规数据分析和预处理,由于我们可能会在后续的演示和比较中反复涉及,因此为方便起见在这里附上。  ### 3.5 将 SQL 结果转换为 Pandas DataFrame,再转换为 NumPy 数组 如果还没有安装 Pandas 和 NumPy 软件包,可以通过 Conda 安装,类似于上面 3.3 节。 然后运行以下示例: ### transform SQL results "sqlData"to Pandas dataframe "df", then further to NumPy array "arrayN" for further ML pipelines import pandas as pdsqlData = "SELECT * from DataMining.IrisDataset"df= pd.io.sql.read_sql(sqlData, conn)df = df.drop('ID', 1)df = df[['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species']] # set the labels to 0, 1, 2, for NumPy matrixdf.replace('Iris-setosa', 0, inplace=True)df.replace('Iris-versicolor', 1, inplace=True)df.replace('Iris-virginica', 2, inplace=True) # turn dataframe into Numpy arrayarrayN = df.to_numpy() ### 6. CLose and clean - if connection is not needed anymore?#curs.close()#conn.close() 我们例行查看一下当前数据: df.head(5) df.describe() 现在,我们得到了一个 DataFrame,以及一个来自源数据表的标准化 NumPy 数组。   当然,我们在这里可以尝试各种常规分析,一个 ML 人员会按照下述步骤开始,在 Python 中替换 R([链接](http://www.lac.inpe.br/~rafael.santos/Docs/CAP394/WholeStory-Iris.html))。 数据源引自此处   ### 3.6 拆分数据并通过 SQL 写回 IRIS 数据库: 当然,我们可以像往常一样将数据拆分为训练集和验证集或测试集,然后将它们写回临时数据库表,实现 IRIS 一些即将推出的精彩 ML 功能: import numpy as np from matplotlib import pyplotfrom sklearn.model_selection import train_test_split # keep e.g. 20% = 30 rows as test data; trained on another e.g. 80% = 120 rowsX = arrayN[:,0:4]y = arrayN[:,4]X_train, X_validation, Y_train, Y_validation = train_test_split(X, y, test_size=0.20, random_state=1, shuffle=True) # make 80% of random rows into a Train setlabels1 = np.reshape(Y_train,(120,1))train = np.concatenate([X_train, labels1],axis=-1) # make 20% of left rows into Test setlTest1 = np.reshape(Y_validation,(30,1))test = np.concatenate([X_validation, lTest1],axis=-1) # write the train data set into a Pandas framedfTrain = pd.DataFrame({'SepalLength':train[:, 0], 'SepalWidth':train[:, 1], 'PetalLength':train[:, 2], 'PetalWidth':train[:, 3], 'Species':train[:, 4]})dfTrain['Species'].replace(0, 'Iris-setosa', inplace=True)dfTrain['Species'].replace(1, 'Iris-versicolor', inplace=True)dfTrain['Species'].replace(2, 'Iris-virginica', inplace=True) # write the test data into another Pandas framedfTest = pd.DataFrame({'SepalLength':test[:, 0], 'SepalWidth':test[:, 1], 'PetalLength':test[:, 2], 'PetalWidth':test[:, 3], 'Species':test[:, 4]})dfTest['Species'].replace(0, 'Iris-setosa', inplace=True)dfTest['Species'].replace(1, 'Iris-versicolor', inplace=True)dfTest['Species'].replace(2, 'Iris-virginica', inplace=True) ### 3. specify temp table names#dataTable = 'DataMining.IrisDataset'dtTrain = 'TRAIN02'dtTest = "TEST02" ### 4. Create 2 temporary tables - you can try drop tables then re-create them every timecurs.execute("Create Table %s (%s DOUBLE, %s DOUBLE, %s DOUBLE, %s DOUBLE, %s VARCHAR(100))" % (dtTrain, dfTrain.columns[0], dfTrain.columns[1], dfTrain.columns[2], dfTrain.columns[3], dfTrain.columns[4]))curs.execute("Create Table %s (%s DOUBLE, %s DOUBLE, %s DOUBLE, %s DOUBLE, %s VARCHAR(100))" % (dtTest, dfTest.columns[0], dfTest.columns[1], dfTest.columns[2], dfTest.columns[3], dfTest.columns[4])) ### 5. write Train set and Test set into the tales. You can try to delete old record then insert everytime. curs.fast_executemany = Truecurs.executemany( "INSERT INTO %s (SepalLength, SepalWidth, PetalLength, PetalWidth, Species) VALUES (?, ?, ?, ? ,?)" % dtTrain,     list(dfTrain.itertuples(index=False, name=None)) )curs.executemany( "INSERT INTO %s (SepalLength, SepalWidth, PetalLength, PetalWidth, Species) VALUES (?, ?, ?, ? ,?)" % dtTest,     list(dfTest.itertuples(index=False, name=None)) ) ### 6. CLose and clean - if connection is not needed anymore?#curs.close()#conn.close() 现在,如果切换到 IRIS 管理控制台或终端 SQL 控制台,应该看到已创建 2 个临时表:120 行的 TRAIN02 和 30 行的 TEST02。 那么本篇快速笔记到这里就结束了。 ## 4. 注意事项 * 以上内容可能会被更改或完善。  ## 5. 未来计划 我们将使用 IRIS 的 PyODBC、PySPark 和 Python Native API 替换第 3.3 和 3.4 节。除非有人愿意帮忙编写一篇快速笔记,我也将对此不胜感激。    
文章
姚 鑫 · 八月 8, 2021

方法关键字SoapBindingStyle,SoapBodyUse,SoapMessageName,SoapNameSpace

# 第七十七章 方法关键字 - SoapBindingStyle 指定此方法用作`web方法`时使用的绑定样式或`SOAP`调用机制。仅适用于定义为`web服务`或`web客户端`的类。 # 用法 若要重写方法使用的默认绑定样式(当它用作`web方法`时),请使用以下语法: ```java Method name(formal_spec) As returnclass [ WebMethod, SoapBindingStyle = soapbindingstyle ] { //implementation } ``` 其中`soapbindingstyle`是下列之一: - `document`文档(默认)—此`web方法`使用文档样式的调用。 使用这种绑定风格,`SOAP消息`被格式化为文档,并且通常只有一个部分。 在`SOAP消息`中,``元素通常包含一个子元素。``元素的每个子元素对应于一个消息部分。 - `rpc` —这个`web方法`使用`rpc`(远程过程调用)风格的调用。 使用这种绑定风格,`SOAP消息`被格式化为具有多个部分的消息。 在`SOAP消息`中,``元素包含一个子元素,其名称取自相应的操作名称。这个元素是一个生成的包装元素,它为方法的参数列表中的每个参数包含一个子元素。 重要提示:对于手动创建的`web服务`,该关键字的默认值通常是合适的。当使用`SOAP`向导从`WSDL`生成`web客户端`或`服务`时,InterSystems IRIS会将此关键字设置为适合该`WSDL`;如果修改该值,`web客户端`或服务可能不再工作。 # 详解 此关键字允许指定`web方法`使用的绑定样式。它影响`SOAP`主体的格式(但不影响任何`SOAP`头)。 # 默认 如果省略此关键字,则``元素的样式属性将改为由`SoapBindingStyle`类关键字的值确定。 # 与WSDL的关系 SoapBindingStyle方法关键字指定了WSDL的部分中< soap:operation >元素的样式属性的值。例如,如果SoapBindingStyle方法关键字是document,则WSDL可能如下所示: ```xml ... ... ... ``` 相比之下,如果`SoapBindingStyle`是`rpc`,则`WSDL`可以改为如下所示: ```xml ... ... ... ``` 绑定样式还会影响`web方法`的请求和响应``元素,如下所示: = 如果绑定样式是文档,默认情况下,每条消息只有一个部分。例如: ```xml ``` 如果`ARGUMENTSTYLE`参数是`message`,那么一条消息可以有多个部分。例如: ```xml ``` - 如果绑定样式是`rpc`,消息可以有多个部分。例如: ```xml ``` # 与 %XML.DataSet 一起使用 如果将此关键字与使用 `%XML.DataSet` 类型的对象作为输入或输出的方法一起使用,则存在一些限制。 # 第七十八章 方法关键字 - SoapBodyUse 当此方法用作 `Web方法`时,指定此方法的输入和输出使用的编码。仅适用于定义为 `Web服务`或 `Web客户端`的类。 # 用法 若要重写方法的输入和输出使用的默认编码(当它用作`web方法`时),请使用以下语法: ```java Method name(formal_spec) As returnclass [ WebMethod, SoapBodyUse = soapbodyuse ] { //implementation } ``` 其中`soapbodyuse`是下列之一: - `literal`文字(默认)-此`web方法`使用文字数据。也就是说,`SOAP消息`的``中的`XML`与`WSDL`中给出的模式完全匹配。 - `encoded`编码——这个`web方法`使用`SOAP`编码的数据。也就是说,`SOAP消息`的``中的`XML`使用了适合所使用的`SOAP`版本的`SOAP`编码,如以下规范所要求的: - `SOAP 1.1` (https://www.w3.org/TR/2000/NOTE-SOAP-20000508/) - `SOAP 1.2` (https://www.w3.org/TR/soap12-part2/) 重要提示:对于手动创建的`web服务`,该关键字的默认值通常是合适的。当使用`SOAP`向导从`WSDL`生成`web客户端`或服务时,InterSystems IRIS会将此关键字设置为适合该WSDL;如果修改该值,web客户端或服务可能不再工作。 # 详情 此关键字指定`web方法`的输入和输出的编码。 # 默认 如果省略此关键字,将使用`SoapBodyUse`类关键字的值。 # 与 %XML.DataSet 一起使用 如果将此关键字与使用 `%XML.DataSet` 类型的对象作为输入或输出的方法一起使用,则存在一些限制。 # 第七十九章 方法关键字 - SoapMessageName 指定此`web方法`的响应消息的``元素的`name`属性。 仅适用于定义为`web服务`或`web客户端`的类。 # 用法 要覆盖响应消息的``元素的默认名称,请使用以下语法: ```java Method name(formal_spec) As returnclass [ WebMethod, SoapMessageName = MyResponse ] { //implementation } ``` 其中`soapmessagename`是在`XML`中有效的任何标识符。 # 详解 注意:此关键字仅对使用`SoapBindingStyle`等于`document`(这是默认设置)的`web方法`有效。 此关键字指定响应消息正文的子元素的名称。 # 默认 如果省略此关键字,消息名称就是结尾附加了响应的`web方法`的名称。 `web方法`的名称取自`web服务`中的`web方法`定义;这只能通过重命名该方法来更改。 # 与WSDL的关系 `SoapMessageName`关键字影响`web服务`的`WSDL`的``和``部分。例如,以下web方法: ```java Method Add(a as %Numeric,b as %Numeric) As %Numeric [ SoapMessageName=MyResponseMessage,WebMethod ] { Quit a + b } ``` 对于这个`web服务`,WSDL的``和``部分如下所示: ```xml ``` 默认情况下,如果方法没有指定`SoapMessageName`关键字,`AddSoapOut`消息将包含一个名为`addressponse`的元素,而不是`MyResponseMessa` 注意,`SoapMessageName`不影响响应消息的子元素(例如`AddResult`)。 如果使用`SOAP Wizard`从`WSDL`生成`web`服务或客户端,InterSystems IRIS将此关键字设置为适合该`WSDL`的关键字。 # 对SOAP消息的影响 `web服务`可能会发送如下响应消息: ```xml 42 ``` 默认情况下,如果该方法没有指定`SoapMessageName`关键字,则``元素将改为``。 # 第八十章 方法关键字 - SoapNameSpace 指定`web方法`使用的`XML`命名空间。 仅适用于定义为`web服务`或`web客户端`的类。 # 用法 要覆盖方法使用的默认`XML`命名空间(当该方法作为一个`web方法`使用时),请使用以下语法: ```java Method name(formal_spec) As returnclass [ SoapNameSpace = "soapnamespace", WebMethod ] { //implementation } ``` 其中`soapnamespace`是一个命名空间`URI`。 注意,如果`URI`包含冒号(`:`),则字符串必须用引号括起来。 也就是说,你可以使用以下方法: ```java Method MyMethod() [ SoapNameSpace = "http://www.mynamespace.org", WebMethod ] ``` 或以下: ```java Method MyMethod() [ SoapNameSpace = othervalue, WebMethod ] ``` 但不包括以下内容: ```java Method MyMethod() [ SoapNameSpace = http://www.mynamespace.org, WebMethod ] ``` 重要提示:对于手动创建的`web服务`,该关键字的默认值通常是合适的。 使用`SOAP`向导从`WSDL`生成`web`客户端或服务时,InterSystems IRIS将此关键字设置为适合该`WSDL`的关键字; 如果修改该值,`web客户端`或服务可能不再工作。 # 详解 这个关键字指定了这个`web方法`使用的`XML`名称空间。 注意:这个关键字只有在方法使用`rpc`样式绑定时才有效果。 也就是说,方法(或包含它的类)必须用`SoapBindingStyle`等于`rpc`来标记。 (如果为使用文档样式绑定的方法指定此关键字,则WSDL将不会是自一致的。) # 默认 如果忽略此关键字,则该方法位于`web服务`或客户端类的`namespace`参数指定的名称空间中。 # WSDL的关系 对于InterSystems IRIS web服务服务,`SoapNameSpace`关键字影响``元素中的名称空间声明。 这里添加了指定的名称空间(例如`http://www.customtypes.org`)。 例如: ```xml ... xmlns:ns2="http://www.customtypes.org" xmlns:s0="http://www.wsns.org" ... targetNamespace="http://www.wsns.org" ``` 在本例中,`http://www.customtypes.org`名称空间被分配给前缀`ns2`。 请注意,`WSDL`通常还声明了`web服务`的名称空间(`http://www.wsns.org`)。 在本例中,该名称空间被分配给前缀`s0`,并且也用作目标名称空间。 # 对SOAP消息的影响 一个可能的`SOAP消息`可能如下所示(为了可读性添加了换行符和空格): ```xml 42 ``` 注意,``元素位于`http://www.webservicetypesns.org`名称空间中。 相反,如果我们没有指定`SoapNameSpace`关键字,则消息将如下所示: ```xml 42 ``` 在本例中,``元素位于web服务的名称空间`http://www.wsns.org`中。
文章
姚 鑫 · 三月 11, 2021

第八章 SQL修改数据库

# 第八章 SQL修改数据库 可以对现有的表使用SQL语句,也可以对相应的持久化类使用ObjectScript操作来修改InterSystems IRIS®数据平台数据库的内容。 不能修改定义为只读的持久类(表)。 使用SQL命令为维护数据的完整性提供了自动支持。 SQL命令是一个原子操作(全部或没有)。 如果表上定义了索引,SQL将自动更新它们以反映更改。 如果定义了任何数据或引用完整性约束,SQL将自动执行它们。 如果有任何已定义的触发器,执行这些操作将拉动相应的触发器。 # 插入数据 可以使用SQL语句或设置和保存持久化类属性将数据插入表中。 ## 使用SQL插入数据 `INSERT`语句将一条新记录插入SQL表中。 可以插入一条记录或多条记录。 下面的示例插入一条记录。 它是插入单个记录的几种可用语法形式之一: ```sql INSERT INTO MyApp.Person (Name,HairColor) VALUES ('Fred Rogers','Black') ``` 以下示例通过查询现有表中的数据插入多条记录: ```sql INSERT INTO MyApp.Person (Name,HairColor) SELECT Name,Haircolor FROM Sample.Person WHERE Haircolor IS NOT NULL ``` 还可以发出`INSERT`或`UPDATE`语句。 如果SQL表中不存在新记录,则该语句将该记录插入该SQL表中。 如果记录存在,则该语句使用提供的字段值更新记录数据。 ## 使用对象属性插入数据 可以使用ObjectScript插入一条或多条数据记录。 创建一个现有持久化类的实例,设置一个或多个属性值,然后使用`%Save()`插入数据记录: 下面的例子插入一条记录: ```java SET oref=##class(MyApp.Person).%New() SET oref.Name="Fred Rogers" SET oref.HairColor="Black" DO oref.%Save() ``` 下面的例子插入多条记录: ```java SET nom=$LISTBUILD("Fred Rogers","Fred Astare","Fred Flintstone") SET hair=$LISTBUILD("Black","Light Brown","Dark Brown") FOR i=1:1:$LISTLENGTH(nom) { SET oref=##class(MyApp.Person).%New() SET oref.Name=$LIST(nom,i) SET oref.HairColor=$LIST(hair,i) SET status = oref.%Save() } ``` # UPDATE语句 `UPDATE`语句修改SQL表中的一条或多条现有记录中的值: ```java UPDATE语句修改SQL表中的一条或多条现有记录中的值: ``` # 在插入或更新时计算字段值 在定义计算字段时,可以指定ObjectScript代码来计算该字段的数据值。 可以在插入、更新行、插入和更新行或查询行时计算此数据值。 下表显示了每种计算操作类型所需的关键字以及字段/属性定义示例: - 只在插入时计算 - SQL DDL `COMPUTECODE`关键字`Birthday VARCHAR(50) COMPUTECODE {SET {Birthday}=$PIECE($ZDATE({DOB},9),",")_" changed: "_$ZTIMESTAMP}` - 持久化类定义`SqlComputeCode`和`SqlComputeCode`关键字属性生日为`%String(MAXLEN = 50) [SqlComputeCode = {SET {Birthday}=$PIECE($ZDATE({DOB},9),",") _" changed: "_$ZTIMESTAMP}, SqlComputed];` - 只在更新时计算 - SQL DDL DEFAULT, `COMPUTECODE`,和`COMPUTEONCHANGE`关键字`Birthday VARCHAR(50) DEFAULT ' ' COMPUTECODE {SET {Birthday}=$PIECE($ZDATE({DOB},9),",")_" changed: "_$ZTIMESTAMP} COMPUTEONCHANGE (DOB)` - 在插入和更新上都进行计算 - SQL DDL `COMPUTECODE`和`COMPUTEONCHANGE`关键字`Birthday VARCHAR(50) COMPUTECODE {SET {Birthday}=$PIECE($ZDATE({DOB},9),",")_" changed: "_$ZTIMESTAMP} COMPUTEONCHANGE (DOB)` - 持久化类定义`SqlComputeCode`, `SqlComputed, and SqlComputeOnChange`属性关键字属性`Birthday As %String(MAXLEN = 50) [SqlComputeCode = {SET {Birthday}=$PIECE($ZDATE({DOB},9),",") _" changed: "_$ZTIMESTAMP}, SqlComputed, SqlComputeOnChange = DOB];` - 计算对查询 - SQL DDL `COMPUTECODE`和计算或瞬态关键字B`irthday VARCHAR(50) COMPUTECODE {SET {Birthday}=$PIECE($ZDATE({DOB},9),",")_" changed: "_$ZTIMESTAMP}`计算 - 持久类定义`SqlComputeCode`, `SqlComputed, and calculate`或瞬态属性关键字属性`Birthday`为%`String(MAXLEN = 50) [SqlComputeCode = {SET {Birthday}=$PIECE($ZDATE({DOB},9),",") _" changed: "_$ZTIMESTAMP}, SqlComputed, calculate]`; DDL `DEFAULT`关键字在插入时优先于计算数据值。 `DEFAULT`必须接受一个数据值,例如空字符串; 不能为空。 在持久类定义中,InitialExpression属性关键字在插入时不会覆盖`SqlComputed`数据值。 DDL `COMPUTEONCHANGE`关键字可以使用单个字段名,也可以使用逗号分隔的字段名列表。 这些字段名指定了哪些字段更新时会触发对该字段的计算; 列出的字段名称必须存在于表中,但它们不必出现在计算代码中。 必须指定实际的字段名; 不能指定星号语法。 在修改记录时,可以使用`ON UPDATE`关键字短语将字段设置为文字或系统变量(如当前时间戳),而不是使用`COMPUTECODE`和`COMPUTEONCHANGE`。 `ON UPDATE`短语同时修饰`INSERT`和`UPDATE`; 若要只在更新时修改,请使用默认短语和更新短语。 每次查询访问该字段时,DDL计算或`TRANSIENT`关键字都会计算一个数据值。 该字段不需要在选择列表中指定。 例如,`SELECT Name FROM MyTable WHERE LENGTH(Birthday)=36`在计算条件表达式之前计算生日字段。 管理门户Open Table选项执行一个查询,因此计算计算的和临时的数据值。 计算字段限制: - 不更新的更新:为记录中的字段提供与它们之前的值相同的值的更新实际上并不更新记录。 如果没有对记录执行真正的更新,则不会调用`COMPUTEONCHANGE`。 即使没有对一条记录执行真正的更新,也会在更新操作上调用`ON UPDATE`。 如果希望在更新时总是重新计算已计算字段,而不管记录是否实际更新,请使用更新触发器。 - 用户为计算字段指定的显式值: - `INSERT`:在`INSERT`时,您总是可以向`COMPUTECODE`、`DEFAULT`或`On UPDATE`字段提供显式的值。 InterSystems SQL总是采用显式的值,而不是生成的值。 - 更新`COMPUTEONCHANGE`:更新操作可以为`COMPUTEONCHANGE`字段提供显式的值。 InterSystems SQL总是采用显式的值,而不是计算的值。 - 更新时更新:更新操作不能为`ON UPDATE`字段提供显式值。 InterSystems SQL忽略用户提供的值,并接受`ON UPDATE`生成的值。 但是,InterSystems SQL确实会对显式值执行字段验证,例如,如果提供的值大于最大数据大小,就会生成`SQLCODE -104`错误。 - 计算或暂态:插入或更新操作不能为计算或暂态字段提供显式值,因为计算或暂态字段不存储数据。 但是,InterSystems SQL确实会对显式值执行字段验证,例如,如果提供的值大于最大数据大小,就会生成`SQLCODE -104`错误。 # 删除语句 `DELETE`语句从SQL表中删除一条或多条现有记录: ```sql DELETE FROM MyApp.Person WHERE HairColor = 'Aqua' ``` 可以执行`TRUNCATE TABLE`命令删除表中的所有记录。 还可以使用`delete`删除表中的所有记录。 `DELETE`(默认情况下)提取删除触发器; `TRUNCATE TABLE`不拉出删除触发器。 使用`DELETE`删除所有记录不会重置表计数器; `TRUNCATE TABLE`重置这些计数器。 # 事务处理 事务是一系列插入、更新、删除、插入或更新以及截断表数据修改语句,它们组成单个工作单元。 `SET TRANSACTION`命令用于设置当前进程的事务参数。 还可以使用`START TRANSACTION`命令设置相同的参数。 这些事务参数在多个事务中继续有效,直到显式更改为止。 `START TRANSACTION`命令显式地启动事务。 这个命令通常是可选的; 如果事务`%COMMITMODE`是隐式或显式的,事务从第一个数据库修改操作自动开始。 如果事务`%COMMITMODE`为`NONE`,则必须显式指定`START transaction`来启动事务处理。 如果事务成功,提交其更改可以是隐式(自动)或显式的; `%COMMITMODE`值决定是否需要显式地使用`COMMIT`语句来永久地将数据修改添加到数据库并释放资源。 如果事务失败,可以使用`ROLLBACK`语句撤消其数据修改,这样这些数据就不会进入数据库。 注意:通过管理门户执行SQL查询接口运行SQL时,不支持SQL事务语句。 这个接口旨在作为开发SQL代码的测试环境,而不是用于修改实际数据。 ## 事务和保存点 在InterSystems SQL中,可以执行两种事务处理:完整事务处理和使用保存点的事务处理。通过完整的事务处理,事务将从`START TRANSACTION`语句(显式或隐式)开始,一直持续到`COMMIT`语句(显式或隐式)结束事务并提交所有工作,或者`ROLLBACK`语句反转事务期间完成的所有工作。 通过保存点,InterSystems SQL支持事务中的级别。可以使用`START TRANSACTION`语句(显式或隐式)开始事务。然后,在事务期间,可以使用`SAVEPOINT`在程序中指定一个或多个命名保存点。可以在一个事务中最多指定255个命名保存点。添加一个保存点会增加`$TLEVEL`事务级别计数器。 - `COMMIT`提交事务期间执行的所有工作。保存点将被忽略。 - `ROLLBACK`将回滚事务期间执行的所有工作。保存点将被忽略。 - `ROLLBACK TO SAVEPOINT`点名将回滚自点名指定的`SAVEPOINT`以来执行的所有工作,并以适当数量的保存点级别将内部事务级别计数器递减。例如,如果建立了两个保存点`svpt1`和`svpt2`,然后回滚到`svpt1`,则`ROLLBACK TO SAVEPOINT` `svpt1`会反转自`svpt1`以来所做的工作,在这种情况下,将事务级别计数器减2。 ## 非事务操作 当事务生效时,以下操作不包括在事务中,因此无法回滚: - `IDKey`计数器增量不是事务操作。`IDKey`由`$INCREMENT`(或`$SEQUENCE`)自动生成,它维护独立于SQL事务的计数。例如,如果插入`IDKey`为17、18和19的记录,然后回滚此插入,则下一条要插入的记录的`IDKey`将为20。 - 缓存查询的创建、修改和清除不是事务操作。因此,如果在事务期间清除高速缓存的查询,然后回滚该事务,则在回滚操作之后,高速缓存的查询将保持清除状态(不会恢复)。 - 事务内发生的DDL操作或调谐表操作可以创建和运行临时例程。此临时例程被视为与缓存查询相同。也就是说,临时例程的创建、编译和删除不被视为事务的一部分。临时例程的执行被认为是事务的一部分。 ## 事务锁 **事务使用锁来保护唯一的数据值。例如,如果进程删除了唯一的数据值,则该值在事务持续时间内被锁定。因此,在第一个事务完成之前,另一个进程无法使用相同的唯一数据值插入记录。这可以防止回滚导致具有唯一性约束的字段出现重复值。这些锁由`INSERT`、`UPDATE`、`INSERT`或`UPDATE`和`DELETE`语句自动应用,除非该语句包含`%NOLOCK`限制参数。** ## 事务大小限制 除了日记文件的空间可用性外,可以在事务中指定的操作数量没有限制。锁表的大小通常不会施加限制,因为InterSystems IRIS提供自动锁升级。 每个表有1000个锁的默认锁阈值。对于当前事务,一个表可以有1000个唯一的数据值锁。第100个锁定操作在事务持续时间内将该表的锁定升级为表锁。 此锁定阈值可使用以下任一选项进行配置: - 调用`$SYSTEM.SQL.SetLockThreshold()`方法。此方法更改当前系统范围的值和配置文件设置。要确定当前的锁升级阈值,请使用`$SYSTEM.SQL.GetLockThreshold()`方法。 - 转到管理门户。从系统管理中,依次选择配置、SQL和对象设置、SQL。在此屏幕上,可以查看和编辑锁定阈值的当前设置。 可以终止的子节点(子表)的数量没有限制。所有子节点终止都被记录下来,因此可以回滚。 ## 读取未提交的数据 可以通过为发出查询的进程设置`SET TRANSACTION`或`START TRANSACTION`来指定读取隔离级别。 - 提交未提交的隔离级别:对于其他用户进行查询(只读)访问,可以看到未提交的对数据的插入,更新和删除。如果未指定任何事务,则为默认设置。 - 已验证隔离级别:可供其他用户以查询(只读)访问的方式看到未提交的对数据的插入,更新和删除。提供对查询条件所使用并由查询显示的数据的重新检查。 - 读取已提交的隔离级别:未提交的插入和更新对数据所做的更改未显示在查询结果集中。查询结果集仅包含已提交的插入和更新。但是,未提交的删除对数据所做的更改将显示在查询结果集中。 不管当前的隔离级别如何,以下`SELECT`命令子句始终返回未提交的数据:聚合函数,`DISTINCT`子句,`GROUP BY`子句或带有`%NOLOCK`关键字的`SELECT`。 ## ObjectScript事务命令 ObjectScript和SQL事务命令是完全兼容和可互换的,但以下情况除外: **如果没有当前事务,则`ObjectScript TSTART`和`SQL START TRANSACTION`都将启动一个事务。但是,`START TRANSACTION`不支持嵌套事务。因此,如果需要(或可能需要)嵌套事务,则最好使用`TSTART`启动事务。如果需要与SQL标准兼容,请使用`START TRANSACTION`。** ObjectScript事务处理为嵌套事务提供了有限的支持。 SQL事务处理为事务中的保存点提供支持。
文章
Qiao Peng · 十月 6, 2022

集成产品的业务行为监控

最近一些用户问到监控集成平台业务行为查询的问题,例如如何查询服务的平均耗时、发生错误的服务数量... 业务行为监控对于集成平台来说非常重要,可以帮助我们: 监控系统健康情况 — 查看系统性能表现。例如发现队列积压和长耗时的消息处理,都可能是性能问题的表现。 排查异常 — 通过查看业务行为数据,帮助我们判断特定的业务组件配置是否是造成性能瓶颈的主要原因。 做业务规划 — 通过业务行为数据,了解各个业务量变化情况,并辅助我们做业务规划。 做硬件规划 — 通过长期跟踪消息吞吐量的变化了解性能的变化和业务量的增长,进而辅助我们做硬件计划,避免出现在性能问题。 仅提供这些查询是很容易的,但要更好地监控集成平台的业务行为,需要更深入的了解InterSystems集成架构。 InterSystems集成架构 无论Ensemble、Health Connect还是InterSystems IRIS,它们都具有下面的集成架构: 通过业务服务向外发布服务、通过业务操作连接第三方接口、通过业务流程协同业务操作,它们统称为集成业务组件。可以简单理解为业务行为是由这些业务组件完成的: 业务服务 = 服务,它启动一个业务流程; 业务操作 = 接口,它通过接口操作第三方业务系统; 业务流程 = 流程,它控制一笔业务需要按什么流程调用哪些接口。 要监控业务行为,服务、接口和流程都需要监控。 另一个需要了解的概念是关于耗时的: 完整的业务流程由会话(Session)表达,会话完成的耗时(会话耗时)和服务耗时、接口耗时不是同一件事。例如下图的一笔业务,HL7FileService这个服务在第4步就结束了,但整笔业务还在还在继续,直到第9步才结束。由服务调用者来看,服务耗时是第4步到第1步的时间差;而整个业务流程(会话)耗时是第9步到第1步的时间差。所以要注意区分服务耗时和会话耗时的概念,而通常这2个耗时都需要关注。 监控业务行为的手段 InterSystems提供了多种方式和数据,帮助监控业务行为,包括消息、业务活动量采集、自动化的业务行为报警等多种手段。 1. 消息 InterSystems 集成产品的所有消息都是自动持久化的,也就是都保存在内建的数据库里,并且通过SQL就可以查询分析的。消息分为消息头和消息体:消息头记录消息传递的流程、时间、状态、目标等信息;消息体记录具体传递的消息内容。 消息头表(Ens.MessageHeader)有以下重要字段: 字段 说明 Id 消息头主键。会话的第一个消息的Id和其SessionId相同。 SessionId 消息所属的会话。可以用这个字段将所有相同会话的消息关联起来。 IsError 是否处理错误。 TimeCreated 消息创建时间。 TimeProcessed 消息处理时间。 CorrespondingMessageId 与此消息是请求/响应消息对里的另一个消息Id。可以通过它获取相应的响应消息。 MessageBodyId 对应的消息体对象Id。可以用它找到消息体数据 可以看出,通过分析消息头表,就可以得出几乎所有业务行为的分析结果,借助消息体表,更可以分析所有的业务行为。 但实际分析业务行为时,我们并不总是用消息头表。原因是: 消息头表可能非常大:例如,我们国内最大的医院客户的日消息吞吐量已经超过1亿条,按消息3个月的保存需求计算,就是90亿条以上的记录数。在不使用消息仓库的情况下,对这么大量的消息头进行频繁、复杂的SQL分析显然不会得到多好的性能。 定期的消息清除:考虑到硬件资源的限制,通常都会设置定期清除消息,例如自动删除3个月前的消息。只保留有限时间的消息,无法支持更长时间范围的业务行为分析。 因此,InterSystems提供了业务活动量采集,来弥补消息分析的不足。 2. 业务活动量采集 业务活动量采集是通过一个系统提供的业务操作自动采集、统计业务活动,并保存在特定的SQL表中供查询分析。只要将Ens.Activity.Operation.Local加入集成产品(Production)中,它会自动采集所有业务组件的活动量数据,并保存到活动数据表中。 活动数据表是三张表(Ens_Activity_Data.Days\ Ens_Activity_Data.Hours\ Ens_Activity_Data.Seconds): 这3张表记录按天\小时\秒来统计的业务组件(BS\BP\BO)的调用历史。因为仅是统计数据,数据量远小于消息头表、且不受消息头表定期清除的影响,适合更长时间范围的业务活动分析。而这3张表提供了时间尺度的统计数据,方便由粗到细追踪到问题根源。 这些表有以下重要字段: 字段 说明 timeslot 统计时间。 HostName 业务组件名称。 HostType 业务组件类型:1为BS、2为BP、3为BO。 totalduration 在统计时间段内的总处理时间。 totalcount 在统计时间段内的总消息数量。 如何开启业务活动量采集?只要把Ens.Activity.Operation.Local加入Production的BO中: * 注意,如果业务服务使用 SendRequest 开启业务流程,需要做代码开启,参考:https://docs.intersystems.com/irisforhealth20221/csp/docbook/DocBook.UI.Page.cls?KEY=EMONITOR_activity#EMONITOR_activity_overview 通过分析活动数据表,我们也可以得到业务行为监控的数据。但分析消息表、活动数据表,都是被动的、事后分析的。要想得到即时的性能提醒,InterSystems还提供了一系列的业务行为报警设置,让其自动报警。 3. 业务行为报警 业务组件上有很多业务行为报警设置,例如业务操作和业务流程组件有队列长度计数告警和队列等待处理时间告警设置,在队列长度超过设置值、队列中消息等待时间超过设置值的情况下,自动发出警告。很多用户设置将这些警告发送到管理员的钉钉、微信或短信平台上,以及时提醒。 通过上面提到的这些业务行为监控手段,可以加强对集成产品的监控、了解业务情况。下面我们具体看看常见的监控需求如何实现。 常见业务行为查询需求 1. 服务、接口和流程总览。了解集成产品有哪些组件,以及这些组件的当前运行状态(启用/禁用)。 对集成产品中业务组件及状态查询,InterSystems提供了一个查询:Ens.Config.Production类的EnumerateConfigItems查询。它的头两个入参是Production名称和组件类型。组件类型为:1 - 业务服务;2 - 业务流程;3 - 业务操作。 例如下面代码列出所有的业务服务组件、备注、是否启用、请求消息类型和响应消息类型: ClassMethod GetServiceList() { Set tStatement = ##class(%SQL.Statement).%New(),tRet=0 Set tStatus = tStatement.%PrepareClassQuery("Ens.Config.Production","EnumerateConfigItems") Set tResult = tStatement.%Execute("HCCPKG.FoundationProduction",1) While (tResult.%Next() '= 0) { W tResult.%Get("ConfigName"),"--",tResult.%Get("CommentOrClassName"),"--",tResult.%Get("Enabled"),"--",tResult.%Get("RequestClasses"),"--",tResult.%Get("ResponseClasses"),! } Do tResult.%Close(),tStatement.%Close() Quit tRet } 可以使用表Ens_Config.Item,它提供类似的信息,但没有区分业务组件类型。 2. 服务和接口的调用次数和耗时 通过上面提到的业务活动量采集表Ens_Activity_Data.*,可以容易地获得这类调用信息。 例如下面的SQL是查询业务服务当日被调用的次数和平均耗时,并按平均耗时降序排序: SELECT hostname AS "服务名称", SUM(totalcount) AS "调用次数", ROUND(SUM(totalduration)/SUM(totalcount),4 )*1000 AS "平均服务耗时(ms)" FROM Ens_Activity_Data.Days WHERE timeslot = (SELECT Date(NOW())) AND HostType=1 AND HostName not LIKE 'ENS.%' GROUP BY hostname ORDER BY "平均服务耗时(ms)" DESC 3. 服务调用失败(错误)的次数 可以使用SQL通过消息头表的消息处理错误列(IsError),并关联到会话查询其业务服务: SELECT B.sourceconfigname AS "服务名称", Count(*) AS "错误次数" FROM (SELECT distinct sessionid as sessionid FROM Ens.MessageHeader WHERE IsError =1 AND TimeCreated BETWEEN '2022-08-22 00:00:00' AND '2022-08-23 00:00:00') A, Ens.MessageHeader B WHERE A.sessionid=B.id AND NOT B.SourceConfigName %STARTSWITH 'ENS.' GROUP BY B.sourceconfigname 4. 查询具有特定消息体信息的消息历史 例如查询HL7 V2消息处理历史,并返回服务名称、患者编号、开始时间、结束时间、服务耗时、服务状态、请求消息内容,响应消息内容。这类查询涉及到消息体内容(患者编号),以及请求消息和响应消息的关联关系。 对于结构化消息体,可以直接关联到消息体表;但对于半结构化消息体,例如HL7 V2消息或自定义XML字符串的消息,可以通过SearchTable技术,建立对特定消息体内容的索引表,加速查询速度。在上面的查询例子中,要找到HL7 V2消息里的患者编号,可以通过HL7的SearchTable表:EnsLib_HL7.SearchTable 进行查询,而患者编号是其字段PropId = 4的记录中PropValue字段的值。对于请求和响应消息的关联,通过消息头表的CorrespondingMessageId字段即可获得。 因此上面的查询例子,可以用下面的SQL获得: SELECT MI."服务名称", MI."患者编号", MI."服务开始时间", MI."服务结束时间", MI."服务耗时(ms)", MI."服务状态", MB.RawContent As "请求消息", ME.RawContent As "响应消息" FROM (SELECT MHB.Id,MHB.sourceconfigname AS "服务名称", ST.PropValue AS "患者编号", MHB.TimeCreated AS "服务开始时间", MHE.TimeProcessed AS "服务结束时间", DATEDIFF(ms, MHB.TimeCreated, MHE.TimeProcessed) AS "服务耗时(ms)", MHB.Status AS "服务状态", MHB.MessageBodyId As MHBBId, MHE.MessageBodyId As MHEBId FROM Ens.MessageHeader MHB LEFT JOIN EnsLib_HL7.SearchTable ST ON MHB.MessageBodyId = ST.DocId AND ST.PropId = 4 LEFT JOIN Ens.MessageHeader MHE ON MHE.CorrespondingMessageId = MHB.Id WHERE MHB.ID = MHB.SessionId AND MHB.MessageBodyClassName='EnsLib.HL7.Message' AND TimeCreated BETWEEN '2022-08-22 00:00:00' AND '2022-08-23 00:00:00') MI LEFT JOIN EnsLib_HL7.Message MB ON MI.MHBBId=MB.ID LEFT JOIN EnsLib_HL7.Message ME ON MI.MHEBId=ME.ID 可能大家好奇,我是怎么知道的患者编号在EnsLib_HL7.SearchTable的PropId = 4的记录中? 在管理门户(SMP)中,大家经常通过消息查看器页面查询和检索消息,查询和检索条件选项非常丰富,包括消息头、消息体、虚拟文档字段和Search Table字段都可以作为查询条件。 在消息查看器页面里设置好查询条件,进行搜索后再查看它生成的SQL语句(通过按钮“显示查询”),我们就知道怎么写SQL了: 没看到“显示查询”按钮?在对应的命名空间下执行下面的命令即可: Set ^Ens.Debug("UtilEnsMessages","sql")=1 5. 查询业务组件间的调用关系 例如想获得所有的服务名称、对应生产方和消费方。 这类的需求很典型,但在InterSystems集成产品里,并不是那么直观可以获得的。为什么?因为消费方并非是固定的。 在基于消息路由规则的场景下,消费方是消息最终发送的目标接口,理论上可以通过查询路由规则获得。 在业务流程模型里,调用方(消费方)可以是动态的。 当在发布/订阅模式下,订阅方并不是“编码”实现的,而且可以灵活的参与或取消订阅。 因此要想知道明确的生产方和消费方需要知道很多信息,而这也是业务流程灵活性的体现。我们可以考虑通过对消息流程历史的分析来简单获得: SELECT distinct B.sourceconfigname AS '生产方',A.TargetConfigName AS '消费方' FROM (SELECT sessionid,TargetConfigName FROM Ens.MessageHeader WHERE TargetBusinessType='3') A, (SELECT sessionid,sourceconfigname FROM Ens.MessageHeader WHERE id=sessionid) B WHERE A.sessionid=B.sessionid 因为消息可能被删除,因此需要注意使用它的局限性。(这里的TargetBusinessType='3'中的数字3表示BusinessOperation) 6. 查询会话耗时 前面介绍了服务耗时和会话耗时的差异,会话耗时和服务耗时有时并不一致。要分析每笔会话的完整耗时?可以参考这个会话耗时的SQL查询方法: SELECT SourceConfigName AS "服务名称", max(ti) AS "会话最大耗时", min(ti) AS "会话最小耗时", avg(ti) AS "会话平均耗时" FROM (SELECT a.SourceConfigName, a.SessionId, tp,TimeCreated, {fn TIMESTAMPDIFF(SQL_TSI_SECOND,TimeCreated,tp)} as ti FROM (SELECT SourceConfigName ,SessionId, TimeCreated FROM Ens.MessageHeader WHERE ID= SessionId) a, (SELECT SessionId, max(TimeProcessed) tp FROM Ens.MessageHeader GROUP BY SessionId) b WHERE a.sessionid=b.sessionid) GROUP BY SourceConfigName 如果大家有其它对业务行为分析的需求,欢迎留言。
文章
Hao Ma · 四月 28, 2023

IRIS, Caché监控指导 - 控制台日志和错误

这里只讨论Caché和IRIS本身产生的错误和警告。用户在维护工作中通常会需要更多的内容, 那些我们在后面的 “系统性能指标”里介绍。另外, 关于集成平台的告警和日志, 也会在后面单独讨论。集成平台,也就是Ensemble Production,是IRIS系统上运行的应用,它的日志,告警,以及指标,测量,是单独的内容。 ### 控制台日志 控制台日志是系统运行状态的日志文件,在IRIS里是messages.log, 在Cache‘里的名字是console.log,默认放在安装目录的mgr子目录。 用户也可以在管理门户的"系统操作>系统日志>控制台日志"里查看。以下是一个实际的例子: ``` *** Recovery started at Fri Jan 03 16:26:05 2020 Current default directory: c:\intersystems\hsap\mgr Log file directory: c:\intersystems\hsap\mgr\ WIJ file spec: c:\intersystems\hsap\mgr\CACHE.WIJ Recovering local (c:\intersystems\hsap\mgr\CACHE.WIJ) image journal file... Starting WIJ recovery for 'c:\intersystems\hsap\mgr\CACHE.WIJ'. 0 blocks pending in this WIJ. Exiting with status 3 (Success) 01/03/20-16:26:43:627 (8108) 2 Failed to allocate 2880MB shared memory using large pages. Switching to small pages. 01/03/20-16:26:43:627 (8108) 0 Allocated 2880MB shared memory: 2048MB global buffers, 512MB routine buffers 01/03/20-16:26:43:627 (8108) 0 Intel Sandy Bridge AES-NI instructions detected. 01/03/20-16:26:43:731 (8108) 0 Jrn info from prior WIJ (imflags: 0): (Skip multiple records…) 01/04/20-00:00:00:804 (6900) 1 Warning: Alternate and primary journal directories are the same 01/04/20-00:00:00:820 (16272) 0 CACHE JOURNALING SYSTEM MESSAGE Journaling switched to: c:\intersystems\hsap\mgr\journal\20200104.001 01/04/20-10:15:41:703 (16096) 0 DELETE: c:\intersystems\hsap\mgr\journal\20191231.001 01/04/20-10:15:41:734 (12224) 0 Purging old application errors 01/05/20-00:00:00:497 (6900) 1 Warning: Alternate and primary journal directories are the same...(repeated 1 times) 01/05/20-00:00:00:528 (12472) 0 CACHE JOURNALING SYSTEM MESSAGE Journaling switched to: c:\intersystems\hsap\mgr\journal\20200105.001 01/05/20-00:00:01:653 (11436) 1 %SYS.Task.FeatureTracker failed to transfer data 01/05/20-18:18:34:726 (2260) 0 DELETE: c:\intersystems\hsap\mgr\journal\20200101.001 01/05/20-18:18:34:789 (14444) 0 Purging old application errors ``` 控制台日志的记录包括系统的启动停止记录,许可证的使用情况,各种底层资源,网络连接,数据库日志等等的所有内容。记录按严重程度四个等级,分别是 - 0: 一般性消息 (Information/Status) - 1: 警告性错误 (Warning) - 2: 严重错误 (Severe) - 3: 致命性错误 (Fatal) **除了等级为0的一般性消息,剩下的1,2,3个等级的记录都被称为错误**。错误的严重程度从1到3逐渐升高,其中2和3两个级别通常被称为严重错误(Serious Alert)。默认的配置下,它们会被写如另一个警告通知的日志Alert.log,以通知管理员。比如上面例子中的第9行 *“01/03/20-16:26:43:627 (8108) 2 Failed to allocate 2880MB shared memory using large pages. Switching to small pages.”*,就是一个Severe错误,应该被管理员马上知晓。 ### 错误是怎么来的 这里只说控制台日志中呈现的错误,也就是等级1-3的记录,它们的来源有: #### 系统底层直接产生的错误 系统底层产生的基本是严重错误 (Severe)和致命性错误 (Fatal)级别,这里给几个常见的例子,文章z > Write to journal file has failed > ECP client daemon/connection is hung > Failure during PIJ processing - Declaring a crash > Error reading block – recovery read error > Error writing block – recovery write error > ... 这里是[完整的列表的链接](https://docs.intersystems.com/iris20231/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_monitor#GCM_monitor_errors) #### 系统监视器产生的错误 系统运行中默认的系统监视器(System Monitor)和健康监视器(Health Monitor)负责系统的监控工作。他们工作在%SYS命名空间,默认会随系统启动而启动,然后以30秒一次的频率读取系统指标,当指标达到并超过预置的门限值时会触发错误信息写入控制台日志, 级别是Warning(警告)或者严重错误 (Severe)。 > 这里只列出默认情况下什么样的指标会触发错误消息,而背后详细的技术细节会在 “Caché和IRIS的监控器”章节介绍。 **系统监视器产生的错误**针对系统的运行状态和资源,**产生告警的规则不可修改**。注意下图中Warning, Alert对应着Console log里的Waring级别和Severe或者fatal级别。 下表可以在最新版本的IRIS文档中的[这个链接](https://docs.intersystems.com/iris20231/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_healthmon#GCM_healthmon_sysmon_alerts)找到。 ![image-20230419161017432](.pictures/image-20230419161017432.png) #### 健康监视器产生的错误 Health Monitor是另一个评估内部产生的指标的监视器,也是工作在%sys命名空间。它和System Monitor的区别是: **用户可以修改评估的规则,设置适合自己的告警门限值。** Health Monitor默认不激活,因此需要人工启动。但是,如果你发现虽然你没有启动HealthMonitor, 但系统正按照它的规则发送通知,那也不用奇怪,那就是这个监视器的一部分规则不需要启动就自动工作。它的监控项目和规则如下图。 ``` Sensor Base Max Max M Warn Warn M ------ ---- --- ----- ---- ------ CPUPct 50 90 0 80 0 CPUusage 50 85 0 75 0 CSPActivity 100 0 2 0 1.6 CSPActualConnections 100 0 2 0 1.6 CSPGatewayLatency 1000 2000 0 1000 0 CSPInUseConnections 100 0 2 0 1.6 CSPPrivateConnections 100 0 2 0 1.6 CSPSessions 100 0 2 0 1.6 DBLatency 1000 3000 0 1000 0 DBReads 1024 0 2 0 1.6 DBWrites 1024 0 2 0 1.6 DiskPercentFull 50 99 0 95 0 ECPAppServerKBPerMinute 1024 0 2 0 1.6 ECPConnections 100 0 2 0 1.6 ECPDataServerKBPerMinute 1024 0 2 0 1.6 ECPLatency 1000 5000 0 3000 0 ECPTransOpenCount 100 0 2 0 1.6 ECPTransOpenSecsMax 60 0 2 0 1.6 GlobalRefsPerMin 1024 0 2 0 1.6 GlobalSetKillPerMin 1024 0 2 0 1.6 JournalEntriesPerMin 1024 0 2 0 1.6 JournalGrowthRate 1024 0 2 0 1.6 LicensePercentUsed 50 0 1.5 0 0 LicenseUsedRate 20 0 1.5 0 0 LockTablePercentFull 50 99 0 85 0 LogicalBlockRequestsPerMin 1024 0 2 0 1.6 MirrorDatabaseLatencyBytes 20000000 0 2 0 1.6 MirrorDatabaseLatencyFiles 3 0 2 0 1.6 MirrorDatabaseLatencyTime 1000 8000 0 4000 0 MirrorJournalLatencyBytes 20000000 0 2 0 1.6 MirrorJournalLatencyFiles 3 0 2 0 1.6 MirrorJournalLatencyTime 1000 5000 0 3000 0 PhysicalBlockReadsPerMin 1024 0 2 0 1.6 PhysicalBlockWritesPerMin 1024 0 2 0 1.6 ProcessCount 100 0 2 0 1.6 RoutineCommandsPerMin 1024 0 2 0 1.6 RoutineLoadsPerMin 1024 0 2 0 1.6 RoutineRefsPerMin 1024 0 2 0 1.6 SMHPercentFull 50 98 0 85 0 ShadowConnectionsLatency 1000 0 2 0 1.6 ShadowLatency 1000 0 2 0 1.6 TransOpenCount 100 0 2 0 1.6 TransOpenSecsMax 60 0 2 0 1.6 WDBuffers 1024 0 2 0 1.6 WDCycleTime 60 0 2 0 1.6 WDWIJTime 60 0 2 0 1.6 WDWriteSize 1024 0 2 0 1.6 ``` 表格很长,共列有47个sensor。大概的意思都可以从名字看出来,如果要阅读具体的定义,请看[原始文档](https://docs.intersystems.com/iris20231/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_healthmon#GCM_healthmon_overview_api_sensors)。后面的几个字段,Base,Max,Max M,Warn,Warn M定义了这个sensor是怎么确定门限值的。 **第1类:固定的门限值** 也就是定义了Base, Max, Warning字段内容的条目,比如这个条目: ``` Sensor Base Max Max M Warn Warn M ------ ---- --- ----- ---- ------ CPUusage 50 85 0 75 0 ``` - 只要System Monitor已经启动,这个rule就工作,不需要Health Monitor处于激活状态。 - 只用max和warning值来决定是否超门限。连续3次读数超过了max值,产生alert, 连续5次读数超过了warning值,产生warning. 对于这些指标,系统允许用户自己设置告警的门限值,比如把上面CSPUsage的Warn, Max修改为80,90,或其他适合用户自己的数值。 **第2类:动态的门限值** 也就是定义了Base, Multiplier, WarningMultiplier字段内容的条目。例如下面的DBReads: ``` Sensor Base Max Max M Warn Warn M ------ ---- --- ----- ---- ------ DBReads 1024 0 2 0 1.6 ``` 统计学上, 均值(Mean)加3个Sigma是最常用的判断一个采样是否偏离太多的方法。因为是要设置门限值,这里添加了两个乘法系数(Multiplier), MAX M和Warn M,得到一个动态的门限值。当sensor的采样值比均值大的太多了,就会产生错误条目,warning或者alert. 具体的公式是: `产生错误的门限= Max((平均值+3*Sigma)*Multiplier,最大值+Sigma)` 因为大多数情况Max()中第一项的数值都是大的,所以公式可以翻译为:比(平均值+3个sigma)*Multiplier,就是告警的门限。 以上面例子中DBReads说明: Base= 1024, Multiplier=2;WarningMultplier=1.6。监视器开始运行时,会不停的计算这个时段内的平均值,最大值和sigma,产生一个Chart。假如某一刻平均值=2145, sigma=141,最大值为2327,那么根据上面的公式: `Warning门限 = Max((2145+3*141) * 1.6,2327+141) = 4108.8` `Alert门限 = Max((2145+3*141) * 2, 2327+141) = 5136` 也就是说,当您DBReads, 也就是数据库读的平均值是2145/s时,突然来了一个超过4109/s的业务,就会有一个错误信息写到控制台日志,严重级别是1, warning。短时间数据库查询再增加,超过alert门限,就会有严重级别为2的控制台日志被写入,同时这个条目也会写到alert.log文件,用来向管理员发通知。 可以看出,这个设置中base不重要,它只在监视器刚启动时有用,而使这个门限值有意义的是multipler的设置。 这要求用户对自己的业务很熟悉,而且经常的调整才能得到合适的设置,否则就可能在业务波动大的时候得到很多的错误信息,或者得不到自己想要记录的大的业务波动。 重要的一点: 拥有动态门限值的sensor默认是不工作的, 您想要激活Health Monitor。 是这么操作的: ``` %SYS>do ^%SYSMONMGR 1) Start/Stop System Monitor 2) Set System Monitor Options 3) Configure System Monitor Classes 4) View System Monitor State 5) Manage Application Monitor 6) Manage Health Monitor 7) View System Data 8) Exit Option? 6 1) Enable/Disable Health Monitor 2) View Alerts Records 3) Configure Health Monitor Classes 4) Set Health Monitor Options 5) Exit Option? 1 ...后面省略... ``` 用户还可以自定义了Chart,或者修改及自定义默认的14个监控时段(Period),以得到更准确的错误产生的控制。有关这些内容请参见在线文档。 #### 上层应用产生的错误 上层的应用可以调用代码把它认为的错误写到控制台日志,而且可以任意定义级别。实际情况中,真正使用控制台日志来发送应用层面的错误的不多,如果您的系统中发现有, 那么一定能很清楚的把这部分错误条目和上述Caché , IRIS系统本身产生的区分出来。 ### 维护人员对错误的控制 维护人员对错误的控制最基本就是调整健康监视器的门限值。上面讲了两类门限值的理论,下面给出一个例子,显示是怎么修改固定门限值的设置的: ```bash %SYS>do ^%SYSMONMGR 1) Start/Stop System Monitor 2) Set System Monitor Options 3) Configure System Monitor Classes 4) View System Monitor State 5) Manage Application Monitor 6) Manage Health Monitor 7) View System Data 8) Exit Option? 6 1) Enable/Disable Health Monitor 2) View Alerts Records 3) Configure Health Monitor Classes 4) Set Health Monitor Options 5) Exit Option? 3 1) Activate/Deactivate Rules 2) Configure Periods 3) Configure Charts 4) Edit Sensor Objects 5) Reset Defaults 6) Exit Option? 4 1) List Sensor Objects 2) Edit Sensor Object 3) Exit Option? 1 Sensor Base Max Max M Warn Warn M ------ ---- --- ----- ---- ------ CPUPct 50 90 0 80 0 CPUusage 50 85 0 75 0 CSPActivity 100 0 2 0 1.6 CSPActualConnections 100 0 2 0 1.6 CSPGatewayLatency 1000 2000 0 1000 0 CSPInUseConnections 100 0 2 0 1.6 CSPPrivateConnections 100 0 2 0 1.6 CSPSessions 100 0 2 0 1.6 DBLatency 1000 3000 0 1000 0 DBReads 1024 0 2 0 1.6 DBWrites 1024 0 2 0 1.6 DiskPercentFull 50 99 0 95 0 ...(省略多行) WDWriteSize 1024 0 2 0 1.6 1) List Sensor Objects 2) Edit Sensor Object 3) Exit Option? 2 Sensor? 12 DiskPercentFull Base? 50 => Enter either an Alert Value or a Multiplier Alert Value? 99 => 90 Setting Max Multiplier and Warn Multiplier to 0. Enter a Warn Value Warn Value? 95 => 60 Sensor object DiskPercentFull updated. Base 50 MaxMult 0 AlertValue 90 WarnMult 0 WarnValue 60 1) List Sensor Objects 2) Edit Sensor Object 3) Exit ``` 上面的操作中有一个‘神秘数字’ 12. 怎么知道DiskPercentFull是第12个sensor? 我是从列表里数下来的。 注意在修改senosr会出提示要把System Monitor停掉。修改后再启动System Monitor, 这样如果您的硬盘使用超过了60%, 就会收到类似的控制台日志条目: > 12/12/18-15:32:37:349 (13700) 1 [SYSTEM MONITOR] DiskPercentFull(d:\htp\data\) Warning: DiskPercentFull = 74.52 ( Warnvalue is 60). **重复发生的错误会被再次写入控制台日志吗?** 除了最底层的,关乎系统整体运行的少数错误,大部分错误如果在一个小时内重复发生,只有第一次会被写入日志。这就要求用户必须有实时的通知机制, ### 控制台日志的管理 大多数用户不需要操心控制台日志的管理,少数很老的Caché的用户会拥有一个很大尺寸的console.log, 最大的曾见过80MB的文件,这时候从操作门户的页面去查看,已经出现了显示的延迟。 console.log或者messages.log的大小是由系统设置参数‘MaxConsoleLogSize’设定的。 在IRIS和最近些版本的Caché中, 这个设置的值是5MB(您可以在**操作门户的‘系统>设置>其它设置>启动**‘的列表里查看或者修改这个值。 如果一个控制台日志超过了5MB, 会自动切换, 原来的console.log, message.log,会改名为console.log.old.yyyymmdd, 或者message.log.old.yyyymmdd。
文章
Qiao Peng · 六月 11, 2023

统一语义数据平台

数据平台一直在进化:从数据中心到数据中台,离散的数据资产得到进一步梳理和整合、按业务封装数据和操作数据的方法,并逐步提供了企业统一的访问、更新、检索、查询等数据服务。 然而市场上不乏听到数据平台的成功案例,却鲜见这些案例得到大规模推广。原因是什么呢? 一. 传统数据平台建设的挑战 传统数据平台的数据模型基于各自厂商的理解,缺乏统一行业数据模型和行业语义。可供参考的国内卫生信息数据元、数据集标准并非完整的行业语义,例如没有业务实体模型和数据元关系定义。传统的数据平台建设通常根据业务域,围绕数据应用需求组织数据。经常看到按业务域划分为CDR(临床数据中心)、ODR(运营数据中心)、RDR(科研数据中心)...... 这造成了几个挑战: 1. 按业务域、而非业务实体来划分数据,虽然方便相应的业务域数据分析,但跨业务域重叠的业务实体数据,例如患者,需要跨数据中心同步。这些同步由于数据模型上的差异,往往非全息拷贝。随着同步次数越多,跨数据中心的数据越失真,造成数据资产多源不统一、数据资产一致性问题和时效性问题。 2. 数据平台产品语义表达上参差不齐,业务用户依赖数据工程师对数据理解和操作,无论是统计分析还是机器学习,海量的实施工作无法满足业务敏捷性要求; 3. 数据平台及数据应用建设依赖单一厂商的能力,而建设成果,包括数据工具、分析指标和应用都无法跨数据平台复用。往往项目都在做低水平重复建设。 4. 数据互操作标准化程度低,数据的同步、迁移困难。在缺乏数据层互操作性的情况下,各类数据中心建设的依然是数据孤岛。 5. 由于数据中心往往忽视互操作建设,数据缺乏流动,进入数据平台后,往往成为死水一潭。 二. 如何应对挑战 如何解决这些数据平台建设困境?应该如何建设数据平台? 数据资产不是仅为分析服务的,更重要的是作为生产要素在生产全过程中发挥价值- 这就涉及到数据生成、采集、交换、决策… 在这个全过程链条上的数据互操作能力尤为重要。 HIMSS将互操作定义为4级:基础级、结构级、语义级和组织级,并认为只有到达语义级,才是标准的、才能实现广泛的互操作能力。要达到语义级的互操作,需要进行五位一体的标准化:词汇/术语标准、内容标准、传输标准、隐私和安全标准、标识符标准。 随着我们越来越依赖于机器处理数据、发掘数据背后的知识,对数据资产的开放性和互操作性的要求达到了更高的水平 - 实现机器可以理解的互操作。2016年发表在Scientific Data针对科学数据管理和监管,提出了数据的可发现(Findable)、可访问(Accessible)、可互操作(Interoperable)、可复用(Reusable)的FAIR指导原则。 这些原则的核心是让机器可以理解数据所需的语义层面的要求,尤其是可互操作和可复用两部分提到的语义级要求 - 广泛使用的语言、词汇表、元数据引用、符合相关领域的社区标准... 大家都不约而同地指向了统一行业语义。传统数据中心面临的上述挑战,正是因为缺乏统一的行业语义、缺乏统一的语义级互操作。 那什么是统一语义? 三. 统一语义数据平台 圣经记载人类曾经联合起来兴建能通往天堂的高塔 - 巴别塔、也称通天塔。上帝为了阻止人类的计划,让人类说不同的语言。人类相互之间不能沟通,造塔计划因此失败。 统一语言是数据能够互相理解、并利用数据的前提。 语言包含2个层面: 1. 语义:真实世界事物及其关系的表达方法。例如不同电子病历系统对疑似肺癌的记录,可能记录为以下三种之一: A。问题: 癌症 身体部位:肺 确定程度:疑似 B。问题: 肺癌 确定程度:疑似 C。问题: 疑似肺癌 这三种语义表达不统一。没有统一的语义就像图里的电源插座,每个国家规格都不同,是不可能互联互通的。 2. 语法:语言的结构规则,包括词法和句法。而词法和句法都可能有歧义,就像图中示例的那样。 行业数据需要通过统一语义达到互联互通。对数据而言,统一语义不仅在数据模型(语义)、也在数据使用方式(语法)上。不仅数据语义是统一的,操作/互操作数据的方法也是统一的,并且需要能避免词法和句法歧义,才能达到语义级互操作能力! 是不是一定要统一语义?要看数据用途:对于特定的、简单的数据任务,简化的数据模型和数据处理方法可能已经足够,但对于复杂的、跨领域的数据任务,如广泛的自然语言处理、知识图谱构建、大规模机器学习等,统一语义是非常有价值的。 显然,对于数据平台这类多用途平台,应该统一数据语义。 四. 如何建设行业统一语义数据平台 数据平台建设向统一语义迈进,而统一的行业语义模型,应该针对行业用户友好:直观、完整、语义简单、没有二义性,易于数据探索与使用。 统一语义是指要统一物理数据模型和操作数据的语言吗?是要限定到特定的技术栈吗? 先看一下数据库的结构化查询语言(SQL):众多的关系型数据库、甚至很多非关系型数据库都支持ANSI SQL语言。SQL定义了自己的语义 - 表、字段、视图、存储过程... 和自己的语法 - 数据定义语言(DDL)、数据操作语言(DML),但它并没有定义任何数据的物理存储方案!也正因如此,任何数据库厂商、任何数据物理存储方案,都可以通过自己的SQL编译器来支持SQL和SQL客户端,从而屏蔽数据库物理层差异,使用相同的SQL语言共同建设SQL生态。这也是SQL生态壮大的原因之一。 SQL的成功告诉我们,统一行业语义是对行业数据的逻辑表达层的要求,它不应对任何数据库技术底层做要求,也就是不应限定任何技术栈。 前面提到统一的数据操作/互操作能力是统一语义的一部分,是要用单一的数据操作方法吗?数据有多种操作方式,每种操作方式都有自己适用的场景,如下: 对同一份数据提供多模型的操作能力,会极大提升语义层的操作/互操作的便捷性,是非常重要的统一语义特性。重要的是可以针对同一份语义数据进行多种模型的操作/互操作,而不是建立针对每种模型的多套语义,并进行数据复制。 也就是说统一语义,并不是数据只能有一种操作/互操作方式,而应提供对同一份统一语义数据的多种操作/互操作方式。 五. InterSystems统一语义数据平台建设 基于上面的建设思路,InterSystems的医疗信息统一语义平台通过对行业语义的理解和其智能数据编织能力,提供医疗信息数据基座。 5.1 行业语义选择 - FHIR 行业语义应具有开放性、成熟性、准确性、完整性、灵活性、简单性、非二义性、可互操作性、机器可理解,并被广泛接受与认可。纵观医疗信息行业,虽然有不少通用数据模型,但目前最满足上述条件的是HL7 FHIR。它的资源模型覆盖面广,不仅是临床、还包括管理、科研等;不仅包括通用数据模型 - FHIR资源模型,还有对其统一的互操作方法 - FHIR API;按80/20原则设计,允许对资源模型和API进行扩展;资源模型和API简单、并有详细的用例指南;FHIR资源模型、API、扩展都可以被计算机理解;FHIR拥有庞大的用例,并且其触角不断扩展到医疗信息应用的各个层面和各个方向。 另外,更重要的是,FHIR的定位就是行业语义标准 - 逻辑层的标准,任何厂商只需要提供自己的FHIR服务器,就可以利用任何技术栈发布统一的FHIR资源和FHIR API,而屏蔽底层不同类型的数据存储方案、数据模型和数据操作方法。因而它是一个强大的生态标准,所有厂商和用户都可以参与其中。 InterSystems的解决方案选择FHIR作为统一语义,在支持FHIR的6种互操作范式的基础上,提供对FHIR资源的SQL投射 - 无需数据拷贝,就可以使用SQL大规模查询FHIR资源,对统计分析、机器学习提供简单易用的数据操作能力。 5.2 利用数据编织技术,无需推倒重来 如果正在规划数据平台,应考虑按统一语义建设。如果已经建设有各类数据中心,并不需要将已有的建设成果推倒重来。InterSystems的解决方案通过数据编织技术,将数据源编织在一起,并建立逻辑上的统一语义层。原有数据中心和其各类应用继续运行,通过统一语义层来支撑新的数据利用和应用创新。 InterSystems利用数据编织技术,提供针对所有数据源、数据模型、互操作标准的接入能力和适配器。现有的数据中心被视为数据源,只需接入而无需推倒现有建设成果。 InterSystems的多模型能力,将这些离散的数据源统一转换、表达,将多数据源的数据,以FHIR资源这个统一语义模型,发布多种数据模型的数据服务:包括FHIR JSON模型、FHIR对象模型、FHIR SQL模型,满足多种应用场景对统一语义数据的最佳操作方式。 InterSystems数据引擎,为统一语义层提供高性能、横向可扩展的持久化层,满足不同规模的数据用户所需的性能和弹性。 InterSystems提供FHIR与互联互通、HL7 V2、CDA等通用模型的开箱即用的转换能力和对用户自定义模型的自定义转换能力,提供全方位的统一语义互操作能力。
文章
Hao Ma · 一月 10, 2021

使用规范优先的方式开发REST API

在本文中,我想谈一谈规范优先的 REST API 开发方式。 传统的代码优先 REST API 开发是这样的: * 编写代码 * 使其支持 REST * 形成文档(成为 REST API) 规范优先遵循同样的步骤,不过是反过来的。 我们先制定规范(同时兼做文档),然后根据它生成一个样板 REST 应用,最后编写一些业务逻辑。 这是有好处的,因为: * 对于想要使用你的 REST API 的外部或前端开发者,你总是有相关且有用的文档 * 使用 OAS (Swagger) 创建的规范可以导入各种工具,从而进行编辑、客户端生成、API 管理、单元测试和自动化,或者许多其他任务的简化 * 改进了 API 架构。 在代码优先的方式中,API 是逐个方法开发的,因此开发者很容易失去对整体 API 架构的跟踪,但在规范优先的方式中,开发者被强制从 API 使用者的角度与 API 进行交互,这通常有助于设计出更简洁的 API 架构 * 更快的开发速度 - 由于所有样板代码都是自动生成的,你无需编写代码,只需开发业务逻辑。 * 更快的反馈循环 - 使用者可以立即查看 API,并且只需修改规范即可轻松提供建议 让我们以规范优先的方式开发 API 吧! ### 计划 1. 使用 swagger 制定规范 * Docker * 本地 * 在线 2. 将规范加载到 IRIS 中 * API 管理 REST API * ^REST * 类 3. 我们的规范会怎样? 4. 实现 5. 进一步开发 6. 注意事项 * 特殊参数 * CORS 7. 将规范加载到 IAM 中   ### 制定规范   勿庸置疑,第一步是编写规范。 InterSystems IRIS 支持 Open API 规范 (OAS): > **OpenAPI 规范** (以前称为 Swagger 规范)是 REST API 的 API 描述格式。 OpenAPI 文件允许描述整个 API,包括: > > * 可用端点 (`/users`) 和每个端点上的操作(`GET /users`、`POST /users`) > * 每次操作的操作参数输入和输出 > * 身份验证方法 > * 联系信息、许可证、使用条款和其他信息。 > > API 规范可以使用 YAML 或 JSON 编写。 格式易于学习,并且对人和机器都可读。 完整的 OpenAPI 规范可在 GitHub 上找到:[OpenAPI 3.0 规范](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)   - 来自 Swagger 文档。 我们将使用 Swagger 编写 API。 使用 Swagger 有几种方法: * [在线](https://editor.swagger.io/) * Docker:`docker run -d -p 8080:8080 swaggerapi/swagger-editor` * [本地安装](https://swagger.io/docs/open-source-tools/swagger-editor/) 安装/运行 Swagger 后,你应该在 Web 浏览器中看到以下窗口: 在左侧编辑 API 规范,在右侧可以立即看到渲染的 API 文档/测试工具。 我们将第一个 API 规范加载到其中(使用 [YAML](https://en.wikipedia.org/wiki/YAML))。 这是一个简单的 API,包含一个 GET 请求 - 返回指定范围内的随机数。   Math API 规范 swagger: "2.0" info: description: "Math" version: "1.0.0" title: "Math REST API" host: "localhost:52773" basePath: "/math" schemes: - http paths: /random/{min}/{max}: get: x-ISC_CORS: true summary: "Get random integer" description: "Get random integer between min and max" operationId: "getRandom" produces: - "application/json" parameters: - name: "min" in: "path" description: "Minimal Integer" required: true type: "integer" format: "int32" - name: "max" in: "path" description: "Maximal Integer" required: true type: "integer" format: "int32" responses: 200: description: "OK" 以下是其包含的内容。 有关 API 和使用的 OAS 版本的基本信息。 swagger: "2.0" info: description: "Math" version: "1.0.0" title: "Math REST API" 服务器主机、协议(http、https)和 Web 应用程序名称: host: "localhost:52773" basePath: "/math" schemes: - http 接下来指定路径(完整的 URL 是 `http://localhost:52773/math/random/:min/:max`)和 HTTP 请求方法(get、post、put、delete): paths: /random/{min}/{max}: get: 之后,指定有关请求的信息: x-ISC_CORS: true summary: "Get random integer" description: "Get random integer between min and max" operationId: "getRandom" produces: - "application/json" parameters: - name: "min" in: "path" description: "Minimal Integer" required: true type: "integer" format: "int32" - name: "max" in: "path" description: "Maximal Integer" required: true type: "integer" format: "int32" responses: 200: description: "OK" 在此部分中,我们定义请求: * 为 CORS 启用此路径(稍后将详细介绍) * 提供 _summary_ 和 _description_ * _operationId_ 允许规范内引用,它也是我们的实现类中生成的方法名 * _produces_ - 响应格式(例如文本、xml、json) * _parameters_ 指定输入参数(在 URL 或正文中),在我们的示例中,我们指定 2 个参数 - 随机数生成器的范围 * _responses_ 列出服务器的可能响应 如你所见,这种格式并不是特别有挑战性,虽然还有很多可用功能。这里是[规范](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md)。 最后,我们将定义导出为 JSON。 转到“文件 → 转换”并另存为 JSON。 规范应如下所示:   Math API 规范 { "swagger": "2.0", "info": { "description": "Math", "version": "1.0.0", "title": "Math REST API" }, "host": "localhost:52773", "basePath": "/math", "schemes": [ "http" ], "paths": { "/random/{min}/{max}": { "get": { "x-ISC_CORS": true, "summary": "Get random integer", "description": "Get random integer between min and max", "operationId": "getRandom", "produces": [ "application/json" ], "parameters": [ { "name": "min", "in": "path", "description": "Minimal Integer", "required": true, "type": "integer", "format": "int32" }, { "name": "max", "in": "path", "description": "Maximal Integer", "required": true, "type": "integer", "format": "int32" } ], "responses": { "200": { "description": "OK" } } } } } }   ### 将规范加载到 IRIS 中 现在我们有了规范,我们可以在 InterSystems IRIS 中为此 REST API 生成样板代码。 要进入此阶段,我们需要三个东西: * REST 应用程序名称:我们生成的代码的包(假定为 `math) JSON 格式的 OAS 规范:我们刚刚在上一步中创建 Web 应用程序名称:用于访问我们的 REST API 的基本路径(我们的示例中为 /math`) 有三种方法使用我们的规范来生成代码,它们本质上是相同的,只是提供了多种访问相同功能的方式 1. 调用 `^%REST` 例程(在交互式终端会话中 `Do ^%REST`), [参见文档](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GREST_routine)。 2. 调用 `%REST` 类(`Set sc = ##class(%REST.API).CreateApplication(applicationName, spec)`,非交互式),[参见文档](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_objectscriptapi)。 3. 使用 API 管理 REST API,[参见文档](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_apimgmnt)。 我认为文档足以描述所需的步骤,因此选择一个即可。 我再补充两点说明: * 在第 1 种和第 2 种方法中,可以向动态对象传递文件名或 URL * 在第 2 种和第 3 种方法中,**必须** 进行一个额外的调用才能创建 Web 应用程序:`set sc = ##class(%SYS.REST).DeployApplication(restApp, webApp, authenticationType)`,所以在我们的示例中为 `set sc = ##class(%SYS.REST).DeployApplication("math", "/math")`,从 `%sySecurity` 包含文件获取 `authenticationType` 参数的值,相关条目为 `$$$Authe*`,因此对于未经身份验证的访问,传递 `$$$AutheUnauthenticated`。 如果省略,该参数默认为密码身份验证。   ### 我们的规范会怎样? 如果你已成功创建应用,新的 `math` 包应该包含三个类: * _Spec_ - 按原样存储规范。 * _Disp_ - 在调用 REST 服务时直接调用。 它封装 REST 处理并调用实现方法。 * _Impl_ - 保存 REST 服务的实际内部实现。 你只应该编辑此类。 [文档](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_intro#GREST_intro_classes)包含有关这些类的更多信息。 ### 实现 最初,我们的实现类 `math.impl` 只包含一个方法,对应于我们的 `/random/{min}/{max}` 操作: /// Get random integer between min and max /// The method arguments hold values for: /// min, Minimal Integer /// max, Maximal Integer ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject { //(Place business logic here) //Do ..%SetStatusCode() //Do ..%SetHeader(,) //Quit (Place response here) ; response may be a string, stream or dynamic object } 让我们从简单的实现开始: ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject { quit {"value":($random(max-min)+min)} } 最后,我们可以通过在浏览器中打开此页面来调用我们的 REST API:`http://localhost:52773/math/random/1/100` 输出应该是: { "value": 45 } 此外,在 Swagger 编辑器中按 `Try it out`(试用)按钮并填写请求参数也会发送同样的请求: 恭喜! 我们使用规范优先的方式创建的第一个 REST API 现在已经生效!   ### 进一步开发 当然,我们的 API 不是静态的,我们需要添加新路径等等。 在规范优先的开发中,首先要修改规范,然后更新 REST 应用程序(调用与创建应用程序相同),最后编写代码。 请注意,规范更新是安全的:你的代码不会受到影响,即使从规范中删除路径,在实现类中也不会删除方法。   ### 注意事项 更多说明! #### 特殊参数 InterSystems 向 swagger 规范添加了特殊参数,如下所示: 名称 数据类型 默认值 位置 描述 x-ISC_DispatchParent 类名 %CSP.REST 信息 调度类的超类。 x-ISC_CORS 布尔 false 操作 一个标志,指示对此端点/方法组合的 CORS 请求应该获得支持。 x-ISC_RequiredResource 数组   操作 以逗号分隔的已定义资源及其访问模式(资源:模式)的列表,这些资源和模式是访问 REST 服务的此端点所必需的。 示例:["%Development:USE"] x-ISC_ServiceMethod 字符串   操作 后端调用的用于维护此操作的类方法的名称;默认值为 operationId,通常就很合适。   #### CORS 有三种方法启用 CORS 支持。 1. 在逐条路由的基础上,将 `x-ISC_CORS` 指定为 true。 我们的 Math REST API 中就是这样做的。 2. 在每个 API 的基础上,添加 Parameter HandleCorsRequest = 1; 然后重新编译该类。 规范更新后它也会保留。 3. (推荐)在每个 API 的基础上,实现自定义调度器超类(应该扩展 `%CSP.REST`)并编写 CORS 处理逻辑。 要使用此超类,请将 `x-ISC_DispatchParent` 添加到规范中。 ### 将规范加载到 IAM 中   最后,我们将规范添加到 IAM中,以便将其发布给其他开发者。 如果您尚未开始使用 IAM,请参见[此文章](https://community.intersystems.com/post/introducing-intersystems-api-manager)。 它还涵盖了通过 IAM 提供 REST API,所以我在这里不做介绍。 您可能需要修改规范的 `host` 和 `basepath` 参数,使它们指向 IAM,而不是 InterSystems IRIS 实例。 打开 IAM 管理员门户,转到相关工作区的 `Specs`(规范)选项卡。 点击 `Add Spec`(添加规范)按钮并输入新 API 的名称(我们的示例中为 `math`)。 在 IAM 中创建新规范后,点击 `Edit`(编辑)并粘贴规范代码(JSON 或 YAML - IAM 都支持): 不要忘记点击 `Update File`(更新文件)。 现在我们的 API 已发布给开发者。 打开开发者门户,然后点击右上角的 `Documentation`(文档)。 除了三个默认 API,还应该看到我们的新 `Math REST API`: 打开它: 现在,开发者可以看到我们的新 API 的文档,并在同一个地方试用它! ###   ### 结论   InterSystems IRIS 简化了 REST API 的开发过程,规范优先的方式使 REST API 生命周期管理更快更简单。 通过这种方式,你可以使用各种工具来完成各种相关任务,例如客户端生成、单元测试、API 管理等等。   ### 链接 * [OpenAPI 3.0 规范](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md) * [创建 REST 服务](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST) * [从 IAM 开始](https://community.intersystems.com/post/introducing-intersystems-api-manager) * [IAM 文档](https://docs.intersystems.com/irislatest/csp/docbook/apimgr/index.html)
文章
姚 鑫 · 七月 26, 2023

第三章 HL7 架构和可用工具 - 使用 HL7 架构结构页面

# 第三章 HL7 架构和可用工具 - 使用 HL7 架构结构页面 ## 使用 HL7 架构结构页面 通过 `HL7` 架构页面,可以导入和查看 `HL7` 版本 `2` 架构规范。要显示此页面,请从主页中选择互操作性 > 互操作 > HL7 v2.x > HL7 v2.x 架构结构。有关使用此页面的一般信息,请参阅在产品中使用虚拟文档中的“使用架构结构页面”。 `HL7` 模式页面提供了一个附加选项卡:消息类型。此选项卡将两个消息结构标识为请求/响应对。 ## 查看文档类型列表 要列出某个类别中的所有文档类型结构,请首先选择该类别,然后单击“`DocType` 结构”选项卡。 ## 查看消息结构 要查看消息结构的内部组织,请从 `HL7` 架构页面上的 `DocType` 结构选项卡单击其名称(选择互操作性 > 互操作 > HL7 v2.x > HL7 v2.x 架构结构)。 `InterSystems` 产品使用以下视觉提示和命名约定在“结构”部分中显示消息的段结构。 - 组成消息结构的段按从上到下的顺序列出。 - 段名称必须全部大写。 - 显示每个消息段的三个字母名称:`MSH`、`NTE`、`PID` 等。该名称指示 `HL7` 消息结构中该位置存在的段类型。包含选项、重复或包含一组其他段的段的名称会在名称中附加特殊字符。 - 绿色虚线包围可选的段、组或字段。 - 可以重复的段在段名称后附加了括号。例如,如果`PID`段可以重复,则出现`PID()`。 - 包含其他段选择的段被视为段的联合。这些联合段的段名称后附加有“`union`”一词。只有联合中包含的段之一可以出现在消息结构内的该位置。 - 包含一组段的段在段名称后附加了字母“`grp`”。要展开或折叠组,请使用组名称旁边的箭头图标。 - 双击段名称可在单独的窗口中打开该段的结构。 ## 查看段结构 要查看消息段的结构,请在与上一节中显示的示例类似的任何页面中单击其名称。 `InterSystems` 产品显示一个表格,其中列出了该段中的所有字段。这是 `HL7` 架构段结构页面。 例如,如果单击 `2.3:ADT_A01` 消息结构中的 `PR1` 段,`InterSystems` 产品将显示以下页面。 各列如下: - `Field` 字段 — 用于访问段内字段的数字。 - `Description` 描述 — 字段的简短描述。 - `Property Name` 属性名称 — 用于访问段内字段的名称。 - `Data Structure` - 对于使用数据结构的更复杂的字段值,需要进一步的语法详细信息才能完成`segment:field` 虚拟属性路径。可以通过单击此列中的名称来获取此信息 - `Symbol` 符号——表示字段的语法规则。此列中的字符指示是否可以预期此字段在消息段中存在、不存在或重复。可能的值 Symbol |Meaning ---|--- `!`| (仅限`1`)该字段为必填字段;它只能出现一次。 `?`| (`0`或`1`)该字段是可选的,但如果发生,则可能只出现一次。 `+`| (`1`个或多个)该字段可以重复一次或多次。 `*`| (`0`或更多)该字段可以重复`0`次或多次。 `&`| 该字段可能存在,并且可能重复,但仅在某些条件下。 `n*`| `0` 到 `n`) 该字段最多重复 `n` 次。 - `Repeat Count` - 该字段可以重复的最大次数(如果重复,并且有最大值)。 - `Minimum Length` - 字段中的最小字符数。该字段的每次重复都必须包含此数量的字符。 - `Maximum Length` - 字段中的最大字符数。该字段的每次重复都可以包含此数量的字符。 - `Required` - 显示 `R` 表示必需,`O` 表示可选。 - `Repeating` - 显示 `1` 表示 `true`,`0` 表示 `false`。 - `Code Table` - 单击条目可查看可在此字段中输入的有效代码。 - `Alternate Description`替代描述 - 该领域的第二个更长的描述。 可以使用此信息(尤其是“属性名称”列)以“段:字段”格式构建虚拟属性路径。以下是涉及 `2.3:ADT_A01` 消息结构中 `PR1` 段的简单字段值的虚拟属性路径示例。 `()` 快捷语法指示重复字段的所有可用实例,而 `(1)` 指示第一个实例: ```java PR1grp().PR1:ProcedureType PR1grp().PR1:ProcedureCode() PR1grp().PR1:ProcedureCode(1) PR1grp().PR1:ProcedureCode(x) PR1grp().PR1:ProcedurePriority ```