清除过滤器
文章
Jingwei Wang · 九月 20, 2022
在虚拟化环境中使用镜像,构成镜像的InterSystems IRIS实例被安装在虚拟主机上,创造了一个混合的高可用性解决方案,将镜像的优点与虚拟化的优点结合起来。镜像通过自动故障切换对计划内或计划外的故障提供即时响应,而虚拟化HA软件在计划外的机器或操作系统故障后自动重新启动承载镜像成员的虚拟机。这允许失败的成员迅速重新加入镜像,充当备份(或在必要时作为主机)。
当镜像被配置在虚拟化环境中时,请参考以下建议:
故障转移成员的虚拟主机和备机不可以配置在同一台物理机上。
为了避免单点存储故障,故障转移成员上的InterSystems IRIS实例所使用的存储应永久隔离在不同磁盘组或存储阵列的独立数据存储中。
在虚拟化平台层面上进行的一些操作,如备份或迁移,可能会导致故障转移成员长时间没有反应,从而导致不需要的故障转移或不理想的警报频率。为了解决这个问题,你可以增加QoS超时设置。
在进行导致故障转移成员连接中断的计划性维护操作时,你可以暂时停止备份上的镜像,以避免不必要的故障转移和警报。
在镜像成员上必须非常谨慎地使用快照管理,因为将一个成员恢复到早期的快照,既会删除该成员的最新状态(例如,自拍摄快照以来,该成员可能已经从主机变为备机),也会删除其他成员仍然拥有的日志数据。 使用虚拟机备份恢复镜像时请特别注意:
被恢复到早期快照的故障转移成员只能从power-off 状态下恢复;从power-on 状态下恢复会造成两个故障转移成员同时作为主机的可能性。
如果被恢复到早期快照的故障转移成员在没有获得自快照以来创建的所有日志数据的情况下成为主要成员--例如,因为它被迫成为主要成员--所有其他镜像成员必须被重建(如需重建镜像,请联系WRC)。
文章
Michael Lei · 十二月 27, 2022
一个简单的生产配置,使 FHIR 交易捆绑包能够通过 Box 和 Dropbox 加载到 InterSystems® FHIR® 服务器中。使用包含的 MFT 连接组件和 14 行自定义业务流程,此生产配置会将您的交易捆绑包处理到 FHIR 资源,以便立即使用,就像哈利·波特的魔法一样。
我首先会展示该生产配置的短视频导览、MFT 连接以及 IRIS 上 Box 和 Dropbox 的 Oauth2 应用配置,接下来循序渐进地展示一些步骤,让您使用您喜欢的任何 MFT 供应商以及您选择的任何工作流程、桌面、API 或 Web 控制台拖放操作。
一些陷阱:
OAUTH2 回调需要使用 IRIS 的 SSL 端点来提供重定向… 最好在 Health Connect Cloud 上尝试一下!
Dropbox for Business 在基于团队的令牌方面面临挑战,个人 Dropbox 则运行良好。 这不是无法忍受的情形,但需要一些耐心。
配置 MFT 连接时,注意 Dropbox 的 baseurl 上的“/”(确保它存在)。
对于 Box 和 DropBox 的路径,MFT 出站适配器都需要具有结尾“/”。
现在,鉴于以上获奖的 OBS 支持的内容可能有不足之处,如果 InterSytems 文档还不够,下面是需要遵循的步骤。
步骤概览:
将 FHIRDrop 或 FHIRBox 应用创建到一个点,然后停止! (协作和倾听)
在您的 InterSystems FHIR 服务器、HealthConnect 或 I4H 上配置 MFT 连接。
完成您的 FHIRDrop 或 FHIRBox 应用,提供来自 MFT 连接的重定向 URL。
授权您的 MFT 连接。
构建您的生产配置。
正常拖放
创建 FHIRDrop 或 FHIRBox 应用
这里的想法是在每个 Box 和 Dropbox 开发者控制台中“开始”您的应用配置,这会让您看到客户端 ID 和客户端密码,然后让选项卡挂起并移动到 IRIS MFT 连接。
(协作和倾听)只需收集您的客户端 ID 和客户端密码,挂起浏览器选项卡,然后继续:
配置 MFT 连接
基 URL:https://api.box.com/2.0
基 URL:https://api.dropboxapi.com/2/(注意结尾斜杠)
完成应用注册
现在,回到应用注册并完成应用。确保插入上述步骤中的重定向 URL,并添加与 file.read、file.write 有关的范围。
授权您的托管文件传输连接
回到您的托管文件传输连接并通过调用“Get Access Token”(获取访问令牌)来“授权”连接。
构建您的生产配置
生产配置
以下是自定义业务流程,生产配置的源代码:https://gitlab.com/isc_cloud/fhir-drop-fhir-box-mft-2-fhir
正常拖放!
现在,获取 FHIR!

文章
Michael Lei · 五月 24, 2021
作为一个软件架构师,如果要设计一个企业级的架构来满足当前的业务需求时,你需要达到5级的水平,这是一个巨大的挑战。有了InterSystems IRIS。这是有可能的。通过1个产品,你可以得到SQL + NoSQL + ESB + BI + Open Analytics + Real Time Virtual cubes + NLP + AutoML + ML(使用Python)和高级云支持 + Sharding支持。
以Oracle为例,你需要Oracle数据库+ Oracle NoSQL+ Oracle BI+ Oracle SOA套件+一些附加组件,如R、Partitioning和RAC以及一些Oracle云产品的NLP和AI功能。对于开源架构、IBM或者其他友商来说,也是类似的,你需要的组件只可能更多。
医疗机构在数字化转型在技术上最大的挑战之一,就是技术栈过于丰富、快速变化以及对人的技术要求很高(这也是为什么全栈工程师最值钱的原因);一个统一、高效、稳定、现代化、专为医疗定制的一体化数据平台能帮助客户很好地解决这个问题。IRIS for Health 是您的不二选择。
文章
Michael Lei · 六月 1, 2022
InterSystems Production 监控是管理门户中的一个页面,用于显示当前运行的Production监控信息。我喜欢这个页面的样子,但这并不适合所有人。
2022年5月13日,我在开发者社区上看到这个帖子。
https://community.intersystems.com/post/creating-custom-monitoring-page
我同意马克的观点,Production监控很复杂。我想创建一个更漂亮干净的监控页面。
我开始着手制作一个利用类方法提供Production数据的 CSP(Cache Server Page)页面。
我与马克分享了我的第一次尝试。他根据自己的想法定制了这个页面。我喜欢他对页面的布局,使其在视觉上更有吸引力。我把他的设计整合到我的应用程序Production监控中。
我看到马克对业务服务的显示进行了过滤,只显示需要注意的服务。他在页面的底部添加了服务器的名称和它的镜像状态。
你可以在Open Exchange和当前的竞赛中找到我的Production监控器的应用。
https://openexchange.intersystems.com/package/production-monitor
公告
Claire Zheng · 十一月 22, 2021
大家好!
我们很高兴地向大家介绍我们开发者社区中文版的新版主——段海华( @Duan.Haihua)!
让我们以热烈的掌声欢迎段海华( @Duan.Haihua),以下是他的个人介绍。
段海华,东华医为利润中心副主管
以下是他的个人介绍 @Duan.Haihua:
– 我主持参加了多家医院的互联互通评审工作,并主持了互联互通成熟度测评(Health Information Connectivity Maturity Evaluation, HICME)工具的开发与维护。我有丰富的基于InterSystems产品和项目工作经验,对互联互通测评相关标准有较深刻的理解。
– 通过开发者社区,我希望能够帮助刚开始接触互联互通标准的开发者快速理解相应内容,分享互联互通测评过程中常见问题的解决方案,为开发者对系统标准化改造过程提供参考意见。
热烈欢迎!
感谢段海华的付出( @Duan.Haihua)!我们相信你会成为一名优秀的版主! 👏🏼 热烈欢迎👏!!!
文章
Michael Lei · 八月 18, 2022
Hi 社区的朋友们,大家好!
有时我们需要以编程方式自动将CSV数据从文件或者UR网址L导入到InterSystems IRIS。我们希望创建具有适当数据类型的类并导入数据。
我在Open Exchange上发布了一个模块csvgen,它正是这样做的。
如果你只需要将CSV文件导入IRIS,你可以这么做:
USER>do ##class(community.csvgen).Generate("/usr/data/titanic.csv",,"Data.Titanic")
Class name: Data.Titanic
Header: PassengerId INTEGER,Survived INTEGER,Pclass INTEGER,Name VARCHAR(250),Sex VARCHAR(250),Age INTEGER,SibSp INTEGER,Parch INTEGER,Ticket VARCHAR(250),Fare MONEY,Cabin VARCHAR(250),Embarked VARCHAR(250)
Records imported: 891
USER>
或者你的CSV文件在互联网上, 例如GitHub上面的新冠疫情数据 你可以这样获得数据:
USER>d ##class(community.csvgen).GenerateFromURL("https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_daily_reports/05-29-2020.csv",",","Data.Covid19")
Class name: Data.Covid19
Header: FIPS INTEGER,Admin2 VARCHAR(250),Province_State VARCHAR(250),Country_Region VARCHAR(250),Last_Update DATE,Lat MONEY,Long_ DOUBLE,Confirmed INTEGER,Deaths INTEGER,Recovered INTEGER,Active INTEGER,Combined_Key VARCHAR(250),Incidence_Rate DOUBLE,Case-Fatality_Ratio DOUBLE
Records imported: 3522
USER>
Installation安装
从ZPM安装:
USER>zpm
zpm:USER>install csvgen
csvgen 模块是打包了 CSV2CLASS method .
ObjectiveScript 的profile还不是很理想,欢迎各种建议意见。谢谢!
问题
water huang · 四月 22, 2021
m 里面如何获取cpu的序列号? 可以调用操作系统的命令来获取CPU序列号。例如在Cache' for Windows上,可以执行:SAMPLES>s args=3SAMPLES>s args(1)="CPU"SAMPLES>s args(2)="get"SAMPLES>s args(3)="ProcessorID"SAMPLES>d $ZF(-100,"","wmic",.args)ProcessorId0FABFBFF000506EX0FABFBFF000006EX0FABFBFF000006EX0FABFBFF000006EX 乔工,请问 $zf函数的使用,在哪里可以查询到它的所有使用说明 InterSystems Cache'InterSystems IRIS 刚才试了一下,这个不行呢 感谢你的回答 但是我用的是ensemble2016 是Windows吗?在Windows命令行,执行wmic CPU get ProcessorID,能得到CPU序列号吗? Ensemble 2016有点久,没有$ZF(-100)。用$ZF(-1):
https://cedocs.intersystems.com/ens20161/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fzf-1#RCOS_B80417 是Windows,在Windows命令行,执行wmic CPU get ProcessorID,能得到CPU序列号 刚才试了 还是不行 用$ZF(-1), 可以将OS命令输出保存到文件里。例如:w $ZF(-1,"wmic CPU get ProcessorID > c:\temp\cpuinfo.txt") 系统是windows 10 试了,不行,返回的值是1 返回值是1,说明报错了。确认一下是否OS命令写正确了。另外,输出不是看返回值,是看输出的文件 我直接复制的你写的这个命令。
文章
jieliang liu · 五月 15
各位开发者,大家好!
或许您不得不实现一些场景,这些场景不需要 FHIR 仓库但需要转发 FHIR 请求、管理响应,并且可能运行转换或在两者之间提取一些值。 在这里,您会找到一些可以使用 *InterSystems IRIS For Health* 或 *HealthShare Health Connect* 实现的示例。
在这些示例中,我使用了具有 [FHIR 互操作性适配器](https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=HXFHIR_fhir_adapter)和 `HS.FHIRServer.Interop.Request` 消息的互操作性生产配置。
第一个场景从头开始构建 FHIR 请求(可以来自文件,也可以来自 SQL 查询),然后将其发送到外部 FHIR 服务。
下一个场景是一种 FHIR 传递,用于将请求和响应传递到外部 FHIR 仓库,另外还管理 OAuth 令牌。
最后一个场景包括接收 FHIR 请求,然后将其转发到外部 FHIR 服务,但会提取信息或更改其间的某些字段。
您将在 Open Exchange 应用程序中找到实现细节 :)
希望这对您有用!
文章
姚 鑫 · 七月 6, 2021
# 第二十九章 从XML架构生成类
Studio提供了一个向导,该向导读取XML模式(从文件或URL),并生成一组支持XML的类,这些类对应于模式中定义的类型。
所有的类都扩展`%XML.Adaptor`。
指定一个包来包含类,以及控制类定义细节的各种选项。
向导还可以作为类方法使用,也可以使用该类方法。
在内部,SOAP向导在读取WSDL文档并生成web客户端或web服务时使用此方法;
注意:使用的任何XML文档的XML声明都应该指明该文档的字符编码,并且文档应该按照声明的方式进行编码。如果未声明字符编码,InterSystems IRIS将使用本书前面的“输入和输出的字符编码”中描述的默认值。如果这些默认值不正确,请修改XML声明,使其指定实际使用的字符集。
# 使用向导
要使用XML架构向导,请执行以下操作:
1. 选择 Tools > Add-Ins > XML Schema Wizard.

