搜索​​​​

清除过滤器
文章
姚 鑫 · 一月 18, 2023

第四十九章 使用 ^SystemPerformance 监视性能 - 复制配置文件

# 第四十九章 使用 ^SystemPerformance 监视性能 - 复制配置文件 # 复制配置文件 可以使用以下 `API` 命令将现有配置文件复制到具有不同名称的文件: ```java set rc=$$copyprofile^SystemPerformance("sourceprofilename","targetprofilename") ``` 必须指定: - `sourceprofilename` - 现有配置文件的名称 - `targetprofilename` - 要创建的配置文件的名称。这必须用双引号引起来。 如果成功,该函数返回 `1`。如果不成功,它返回一个 `0` 后跟一个 `carat` 和任何错误的原因。 其中 `sourceprofilename` 是现有配置文件的名称,`targetprofilename` 必须是唯一的并且不能包含空格或空白字符。 例如,要复制 `2minrun` 配置文件,请输入以下内容: ```java set rc=$$copyprofile^SystemPerformance("2minrun","5minrun") ``` 下次运行 `^SystemPerformance` 实用程序时,配置文件列表包括以下配置文件名称和说明: ```java 2minrun A 2-minute run sampling every 30 seconds 5minrun A 2-minute run sampling every 30 seconds ``` 现在可以按照本指南这一部分的编辑配置文件中的说明编辑新配置文件。 # 删除配置文件 可以使用以下 `API` 命令删除现有配置文件(预定义的“测试”配置文件除外): ```java set rc=$$delprofile^SystemPerformance("profilename") ``` - `profilename` - 要删除的配置文件的名称。这必须用双引号引起来。 如果成功,该函数返回 `1`。如果不成功,它返回一个 `0` 后跟一个 `carat` 和任何错误的原因。 例如,要删除 `2minrun` 配置文件,请输入以下内容: ```java set rc=$$delprofile^SystemPerformance("2minrun") ``` 下次运行 `^SystemPerformance` 实用程序时,配置文件列表不包括 `2minrun` 配置文件。 # ^SystemPerformance Utility 创建的性能报告 `^SystemPerformance` 实用程序生成特定于平台的报告,如本章所述。该报告分为几个部分,如下面的清单所示: ```java Configuration IRISTEST3 on machine testsystem Customer: InterSystems Development License : 123456 InterSystems IRIS Version String: InterSystems IRIS for Windows (x86-32) 2018.1 (Build 508) Fri Jan 26 2018 17:51:22 EDT -------------------------------------------------------------- Profile Profile run "test" started at 10:07 on Jun 01 2016. Run over 10 intervals of 30 seconds. -------------------------------------------------------------- license Product=Enterprise License Type=Concurrent User Server=Multi Platform=Heterogeneous Licensed Users=1000 Licensed CPUs=16 . . . -------------------------------------------------------------- End of InterSystems IRIS Performance Data Report ``` 本节中的表格描述了每个特定于平台的报告的部分。这些部分在每个表中按字母顺序列出,以帮助您更轻松地找到特定部分。仅收集一次的数据标有星号 (`*`)。其余数据是在整个配置文件运行过程中收集的。 注意:在以下所有表格中,标有 `*` 的数据每次运行收集一次。
文章
姚 鑫 · 七月 8, 2021

第三十一章 检查命名空间和类

# 第三十一章 检查命名空间和类 类`%XML.Namespaces`提供了两个类方法,可用于检查XML命名空间及其包含的类: ## GetNextClass() ```java classmethod GetNextClass(namespace As %String, class As %String) as %String ``` 返回给定`XML`命名空间中给定类之后的下一个类(按字母顺序)。当没有更多的类时,此方法返回`NULL`。 ## GetNextNamespace() ```java classmethod GetNextNamespace(namespace As %String) as %String ``` 返回给定命名空间之后的下一个命名空间(按字母顺序)。当没有更多的命名空间时,此方法返回`NULL`。 在这两种情况下,只考虑当前的InterSystems IRIS命名空间。此外,映射的类也会被忽略。 例如,以下方法列出当前InterSystems IRIS命名空间的XML命名空间及其类: ```java ClassMethod WriteNamespacesAndClasses() { Set ns="" Set ns=##class(%XML.Namespaces).GetNextNamespace(ns) While ns '="" { Write !, "The namespace ",ns, " contains these classes:" Set cls="" Set cls=##class(%XML.Namespaces).GetNextClass(ns,cls) While cls '="" { Write !, " ",cls Set cls=##class(%XML.Namespaces).GetNextClass(ns,cls) } Set ns=##class(%XML.Namespaces).GetNextNamespace(ns) } } ``` 在终端中执行时,此方法会生成如下所示的输出: ```java The namespace http://www.address.org contains these classes: ElRef.NS.Address GXML.AddressNS MyApp4.Obj.Address MyAppNS.AddressNS Obj.Attr.Address Obj.Ns.Address Obj.Ns.AddressClass The namespace http://www.doctor.com contains these classes: GXML.DoctorNS The namespace http://www.one.org contains these classes: GXML.AddressNSOne GXML.DoctorNSOne GXML.PersonNSOne ... ```
文章
Jingwei Wang · 七月 25, 2022

ESB和注册中心概述

本章介绍了使用InterSystems IRIS作为企业服务总线,描述了InterSystems IRIS ESB架构,并提供了部署ESB的概述。 企业服务总线的概念 企业服务总线(ESB)提供了一个单点来访问和管理具有SOAP、REST或其他网络API的应用程序。ESB提供了以下功能。 提供一个中心来发现和访问服务。 将应用程序与服务隔离开来,允许你在一个地方改变服务的描述,而不需要更新所有依赖它的应用程序。例如,你可以改变服务器的地址,甚至服务的API,并在ESB中处理这些变化。这为你的应用程序提供了协议独立性。如果一个应用程序是使用SOAP API开发的,但一个新的服务用REST API提供了额外的功能,ESB可以使新的REST服务作为REST和SOAP API可用,允许你添加新服务的功能,同时保持对现有应用程序的兼容性。 提供一种机制来组织和跟踪企业使用的应用程序以及这些应用程序之间的依赖关系。。 ESB架构 InterSystems IRIS ESB架构有以下组成部分。 将传入的请求与服务器连接起来的路由机制。这是由具有专门业务服务(BS)和业务操作(BO)的 production实现的,也可以选择业务流程(BP)。 公共服务注册中心--这个服务注册中心可以通过ESB公共REST API被ESB客户访问。开发人员使用它来获取有关通过 ESB 可用的服务的信息。 外部服务注册中心--这个服务注册中心只能在ESB production中访问。它向ESB业务主机提供端点信息。 SAML令牌验证服务。 ESB production和服务注册处都应该被定义在一个专门用于ESB的命名空间中。 注意:InterSystems建议在一个InterSystems IRIS实例上只运行一个ESB production。 在其最简单的形式中,ESB由透传服务、透传操作和服务注册表组成。下面说明了一个简单的ESB的架构。 通常,应用程序开发人员使用网页或应用程序来查询 ESB 服务注册表,以了解可用的服务并获得提供对底层服务的访问的透传业务服务的 URL。这个查询和服务发现过程是在开发者创建客户端应用程序时进行的。一旦开发者拥有了访问服务所需的URL、文档和其它信息,客户端应用程序就不需要为了调用服务而访问服务注册表。在某些环境中,客户端应用程序可能会对公共API进行运行时调用,以确保注册表项在上次访问后没有被修改。 客户机应用程序调用透传服务。透传服务将消息发送到在其目标设置中指定的透传操作。透传操作被配置为在外部服务注册表中查找端点URL。然后,它使用该端点URL调用外部服务。外部服务将响应返回给透传操作,后者将响应指向透传服务。透传服务反过来将响应返回给客户端应用程序。 除了透传服务和操作外,你还可以定义更复杂的服务、操作和业务流程,以增加一些功能,例如,根据呼叫的内容,将呼叫从一个传入的服务路由到多个外部服务。这允许ESB提供一个单一外部服务所不能提供的服务。客户端应用程序与实现扩展服务所需的外部服务隔绝。 修改用于服务的参数或协议。如果一组应用程序是使用一种服务开发的,但后来有了具有不同 API 的高级服务,与其修改每个应用程序,不如在 ESB 中进行转换。客户端应用程序与从一个外部服务转换到另一个服务所需的变化隔绝。直接在ESB上实现服务。如果没有符合需求的外部服务,可以使用ObjectScript在ESB上实现一个。 但是,将这些更复杂的服务添加到 ESB 上有一个效率成本。这些复杂服务的额外处理成本减缓了ESB处理请求的时间,降低了吞吐量。对于需要非常高吞吐量的ESB系统,你可以通过消除持久化消息来减少处理请求的开销。持久性消息是指从透传服务发送到透传操作的对象,以及由操作返回给服务的对象。这些对象被存储在InterSystems IRIS数据库中。这些持久性的消息在跟踪和报告ESB处理的调用以及排除任何问题时非常有用。但创建这些对象需要资源,对于吞吐量非常高的系统,这些对象所需的存储量可能非常大。为了维护系统,你必须经常清除这些信息。你可以抑制对持久化消息的使用,以获得效率,但代价是降低灵活性。更多信息请参见 "在通过式服务和操作中抑制持久化消息"。 注意:如果你正在运行HealthShare产品,HealthShare服务注册表与InterSystems IRIS服务注册表不同。HealthShare服务注册表提供了与InterSystems IRIS外部服务注册表类似的功能。在大多数情况下,你应该继续使用HealthShare服务注册表,而不是使用InterSystems IRIS外部服务注册表。 配置ESB 下面包含安装和配置InterSystems IRIS的几个配置程序: 创建一个具有互操作性的命名空间,以包含ESB production和服务注册。 配置Web网关。 创建外部服务注册表条目,定义ESB所使用的外部服务的端点。 创建 ESB production,添加提供服务的业务服务和业务操作,并启动该 production。 创建通过管理门户和公共 REST API 访问公共服务注册中心所需的角色和用户。 创建将业务服务提供给客户所需的Web应用程序。 创建公共服务注册表条目,描述通过ESB访问的服务。
文章
姚 鑫 · 七月 14, 2021

第三章 处理文件和目录名

# 第三章 处理文件和目录名 `%Library.File`类提供了几个可用于处理文件名和目录名的类方法。在大多数情况下,文件和目录不需要存在即可使用这些方法。 ## 获取文件名和目录名 `%Library.File`类提供可用于获取部分文件名和目录名的类方法。 在给定完整路径名的情况下,使用`GetDirectory()`和`GetFilename()`分别获取目录和短文件名。对于此方法,不允许使用部分目录名。 ```java DHC-APP>set filename = "e:\temp\config.txt" DHC-APP>write ##class(%File).GetDirectory(filename) E:\temp\ DHC-APP>write ##class(%File).GetFilename(filename) config.txt ``` 在给定文件名的情况下,使用`CanonicalFilename()`从根目录获取完整路径: ```java DHC-APP>set filename = "cache.dat" DHC-APP>write ##class(%File).CanonicalFilename(filename) e:\dthealth\db\dthis\data\cache.dat DHC-APP>write ##class(%File).CanonicalFilename("foo.dat") ``` 如果文件无法打开,`CanonicalFilename()`方法将返回空字符串。 给定一个目录名,使用`ComputeFullDBDir()`构造目录名的规范形式。 ```java DHC-APP>write ##class(%File).ComputeFullDBDir("foodirectory") C:\InterSystems\Cache\mgr\foodirectory\ ``` 在给定目录名的情况下,使用`GetDirectoryLength()`和`GetDirectoryPiess()`分别获取目录中的片断数和特定片断的片断数。片断可以用斜杠(`/`)或反斜杠(`\`)分隔,具体取决于操作系统。 ```java DHC-APP>set dir = "e:\temp" DHC-APP>write ##class(%File).GetDirectoryLength(dir) 2 DHC-APP>write ##class(%File).GetDirectoryPiece(dir,1) E: ``` 在给定文件名或目录名的情况下,使用`ParentDirectoryName()`获取父目录。 ```java DHC-APP>set dir = "stream" DHC-APP>write ##class(%File).ParentDirectoryName(dir) E:\DtHealth\db\dthis\data\ ```` ## 规范化文件名和目录名 `%Library.File`类提供返回规范化文件名和目录名的类方法(遵循运行服务器的操作系统的命名规则)。当通过将名称片段附加到现有名称来创建新的文件名和目录名时,这些选项非常有用。 给定一个文件名,`NormalizeFilename()`返回规范化的文件名. 给定目录名,`NormalizeDirectory()`返回规范化的目录名。 这些方法返回适合在底层操作系统上使用的规范化名称,并将尝试标准化斜杠(`/`)或反斜杠(`\`)路径分隔符。 Windows示例: ```java DHC-APP>write ##class(%File).NormalizeDirectory("stream") E:\DtHealth\db\dthis\data\stream\ DHC-APP>write ##class(%File).NormalizeFilename("c:\temp//config.txt") C:\temp\config.txt ``` UNIX示例: ```java USER>set filename = "/tmp//samples/myfile.txt" USER>write ##class(%File).NormalizeFilename(filename) /tmp/samples/myfile.txt USER>write ##class(%File).NormalizeDirectory("stream") /InterSystems/IRIS/mgr/user/stream/ ``` 在调用这些方法之一以规范化相对于指定目录的目录名或文件名时,请添加第二个参数。该目录必须存在。 Windows示例: ```java DHC-APP>write ##class(%File).NormalizeFilename("config.txt", "e:\temp") E:\temp\config.txt DHC-APP>write ##class(%File).NormalizeDirectory("stream", "") E:\DtHealth\db\dthis\data\stream\ ``` Unix示例: ```java USER>write ##class(%File).NormalizeFilename("myfile.txt", "/tmp/samples") /tmp/samples/myfile.txt USER>write ##class(%File).NormalizeDirectory("stream", "") /InterSystems/IRIS/mgr/user/stream/ ``` `SubDirectoryName()`方法类似于`NormalizeDirectory()`的双参数形式,只是参数的顺序相反。此外,该目录不需要存在。在第三个参数中传递1以添加尾部分隔符,或者传递0以省略它(默认值)。 Windows示例: ```java DHC-APP>write ##class(%File).SubDirectoryName("C:\foobar", "samples") C:\foobar\samples DHC-APP>write ##class(%File).SubDirectoryName("", "stream", 1) E:\DtHealth\db\dthis\data\stream\ ``` Unix示例: ```java USER>write ##class(%File).SubDirectoryName("/foobar", "samples") /foobar/samples USER>write ##class(%File).SubDirectoryName("", "stream", 1) /InterSystems/IRIS/mgr/user/stream/ ``` ## 用空格处理文件和目录名 对于包含空格的文件名和目录名,请使用`NormalizeFilenameWithSpaces()`,它会根据主机平台处理路径名中的空格。与规格化文件名()和规格化目录()不同,此方法只接受一个参数,不能相对于另一个目录规格化文件或目录名,也不能相对于默认目录规格化部分文件或目录名。 在Windows系统上,如果路径名包含空格,并且文件或目录不存在,则该方法返回用双引号括起来的路径名。如果路径名包含空格,并且文件或目录确实存在,则该方法返回路径名的简短形式。如果路径名不包含空格,该方法将原封不动地返回路径名。 ```java DHC-APP>write ##class(%File).NormalizeFilenameWithSpaces("C:\temp\nonexistant folder") "C:\temp\nonexistant folder" DHC-APP>write ##class(%File).NormalizeFilenameWithSpaces("C:\temp\existant folder") "C:\temp\existant folder" DHC-APP>write ##class(%File).NormalizeFilenameWithSpaces("iris.dat") iris.dat DHC-APP>write ##class(%File).NormalizeFilenameWithSpaces("cache.dat") cache.dat ``` 在Unix系统上,如果路径名包含空格,该方法将返回用双引号括起来的路径名。如果路径名不包含空格,该方法将原封不动地返回路径名。 ```java USER>write ##class(%File).NormalizeFilenameWithSpaces("/InterSystems/my directory") "/InterSystems/my directory" USER>write ##class(%File).NormalizeFilenameWithSpaces("iris.dat") iris.dat ``` ## 构建和解构文件和目录名 `%Library.File`类提供的类方法允许从路径数组构造文件名,或将文件名解构为路径数组。 给定一个路径数组,`Construct()`组装路径并返回文件名。构造的文件名适合服务器平台。在没有参数的情况下调用此方法会返回默认目录。 给定一个文件名,`Deconstruct()`分解文件名并返回一个路径数组。阵列的内容适合服务器平台。 下面的Windows示例将数组目录传递给`Construction()`。最后一个数组位置中的空字符串表示返回的文件名应以`a \`结尾。 ```java USER>zwrite dirs dirs=4 dirs(1)="C:" dirs(2)="Temp" dirs(3)="samples" dirs(4)="" USER>write ##class(%File).Construct(dirs...) C:\Temp\samples\ ``` 下面的Unix示例在不带参数的情况下调用`Construction()`。该方法返回默认目录。 ```java USER>set default = ##class(%File).Construct() USER>write default /InterSystems/IRIS/mgr/user ``` 下面的Unix示例调用`Deconstruct()`,它获取变量default中的路径并将它们存储在数组`defaultdir`中。 ```java USER>do ##class(%File).Deconstruct(default, .defaultdir) USER>zwrite defaultdir defaultdir=4 defaultdir(1)="InterSystems" defaultdir(2)="IRIS" defaultdir(3)="mgr" defaultdir(4)="user" ``` ## 获取System Manager目录 使用`ManagerDirectory()`方法获取`installdir/mgr`目录的完全限定名称。例如: ```java DHC-APP>write ##class(%File).ManagerDirectory() C:\InterSystems\Cache\mgr\ ```
文章
姚 鑫 · 九月 29, 2021

第二十九章 SQL命令 DISTINCT