2. 在第一个屏幕上,指定要使用的XML模式。
做以下其中一项:
- 对于模式文件Schema File,选择Browse 以选择XML模式文件。
- 对于URL,指定模式的URL。

3. 选择Next。
下一个屏幕显示模式,以便可以验证选择了正确的模式。

4. 可选择以下选项:
- 保留空类Keep Empty Classes,它指定是否保留没有属性的未使用的类。
如果选择此选项,则不会在向导结束时删除此类;
否则,将删除它们。
- “不创建数组属性”Create No Array Properties控制向导是否生成数组属性。
如果选择此选项,向导不会生成数组属性,而是生成另一个表单。
- 为可为空的元素生成XMLNIL属性参数,它控制向导是否为生成的类中适用的属性指定XMLNIL属性参数。
该选项适用于每个对应于用`nillable="true"`指定的XML元素的属性。
如果选择此选项,向导将向属性定义添加`XMLNIL=1`。
否则不添加该参数。
该参数的详细信息请参见将对象投影到XML中的“处理空字符串和空值”。
- 为可为空的元素生成`XMLNILNOOBJECT`属性参数,它控制向导是否为生成的类中适用的属性指定`XMLNILNOOBJECT`属性参数。
该选项适用于每个对应于用`nillable="true"`指定的XML元素的属性。
如果选择此选项,向导将向属性定义添加`XMLNILNOOBJECT=1`。
否则不添加该参数。
该参数的详细信息请参见将对象投影到XML中的“处理空字符串和空值”。
5. 选择Next。
下一个屏幕显示关于要生成的类的选项的一些基本信息。
6. 在这个屏幕上,指定以下选项:
- 如果希望向导编译生成的类,可以选择“编译生成的类”。
- 可选择“添加NAMESPACE类参数”来指定`NAMESPACE`参数。
在本例中,`NAMESPACE`被设置为模式中`targetNamespace`的值。
如果不设置此选项,则不指定`NAMESPACE`。
建议在所有情况下都选择这个选项,因为每个支持XML的类都应该分配给一个XML名称空间。
(但是,为了向后兼容,可以将此选项清除。)
- 如果希望生成的类是持久类,请选择Create persistent classes。然后类扩展`%Persistent`。
可以稍后在向导中针对各个类更改这一点。
- 如果生成持久类,可以选择如何处理由另一个` b`的``组成的`` a。当向导生成一个包含属性`a`的持久类时,该属性有三种可能的形式。
可以将其定义为对象列表、一对多关系(默认)或父子关系。
下表总结了这些选择:
在持久性类中为集合属性使用关系 |向多对关系添加索引 |使用父子关系|生成的属性A的形式
---|---|---|---
selected (default)| not selected| not selected| 无索引的一对多关系
selected (default) |selected| not selected| 在多侧与索引的一对多关系
selected (default) |如果选择Use parent-child relationship,则忽略此选项| selected| 父子关系
not selected| not selected| not selected |List of objects
此外,如果未选择使用父子关系,则可以选择将`%OnDelete`方法添加到类以级联删除。如果选择此选项,当向导生成类定义时,它会在这些类中包含`%OnDelete()`回调方法的实现。生成的`%OnDelete()`方法删除类引用的所有持久对象。如确实选择了使用父子关系,请不要选择此选项;父子关系已经提供了类似的逻辑。
注意:如果修改生成的类,请确保根据需要修改`%OnDelete()`回调方法。
如果生成持久类,向导可以向每个对象类型类添加临时属性,以便可以为对象投影InterSystems IRIS内部标识符。选项如下:
- None-如果选择此选项,向导不会添加此处描述的任何属性。
- Use Id -如果选择此选项,向导将向每个对象类型类添加以下属性:
```
Property %identity As %XML.Id (XMLNAME="_identity", XMLPROJECTION="ATTRIBUTE") [Transient];
```
- Use Oid -如果选择此选项,向导将向每个对象类型类添加以下属性:
```
Property %identity As %XML.Oid (XMLNAME="_identity", XMLPROJECTION="ATTRIBUTE") [Transient];
```
- Use GUID-如果选择此选项,向导将向每个对象类型类添加以下属性:
```
Property %identity As %XML.GUID (XMLNAME="_identity", XMLPROJECTION="ATTRIBUTE") [Transient];
```
底部的表格列出了模式中的XML名称空间。在这里,指定包含该行中显示的XML名称空间的类的包。要执行此操作,请在程序包名字段中为该行指定程序包名。
7. 选择下一步。
8. 在下一个屏幕上,指定以下选项:
- Java Enabled - 如果选择此选项,则每个类都包括一个Java映射。
- Data Population数据填充-如果选择此选项,则除`%XML.Adaptor`外,每个类还继承会`%Populate`。
- SQL Column Order-如果选择此选项,每个属性将为`SqlColumnNumber`关键字指定一个值,以便属性在SQL中的顺序与它们在架构中的顺序相同。
- No Sequence Check-如果选中此选项,向导将生成的类中的`XMLSEQUENCE`参数设置为0。在某些情况下,如果XML文件的元素顺序与XML架构不同,则此选项非常有用。
默认情况下,`XMLSEQUENCE`参数在生成的类中设置为1。这可确保属性以与架构中相同的顺序包含在类定义中。
- XMLIGNORENULL-如果选择此选项,向导会将`XMLIGNORENULL=1`添加到类定义中。否则,它不会添加此参数。
- 将流用于二进制Use Streams for Binary - 如果选择此选项,向导将为`xsd:base64Binary`类型的任何元素生成`%Stream.GlobalBinary`类型的属性。如果清除此选项,则该属性的类型为`%xsd.base64Binary`。
请注意,向导将忽略`xsd:base64Binary`类型的任何属性。
- 在复选框下方,该表列出了向导将生成的类。对于每个类,确保适当地设置了`Extensions/Type`。在此,可以选择以下选项之一:
- 持久类`Persistent` -如果选择此选项,则类是持久性类。
- `Serial`-如果选择此选项,则类为序列类。
- `Registered Object`-如果选择此选项,则类为注册对象类。
所有生成的类还扩展`%XML.Adaptor`。
- 在表的右列中,为每个应编制索引的属性选择索引。
9. 选择Finish(完成)。
然后,向导将生成这些类,并在需要时编译它们。
对于这些类的属性,如果架构中相应元素的名称以下划线(_)开头,则属性名称以百分号(%)开头。
# 以编程方式生成类
XML架构向导也可用作`%XML.Utils.SchemaReader`类的`process()`方法。要使用此方法,请执行以下操作:
1. 创建`%XML.Utils.SchemaReader`的实例。
2. 可以选择设置实例的属性以控制其行为。
3. 可以选择创建InterSystems IRIS多维数组,以包含有关其他设置的信息。
4. 调用实例的`process()`方法:
```
method Process(LocationURL As %String,
Package As %String = "Test",
ByRef Features As %String) as %Status
```
- LocationURL必须是架构的URL或架构文件的名称(包括其完整路径)。
- Package是用于放置生成的类的包的名称。如果不指定程序包,InterSystems IRIS将使用服务名称作为程序包名称。
- Feature是在上一步中选择创建的多维数组。
# 每种XSD类型的默认IRIS数据类型
对于它生成的每个属性,XML架构向导会根据架构中指定的XSD类型自动使用适当的InterSystems IRIS数据类型类。下表列出了XSD类型和相应的InterSystems IRIS数据类型:
用于XML类型的InterSystems IRIS数据类型
源文档中的XSD类型 |生成的IRIS类中的数据类型
---|---
anyURI | %xsd.anyURI
base64Binary | `%xsd.base64Binary`或`%Stream.GlobalBinary`,具体取决于选择的选项。确定每个字符串是否可能超出字符串长度限制,如果可能,则将生成的属性从`%xsd.base64Binary`修改为适当的流类。)
boolean | `%Boolean`
byte | `%xsd.byte`
date | `%Date`
dateTime | `%TimeStamp`
decimal |`%Numeric`
double | `%xsd.double`
float | `%xsd.float`
hexBinary | `%xsd.hexBinary`
int |`%xsd.int`
integer |`%Integer`
long | `%Integer`
negativeInteger | `%xsd.negativeIntege`
nonNegativeInteger | `%xsd.nonNegativeInteger`
nonPositiveInteger |`%xsd.nonPositiveInteger`
positiveInteger | `%xsd.positiveInteger`
short | `%xsd.short`
string | `%String` (注意:责任确定每个字符串是否可能超出字符串长度限制,如果可能,则将生成的类型修改为适当的流类。)
time | `%Time`
unsignedByte | `%xsd.unsignedByte`
unsignedInt |`%xsd.unsignedInt`
unsignedLong | `%xsd.unsignedLong`
unsignedShort | `%xsd.unsignedShort`
no type given | `%String`
# 生成的属性的属性关键字
对于它生成的每个属性,XML架构向导还使用架构中的信息自动设置以下关键字:
- Description
- Required
- ReadOnly (如果相应的元素或属性是用固定属性定义的)
- InitialExpression (该值取自架构中的固定属性)
- Keywords related to relationships
# 生成的属性的参数
对于它生成的每个属性,XML架构向导会根据需要自动设置`XMLNAME`、`XMLPROJECTION`和所有其他与XML相关的参数。它还根据需要设置其他参数,如`MAXVAL`、`MINVAL`和`VALUELIST`。
# 调整为超长字符串生成的类
在极少数情况下,可能需要编辑生成的类来容纳超长的字符串或二进制值,超出字符串长度限制。
对于任何字符串类型,XML架构都不包含任何指示字符串长度的信息。XML架构向导将所有字符串值映射到InterSystems IRIS `%String`类,并将所有`base64Binary`值映射到`%xsd.base64Binary`类。这些选择可能不合适,具体取决于类要承载的数据。
在使用生成的类之前,应该执行以下操作:
- 检查生成的类,找到定义为`%string`或`%xsd.base64Binary`的属性。考虑将在其中使用这些类的上下文,特别是这些属性。
- 如果认为`%string`属性可能需要包含超出字符串长度限制的字符串,请将该属性重新定义为适当的字符流。同样,如果认为`%xsd.base64Binary`属性可能需要包含超过相同限制的字符串,请将该属性重新定义为适当的二进制流。
- 另请注意,对于类型为`%string`、`%xsd.string`和`%BINARY`的属性,默认情况下,`MAXLEN`属性参数为`50`个字符。可能需要指定更高的限制才能进行正确的验证。
(对于`%xsd.base64Binary`类型的属性,`MAXLEN`为`“”`,这意味着不会通过验证检查长度。但是,字符串长度限制确实适用。)
文章
姚 鑫 · 四月 13, 2021
# 第二章 定义和构建索引(一)
# 概述
索引是由持久类维护的结构,InterSystems IRIS®数据平台可以使用它来优化查询和其他操作。
可以在表中的字段值或类中的相应属性上定义索引。(还可以在多个字段/属性的组合值上定义索引。)。无论是使用SQL字段和表语法还是类属性语法定义相同的索引,都会创建相同的索引。当定义了某些类型的字段(属性)时,InterSystems IRIS会自动定义索引。可以在存储数据或可以可靠派生数据的任何字段上定义附加索引。InterSystems IRIS提供了几种类型的索引。可以为同一字段(属性)定义多个索引,为不同的目的提供不同类型的索引。
无论是使用SQL字段和表语法,还是使用类属性语法,只要对数据库执行数据插入、更新或删除操作,InterSystems IRIS就会填充和维护索引(默认情况下)。可以覆盖此默认值(通过使用`%NOINDEX`关键字)来快速更改数据,然后作为单独的操作生成或重新生成相应的索引。可以在用数据填充表之前定义索引。还可以为已经填充了数据的表定义索引,然后作为单独的操作填充(构建)索引。
InterSystems IRIS在准备和执行SQL查询时使用可用的索引。**默认情况下,它选择使用哪些索引来优化查询性能。** 可以根据需要覆盖此默认值,以防止对特定查询或所有查询使用一个或多个索引。
## 索引属性
每个索引都有一个唯一的名称。此名称用于数据库管理目的(报告、索引构建、删除索引等)。**与其他SQL实体一样,索引同时具有SQL索引名和相应的索引属性名;这些名称在允许的字符、区分大小写和最大长度方面有所不同。如果使用`SQL CREATE INDEX`命令定义,系统将生成相应的索引属性名称。如果使用持久类定义进行定义,则`SqlName`关键字允许用户指定不同的SQL索引名(SQL映射名称)。** Management Portal SQL界面的Catalog Details显示每个索引的SQL索引名称(SQL映射名称)和相应的索引属性名称(索引名称)。
索引类型由两个索引类关键字`Type`和`Extent`定义。IRIS提供的索引类型包括:
- 标准索引(`Type = index`)——一个持久数组,它将索引值与包含该值的行的 RowID相关联。
**任何没有明确定义为位图索引、位片索引或区段索引的索引都是标准索引。**
- 位图索引(`Type = Bitmap`)——一种特殊的索引,使用一系列位字符串来表示与给定索引值对应的RowID值集;
InterSystems IRIS包括许多位图索引的性能优化。
- 位片索引(`Type = Bitslice`)——一种特殊的索引,能够非常快速地计算某些表达式,例如总和数和范围条件。
某些SQL查询自动使用位片索引。
- 区段索引(`Extent Indices`)——一个区段中所有对象的索引。
有关更多信息,请参阅类定义参考中的区段索引关键字页。
- 范围索引-范围中所有对象的索引。
**表(类)的最大索引数为400。**
## 存储类型和索引
这里描述的索引功能适用于存储在持久化类中的数据。
InterSystems SQL支持使用InterSystems IRIS默认存储结构存储的数据的索引功能:`%Storage.Persistent`(`%Storage.Persistent`映射类)。
InterSystems SQL还支持使用`%Storage.SQL`(`%Storage.SQL`映射的类)存储的数据的索引功能。可以使用函数索引类型为`%Storage.SQL`映射的类定义索引。索引的定义方式与使用默认存储的类中的索引相同,但有以下特殊注意事项:
- 如果`IdKey`函数索引不是系统自动分配的,则该类必须定义`IdKey`函数索引。
- 此功能索引必须定义为索引。
请注意,不应直接调用`%Storage.Persistent`和`%Storage.SQL`类方法。相反,应该使用`%Persistent`类方法和本章中描述的操作调用索引功能。
## 索引全局名称
使用以下两种策略之一生成用于存储索引数据的下标全局:
- `%CLASSPARAMETER USEEXTENTSET=0`使用全局命名策略创建由用户指定的名称、附加的字母代码和索引名称组成的“传统”全局名称。用户可以理解这些全局名称,但它们可能很长,并且效率低于散列的全局名称。
- 如果`USEEXTENTSET=0`且未指定`DEFAULTGLOBAL`,则以下示例将描述生成的全局名称:`Sample.MyTest`持久类将定义名为`^Sample.MyTestD`的master map全局名`^Sample.MyTestD`位图范围索引全局名`^Sample.MyTestI(“$MyTest”)`(或`^Sample.MyTestI(“DDLBEIndex”)`),并且对于定义的索引`NameIDX`,它将定义名为`^Sample.MyTestI(“DDLBEIndex”)`的全局名。请注意,这些全局变量指定的是持久性类名(区分大小写),而不是SQL表名。
- 如果`USEEXTENTSET=0`并指定了`DEFAULTGLOBAL`,则指定的全局名称将替换永久类名。这允许指定一个比持久类名称更短或更清晰的名称。例如,如果`DEFAULTGLOBAL=“MyGlobal”`,则全局变量的名称如下:`^MyGlobalD`和`^MyGlobalI(“NameIDX”)`。
- `%CLASSPARAMETER USEEXTENTSET=1`使用创建哈希全局名称的全局命名策略。这包括对包名进行散列,对类名进行散列,然后追加一个点和一个标识索引的连续整数后缀。这些全局名称对用户来说不太容易理解,但往往更短、效率更高。
整数后缀仅作为索引名的关键字;与索引名和索引类型相关联的字段对整数编号没有影响。例如,`^EW3K.CgZk.1` 是 `Master Map`,`^EW3K.CgZk.2`是位图范围(`Bitmap Extent`),`^EW3K.CgZk.3`是`LastName`字段的已定义标准索引`NameIDX`,`^EW3K.CgZk.4`是已定义索引`WorkIdIDX`。如果删除`NameIDX`,则全局`^EW3K.CgZk.3`也会被删除,从而在整数序列中产生间隙。如果为`LastName`字段定义`LNameIDX`,则会创建全局`^EW3K.CgZk.5`;但是,如果稍后为`FullName`字段创建位图索引`NameIDX`,则全局索引将再次为`^EW3K.CgZk.3`。
- 如果`USEEXTENTSET=1`并且未指定`DEFAULTGLOBAL`,则包名和类名将被散列,如上所述。将追加连续的整数后缀。
- 如果`USEEXTENTSET=1`并指定了`DEFAULTGLOBAL`,则使用`DEFAULTGLOBAL`名称,而不是散列的包名和类名。将追加连续的整数后缀。例如,如果`DEFAULTGLOBAL="MyGlobal"`,则全局变量的名称如下:`^MyGlobal.1`和`^MyGlobal.3`
如果使用`CREATE TABLE`命令定义表,则`USEEXTENTSET`默认为1。因此,默认情况下,`CREATE TABLE`创建散列全局名称。可以使用`%CLASSPARAMETER`关键字以及`USEEXTENTSET`和`DEFAULTGLOBAL`参数更改此默认行为。可以使用`$SYSTEM.SQL.Util.SetOption()`方法在系统范围内更改此默认设置。
```java
SET status=$SYSTEM.SQL.Util.SetOption("DDLUseExtentSet",0,.oldval).
```
如果定义投影到表的持久类,则`USEEXTENTSET`默认为0。因此,默认情况下,使用传统的全局名称。
`DEFAULTGLOBAL`(如果已定义)将作为默认值。如果定义了`ExtentLocation`、`DataLocation`或`IndexLocation`存储关键字,则使用这些值,而不是上述默认值。
可以向`ZWRITE`提供全局名称以显示索引数据。
## Master Map
系统自动为每个表定义一个主图(`Data/Master`)。M`aster Map`不是索引,它是使用其`Map`下标字段直接访问数据本身的`Map`。默认情况下,`Master Map`下标字段是系统定义的`RowID`字段。默认情况下,使用`RowID`字段进行的这种直接数据访问由SQL映射名称(SQL索引名称)`IDKEY`表示。
默认情况下,用户定义的主键不是`IDKEY`。**这是因为使用`RowID`整数查找 `Master Map`总是比使用主键值查找效率更高。** 但是,如果指定主键为`IDKEY`,则主键索引被定义为表的主映射,SQL映射名称为主键SQL索引名。
对于单字段`key/IDKEY,`,主键索引是主映射,但主映射数据访问列仍然是`RowID`。这是因为在记录的唯一主键字段值和其`RowID`值之间存在一对一的匹配,而`RowID`被认为是更高效的查找。对于多字段主键/`IDKEY`,会为`Master Map`指定主键索引名称,并且`Master Map Data Access`列是主键字段。
可以通过Management Portal SQL Catalog Details(管理门户SQL目录详细信息)选项卡查看主图定义。除其他项目外,它还显示存储Master Map数据的全局名称。对于SQL和默认存储,此主映射全局默认为`^Package.classnameD`,并记录命名空间以防止歧义。对于自定义存储,未定义主地图数据存储全局名称;可以使用`DATALOCATIONGLOBAL`类参数指定数据存储全局名称。
对于SQL和默认存储,主映射数据存储在下标全局名为`^package.classnameD` 或 `^hashpackage.hashclass.1`。请注意,全局名指定持久类名,而不是相应的SQL表名,并且全局名区分大小写。可以向`ZWRITE`提供全局名称以显示`Master Map`数据。
**使用`Master Map`访问数据效率很低,尤其是对于大型表。因此,建议用户定义可用于访问`WHERE`条件、联接操作和其他操作中指定的数据字段的索引。**
# 自动定义的索引
定义表时,系统会自动定义某些索引。在为表格定义并在添加或修改表数据时,自动生成以下索引。如果定义:
- 不是`IDKEY`的主键,则系统会生成唯一类型的相应索引。主键索引的名称可以是用户指定的,也可以是从表名派生的。例如,如果定义一个未命名的主键,则相应的索引将命名为`tablenamePKEY#`,其中`#`是每个`UNIQUE`和`PRIMARY KEY`约束的顺序整数。
- 唯一的字段,Intersystems Iris为每个唯一字段生成索引,其中名称T`ableNameUnique#`,其中`#`是每个唯一和主键约束的顺序整数。
- 唯一约束,系统为每个具有指定名称的唯一约束生成索引,为共同定义唯一值的字段编制索引。
- shard key,系统在shard key字段上生成一个索引,命名为`ShardKey`。
可以通过Management Portal SQL Catalog Details选项卡查看这些索引。`CREATE INDEX`命令可用于添加唯一字段约束;D`ROP INDEX`命令可用于删除唯一字段约束。
默认情况下,系统在`RowID`字段上生成`IDKEY`索引。定义身份字段不会生成索引。但是,如果定义标识字段并将该字段作为主键,则InterSystems IRIS将在标识字段上定义`IDKEY`索引并将其作为主键索引。下面的示例显示了这一点:
```sql
CREATE TABLE Sample.MyStudents (
FirstName VARCHAR(12),
LastName VARCHAR(12),
StudentID IDENTITY,
CONSTRAINT StudentPK PRIMARY KEY (StudentID) )
```
同样,如果定义标识字段并为该字段提供唯一约束,则InterSystems IRIS将在标识字段上显式定义`IdKey/Unique`索引。下面的示例显示了这一点:
```sql
CREATE TABLE Sample.MyStudents (
FirstName VARCHAR(12),
LastName VARCHAR(12),
StudentID IDENTITY,
CONSTRAINT StudentU UNIQUE (StudentID) )
```
这些标识索引操作仅在没有明确定义的`idkey`索引时出现,并且表不包含任何数据。
## 位图范围索引
**位图范围索引是表的行的位图索引,而不是针对表的任何指定字段。在位图范围索引中,每个位表示顺序`ROWID`整数值,并且每个位的值指定相应的行是否存在。**
**SQL使用此索引来提高`Count(*)`的性能,返回表中的记录数(行)。**
**一个表最多可以有一个位图区段索引。创建多个位图范围索引导致`SQLCode -400`错误。** 其中 `ERROR #5445: Multiple Extent indices defined:DDLBEIndex.`
所有使用`CREATE TABLE`定义的表都会自动定义位图区段索引。
将此自动生成的索引分配索引名称(索引属性名称)`DDLBEIndex`和SQL MapName (SQL索引名称)`%%DDLBEIndex`。
定义为类的表可以有一个位图区索引,索引名和`$ClassName`的SQL MapName(其中`ClassName`是表的持久化类的名称)。
可以使用带有`BITMAPEXTENT`关键字的`CREATE INDEX`命令将位图区段索引添加到表中,或者重命名自动生成的位图区段索引。
可以通过管理门户SQL Catalog详细选项卡查看表的位图范围索引。虽然表只有一个位图范围索引,但是从另一个表中继承的表在其自身位图范围索引和它从其扩展的表中的位图范围索引中列出。例如,`Sample.employee`表扩展了`Sample.person`表;在目录详细信息映射 `Sample.Employee`列出`$Employee` 和 `$Person Bitmap`范围索引。
在经历许多删除操作的表格中,位图范围索引的存储可以逐渐变得效率较低。可以通过选择表的“目录详细信息”选项卡,“映射”选项和选择重建索引来重建从管理门户中重建位图范围索引。