# 第二十九章 SQL命令 DISTINCT 指定仅返回不同值的`SELECT`子句。 # 大纲 ```sql SELECT [DISTINCT [BY (item {,item2})] ] | [ALL] select-item {,select-item2} ``` ## 参数 - `DISTINCT` - 可选-返回组合选择项值唯一的行。 - `DISTINCT BY (item {,item2})` - 可选-返回按(项)值唯一的行的选择项值。 - `ALL` - 可选-返回结果集中的所有行。默认设置。 # 描述 可选`DISTINCT`子句出现在`SELECT`关键字之后、可选`TOP`子句和第一个`SELECT-ITEM`之前。 `DISTINCT`子句应用于`SELECT`语句的结果集。它将每个不同(唯一)值返回的行数限制为一个任意行。如果未指定`DISTINCT`子句,则默认情况下显示满足选择条件的所有行。`ALL`子句与不指定`DEFAULT`子句相同;如果指定`ALL`,`SELECT`将返回表中满足选择条件的所有行。 `DISTINCT`从句有两种形式: - `SELECT DISTINCT`:为选择项值的每个唯一组合返回一行。可以指定一个或多个选择项。例如,以下查询返回一行,其中包含`Home_State`和`Age`值的每个唯一组合的`Home_State`和`Age`值: ```sql SELECT DISTINCT Home_State,Age FROM Sample.Person ``` - `SELECT DISTINCT BY(Item)`:为项目值的每个唯一组合返回一行。可以指定单个项目或逗号分隔的项目列表。指定的项目或项目列表必须用括号括起来。可以在by关键字和圆括号之间指定或省略空格。选择项列表可以(但不一定)包括指定的项。例如,以下查询返回一行,其中包含`Home_State`和`Age`值的每个唯一组合的`Name`和`Age`值: ```sql SELECT DISTINCT BY (Home_State,Age) Name,Age FROM Sample.Person ``` 项目字段必须按列名指定。有效值包括以下值:列名(`DISTINCT BY(City)`);`%ID`(返回所有行);指定列名的标量函数(`DISTINCT BY(ROUND(Age,-1)`;指定列名的排序函数(`DISTINCT BY(%Exact(City)`。不能按列别名指定字段;尝试这样做会生成`SQLCODE-29`错误。不能按列号指定字段;这将被解释为文字,并返回一行。将文字指定为`DISTINCT`子句中的项值将返回`1`行;返回哪行是不确定的。因此,指定`7`、`‘Chicago’`、`‘’`、`0`或`NULL`都返回`1`行。但是,如果将文字指定为逗号分隔列表中的项值,则该文字将被忽略,并且`DISTINCT`将为指定字段名的每个唯一组合选择一行。 `DISTINCT`子句在`TOP`子句之前应用。如果两者都指定,则`SELECT`只返回具有唯一值的行,唯一值是在`TOP`子句中指定的唯一值行数。 如果`DISTINCT`子句中指定的列包含`NULL`(不包含值)行,则`DISTINCT`将返回一行作为`DISTINCT`(唯一)值的`NULL`,如以下示例所示: ```sql SELECT DISTINCT FavoriteColors FROM Sample.Person ``` ```sql SELECT DISTINCT BY (FavoriteColors) Name,FavoriteColors FROM Sample.Person ORDER BY FavoriteColors ``` `DISTINCT`子句在嵌入式SQL简单查询中没有意义,因为在这种类型的嵌入式SQL中,`SELECT`始终只返回一行数据。但是,嵌入式SQL基于游标的查询可以返回多行数据;在基于游标的查询中,`DISTINCT`子句只返回唯一值行。 ## DISTINCT和ORDER BY `DISTINCT`子句在`ORDER BY`子句之前应用。因此,`DISTINCT`和`ORDER BY`的组合将首先选择满足`DISTINCT`子句的任意行,然后根据`ORDER BY`子句对这些行进行排序。 ## DISTINCT和GROUP BY `DISTINCT`和`GROUP BY`这两个记录按指定字段(或多个字段)分组,并为该字段的每个唯一值返回一条记录。它们之间的一个重要区别是`DISTINCT`在分组之前计算聚合函数。`GROUP BY`计算分组后的聚合函数。以下示例显示了这种差异: ```sql SELECT DISTINCT BY (ROUND(Age,-1)) Age,AVG(Age) AS AvgAge FROM Sample.Person /* Avg(Age)返回表中所有年龄的平均值 */ ``` ```sql SELECT Age,AVG(Age) AS AvgAge FROM Sample.Person GROUP BY ROUND(Age,-1) /* Avg(Age)返回每个年龄组的平均年龄 */ ``` `DISTINCT`子句可以用一个或多个聚合函数字段指定,尽管这很少有意义,因为聚合函数返回单个值。因此,下面的示例返回单行: ```sql SELECT DISTINCT BY (AVG(Age)) Name,Age,AVG(Age) FROM Sample.Person ``` 注意:如果将聚合函数作为唯一项或选择项的`DISTINCT`子句与`GROUP BY`子句一起使用,则`DISTINCT`子句将被忽略。可以使用子查询实现`DISTINCT`、聚合函数和`GROUP BY`的预期组合。 ## 字母大小写与DISTINCT优化 根据为字段定义的排序规则类型,将字符串值不同地分组在一起。默认情况下,字符串数据类型字段使用`SQLUPPER`排序规则定义,该排序规则不区分大小写。 如果字段/特性排序规则类型为`SQLUPPER`,则分组的字段值将全部以大写字母返回。要按原始字母大小写对值进行分组,或以原始字母大小写显示分组字段的返回值,请使用`%Exact`排序规则函数。以下示例显示了这一点,这些示例假设`Home_City`字段是使用排序规则类型`SQLUPPER`定义的,并且包含值`‘New York’`和`‘New York’`: ```sql SELECT DISTINCT BY (Home_City) Name,Home_City FROM Sample.Person /* 将Home_City值按其大写字母值组合在一起将以大写字母返回每个分组城市的名称。因此,返回‘new york’. */ ``` ```sql SELECT DISTINCT BY (Home_City) Name,%EXACT(Home_City) FROM Sample.Person /* 将Home_City值按其大写字母值组合在一起将返回每个分组的城市的名称(原始字母大小写)。因此,可以返回‘New York’或‘new York’,但不能同时返回两者。 */ ``` ```sql SELECT DISTINCT BY (%EXACT(Home_City)) Name,Home_City FROM Sample.Person /* 将Home_City值按其原始字母大小写组合在一起将返回每个分组的城市的名称(原始字母大小写)。 因此,‘New York’和‘new York’都会返回。 未使用优化. */ ``` 可以使用管理门户优化包含`DISTINCT`子句的查询的查询性能。依次选择系统管理、配置、SQL和对象设置、SQL。查看和编辑`GROUP BY`和`DISTINCT`查询必须生成原始值选项。(此优化也适用于`GROUP BY`子句。)。默认值为“否”。 此默认设置按字母值的大写排序规则对字母值进行分组。此优化利用选定字段的索引。因此,只有在一个或多个选定字段存在索引时才有意义。它对存储在索引中的字段值进行排序;字母字符串以全部大写字母返回。您可以设置此系统范围的选项,然后使用`%exact`排序规则函数为特定查询覆盖它以保留字母大小写。 也可以使用`$SYSTEM.SQL.Util.SetOption()`方法快速区分选项在系统范围内设置此选项。要确定当前设置,请调用`$SYSTEM.SQL.CurrentSettings()`,它显示打开的不同优化设置;默认值为`1`。 ## DISTINCT的其他用法 - 流字段:`DISTINCT`对流字段的`OID`进行操作,而不是对其实际数据进行操作。因为所有流字段`OID`都是唯一值,所以`DISTINCT`对实际流字段重复数据值没有影响。`DISTINCT BY(StreamField)`将流字段为空的记录数减少到一个空记录。 - 星号语法:`DISTINCT*`语法是合法的,但没有意义,因为根据定义,所有行都包含一些不同的唯一标识符。不同于(`*`)的语法不合法。 - 子查询:在子查询中使用`DISTINCT`子句是合法的,但没有意义,因为子查询返回单个值。 - 未选择行数据:`DISTINCT`子句可以与不访问任何表数据的`SELECT`一起使用。如果`SELECT`包含`FROM`子句,则在一行中指定`DISTINCT`结果将包含这些非表值;如果未指定`DISTINCT`(或`TOP`),则`SELECT`将产生与`FROM`子句表中的行数相同的行数。如果`SELECT`不包含`FROM`子句,则`DISTINCT`是合法的,但没有意义。 - 聚合函数:可以在聚合函数中使用`DISTINCT`子句,以仅选择要包含在聚合中的不同(唯一)字段值。与`SELECT DISTINCT`子句不同,聚合函数中的`DISTINCT`不包括`NULL`作为`DISTINCT`(唯一)值。请注意,`MAX`和`MIN`聚合函数分析`DISTINCT`子句语法没有错误,但此语法不执行任何操作。 ## DISTINCT和%ROWID 指定`DISTINCT`关键字会导致基于游标的嵌入式SQL查询不设置`%ROWID`变量。即使`DISTINCT`不限制返回的行数,也不设置`%ROWID`。下面的示例显示了这一点: ```java ClassMethod Distinct() { s %ROWID = 999 &sql( DECLARE EmpCursor3 CURSOR FOR SELECT DISTINCT Name, Home_State INTO :name,:state FROM Sample.Person WHERE Home_State %STARTSWITH 'M' ) &sql( OPEN EmpCursor3 ) q:(SQLCODE '= 0) for { &sql( FETCH EmpCursor3 ) q:SQLCODE w !,"RowID: ",%ROWID," row count: ",%ROWCOUNT w " Name=",name," State=",state } &sql( CLOSE EmpCursor3 ) } ``` 查询行为的这种更改仅适用于基于游标的嵌入式`SQL SELECT`查询。动态`SQL SELECT`查询和非游标嵌入式`SQL SELECT`查询从未设置`%ROWID`。 ## DISTINCT和事务处理 指定`DISTINCT`关键字会导致查询检索所有当前数据,包括当前事务尚未提交的数据。忽略事务的`READ COMMITTED`隔离模式参数(如果设置);在`READ UNCOMMITTED`模式下检索所有数据。 # 示例 以下查询为每个不同的`Home_State`值返回一行: ```sql SELECT DISTINCT Home_State FROM Sample.Person ORDER BY Home_State ``` 以下查询为每个不同的`Home_State`值返回一行,但返回该行的其他字段。无法预测检索到的是哪一行: ```sql SELECT DISTINCT BY (Home_State) %ID,Name,Home_State,Office_State FROM Sample.Person ORDER BY Home_State ``` 以下查询为`Home_State`和`Office_State`值的每个不同组合返回一行。根据数据的不同,它要么返回更多行,要么返回与上一个示例相同的行数: ```sql SELECT DISTINCT BY (Home_State,Office_State) %ID,Name,Home_State,Office_State FROM Sample.Person ORDER BY Home_State,Office_State ``` 以下查询使用`DISTINCT BY`为每个不同的名称长度返回一行: ```sql SELECT DISTINCT BY ($LENGTH(Name)) Name,$LENGTH(Name) AS lname FROM Sample.Person ORDER BY lname ``` 下面的查询使用`DISTINCT BY`为`FavoriteColors` `%List`值的每个不同的第一个元素返回一行。它列出`FavoriteColors`为空的不同行: ```sql SELECT DISTINCT BY ($LIST(FavoriteColors,1)) Name,FavoriteColors,$LIST(FavoriteColors,1) AS FirstColor FROM Sample.Person ``` 以下查询按排序规则升序返回从`Sample.Person`检索到的前`20`个不同的`Home_State`值。`“top”`行反映`Sample.Person`中所有行的`ORDER BY`子句排序。 ```sql SELECT DISTINCT TOP 20 Home_State FROM Sample.Person ORDER BY Home_State ``` 以下查询在主查询和`WHERE`子句子查询中都使用`DISTINCT`。它返回`Sample.Person`中的前`20`个不同的`Home_State`值,这些值也在`Sample.Employee`中。如果未提供子查询`DISTINCT`,它将检索`Sample.Person`中与`Sample.Employee`中随机选择的`Home_State`值匹配的`DISTINCT Home_State`值: ```sql SELECT DISTINCT TOP 20 Home_State FROM Sample.Person WHERE Home_State IN(SELECT DISTINCT TOP 20 Home_State FROM Sample.Employee) ORDER BY Home_State ``` 以下查询返回前`20`个不同的`FavoriteColore`值。这反映了`Sample.Person`中所有行的`ORDER BY`子句排序。众所周知,`FavoriteColors`字段具有`NULL`,因此`FavoriteColors`为`NULL`的不同行出现在排序规则序列的顶部。 ```sql SELECT DISTINCT BY (FavoriteColors) TOP 20 FavoriteColors,Name FROM Sample.Person ORDER BY FavoriteColors ``` 还要注意,在前面的示例中,因为`FavoriteColors`是一个列表字段,所以归类序列包括元素长度字节。因此,以三个字母元素(红色)开头的不同列表值列在以四个字母元素(蓝色)开头的列表值之前。
文章
姚 鑫 · 五月 8, 2021

第三章 使用多维存储(全局变量)(四)

# 第三章 使用多维存储(全局变量)(四) # 管理事务 InterSystems IRIS提供了使用全局变量实现完整事务处理所需的基本操作。 InterSystems IRIS对象和SQL自动利用这些特性。 如果直接将事务性数据写入全局变量,则可以使用这些操作。 事务命令是`TSTART`,它定义事务的开始; `TCOMMIT`,它提交当前事务; 和`TROLLBACK`,它将中止当前事务,并撤消自事务开始以来对全局变量所做的任何更改。 例如,下面的ObjectScript代码定义了事务的开始,设置了一些全局变量节点,然后根据`ok`的值提交或回滚事务: ```java /// w ##class(PHA.TEST.Global).GlobalTro(0) ClassMethod GlobalTro(ok) { TSTART Set ^Data(1) = "Apple1" Set ^Data(2) = "Berry1" If (ok) { TCOMMIT } Else { TROLLBACK } zw ^Data q "" } ``` TSTART在InterSystems IRIS日志文件中写入事务开始标记。 这定义了事务的起始边界。 在上面的示例中,如果变量`ok`为`true`(非零),则`TCOMMIT`命令标记事务成功结束,并将事务完成标记写入日志文件。 如果`ok`为`false(0)`,那么`TROLLBACK`命令将撤消自事务开始以来进行的每一个`set`或`kill`操作。 在这种情况下,`^Data(1)`和`^Data(2)`被恢复到原来的值。 **注意,在事务成功完成时,不会写入任何数据。 这是因为事务期间对数据库的所有修改都是在事务过程中正常执行的。 只有在回滚的情况下,数据库中的数据才会受到影响。 这意味着本例中的事务具有有限的隔离性; 也就是说,其他进程可以在事务提交之前看到修改后的全局值。 这通常被称为未提交的读取。 这是好是坏取决于应用程序的需求; 在许多情况下,这是完全合理的行为。 如果应用程序需要更高级别的隔离,则可以通过使用锁来实现。 这将在下一节中进行描述。** ## 锁和事务 要创建隔离事务-也就是说,为了防止其他进程在提交事务之前看到修改的数据-需要使用锁。在ObjectScript中,可以通过`lock`命令直接获取和释放锁定。锁按照约定工作;对于给定的数据结构(如用于持久对象),所有需要锁的代码都使用相同的逻辑锁引用(即,锁命令使用相同的地址)。 在事务中,锁有一个特殊的行为; 在事务过程中获取的任何锁在事务结束之前都不会被释放。 要了解为什么会这样,请考虑典型事务执行的操作: 1. 使用`TSTART`启动事务。 2. 获取要修改的一个或多个节点上的锁。这通常被称为“写”锁。 3. 修改一个或多个节点。 4. 释放锁(或多个锁)。因为我们处于事务中,所以这些锁在此时实际上不会被释放。 5. 使用`TCOMMIT`提交事务。此时,上一步中释放的所有锁实际上都已释放。 如果另一个进程想要查看此事务中涉及的节点,并且不想看到未提交的修改,则它只需在从节点读取数据之前测试锁(称为“读”锁)。因为写锁定一直保持到事务结束,所以在事务完成(提交或回滚)之前,读取进程看不到数据。 大多数数据库管理系统使用类似的机制来提供事务隔离。InterSystems IRIS的独特之处在于它让开发人员可以使用这种机制。这使得有可能为新的应用程序类型创建自定义数据库结构,同时仍然支持事务。当然,可以简单地使用InterSystems IRIS对象或SQL来管理数据,并让事务得到自动管理。 ## 对TSTART的嵌套调用 InterSystems IRIS维护一个特殊的系统变量`$TLEVEL`,该变量跟踪`TSTART`命令被调用的次数。`$TLEVEL`从值`0`开始;每次调用`TSTART`时,`$TLEVEL`的值递增`1`,而每次调用`TCOMMIT`时,`$TLEVEL`的值递减`1`。如果调用`TCOMMIT`导致将`$TLEVEL`设置回`0`,则事务结束(以`COMMIT`结束)。 调用`TROLLBACK`命令总是终止当前事务,并将`$TLEVEL`设置回`0`,而不管`$TLEVEL`的值是多少。 此行为使应用程序能够将事务包装在本身包含事务的代码(如对象方法)周围。例如,持久对象提供的`%Save`方法始终将其操作作为事务执行。通过显式调用`TSTART`和`TCOMMIT`,可以创建包含几个对象保存操作的更大事务: ```java TSTART Set sc = object1.%Save() If ($$$ISOK(sc)) { // 第一次保存有效,执行第二次保存 Set sc = object2.%Save() } If ($$$ISERR(sc)) { // 其中一个保存失败,正在回滚 TROLLBACK } Else { // 提交 TCOMMIT } ``` # 管理并发性 设置或检索单个全局变量节点的操作是原子的;它可以保证始终成功并获得一致的结果。对于多个节点上的操作或控制事务隔离,InterSystems IRIS提供获取和释放锁的功能。 锁由IRIS锁管理器管理。在ObjectScript中,可以通过`lock`命令直接获取和释放锁定。(InterSystems IRIS对象和SQL根据需要自动获取和释放锁)。 # 检查最新的全局变量引用 **最新的全局变量引用记录在ObjectScript `$ZREFERENCE`特殊变量中。`$ZREFERENCE`包含最新的全局引用,包括下标和扩展全局引用(如果指定)。请注意,`$ZREFERENCE`既不指示全局引用是否成功,也不指示指定的全局是否存在。InterSystems IRIS只记录最近指定的全局引用。** ## 裸全球变量引用 在带下标的全局引用之后,InterSystems IRIS会将裸指示符设置为该全局名称和下标级别。然后,可以使用裸全局引用(省略全局名称和更高级别的下标)对相同的全局变量和下标级别进行后续引用。这简化了在相同(或更低)下标级别对相同全局变量的重复引用。 在裸引用中指定较低的下标级别会将裸指示符重置为该下标级别。因此,在使用裸全局变量引用时,始终使用由最新全局引用建立的下标级别。 裸指示符值记录在`$ZREFERENCE`特殊变量中。裸露指示符被初始化为空字符串。在未设置裸指示器的情况下尝试裸全局引用会导致`` 错误。更改命名空间会重新初始化裸体指示符。可以通过将`$ZREFERENCE`设置为空字符串(`“”`)来重新初始化裸指示符。 在下面的示例中,第一个引用中指定了带下标的GLOBAL `^Produce(“fruit”,1)`。InterSystems IRIS将此全局变量名称和下标保存在裸体指示符中,以便后续的裸体全局引用可以省略全局名称`“Production”`和更高下标级别的`“Fruit”`。当`^(3,1)`裸引用达到更低的下标级别时,此新的下标级别将成为任何后续裸全局变引用的假设。 ```java /// w ##class(PHA.TEST.Global).GlobalNake() ClassMethod GlobalNake() { SET ^Produce("fruit",1)="Apples" /* 完整的全局变量引用 */ SET ^(2)="Oranges" /* 裸全局变量全局引用 */ SET ^(3)="Pears" /* 假设下标级别为2 */ SET ^(3,1)="Bartlett pears" /* 转到下标级别3 */ SET ^(2)="Anjou pears" /* 假设下标级别为3 */ WRITE "latest global reference is: ",$ZREFERENCE,! ZWRITE ^Produce KILL ^Produce q "" } ``` ```java DHC-APP>w ##class(PHA.TEST.Global).GlobalNake() latest global reference is: ^Produce("fruit",3,2) ^Produce("fruit",1)="Apples" ^Produce("fruit",2)="Oranges" ^Produce("fruit",3)="Pears" ^Produce("fruit",3,1)="Bartlett pears" ^Produce("fruit",3,2)="Anjou pears" ``` 除了极少数例外,每个全局变量变引用(全引用或裸引用)都会设置裸指示器。`$ZREFERENCE`特殊变量包含最新全局变引用的完整全局名称和下标,即使这是一个裸全局引用。`ZWRITE`命令还显示每个全局的完整全局名称和下标,无论它是否使用裸引用设置。 应谨慎使用裸全局变量引用,因为InterSystems IRIS在不总是明显的情况下设置裸指示器,包括以下情况: - 完整全局变量引用最初设置裸露指示符,随后的完整全局引用或裸露全局引用会更改裸露指示符,即使全局引用不成功。例如,试图写入不存在的全局变量的值会设置裸指示符。 - 无论InterSystems IRIS如何计算后置条件,引用下标全局的后置条件命令都会设置裸指示符。 - 引用下标全局变量的可选函数参数可能设置或不设置裸指示符,具体取决于IRIS是否计算所有参数。例如,`$get`的第二个参数总是设置裸指示符,即使它包含的默认值没有使用。InterSystems IRIS按从左到右的顺序计算参数,因此最后一个参数可能会重置由第一个参数设置的裸指示符。 - 回滚事务的`TROLLBACK`命令不会将裸指示符回滚到事务开始时的值。 如果完整全局变量引用包含扩展全局变量引用,则后续的裸全局变量引用将采用相同的扩展全局引用;不必将扩展引用指定为裸全局引用的一部分。
问题
Weiwei Yang · 一月 25, 2021

docker deploy