**%SYS.Maint.Bitmap实用程序方法压缩位图范围索引,以及位图指数和bitslice索**引。
在以下任何情况下,调用`%BuildIndices()`方法都会构建现有的位图范围索引:未指定`%BuildIndices() pIndexList`参数(构建所有定义的索引);`pIndexList`按名称指定位图范围索引;或`pIndexList`指定任何定义的位图索引。
# 定义索引
## 使用类定义定义索引
在Studio中,可以使用新建索引向导或通过编辑类定义的文本将索引定义添加到`%Persistent`类定义。索引在一个或多个索引属性表达式上定义,后跟一个或多个可选索引关键字(可选)。它采用以下形式:
```sql
INDEX index_name ON index_property_expression_list [index_keyword_list];
```
- `index_name`是有效的标识符。
- `index_property_expression_list`是一个或多个以逗号分隔的属性表达式的列表,它们作为索引的基础。
- `index_keyword_list`是一个可选的索引关键字列表,用逗号分隔,用方括号括起来。
用于指定位图或位片索引的索引类型。
也用于指定唯一的、`IdKey`或`PrimaryKey`索引。
(根据定义,`IdKey`或`PrimaryKey`索引也是唯一索引。)
索引关键字的完整列表出现在类定义引用中。
`index_property_expression_list`参数由一个或多个索引属性表达式组成。
索引属性表达式包括:
- 要建立索引的属性的名称。
- 可选(元素)或(键)表达式,提供对集合子值进行索引的方法。
如果`index`属性不是一个集合,用户可以使用`BuildValueArray()`方法生成一个包含键和元素的数组。
- 可选的排序规则表达式。
它包含一个排序规则名称,后面可选地跟着一个或多个以逗号分隔的排序规则参数列表。
不能为惟一索引、`IdKey`索引或`PrimaryKey`索引指定索引排序规则。
唯一索引或`PrimaryKey`索引从正在建立索引的属性(字段)中获取其排序规则。
`IdKey`索引总是精确(`EXACT`)的排序。
例如,下面的类定义定义了两个属性和一个基于它们的索引:
```java
Class MyApp.Student Extends %Persistent [DdlAllowed]
{
Property Name As %String;
Property GPA As %Decimal;
Index NameIDX On Name;
Index GPAIDX On GPA;
}
```
更复杂的索引定义是:
```sql
Index Index1 On (Property1 As SQLUPPER(77), Property2 AS EXACT);
```
### 可以建立索引的属性
唯一可以被索引的属性是:
- 那些存储在数据库中的
- 那些可以从存储的属性可靠地派生出来的
必须使用`SQLComputed`关键字定义可以可靠地派生(并且未存储)的属性; `SQLComputeCode`指定的代码必须是导出属性值的唯一方法,并且无法直接设置属性。
如果可以直接设置一个派生属性的值,比如是一个简单的情况下(non-collection)属性定义为瞬态和不也定义为计算,然后直接设置属性的值将覆盖`SQLComputeCode`中定义的计算和存储的值不能可靠地来自属性;
这种类型的派生属性称为不确定性。(计算的关键字实际上意味着没有分配实例内存。)
一般规则是,只有定义为`calculate`和`SQLComputed`的派生属性才能被索引。
但是,派生集合有一个例外:派生的(`SQLComputed`)集合是暂时的(没有存储)集合,也没有定义为计算的集合(意味着没有实例内存)可以被索引。
**注意:`IdKey`索引所使用的任何属性的值内都不能有连续的一对竖条(`||`),除非该属性是对持久类实例的有效引用。**
这个限制是InterSystems SQL内部机制所要求的。
在`IdKey`属性中使用`||`会导致不可预知的行为。
### 多个属性的索引
可以在两个或多个属性(字段)的组合上定义索引。在类定义中,使用索引定义的`ON`子句指定属性列表,例如:
```java
Class MyApp.Employee Extends %Persistent [DdlAllowed]
{
Property Name As %String;
Property Salary As %Integer;
Property State As %String(MAXLEN=2);
Index MainIDX On(State,Salary);
}
```
如果需要执行使用字段值组合的查询,例如:
```sql
SELECT Name,State,Salary
FROM Employee
ORDER BY State,Salary
```
### 索引排序
唯一索引、`PrimaryKey`索引或`IdKey`索引不能指定排序规则类型。
对于其他类型的索引,索引定义中指定的每个属性都可以有一个排序规则类型。
应用索引时,索引排序类型应与属性(字段)排序类型匹配。
1. 如果索引定义包含为属性显式指定的排序规则,则索引使用该排序规则。
2. 如果索引定义不包括为属性显式指定的排序规则,则索引使用属性定义中显式指定的排序规则。
3. 如果属性定义不包括显式指定的排序规则,则索引使用属性数据类型的默认排序规则。
例如,`Name`属性被定义为字符串,**因此在默认情况下具有`SQLUPPER`排序规则。**
如果在`Name`上定义一个索引,默认情况下,它接受属性的排序规则,索引也将使用`SQLUPPER`定义。
属性排序和索引排序匹配。
但是,如果比较应用不同的排序规则,例如,`WHERE %EXACT(Name)=%EXACT(:invar)`,则此用法中的属性排序规则类型不再与索引排序规则类型匹配。属性比较排序规则类型与索引排序规则类型之间的不匹配可能会导致不使用索引。因此,在这种情况下,可能希望为具有精确(`EXACT`)排序规则的`Name`属性定义索引。如果`JOIN`语句的`ON`子句指定了排序规则类型,例如,`FROM Table1 LEFT JOIN Table2 ON %EXACT(Table1.Name) = %EXACT(Table2.Name)`,此处指定的属性排序类型与索引排序类型不匹配可能导致InterSystems IRIS不使用该索引。
以下规则控制索引和属性之间的排序规则匹配:
- **匹配的排序规则类型总是最大限度地使用索引。**
- **排序规则类型不匹配,其中属性指定为精确的排序规则(如上所示),并且索引有一些其他的排序规则,允许使用索引,但是它的使用不如匹配排序类型有效。**
- **排序规则类型不匹配,其中属性排序规则不准确,属性排序规则不匹配索引排序规则,这将导致不使用索引。**
要在索引定义中显式地为属性指定排序规则,语法如下:
```sql
Index IndexName On PropertyName As CollationName;
```
- `IndexName`是索引的名称
- `PropertyName`是被索引的属性
- `CollationName`是用于索引的排序规则的类型
例如:
```sql
Index NameIDX On Name As Exact;
```
不同的属性可以有不同的排序规则类型。
例如,在下面的例子中,`F1`属性使用`SQLUPPER`排序,而F2使用`EXACT`排序:
```sql
Index Index1 On (F1 As SQLUPPER, F2 As EXACT);
```
**注意:指定为`Unique`、`PrimaryKey`或`IdKey`的索引不能指定索引排序规则。
索引从属性`collations`中获取其`collation`。**
文章
姚 鑫 · 二月 23, 2022
# 第六十三章 SQL函数 IFNULL
测试`NULL`并返回适当表达式的函数。
# 大纲
```sql
IFNULL(expression-1,expression-2 [,expression-3])
{fn IFNULL(expression-1,expression-2)}
```
# 参数
- `expression-1` - 要计算以确定是否为`NULL`的表达式。
- `expression-2` - 如果`expression-1`为`NULL`,则返回的表达式。
- `expression-3` - 可选-如果`expression-1`不是`NULL`返回的表达式。
如果没有指定`expression-3`,则当`expression-1`不是`NULL`时返回`NULL`值。
返回的数据类型描述如下。
# 描述
支持IFNULL作为SQL通用函数和`ODBC`标量函数。
请注意,虽然这两个执行非常相似的操作,但它们在功能上是不同的。
SQL通用函数支持三个参数。
ODBC标量函数支持两个参数。
SQL通用函数和ODBC标量函数的双参数形式是不一样的;
当`expression-1`不为空时,它们返回不同的值。
SQL通用函数计算表达式1是否为NULL。
它永远不会返回expression-1:
- 如果`expression-1`为`NULL`,则返回`expression-2`。
- 如果`expression-1`不为`NULL`,则返回`expression-3`。
- 如果`expression-1`不为`NULL`,并且没有`expression-3`,则返回`NULL`。
ODBC标量函数计算`expression-1`是否为`NULL`。
它要么返回`expression-1`,要么返回`expression-2`:
- 如果`expression-1`为NULL,则返回`expression-2`。
- 如果`expression-1`不为`NULL`,则返回`expression-1`。
# 返回值数据类型
- `IFNULL(expression-1,expression-2)`:返回`expression-2`的数据类型。
如果`expression-2`是数值字面值,则字符串字面值或`NULL`返回数据类型`VARCHAR`。
- `IFNULL(expression-1,expression-2,expression-3)`:如果`expression-2`和`expression-3`具有不同的数据类型,则返回数据类型优先级更高(包容性更强)的数据类型。
如果`expression-2`或`expression-3`是数值字面值或字符串字面值,则返回数据类型`VARCHAR`。
如果`expression-2`或`expression-3`为`NULL`,则返回非`NULL`参数的数据类型。
如果`expression-2`和`expression-3`的长度、精度或比例不同,则`IFNULL`返回两个表达式的更大长度、精度或比例。
- `{fn IFNULL(expression-1,expression-2)}`:返回`expression-1`的数据类型。
如果`expression-1`是数字字面值、字符串字面值或`NULL`,则返回数据类型`VARCHAR`。
# 日期和时间显示转换
一些`expression-1`数据类型需要从逻辑模式(模式0)转换为ODBC模式(模式1)或显示模式(模式2)。例如DATE和TIME数据类型。
如果`expression-2`或`expression-3`的值不是相同的数据类型,则不能在ODBC模式或`Display`模式下转换该值,并产生一个`SQLCODE`错误:`DATE`数据类型为`-146`;
`-147`为`TIME`数据类型。
例如,`IFNULL(DOB,'nodate',DOB)`不能在ODBC模式或显示模式中执行;
它会发出一个`SQLCODE -146`错误,其中有`%msg Error: 'nodate' is an invalid ODBC/JDBC Date value or Error: 'nodate' is an invalid DISPLAY Date value.`
要在ODBC模式或`Display`模式下执行此语句,必须将该值转换为适当的数据类型:`IFNULL(DOB,CAST('nodate' as DATE),DOB)`。
这将产生日期0,显示为`1840-12-31`。
# %List显示转换
`%LIST`字段是带编码的字符串数据类型字段。如果`Expression-1`是`%List`字段,则相应的`Expression-2`或`Expression-3`值取决于选择模式:
- 在逻辑模式(模式0)或显示模式(模式2)中,`%List`值作为字符串数据类型返回,格式为`$lb("element1","element2",…)`。
因此,`expression-2`或`expression-3`的值必须指定为`%List`,示例如下:
```java
/// d ##class(PHA.TEST.SQLCommand).IfNull()
ClassMethod IfNull()
{
s myquery=3
s myquery(1)="SELECT TOP 20 Name,"
s myquery(2)="IFNULL(FavoriteColors,$LISTBUILD('No Preference'),FavoriteColors) AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New(2) // 2=Display mode
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull()
Name ColorChoice
yaoxin $lb("Red","Orange","Yellow")
xiaoli $lb("No Preference")
姚鑫 $lb("No Preference")
姚鑫 $lb("No Preference")
姚鑫 $lb("No Preference")
姚鑫 $lb("Red","Orange","Yellow","Green")
姚鑫 $lb("Red","Orange","Yellow","Green","Green")
Isaacs,Roberta Z. $lb("Red","Orange","Yellow","Green","Yellow")
Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S. $lb("White")
Fives,James D. $lb("Black")
Vonnegut,Jose P. $lb("Green","White")
Chadbourne,Barb B. $lb("Purple")
Quigley,Barb A. $lb("Yellow")
O'Rielly,Chris H. $lb("Red","Red")
Willeke,Alvin L. $lb("Black","Black")
Orwell,John V. $lb("No Preference")
Umansky,Susan C. $lb("Blue")
Kratzmann,Kirsten C. $lb("No Preference")
Ng,Josephine Z. $lb("White")
Zevon,Heloisa O. $lb("Orange")
20 Rows(s) Affected
```
- 在ODBC模式(模式1)中,`%List`值作为逗号分隔的元素字符串返回:`element1`、`element2`、....
因此,`expression-2`或`expression-3`的值可以指定为字符串,示例如下:
```java
/// d ##class(PHA.TEST.SQLCommand).IfNull1()
ClassMethod IfNull1()
{
s myquery=3
s myquery(1)="SELECT TOP 20 Name,"
s myquery(2)="IFNULL(FavoriteColors,'No Preference',FavoriteColors) AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New(1) // 1=ODBC mode
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull1()
Name ColorChoice
yaoxin $lb("Red","Orange","Yellow")
xiaoli No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 $lb("Red","Orange","Yellow","Green")
姚鑫 $lb("Red","Orange","Yellow","Green","Green")
Isaacs,Roberta Z. $lb("Red","Orange","Yellow","Green","Yellow")
Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S.Chadwick,Zelda S. $lb("White")
Fives,James D. $lb("Black")
Vonnegut,Jose P. $lb("Green","White")
Chadbourne,Barb B. $lb("Purple")
Quigley,Barb A. $lb("Yellow")
```
# NULL处理函数比较
下表显示了各种`SQL`比较函数。
如果逻辑比较测试为`True` (A与B相同),则每个函数返回一个值;如果逻辑比较测试为`False` (A与B不同),则返回另一个值。这些函数允许执行`NULL`逻辑比较。
不能在实际相等(或不相等)条件比较中指定`NULL`。
SQL Function| Comparison| Test Return Value
---|---|---
IFNULL(ex1,ex2) [two-argument form]| ex1 = NULL |True returns ex2 False returns NULL
IFNULL(ex1,ex2,ex3) [three-argument form]| ex1 = NULL | True returns ex2 False returns ex3
{fn IFNULL(ex1,ex2)}| ex1 = NULL | True returns ex2 False returns ex1
ISNULL(ex1,ex2)| ex1 = NULL | True returns ex2 False returns ex1
NVL(ex1,ex2)| ex1 = NULL | True returns ex2 False returns ex1
NULLIF(ex1,ex2)| ex1 = ex2 | True returns NULL False returns ex1
COALESCE(ex1,ex2,...)| ex = NULL for each argument | True tests next ex argument. If all ex arguments are True (NULL), returns NULL. False returns ex
# 示例
在下面的例子中,通用函数和ODBC标量函数都返回第二个表达式(`99`),因为第一个表达式是`NULL`:
```sql
SELECT IFNULL(NULL,99) AS NullGen,{fn IFNULL(NULL,99)} AS NullODBC
99 99
```
在下面的示例中,通用函数和ODBC标量函数示例返回不同的值。
通用函数返回``,因为第一个表达式不是`null`。
`ODBC`示例返回第一个表达式(33),因为第一个表达式不是`NULL`:
```sql
SELECT IFNULL(33,99) AS NullGen,{fn IFNULL(33,99)} AS NullODBC
NUll 33
```
下面的动态SQL示例返回字符串`'No Preference'`,如果`FavoriteColors`是`NULL`;
否则,返回`NULL`:
```java
/// d ##class(PHA.TEST.SQLCommand).IfNull2()
ClassMethod IfNull2()
{
s myquery=3
s myquery(1)="SELECT Name,"
s myquery(2)="IFNULL(FavoriteColors,'No Preference') AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New()
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
w !,"End of data"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull2()
Name ColorChoice
yaoxin
xiaoli No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫
姚鑫
Isaacs,Roberta Z.
```
下面的动态SQL示例返回字符串`'No Preference'`,如果`FavoriteColors`是`NULL`;
否则,它返回`FavoriteColors`的值:
```java
ClassMethod IfNull3()
{
s myquery=3
s myquery(1)="SELECT Name,"
s myquery(2)="IFNULL(FavoriteColors,'No Preference',FavoriteColors) AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New()
s tStatement.%SelectMode=2
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
w !,"End of data"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull3()
Name ColorChoice
yaoxin $lb("Red","Orange","Yellow")
xiaoli No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 $lb("Red","Orange","Yellow","Green")
姚鑫 $lb("Red","Orange","Yellow","Green","Green")
```
下面的例子返回字符串`'No Preference'`如果`FavoriteColors`是`NULL`;
否则,返回字符串`'Preference'`:
```sql
SELECT sqlName,
IFNULL(FavoriteColors,'No Preference','Preference') AS ColorPref
FROM Sample.Person
```
下面的ODBC语法示例如果FavoriteColors为NULL,则返回字符串'No Preference',否则返回FavoriteColors数据值:
```sql
SELECT Name,
{fn IFNULL(FavoriteColors,$LISTBUILD('No Preference'))} AS ColorPref
FROM Sample.Person
```
```java
/// d ##class(PHA.TEST.SQLCommand).IfNull4()
ClassMethod IfNull4()
{
s myquery=3
s myquery(1)="SELECT Name,"
s myquery(2)="{fn IFNULL(FavoriteColors,'No Preference')} AS ColorChoice "
s myquery(3)="FROM Sample.Person"
s tStatement = ##class(%SQL.Statement).%New()
s tStatement.%SelectMode=1
s qStatus = tStatement.%Prepare(.myquery)
s rset = tStatement.%Execute()
d rset.%Display()
w !,"End of data"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).IfNull4()
Name ColorChoice
yaoxin Red,Orange,Yellow
xiaoli No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 No Preference
姚鑫 Red,Orange,Yellow,Green
姚鑫 Red,Orange,Yellow,Green,Green
Isaacs,Roberta Z. Red,Orange,Yellow,Green,Yellow
```
下面的例子在`WHERE`子句中使用了`IFNULL`。
它挑选了`21`岁以下没有最喜欢的颜色偏好的人。
如果`FavoriteColors`为`NULL`, `IFNULL`返回`Age`字段值,用于条件测试:
```sql
SELECT Name,FavoriteColors,Age
FROM Sample.Person
WHERE 21 > IFNULL(FavoriteColors,Age)
ORDER BY Age
```
类似的功能可以参考`NULL`谓词(`IS NULL`, `IS NOT NULL`)。
文章
姚 鑫 · 七月 17, 2021
# 第六章 使用%File对象
如果想要操作文件本身,需要使用`%Library.File`的`%New()`方法实例化`%File`对象。该类还提供了允许使用该文件的实例方法。
注意:本节提供了几个使用`%File`对象的示例,以供说明。
对于简单的文件读写,使用`%Stream.FileCharacter`和%`Stream.FileBinary`。因为它们提供了额外的功能,例如,以正确的模式自动打开文件。
## 创建%File对象的实例
要使用文件,需要使用`%New()`方法实例化表示该文件的%File对象。该文件可能已经存在,也可能不存在于磁盘上。
以下示例在默认目录中为文件`export.xml`实例化一个%File对象。
```java
set fileObj = ##class(%File).%New("export.xml")
```
## 打开和关闭文件
实例化`%File`对象后,需要使用`open()`方法打开文件,以读取或写入该文件:
```java
USER>set status = fileObj.Open()
USER>write status
1
```
使用`Close()`方法关闭文件:
```java
USER>do fileObj.Close()
```
## 检查%File对象的属性
一旦实例化了文件,就可以直接检查文件的属性。
```java
USER>write fileObj.Name
export.xml
USER>write fileObj.Size
2512
USER>write $zdate(fileObj.DateCreated)
11/18/2020
USER>write $zdate(fileObj.DateModified)
11/18/2020
USER>write fileObj.LastModified
2020-11-18 14:24:38
USER>write fileObj.IsOpen
0
```
请注意,`LastModified`是人类可读的时间戳,而不是`$H`格式的日期。
属性“大小Size”、“创建日期DateCreated”、“修改日期DateModified”和“最后修改日期LastModified”是在访问时计算的。为不存在的文件访问这些属性会返回-2,表示找不到该文件。
注意:Windows是目前唯一跟踪实际创建日期的平台。其他平台存储最后一次文件状态更改的日期。
```java
USER>write ##class(%File).Exists("foo.xml")
0
USER>set fooObj = ##class(%File).%New("foo.xml")
USER>write fooObj.Size
-2
```
如果文件已打开,可以通过访问`CanonicalName`属性来查看其规范名称,这是根目录的完整路径。
```java
USER>write fileObj.CanonicalName
USER>set status = fileObj.Open()
USER>write fileObj.IsOpen
1
USER>write fileObj.CanonicalName
c:\intersystems\IRIS\mgr\user\export.xml
```
## 从文件中读取
要读取文件,可以打开文件,然后使用`Read()`方法。
以下示例读取`messages.log`的前`200`个字符。
```java
USER>set messages = ##class(%File).%New(##class(%File).ManagerDirectory() _ "messages.log")
USER>set status = messages.Open("RU")
USER>write status
1
USER>set text = messages.Read(200, .sc)
USER>write text
*** Recovery started at Mon Dec 09 16:42:01 2019
Current default directory: c:\intersystems\IRIS\mgr
Log file directory: .\
WIJ file spec: c:\intersystems\IRIS\mgr\IR
USER>write sc
1
USER>do messages.Close()
```
要从文件中读取整行,请使用`ReadLine()`方法,该方法继承自`%Library.File`的父类`%Library.AbstractStream`。
下面的示例读取`E:\temp\new.txt`的第一行。
```java
/// desc: 读取数据
/// w ##class(Demo.FileDemo).ReadFileData("E:\temp\new.txt")
ClassMethod ReadFileData(str)
{
s fileObj = ##class(%File).%New(str)
s status = fileObj.Open("RU")
w status,!
s text = fileObj.ReadLine(,.sc)
w text,!
w sc,!
d fileObj.Close()
q ""
}
```
## 写入文件
要写入文件,可以打开文件,然后使用`Write()`或`WriteLine()`方法。
以下示例将一行文本写入新文件。
```java
/// desc: 写入数据
/// w ##class(Demo.FileDemo).WriteFileData("E:\temp\new.txt")
ClassMethod WriteFileData(str)
{
s fileObj = ##class(%File).%New(str)
s status = fileObj.Open("RUWSN")
w status,!
s status = fileObj.WriteLine("Writing to a new file.")
w status,!
w fileObj.Size,!
d fileObj.Rewind()
s text = fileObj.ReadLine(,.sc)
w text,!
q ""
}
```
## 倒回文件
从文件读取或写入文件后,希望使用`Rewind()`方法倒回文件,以便可以从文件开头执行操作。
从上一个示例停止的地方开始,`fileObj`现在位于其末尾。倒回文件并再次使用`WriteLine()`会覆盖该文件。
```java
USER>set status = fileObj.Rewind()
USER>write status
1
USER>set status = fileObj.WriteLine("Rewriting the file from the beginning.")
USER>write status
1
USER>write fileObj.Size
40
```
关闭文件并重新打开它也会倒回文件。
```java
USER>do fileObj.Close()
USER>set status = fileObj.Open("RU")
USER>write status
1
USER>set text = fileObj.ReadLine(,.sc)
USER>write sc
1
USER>write text
Rewriting the file from the beginning.
```
## 清除文件
要清除文件,可以打开文件,然后使用`Clear()`方法。这将从文件系统中删除该文件。
以下示例清除默认目录中的`junk.xml`。
```java
USER>write ##class(%File).Exists("junk.xml")
1
USER>set fileObj = ##class(%File).%New("junk.xml")
USER>set status = fileObj.Open()
USER>write status
1
USER>set status = fileObj.Clear()
USER>write status
1
USER>write ##class(%File).Exists("junk.xml")
0
```
文章
Nicky Zhu · 一月 11, 2021
当我向技术人员介绍InterSystems IRIS时,我一般会先讲其核心是一个多模型DBMS。
我认为这是其主要优势(在DBMS方面)。数据仅存储一次。您只需访问您想用的API。
- 您想要数据的概要?用SQL!
- 您想用一份记录做更多事情?用对象!
- 想要访问或设置一个值,并且您知道键?用Globals!
乍一看挺好的,简明扼要,又传达了信息,但当人们真正开始使用InterSystems IRIS时,问题就来了。类、表和Globals是如何关联的?它们之间有什么关系?数据是如何存储的?
本文我将尝试回答这些问题,并解释这些到底是怎么回事。
## 第一部分 模型偏见
处理数据的人往往对他们使用的模型有偏见。
开发者们把数据视为对象。对他们而言,数据库和表都是通过CRUD(增查改删,最好是基于ORM)交互的盒子,但底层的概念模型都是对象(当然这对于我们大多数使用面向对象编程语言的开发者来说没错)。
而DBA大部分时间都在搞关系型DBMS,他们把数据视为表。对象只是行的封装器。
对于InterSystems IRIS,持久类也是一个表,将数据存储在Global中,因此需要进行一些澄清。
## 第二部分 举例
假设您创建了类Point:
```objectscript
Class try.Point Extends %Persistent [DDLAllowed]
{
Property X;
Property Y;
}
```
您也可以用DDL/SQL创建相同的类:
```
CREATE Table try.Point (
X VARCHAR(50),
Y VARCHAR(50))
```
编译后,新类将自动生成一个存储结构,将原生存储在Global中的数据映射到列(对于面向对象开发者而言,是属性):
```
Storage Default
{
%%CLASSNAME
X
Y
^try.PointD
PointDefaultData
^try.PointD
^try.PointI
^try.PointS
%Library.CacheStorage
}
```
这是怎么回事?
自下向上(加粗文字很重要):
- Type: 生成的存储类型,本例中是持久对象的默认存储
- StreamLocation - 存储流的Global
- IndexLocation - 索引Global
- IdLocation - 存储ID自增计数器的Global
- **DefaultData** - 存储将Global值映射到列/属性的XML元素
- **DataLocation** - 存储数据的Global
现在我们的DefaultData是PointDefaultData,让我们分析下它的结构。本质上Global节点有这样的结构:
- 1 - %%CLASSNAME
- 2 - X
- 3 - Y
所以我们可能期望我们的Global是这样的:
```
^try.PointD(id) = %%CLASSNAME, X, Y
```
但如果我们输出 Global 它会是空的,因为我们没有添加任何数据:
```
zw ^try.PointD
```
让我们添加一个对象:
```
set p = ##class(try.Point).%New()
set p.X = 1
set p.Y = 2
write p.%Save()
```
现在我们的Global变成了这样
```
zw ^try.PointD
^try.PointD=1
^try.PointD(1)=$lb("",1,2)
```
可以看到,我们期望的结构%%CLASSNAME, X, Y是用 $lb("",1,2) 设置的,它对应的是对象的X和Y属性(%%CLASSNAME 是系统属性,忽略)。
我们也可以用SQL添加一行:
```
INSERT INTO try.Point (X, Y) VALUES (3,4)
```
现在我们的Global变成了这样:
```
zw ^try.PointD
^try.PointD=2
^try.PointD(1)=$lb("",1,2)
^try.PointD(2)=$lb("",3,4)
```
所以我们通过对象或SQL添加的数据根据存储定义被存储在Global中(备注:可以通过在PointDefaultData 中替换X和Y来手动修改存储定义,看看新数据会怎样!)。
现在,如果我们想执行SQL查询会怎样?
```
SELECT * FROM try.Point
```
这段sql查询被转换为ObjectScript代码, 遍历^try.PointD,并根据存储定义(其中的 PointDefaultData 部分)填充列。
下面是修改。让我们从表中删除所有数据:
```
DELETE FROM try.Point
```
看看我们的Global变成什么样了:
```
zw ^try.PointD
^try.PointD=2
```
可以看到,只剩下ID计数器,所以新对象/行的ID=3。我们的类和表也继续存在。
但如果我们运行会怎样:
```
DROP TABLE try.Point
```
它会销毁表、类并删除Global。
```
zw ^try.PointD
```
看完这个例子,希望您现在对Global、类和表如何相互集成和互补有了更好的理解。根据实际需要选用正确的API会让开发更快、更敏捷、bug更少。
文章
姚 鑫 · 六月 21, 2022
# 第六章 操作位和位串(二)
# 将位序列存储为整数
如果要将一系列布尔参数传递给方法,一种常见的方法是将它们作为编码为单个整数的位序列传递。
例如,`Security.System.ExportAll()` 方法用于从 IRIS 实例中导出安全设置。如果查看此方法的类引用,将看到它的定义如下:
```java
classmethod ExportAll(FileName As %String = "SecurityExport.xml",
ByRef NumExported As %String, Flags As %Integer = -1) as %Status
```
第三个参数 `Flags` 是一个整数,其中每个位代表一种可以导出的安全记录。
```java
Flags - What type of records to export to the file, -1 = ALL
Bit 0 - System
Bit 1 - Events
Bit 2 - Services
Bit 4 - Resources
Bit 5 - Roles
Bit 6 - Users
Bit 7 - Applications
Bit 8 - SSL Configs
Bit 9 - PhoneProvider
Bit 10 - X509Credential
Bit 11 - OpenAMIdentityService
Bit 12 - SQL privileges
Bit 13 - X509Users
Bit 14 - DocDBs
Bit 15 - LDAPConfig
Bit 16 - KMIPServer
```
存储为整数的位串中的位 `0` 表示 `20`,位 `1` 表示 `2^1`,依此类推。如果要导出与位 `5`、`6`、`7`、`8`、`10`、`11` 和 `13` 对应的类型的安全记录,可以通过将 `Flags` 设置为 `2^5` +`2^6` + `2^7`+ `2^8` + `2^10` + `2^11` + `2^13` = `11744` 来完成.
在 ObjectScript 中,这可能看起来像:
```java
set flags = (2**5) + (2**6) + (2**7) + (2**8) + (2**10) + (2**11) + (2**13)
set status = ##class(Security.System).ExportAll("SecurityExport.xml", .numExported, flags)
```
一些 API 定义了宏以使代码更易于阅读。一种这样的情况是在 `DataMove` 实用程序中,其中 `DataMove` 对象是使用 `DataMove.Data.CreateFromMapEdits()` 方法创建的。无需过多介绍细节,该方法在类参考中定义如下:
```java
classmethod CreateFromMapEdits(Name As %String, ByRef Properties As %String,
ByRef Warnings As %String, ByRef Errors As %String) as %Status
```
它有以下参数:
- `Name` - 要创建的 `DataMove` 对象的名称。
- `Properties` - 用于创建对象的属性数组。可以选择指定以下属性。
- `Properties("Description")`- 数据移动操作的描述,默认 =`""`。
- `Properties("Flags")` - 描述操作的标志,默认 = `0`。
- `Properties("LogFile")`- 日志文件的目录和文件名,默认 = `\iris\mgr\DataMovename.log`。
为了使 Properties("Flags") 更容易定义,这些宏可供使用:
控制数据移动的位标志。
- `$$$BitNoSrcJournal` - 允许不记录源数据库
- `$$$BitNoWorkerJobs` - 在复制数据期间不要使用“worker”作业
- `$$$BitBatchMode` - 在“批处理”模式下运行复制作业
- `$$$BitCheckActivate` - 在 `Activate()` 期间调用 `$$CheckActivate^ZDATAMOVE()`
这些宏定义为特定位的计算值,允许设置正确的位,而无需记住哪个位代表哪个标志:
`#;DataMove` 标志属性的定义
- `#define BitNoSrcJournal 1`
- `#define BitNoWorkerJobs 512`
- `#define BitBatchMode 2048`
- `#define BitCheckActivate 4096`
在代码中,可以使用此代码片段来设置标志并创建一个 `DataMove` 对象:
```java
// Set properties("Flags") to 6657
set properties("Flags") = $$$BitNoSrcJournal + $$$BitNoWorkerJobs + $$$BitBatchMode + $$$BitCheckActivate
set status = ##class(DataMove.Data).CreateFromMapEdits("dm", .properties, .warnings, .errors)
```
文章
姚 鑫 · 八月 4, 2022
# 第十七章 源代码文件 REST API 教程(二)
# 获取在命名空间中定义的源代码文件
要获取有关命名空间中源代码文件的信息:
- 首先,使用 `GetDocNames` 方法获取文件的名称。
- 然后用`GetDoc` 方法获取一个文件的内容,也可以用`GetDocs` 方法获取多个文件的内容。
- 如果要提高应用程序的网络效率,可以保留源代码文件的名称和内容的本地缓存,并使用 `GetModifiedDocNames` 方法仅获取内容发生变化的源代码文件的名称或使用带有 `If-None-Match HTTP` 标头的 `GetDoc` 方法。
`GetDocNames` 方法返回映射到命名空间的所有数据库中的所有源代码文件的名称。
```json
{
"status": {
"errors": [],
"summary": ""
},
"console": [],
"result": {
"content": [
{
"name": "%Api.DocDB.cls",
"cat": "CLS",
"ts": "2016-08-03 20:01:42.000",
"upd": true,
"db": "IRISLIB",
"gen": false
},
...
{
"name": "EnsProfile.mac",
"cat": "RTN",
"ts": "2003-09-19 13:53:31.000",
"upd": true,
"db": "INVENTORYR",
"gen": false
},
...
{
"name": "xyz.mac",
"cat": "RTN",
"ts": "2016-08-11 15:05:02.167",
"upd": false,
"db": "INVENTORYR",
"gen": false
}
]
}
}
```
以下 `GetDoc` 调用返回 `xyz.mac` 文件的内容:
```java
http://localhost:52773/api/atelier/v1/INVENTORY/doc/xyz.mac
```
此调用返回:
```json
{
"status": {
"errors": [],
"summary": ""
},
"console": [],
"result": {
"name": "xyz.mac",
"db": "INVENTORYR",
"ts": "2016-09-14 14:10:16.540",
"upd": false,
"cat": "RTN",
"status": "",
"enc": false,
"flags": 0,
"content": [
"ROUTINE xyz",
"xyz ;",
" w \"hello\""
]
}
}
```
# 在命名空间中创建新文件或更新现有文件
要在命名空间中创建新文件或更新现有文件,请使用 `PutDoc` 方法。例如,以下 `REST` 调用在 `INVENTORY` 命名空间中创建一个新的 `xyz.mac` 源代码文件,或者,如果 `xyz.mac` 文件存在,则此调用将文件的原始定义替换为新定义。如果要更新新文件,则必须指定 `HTTP` 标头 `If-None-Match` 以标识文件的当前版本,或指定 `?ignoreConflict=1 URL` 参数以绕过版本检查。有关详细信息,请参阅参考部分中的 `PutDoc`。
```java
PUT http://localhost:52773/api/atelier/v1/INVENTORY/doc/xyz.mac
```
应该指定 `Content-Type application/json` 和以下 `JSON` 消息:
```json
{
"enc": false,
"content": [
"ROUTINE xyz",
"xyz ;",
" w \"hello\""
]
}
```
该调用返回以下 `JSON` 消息。它显示源代码文件已在 `INVENTORYR` 数据库中创建,该数据库是 `INVENTORY` 命名空间中例程的默认数据库。
```json
{
"status": {
"errors": [],
"summary": ""
},
"console": [],
"result": {
"name": "xyz.mac",
"db": "INVENTORYR",
"ts": "2016-09-14 14:10:16.540",
"upd": false,
"cat": "RTN",
"status": "",
"enc": false,
"flags": 0,
"content": []
}
}
```
如果要更新或创建二进制文件,请为 `enc` 指定一个真值,并将二进制内容作为二进制值的 `base64` 编码的块数组包含在内。