生产环境要使用80端口访问服务器,采用直接部署程序的方式,是在Linux服务器上同时部署HealthConnect和Apache。类似的现在想要使用Docker技术,在一台服务器上部署了HealthConnect和Apache容器,该如何配置http.conf文件,使80端口的请求转到HealthConnect上呢?如果这种方式不可行,有没有其他方法呢? 你好, 请参考在Openexchange中发布的例子:Example of InterSystems IRIS with external Apache and WebGateway( https://openexchange.intersystems.com/package/iris-webgateway-example)
问题
Wang li · 三月 30, 2021

HealthShare如何实现socket接口开发

大家好,我想咨询下如何实现用HealthShare开发socket的接口,例如开发socket对外的BS,以及socket调用的BO 您好,InterSystem的产品家族都支持直接使用Socket,借助UDP或TCPIP与第三方通信,参考 TCP:https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=GIOD_tcp UDP:https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=GIOD_udp 如果是使用BS或者BO的话,则可以使用IRIS提供的多种适配器按需开发获得,参考 https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=PAGE_interop_protocols 好的,谢谢
公告
Claire Zheng · 一月 23, 2022

欢迎中文社区新版主@黎卫东

亲爱的社区开发者们, 我很高兴地向大家介绍一位我们的新版主@黎卫东(Weidong Li)。 @黎卫东(Weidong Li)目前担任上海嘉会国际医院集成平台经理一职 以下是@黎卫东(Weidong Li)的一些自我介绍: – 我从加入嘉会之后开始了解 InterSystems 的技术以及集成平台开发。接触并使用了 InterSystems早期的Caché版本,Cube, Ensemble 并升级到了基于 Iris 的 Health Connect 平台,并深度了解这些版本的一些特性以及基于 Health Connect 打造高效的集成平台。 – 本人有超过16年的医疗信息化经验,熟悉 IHE/DICOM/HL V2/V3,有着丰富的领域知识。曾先后就职于 INFINITT,AGFA, Carestream 等主流的 HIT 公司,设计并开发过多种医疗信息系统及设计的信息集成。在目前医院,带领一个小型的团队,基于 Health Connect,从0开始设计了整个集成平台的底层架构,整体业务流程,开发流程,开发规范,发布部署规范,第三方系统接入,运维部署,运维监控以及安全体系,打造了一个安全、高效、高可用的业务中台,连接了临床、非临床、Iot、智慧医院等多个应用场景。 – InterSystems 平台是一个非常优秀的集成平台,借助该技术,给我们业务带来了非常大的便于。我也希望这么优秀的技术,能给更多的兄弟单位和合作伙伴创造价值。我也希望能够在社区与大家一起交流,分享自己的一些实践经验;同时也希望借助这个平台,向大家学习一些优秀的实践。我们一起成长,共同进步! 热烈欢迎! 感谢@黎卫东(Weidong Li),期待您成为一名优秀的新版主! 👏 Welcome @Weidong.Li ! 热烈欢迎黎老师!
文章
Louis Lu · 一月 19, 2023

HL7 V2.5.1 的查询与结果返回

这篇文章主要介绍 HL7 V2.5.1 标准是如何定义查询类请求,以及查询类响应的。相关HL7 V2 的更多基础知识可以参考:HL7v2到底是什么?! 的一系列文章。 1 查询标准的发展 1.1 最早的查询模式 最初,HL7的查询参数通过QRD以及QFR 字段传入。因为这两个字段的设计是为了满足所有的查询需求,所以这两个字段的定义非常随意。 1.2 加强的查询模式 从HL7 V2.3开始,引入了加强版的查询模式,它包含了四种方式: • 嵌入式查询语言类请求查询:自由格式的select SQL语句 • 虚拟表类请求查询:基于特定的select 条件查询服务端的数据库表 • 存储过程类请求查询:执行服务端的存储过程返回数据 • 事件类请求查询:返回基于特定事件的查询结果 1.3 基于2.4 版本的查询 HL 7 v2.3.1之后的版本更清晰地将请求查询的方式与返回查询数据的方式分开,并且强调了“符合性声明”的存在。 HL 7继续支持存储过程、事件查询和虚拟表查询的语义,但推荐使用新的查询方式,即按参数查询(QBP),使语法更清晰。 QBP查询的目的是在一个精确的一致性声明的框架内统一存储过程、事件和虚拟表查询的语义。 同时该标准仍可以继续使用最初模式查询(QRD/QRF),但使用新的查询形式可以更清楚地解释其语义。 2 符合性声明Conformance Statement 符合性声明很像我们熟悉的“接口文档”,在其中定义了哪些数据是可用的,数据将如何被返回,以及哪些变量可以在查询中被赋值以及其约束范围。典型的符合性声明应由下面的内容组成: 介绍部分包含标题、触发事件、模式、特点和目的 查询语法 返回语法 输入规范和注释 返回控制 输出规范和注释 更多符合性声明文档的解释和例子可以参考HL7官方文档。 3 消息格式 正如前面说的,HL 7 v2.3.1之后的版本更清晰地将请求查询的方式与返回查询数据的方式分开,这里重点介绍这两个不同的方式。每种消息的示例会在文章最后给出。 3.1 返回查询结果数据 HL7 定义了三种返回查询结果数据的格式:分段、表格或显示格式。分段格式的响应是由一组HL7段组成。每个查询都会在符合性声明中定义它将返回的HL7片段每个字段的含义。表格式响应是以一组行的形式返回数据,每行一个RDT段。最后的显示查询是以DSP段承载返回数据。 3.1.1 分段响应格式 分段格式的返回是HL7提供数据的传统方式。服务器通过返回HL7段的方式对查询作出响应。例如,对检验数据查询的响应的核心可能由以下分段语法定义。 { PID OBR [{OBX}]。 } 其中,病人信息将在PID段中返回,实验室检验结果在OBR和OBX段中返回。在这种模式下,服务器返回的消息通常与现有的非请求类HL7消息非常接近。 在为分段模式的返回内容定义一致性声明时,数据所有者必须决定它将返回的确切段语法。它应该在必要时阐明每个字段的含义、数据的数量,以及数据是可选的还是必须的。 3.1.2 表格响应格式 表格模式的返回是一个相当传统的由行和列组成的表格。行和列的具体含义会在在该查询的符合性声明中被完整的定义。 当所返回的信息相对简单时,以表格的方式是合适的。但对于涉及复杂的结果嵌套的检验报告来说,它并不是很合适。同时典型的HL7段或段组所携带的数据也可以被建模为一个表格。例如,ADT系统可以将PID、NK1和PV1段拼接到一张表中。但另一方面,在一个单一的表格中包含一个病人的所有就诊历史是很困难的。 3.1.3 显示响应格式 一些情况下,返回的信息不需要被接收的系统保存在数据库里,而只要显示出来就行。 显示响应实际上并不代表组织数据的正式风格。它代表了一个决定,即返回的内容为人类阅读而不是为计算机使用的数据格式。从逻辑上讲,以显示模式返回的内容可能是HL7段模式携带的复杂数据,也可能是由表格模式响应携带的简单记录。 3.2 请求格式 前面介绍的是三种返回查询客户端的方式,现在这里介绍HL7 推荐的三种不同的查询请求方式。 3.2.1 简单参数查询 在简单参数查询中,输入参数在HL7段中连续按顺序传递。 服务器只需要从相应的HL7段中读取它们,并将它们插入到内部函数中执行查询操作。 这是查询的最基本形式,服务端在符合性声明中指定一个固定的参数列表,调用查询时,客户端为每个参数传递一个特定值,这就类似于对数据库调用存储过程并传入参数。 MSH|^~\&|FEH.IVR|HUHA.CSC|HUHA.DEMO||199902031135-0600||QBP^Z58^QBP_Q13|1|D|2.5.1 QPD|Z58^Pat Parm Qry 2|Q502|111069999 RCP|I 3.2.2 示例查询 按示例查询(QBE)是按参数查询(QBP)的扩展,其通过在原本定义的段中发送搜索参数来传递搜索参数,而不是作为QPD段中的字段传递。 例如,如果想要使用QBE执行“查找候选者”查询,则将查询参数保存在PID和或PD 1字段中,并将其中不是查询参数的那些字段留空。 例如,如果宗教不是查询参数之一,则当在查询中发送PID时,PID-17将被留空。 HL 7消息原本定义中不出现的参数,如搜索算法、置信度等, 将继续在QPD段中携带,就像它们在按参数查询一样。 可用作查询参数的确切段和字段将在查询的符合性声明中指定。 MSH|^~\&|FEH.IVR|HUHA.CSC|HUHA.DEMO||199902031135-0600||QBP^Z58^QBP_Q13|1|D|2.5.1 QPD|Z58^Pat Parm Qry 2|Q502 PID|||111069999 RCP|I 3.2.3 选择性查询QSC(Query selection criteria) 第三个方式称为选择性查询QSC,因为它使用了QSC数据类型,而QSC数据类型一般在虚拟表查询中使用。 服务端的符合性声明中将定义客户端可能在表达式中使用的所有变量。 在运行时,客户端能够通过构造类似于“树”节点的方式定义可用的输入参数。 服务端要执行查询,必须可以在运行时分析和解析查询表达式。 服务端可以将输入表达式翻译成它本地可访问数据的语言。 客户端的复杂表达式类似于针对关系数据库的SQL select语句。 MSH|^~\&|FEH.IVR|HUHA.CSC|HUHA.DEMO||199902031135-0600||QBP^Q13^QBP_Q13|1|D|2.5.1 QPD|Z999^Pat Sel Qry 1|Q501|@MedicalRecordNo^EQ^111069999 RCP|I 3.2.4 三种请求格式比较 在使用QSC时,客户端可以选择所提供的任何或所有变量,并且可以为每个变量指定任何允许的运算符和值。 相比之下,在简单参数查询或示例查询中,客户端必须为所提供的所有变量提供值。 简单参数查询易于解析和处理,查询传入参数是预定义好以及有着固定的顺序。 类似地,示例查询也较容易处理,因为参数将出现在定义的段中的固定位置。 相反的,选择性查询需要更多的解析和处理,因为它的灵活性和参数的可选性。 因此,虽然选择性查询向客户端提供了更多功能,但是它对于服务端的处理来说是更繁琐的,简单参数查询和示例查询向客户端提供较少的功能,但通常更易于服务端实现,并且它们往往是基于服务端现有存储过程而提供的。 4 查询返回消息示例 4.1 简单参数查询(QBP)/分段模式返回(RSP) 用户希望查询从1998年5月31日开始到1999年5月31日结束的时间段内,为病历号为“555444222111”的患者分配的所有药物。 使用以下简单参数查询请求消息: MSH|^~\&|PCR|Gen Hosp|PIMS||199811201400-0800||QBP^Z81^QBP_Q11|ACK9901|P|2.5.1|||||||| QPD|Z81^Dispense History^HL7nnnn|Q001|555444222111^^^MPI^MR||19980531|19990531| RCP|I|999^RD| 药房系统识别属于Adam Everyman的医疗记录号“555444222111”,并定位从1998年5月31日开始到1999年5月31日结束的时间段内有4次处方配药,并返回以下RSP消息: MSH|^~\&|PIMS|Gen hosp|PCR||199811201400-0800||RSP^Z82^RSP_Z82|8858|P|2.5.1|||||||| MSA|AA|ACK9901| QAK|Q001|OK|Z81^Dispense History^HL7nnnn|4| QPD|Z81^Dispense History^HL7nnnn|Q001|555444222111^^^MPI^MR||19980531|19990531| PID|||555444222111^^^MPI^MR||Everyman^Adam||19600614|M||C|2222 HOME STREET^^Oakland^CA^94612||^^^^^555^5552004|^^^^^555^5552004|||||34313 2266|||N||||||||| ORC|RE||89968665||||||199805121345-0700|||77^Hippocrates^Harold^H^III^DR^MD||^^^^^555^ 5552104|||||| RXE|1^BID^^19980529|00378112001^Verapamil Hydrochloride 120 mg TAB^NDC|120||mgm|||||||||||||||||||||||||| RXD|1|00378112001^Verapamil Hydrochloride 120 mg TAB^NDC |199805291115-0700|100|||1331665|3||||||||||||||||| RXR|PO|||| ORC|RE||89968665||||||199805291030-0700|||77^Hippocrates^Harold^H^III^DR^MD||^^^^^555^555-5001|||||| RXE|1^^D100^^20020731^^^TAKE 1 TABLET DAILY --GENERIC FOR CALANSR|00182196901^VERAPAMIL HCL ER TAB 180MG ER^NDC |100||180MG|TABLETSA|||G|||0|BC3126631^CHU^Y^L||213220929|0|0|19980821||| RXD|1|00182196901^VERAPAMIL HCL ER TAB 180MG ER^NDC|19980821|100|||213220929|0|TAKE 1 TABLET DAILY --GENERIC FOR CALANSR|||||||||||| RXR|PO|||| ORC|RE||235134037||||||199809221330-0700|||8877^Hippocrates^Harold^H^III^DR^MD||^^^^^555^555-5001||||||RXD|1|00172409660^BACLOFEN 10MG TABS^NDC|199809221415-0700|10|||235134037|5|AS DIRECTED|||||||||||| RXR|PO|||| ORC|RE||235134030||||||199810121030-0700|||77^Hippocrates^Harold^H^III^DR^MD||^^^^^555^555-5001|||||| RXD|1|00054384163^THEOPHYLLINE 80MG/15ML SOLN^NDC|199810121145-0700|10|||235134030|5|AS DIRECTED|||||||||||| RXR|PO 4.2 简单参数查询(QBP)/表格模式返回(RTB) 用户希望获取病历号为“555444222111”的患者的身份信息。使用简单参数查询 MSH|^~\&|PCR|GenHosp|MPI||199811201400-0800||QBP^Z91^QBP_Q13|8699|P|2.5.1|||||||| QPD|Z91^WhoAmI^HL7nnnn|Q0009|555444222111^^^MPI^MR RCP|I|999^RD| RDF|PatientList^CX^20~PatientName^XPN^48~Mother’sMaidenName^XPN^48~DOB^TS^26~Sex^IS^1~Race^CE^80| 以表格方式返回查询结果: MSH|^~\&|MPI|GenHosp|PCR||199811201400-0800||RTB^Z92^RTB_K13|8699|P|2.5.1|||||||| MSA|AA|8699| QAK|Q0009|OK|Z91^WhoAmI^HL7nnnn|1^1| QPD|Z91^WhoAmI^HL7nnnn|Q0009|555444222111^^MPI^MR RDF|PatientList^CX^20~PatientName^XPN^48~Mother’sMaidenName^XPN^48~DOB^TS^26~Sex^IS^1~Race^CE^80| RDT|555444222111^^^MPI^MR|Everyman^Adam||19600614|M|| 4.3 简单参数查询(QBP)/显示模式返回(RDY) 用户希望了解从1998年5月31日开始到1999年5月31日结束的时间段内,为病历号为“555444222111”的患者分配的所有药物。请求消息: MSH|^~\&|PCR|Gen Hosp|PIMS||199909171400-0800||QBP^Z97^QBP_Q15|8699|P|2.5.1|||||||| QPD|Z97^DispenseHistoryDisplay^HL7nnnn|Q005|555444222111^^^MPI^MR||19980531|19990531| RCP|I|999^RD| 返回消息: MSH|^~\&|PIMS|Gen Hosp|PCR||199909171401-0800||RDY^Z98^RDY_K15|8858|P|2.5.1|||||||| MSA|AA|8699| QAK|Q005|OK|Z97^DispenseHistoryDisplay|4 QPD|Z97^DispenseHistoryDisplay^HL7nnnn|Q005|555444222111^^^MPI^MR||19980531|19990531| DSP|| GENERAL HOSPITAL – PHARMACY DEPARTMENT DATE:09-17-99 DSP|| DISPENSE HISTORY REPORT Page 1 DSP||MRN Patient Name MEDICATION Dispense DISP-DATE DSP||555444222111 Everyman,Adam VERAPAMIL HCL 120 mg TAB 05/29/1998 DSP||555444222111 Everyman,Adam VERAPAMIL HCL ER TAB 180MG 08/21/1998 DSP||555444222111 Everyman,Adam BACLOFEN 10MG TABS 09/22/1998 DSP||555444222111 Everyman,Adam THEOPHYLLINE 80MG/15ML SOL 10/12/1998 DSP|| << END OF REPORT >> 4.4 示例查询(QBP)/表格模式返回(RTB) 客户希望查看人口统计学资料如下的患者列表: 姓名:张三 性别:男 生日: 1948年12月11日 客户希望使用peekaboo算法,以及满足80%置信水平。 请求消息: MSH|^~\&|PCR|GenHosp|MPI||199811201400-0800||QBP^Z77^QBP_Q13|8699|P|2.5.1|||||||| QPD|Z77^find_candidates^HL7nnnn|Q0001|peekaboo|80| PID|||||张&三||19481211|M RCP|I|25^RD| RDF|PatientList^CX^20~PatientName^XPN^48~Mother’sMaidenName^XPN^48~DOB^TS^26~Sex^IS^1~Race^CE^80| 返回消息: MSH|^~\&|MPI|GenHosp|PCR||199811201400- 0800||RTB^Z78^RTB_R13|8699|P|2.5.1|||||||| MSA|AA|8699| QAK| QPD|Z77^find_candidates^HL7nnnn|Q0001|peekaboo|80| RDF|PatientList^CX^20~PatientName^XPN^48~Mother’sMaidenName^XPN^48~DOB^TS^26~Sex^IS^1~Race^CE^80| RDT|555444222111^^^MPI&KP.NCA&L^MR|张^三||19481211|M|| 4.5 选择性查询/表格模式返回(RTB) 用户希望了解从1998年5月31日开始到1999年5月31日结束的时间段内,为病历号为"555444222111"的患者分配的所有药物。 将生成以下消息。 请求消息: MSH|^~\&|PCR|Gen Hosp|PIMS||199811201400-0800||QBP^Z95^QBP_Q13|8699|P|2.5.1|||||||| QPD|Z95^Dispense Information^HL7nnnn|Q504|PID.3^EQ^55544422211^AND~RXD.3^GE^19980531^AND~RXD.3^LE^19990531 RCP|I|999^RD| RDF|3|PatientList^ST^20~PatientName^XPN^48~OrderControlCode^ID^2~OrderingProvider^XCN^120~MedicationDispensed^ST^40~DispenseDate^TS^26~QuantityDispensed^NM^20| 返回消息: MSH|^~\&|PIMS|Gen Hosp|PCR||199811201400-0800||RTB^Z96^RTB_K13|8858|P|2.5.1|||||||| MSA|AA|8699| QAK|Q001|OK|Z95^Dispense Information^HL7nnnn|4 QPD|Z95^Dispense Information^HL7nnnn|Q504|PID.3^EQ^55544422211^AND~RXD.3^GE^19980531^AND~RXD.3^LE^19990531 RDF|3|PatientList^ST^20~PatientName^XPN^48~OrderControlCode^ID^2~OrderingProvider^XCN^120~MedicationDispensed^ST^40~DispenseDate^TS^26~QuantityDispensed^NM^20| RDT|555444222111^^^MPI^MR|Everyman^Adam|RE|77^Hippocrates^Harold^H^III^DR^MD |525440345^Verapamil Hydrochloride 120 mg TAB^NDC |199805291115-0700|100 RDT|555444222111^^^MPI^MR|Everyman^Adam|RE|77^Hippocrates^Harold^H^III^DR^MD |00182196901^VERAPAMIL HCL ER TAB 180MG ER^NDC|19980821-0700|100 RDT|555444222111^^^MPI^MR|Everyman^Adam|RE|88^Seven^Henry^^^DR^MD|00172409660^BACLOFEN 10MG TABS^NDC |199809221415-0700|10 RDT|555444222111^^^MPI^MR|Everyman^Adam|RE|99^Assigned^Amanda^^^DR^MD|00054384163^THEOPHYLLINE 80MG/15ML SOLN^NDC|199810121145-0700|10 5 InterSystems IRIS 对于HL7 V2.x 的支持 5.1 内置 HL7 V2.x 文档 方便随时查看HL7 V2.x 各个字段、节点的含义、限制以及可用字典表定义 可以方便的打开一个HL7 V2.x 文档,鼠标悬停就可以看到该字段的解释: 5.2 互操作性 5.2.1 内置的数据转化工具:使用鼠标拖拽就可以进行数据格式的转换 5.2.2 HL7 消息路由编辑器: 图形化页面设置,方便根据HL7 消息字段内容将消息发送到不同目标 5.2.3 消息追踪器:方便追踪在平台中的经过数据的流向
文章
姚 鑫 · 七月 10, 2022

第一章 嵌入式Python概述(一)

# 第一章 嵌入式Python概述(一) 嵌入式 `Python` 允许将 `Python` 与 IRIS 数据平台的本地编程语言 `ObjectScript` 一起使用。当使用嵌入式 `Python` 在 `IRIS` 类中编写方法时,`Python` 源代码与编译后的 `ObjectScript` 代码一起编译为在服务器上运行的目标代码。与使用网关或 `Python`的 `Native SDK` 相比,这允许更紧密的集成。还可以导入 `Python` 包,无论它们是自定义的还是公开的,并在`ObjectScript` 代码中使用它们。 `Python` 对象是 `ObjectScript` 中的一等公民,反之亦然。 - 使用来自 `ObjectScript` 的 `Python` 库 - 此方案假设 `ObjectScript` 开发人员,并且希望利用 `Python` 开发人员社区可用的众多 `Python` 库的强大功能。 - 从 `Python` 调用 `IRIS API` — 此方案假定您是一名 `Python` 开发人员,对 IRIS 不熟悉,并且想知道如何访问 `API`。 - 一起使用 `ObjectScript` 和 `Python` — 这个场景假设在一个由 `ObjectScript` 和 `Python` 开发人员组成的混合团队中,并且想知道如何一起使用这两种语言。 将需要 `2021.2` 或更高版本的正在运行的 `IRIS` 实例,以及取决于操作系统的一些先决条件。还需要知道如何访问终端,即 `IRIS` 命令行工具。 本文档中的一些示例使用来自 `GitHub` 上 `Samples-Data` 存储库的类:`https://github.com/intersystems/Samples-Data`。 建议创建一个名为 `SAMPLES` 的专用命名空间并将样本加载到该命名空间中。如果想查看或修改示例代码,则需要设置集成开发环境 (`IDE`)。推荐使用 `Visual Studio Code`。 本篇并不试图提供嵌入式 `Python` 或使用 `IRIS` 进行编程的全面概述。 # 使用 ObjectScript 中的 Python 库 使用 `Embedded Python`,`ObjectScript` 开发人员可以轻松地使用来自 `IRIS` 的众多可用 `Python` 库(通常称为“包”)中的任何一种,从而无需开发自定义库来复制现有功能。 `IRIS` 在 `/mgr/python` 目录中查找已安装的 `Python` 包 从 `ObjectScript` 准备 `Python` 包以供使用是一个两步过程: 1. 从命令行,从 `Python` 包索引(或其他索引)安装所需的包。 2. 在 `ObjectScript` 中,导入已安装的包以加载包并将其作为对象返回。然后,可以像使用实例化的 `ObjectScript` 类一样使用该对象。 ## 安装 Python 包 在将 `Python` 包与 `Embedded Python` 一起使用之前,请从命令行安装。使用的命令会有所不同,具体取决于使用的是 `Windows` 还是基于 `UNIX` 的系统。 建议将软件包安装到目录 `/mgr/python`。 在 `Windows` 上,使用 `/bin` 目录中的 `irispip` 命令:`irispip install --target \mgr\python `. 例如,可以在 `Windows` 机器上安装 `numpy` 包,如下所示: ```java C:\InterSystems\IRIS\bin>irispip install --target C:\InterSystems\IRIS\mgr\python numpy ``` 在基于 `UNIX` 的系统上,使用 `pip3` 命令:`pip3 install --target /mgr/python `。 例如,可以在 Linux 机器上安装 `numpy` 包,如下所示: ```java $ pip3 install --target /InterSystems/IRIS/mgr/python numpy ``` 注意:如果基于 `UNIX` 的系统没有安装 `pip3`,请使用系统的包管理器安装包 `python3-pip`。 ## 导入 Python 包 `%SYS.Python` 类包含从 `ObjectScript` 使用 `Python` 所需的功能。可以在任何 `ObjectScript` 上下文中使用 `%SYS.Python`,例如类、终端会话或 `SQL`。 要从 ObjectScript 导入 `Python` 包或模块,请使用 `%SYS.Python.Import()` 方法。 例如,假设在 `USER` 命名空间中,以下命令会在终端中导入数学模块: ```java USER>set pymath = ##class(%SYS.Python).Import("math") ``` 数学模块与标准 `Python` 版本打包在一起,因此无需在导入之前安装它。通过在 `pymath` 对象上调用 `zwrite`,可以看到它是内置数学模块的一个实例: ```java USER>zwrite pymath pymath=1@%SYS.Python ; ; ``` 注意:包是 `Python` 模块的集合,但是当导入包时,创建的对象始终是模块类型。 现在,可以像访问任何 `ObjectScript` 对象一样访问数学模块属性和方法: ```java USER>write pymath.pi 3.141592653589793116 USER>write pymath.factorial(10) 3628800 ``` # 示例 此示例使用 `geopy` 包来访问 `OpenStreetMap` 的 `Nominatim` 地理编码工具。地理编码是获取基于文本的位置描述(例如地址或地名)并返回地理坐标(例如纬度和经度)以精确定位地球表面位置的过程。 首先,从命令行安装 `geopy`,如下 `Windows` 示例所示: ```java C:\InterSystems\IRIS\bin>irispip install --target C:\InterSystems\IRIS\mgr\python geopy Collecting geopy Using cached geopy-2.2.0-py3-none-any.whl (118 kB) Collecting geographiclib=1.49 Using cached geographiclib-1.52-py3-none-any.whl (38 kB) Installing collected packages: geographiclib, geopy Successfully installed geographiclib-1.52 geopy-2.2.0 ``` 在基于 UNIX 的系统上,使用: ```java $ pip3 install --target /InterSystems/IRIS/mgr/python geopy ``` 然后在终端中运行以下命令来导入和使用模块: ```java USER>set geopy = ##class(%SYS.Python).Import("geopy") USER>set args = { "user_agent": "Embedded Python" } USER>set geolocator = geopy.Nominatim(args...) USER>set flatiron = geolocator.geocode("175 5th Avenue NYC") USER>write flatiron.address Flatiron Building, 175, 5th Avenue, Flatiron District, Manhattan, New York County, New York, 10010, United States USER>write flatiron.latitude _ "," _ flatiron.longitude 40.74105919999999514,-73.98964162240997666 USER>set cityhall = geolocator.reverse("42.3604099,-71.060181") USER>write cityhall.address Government Center, Cambridge Street, Downtown Crossing, West End, Boston, Suffolk County, Massachusetts, 02203, United States ``` 此示例将 `geopy` 模块导入 `ObjectScript`。然后它使用 `Nominatim` 模块创建一个地理定位器对象。该示例使用地理定位器的 `geocode()` 方法在给定字符串的情况下查找地球上的位置。然后它调用 `reverse()` 方法来查找给定纬度和经度的地址。 需要注意的一点是 `Nominatim()` 采用命名关键字参数,ObjectScript 不直接支持这种结构。解决方案是创建一个包含参数列表的 `JSON` 对象(在这种情况下将 `user_agent` 关键字设置为值“嵌入式 Python”),然后使用 `args...` 语法将其传递给方法。 与前面示例中导入的数学模块相比,对 `geopy` 对象调用 `zwrite` 表明它是安装在 `C:\InterSystems\iris\mgr\python` 中的 `geopy` 包的一个实例: ```java USER>zwrite geopy geopy=2@%SYS.Python ; ; ```
文章
yaoguai wan · 九月 30, 2022

IRIS架构的浅显理解以及windows10、docker安装IRIS Health详解流程及部分问题浅析

前言 本人因技术需要,了解到了IRIS Health产品,在听了产品介绍会之后,感觉该产品是否有趣,并且比较符合自己目前的需求,因此大概了解了下IRIS的架构。以下是本人的浅显理解,如有错误之处欢迎讨论。 本人从产品介绍和社区的文档中,了解到IRIS的大概功能分类 InterSystems IRIS是一款数据平台,适用于软甲开发人员 基于FHIR 整合医疗全流程数据,通过机器学习和人工智能分析 业务优化 数据管理 Sharding数据分片技术 分布式架构 IRIS互操作性 数据分析能力 IRIS对FHIR的支持 机器学习与自动化 其中结合到自己想要研究的领域,想要探索是否可以利用该产品并结合其他工具开发一套通用的专病数据库构建及应用方法。 我目前对该产品的初步定位是对自行提供的数据集合的存储和处理,其中提供包括编码规范、高效存储架构、编程接口、算法在内的辅助工具。针对数据的处理和应用,该产品做的很完善,但是对于数据的获取,例如是否支持extract-transformation-load功能,或者能否利用自定义编程接口实现流批一体化数据抽取。根据上述两个问题,第一步需要安装该产品,所以本人根据社区官网上相关的安装教程进行了安装实验,在此期间发现了社区的教程有些简单并且有些关键点很容易被忽视本人在安装过程中就遇到过一个和系联的工程师讨论了一段时间之后才发现的,因此想与大家分享下安装过程中遇到的一些问题,避免一些问题后,安装过程还是很简单的,傻瓜式操作,后面可以分享下我的安装命令。 问题分享 首先注意产品的平台基本要求,这是平台操作手册上讲的,一定要严格遵守否则会报各种奇怪的错误,IRIS对不同版本的操作系统间的兼容性还有比较严格的,要注意,操作系统和后面容器的配合,我遇到的一个问题就是操作系统的版本和容器的版本不一致导致安装失败。 1、使用docker安装IRIS Health 安装平台介绍:Ubuntu18.04及docker 20.10 要求满足的容器版本20.04 安装过程中遇到的问题:注意linux的发行版本,必须严格遵守,本人在安装过程中最开始使用的是Ubuntu20,使用docker pull containers.intersystems.com/intersystems/iris-community:2022.1.0.209.0 命令可以顺利拉取镜像,但是在启动容器的过程中,一直失败,参考社区中有关启动失败时,设置cpu等限制sudo docker run --name my-iris --cpuset-cpus 0-7 -d store/intersystems/iris-community:2021.2.0.649.0,同样失败,最后发现是ubuntu的版本问题导致的,我最开始只关注了docker的版本,后来使用ubuntu18.04,即可顺利拉取镜像并启动容器。 附:1、查看ubuntu版本命令 cat /proc/version 2、查看docker版本命令 dicker version 2、windows安装IRIS Health 安装平台介绍:windows10(64位) 硬件介绍:16核cpu,32G运存,500G存储。注:因为本人采用的是虚拟机,所以各项配置初始时调整的很高,安装后发现社区版对各种性能有限制,例如cpu最多用8个,内存限制等,所以硬件环境无需太高,但是也不能太低,像windows10这种,首先要保证系统可以顺利安装, 在配置方面,Win10的配置要求其实并不高,具体配置如下: CPU:1GHz或更快的处理器   RAM:1GB(32位)或2GB(64位)   HDD:16GB(32位操作系统)或20GB(64位操作系统)   显卡:DirectX 9或更高版本(包含WDDM 1.0驱动程序)   显示器:1024x600分辨率   简而言之,只要满足或者高于以上要求即可安装。 但是考虑到iris还要占用很大一块运存和存储,所以windows10最少得4G运存和200G存储,这样系统运行不会太卡,iris运行速度也算可以(这个速度我只测试没有运行处理任务的速度)。 安装过程中遇到的问题:1、注意windows10的位数不要安装32位的操作系统,同时IRIS不支持windows7,一开始没注意,一直安装失败。 windows确定操作系统版本以及位数的命令不在赘述,google一下,很简单。 下一步计划 平台建好以后开始考虑数据的获取,下一步首先继续熟悉iris产品架构,然后尝试能否实现ETL与IRIS的对接。 好文,期待下一步分享! linux的安装可以参考一下马老师写的这篇文章https://cn.community.intersystems.com/node/516631,很nice
文章
姚 鑫 · 四月 5, 2021

第十七章 使用触发器

# 第十七章 使用触发器 本章介绍如何在Intersystems SQL中定义触发器。触发器是响应某些SQL事件执行的代码行。本章包括以下主题: # 定义触发器 有几种方法可以为特定表定义触发器: - 在将投影到SQL表的持久性类定义中包含触发定义。例如,`MyApp.person`类的此定义包括`Loggevent`触发器的定义,在每个成功的数据插入到`MyApp.person`表之后,将在每个成功的数据插入后调用: ```java Class MyApp.Person Extends %Persistent [DdlAllowed] { // ... Class Property Definitions Trigger LogEvent [ Event = INSERT, Time = AFTER ] { // Trigger code to log an event } } ``` - 使用SQL创建触发命令创建触发器。这在相应的持久性类中生成触发对象定义。 SQL触发器名称按照标识符命名约定进行操作。 IntersystemsIris®数据平台使用SQL触发名称生成相应的触发类实体名称。 必须拥有`%create_trigger`管理级别权限来创建触发器。必须具有删除触发器的`%drop_trigger`管理级别权限。 **类的最大用户定义触发器数为200。** 注意:Intersystems Iris不支持收集投影的表上的触发。用户无法定义这样的触发器,并且作为子表的集合的投影不认为涉及该基本集合的触发。 Intersystems Iris不支持修改`Security.Roles`和`Security.Users`表的触发器。 # 触发器的类型 触发器由以下内容定义: - 导致它执行的事件类型。触发器可以是单个事件触发器或多事件触发。定义单个事件触发器以在指定表上发生插入,更新或删除事件时执行。定义多事件触发器以执行当在指定的表中发生多个指定的事件中的任何一个时执行。可以使用类定义或创建触发命令定义插入/更新,更新/删除或插入/更新/删除多事件触发器。事件类型在Class定义中指定了所需的事件触发器关键字。 - 触发器执行的时间:在事件发生之前或之后。 这是由可选的`Time trigger`关键字在类定义中指定的。 默认为`Before`。 - 可以将多个触发器与同一事件和时间相关联;在这种情况下,可以使用`order trigger`关键字来控制触发多个触发器的顺序。先触发顺序较低的触发器。 如果多个触发器具有相同的`Order`值,则不指定它们的触发顺序。 - 可选的`Foreach trigger`关键字提供了额外的粒度。 该关键字控制触发器是每一行触发一次(`Foreach = row`),还是每一行或对象访问触发一次(`Foreach = row/object`),还是每语句触发一次(`Foreach = statement`)。 没有`Foreach trigger`关键字定义的触发器每一行触发一次。 如果触发器是用`Foreach = row/object`定义的,那么触发器也会在对象访问期间的特定点被调用,如本章后面所述。 可以使用`INFORMATION.SCHEMA.TRIGGERS`的`ACTIONORIENTATION`属性列出每个触发器的`Foreach`值 下面是可用的触发器及其等价的回调方法: - `BEFORE INSERT` (等价于 `%OnBeforeSave()`) - `AFTER INSERT` (等价于 `%OnAfterSave()`) - `BEFORE UPDATE` (等价于 `%OnBeforeSave()`) - `AFTER UPDATE` (等价于 `%OnAfterSave()`) - `BEFORE UPDATE OF specified column(s)` - `AFTER UPDATE OF specified column(s)` - `BEFORE DELETE` (等价于 `%OnDelete()`) - `AFTER DELETE` (等价于 `%OnAfterDelete()`) 注意:当触发器执行时,它不能直接修改正在处理的表中的属性值。 这是因为InterSystems IRIS在字段(属性)值验证代码之后执行触发代码。 例如,触发器不能将LastModified字段设置为正在处理的行中的当前时间戳。 但是,触发器代码可以对表中的字段值发出更新。 更新执行自己的字段值验证。 ## AFTER Triggers 在`INSERT`、`UPDATE`或`DELETE`事件发生后执行`AFTER`触发器: - 如果`SQLCODE=0`(事件成功完成),InterSystems IRIS将执行`AFTER`触发器。 - 如果`SQLCODE`是负数(事件失败),系统间IRIS就不会执行`AFTER`触发器。 - 如果`SQLCODE=100`(没有发现要插入、更新或删除的行),则系统间IRIS执行`AFTER`触发器。 ## 递归触发器 触发器执行可以是递归的。 例如,如果表`T1`有一个对表`T2`执行插入操作的触发器,表`T2`也有一个对表`T1`执行插入操作的触发器。 当表`T1`有一个调用例程/过程的触发器,并且该例程/过程执行对`T1`的插入操作时,也可以发生递归。 触发器递归的处理取决于触发器的类型: - 行和行/对象触发器:InterSystems IRIS不阻止行触发器和行/对象触发器递归地执行。 处理触发器递归是程序员的责任。 如果触发代码不处理递归执行,则可能发生runtime ``错误。 - 语句触发器:InterSystems IRIS阻止`AFTER`语句触发器递归执行。 如果InterSystems IRIS检测到该触发器在执行堆栈中已经被调用,它将不会发出AFTER触发器。 没有错误发出; 触发器不会被第二次执行。 InterSystems IRIS不会阻止`BEFORE`语句触发器递归地执行。 在触发递归之前处理是程序员的责任。 如果`BEFORE`触发器代码不处理递归执行,可能会发生runtime ``错误。 # Trigger Code 每个触发器包含执行触发操作的一行或多行代码。 每当与触发器关联的事件发生时,SQL引擎就会调用这段代码。 如果触发器是使用CREATE触发器定义的,则可以用ObjectScript或SQL编写此操作代码。 (InterSystems IRIS将SQL编写的代码转换为类定义中的ObjectScript。) 如果触发器是使用Studio定义的,那么这个操作代码必须用ObjectScript编写。 因为触发器的代码不是作为过程生成的,所以触发器中的所有局部变量都是公共变量。 这意味着触发器中的所有变量都应该用一个新语句显式声明; 这可以防止它们与调用触发器的代码中的变量发生冲突。 ## %ok, %msg, and %oper 系统变量 - `%ok`:仅在触发器代码中使用的变量。 如果触发代码成功,它设置`%ok=1`。 如果触发代码失败,它设置`%ok=0`。 如果在触发器执行期间发出`SQLCODE`错误,InterSystems IRIS将设置`%ok=0`。 当`%ok=0`时,触发器代码中止,触发器操作和调用触发器的操作被回滚。 如果插入或更新触发器代码失败,并且表中定义了一个外键约束,InterSystems IRIS将释放外键表中相应行上的锁。 触发代码可以显式设置`%ok=0`。 这会创建一个运行时错误,中止触发器的执行并回滚操作。 通常,在`设置%ok=0`之前,触发器代码显式地将`%msg`变量设置为用户指定的字符串,用于描述这个用户定义的触发器代码错误。 `%ok`变量是一个必须显式更新的公共变量。 在完成非触发代码`SELECT`、`INSERT`、`UPDATE`或`DELETE`语句后,`%ok`的值与之前的值没有变化。 `%ok`仅在执行触发器代码时定义。 `%msg`:触发代码可以显式地将`%msg`变量设置为描述运行时错误原因的字符串。 设置变量`%msg`。 `%oper`:仅在触发器代码中使用的变量。 触发器代码可以引用变量`%oper`,该变量包含触发触发器的事件(插入、更新或删除)的名称。 ## {fieldname}语法 在触发器代码中,可以使用特殊的`{fieldname}`语法引用字段值(对于属于触发器关联的表的字段)。 例如,下面是`MyApp`中`LogEvent`触发器的定义。 `Person`类包含一个对`ID`字段的引用,如`{ID}`: ```java Class MyApp.Person Extends %Persistent [DdlAllowed] { // ... Definitions of other class members /// This trigger updates the LogTable after every insert Trigger LogEvent [ Event = INSERT, Time = AFTER ] { // get row id of inserted row NEW id,SQLCODE,%msg,%ok,%oper SET id = {ID} // INSERT value into Log table &sql(INSERT INTO LogTable (TableName, IDValue) VALUES ('MyApp.Person', :id)) IF SQLCODE
文章
Lele Yang · 六月 8, 2021

FAQ 常见问题系列--应用篇 升级系统后打开管理门户SMP报错5001

如果您在升级了系统之后,打开SMP时看到如下报错,ERROR #5001: 对象的服务器版本与客户端发送的版本不一致: %ZEN.Component.vgroup 一般情况下,这个报错是由浏览器缓存中残留的过期信息造成的,可以通过清除浏览器缓存来解决。 如果清除浏览器缓存之后仍然未能解决此问题,请您前往系统安装目录, 如:d:\InterSystems\IRIS\CSP\broker,查看css/js文件的时间戳在本次更新安装之后是否并没有相应地更新,如果没有,请您做以下尝试,1)在IRIS中更改数据库IRISLIB, 之前的版本是CACHELIB, 取消只读装载,方法如下,打开管理门户SMP, 系统管理->配置->本地数据库,选择要更改的数据库,取消"只读方式挂载",点击【保存】。2)重新编译%Zen组件,w ##class(%SYSTEM.OBJ).Compile("%ZEN.Component.vgroup")此时,再次前往,d:\InterSystems\IRIS\CSP\broker,会看到相关的css/js文件时间戳已经更新。3)恢复上述数据库的只读装载。 如果以上办法仍然未能解决您的问题,建议您联系WRC,寻求Support的进一步帮助。
公告
Claire Zheng · 三月 11, 2021

欢迎中文社区新版主@姚鑫

亲爱的社区开发者们, 我很高兴地向大家介绍一位我们的新版主@姚鑫 @姚鑫是东华医为的一名开发工程师。 以下是@姚鑫的自我介绍: 大家好,我非常热衷于与开发者们分享我的技术经验。作为一名移动端全栈开发工程师,我发表了《Caché 23种设计模式》《Caché 算法与数据结构》《Caché 从入门到精通》《疯狂 Caché》《Caché 命令大全》《Caché 函数大全》《Caché 变量大全》《Caché SQL 必知必会》等一系列文章集。 在CSDN(中国最大的开发者社区之一),我被认证为博客专家,CSDN优秀讲师。 在掘金网这个帮助开发者成长的知名社区,获得了2020年掘金十大年度人气作者称号。 我很荣幸能够加入InterSystems开发者社区(中文版)并参与到社区管理、运营中,希望可以同使用InterSystems技术的开发者更好地交流、共同成长。 感谢@姚鑫,恭喜你成为新版主! 感谢@姚鑫!期待更多大作! 欢迎新版主@姚鑫 欢迎新版主@姚鑫期待将来分享更多关于IRIS数据平台的经验