搜索​​​​

清除过滤器
文章
王喆 👀 · 九月 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。
文章
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时的影响。谢谢!
问题
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)
文章
Qiao Peng · 六月 8, 2022

数字孪生医院:一个围绕着智慧医院决策的建模、管理和行动闭环的全新方式

01 智慧医院的本质 什么是智慧医院? 国家卫健委提出建设智慧医院的目的是要不断增强人民群众的获得感,要求医院的流程更便捷、服务更高效、管理更精细,主要聚焦于三大领域,面向医务人员的智慧医疗、面向患者的智慧服务以及面向管理的智慧管理。 如果从技术角度进行高度抽象,我们把智慧医院的本质浓缩为一点,就是为智慧的科学决策以及基于科学决策的业务执行闭环。在现实世界里,我们要从真实业务(调查研究)中获得决策依据,同时还要有决策的方法,把决策应用在现实世界上,来影响和改变现实世界。 02 科学决策的三个阶段 第一个时代是人工决策时代。我们医疗行业就是望闻问切,我们能拿到的数据非常有限,所以属于一个数据饥渴的时代。我们通过有限的数据来进行决策,非常容易落入我们经常批判的“三拍”型决策,“拍脑袋决策、拍胸脯保证、拍屁股走人”。显然这个决策效率不是很高。 人工决策时代:“数据稀缺”时代的决策 第二个时代是信息化时代。我们有了很多数据,能够把部分现实世界来进行数字化表达,我们也通过信息辅助人工决策进行智能决策,例如通过药品知识库推动合理用药,通过人工智辅助影像判断等等。 然而我们依旧面临着很多数据问题。首先是数据还不够完整,经常无法获取需要的数据,数据模型也不能完全表达医院的实际业务;其次,我们也还是会遇到很多数据来源问题(我们常常不知道该信任哪一份数据)、数据的关联性、实时性等问题。系统多了、数据多了,但同时孤岛也多了、噪音也多了,如何通过技术的手段让真实世界在数字化的世界里面完整、客观表达,这是实现数字化医院最基础的工作。 此外,我们仍然没有解决人工决策的局限性,例如决策所依赖的依据,我们的大脑能够处理的依据差不多十个左右,但是现在医学发展也有很多的数据,比如有临床数据、有功能遗传学、有蛋白质组学的等等,我们面对着上千个决策因素,但受制于人类自身的限制,我们也很难直观地理解和认识这些数据并发挥其价值。 人工决策的局限 数据来源:Evidence-Based Medicine and the Changing Nature of Healthcare: 2007 IOM Annual Meeting Summary 在数字化时代,决策模式从信息辅助决策升级为人工干预的智能决策。数字化时代的决策有四个要素。 第一是全面的数据表达,我们要把真实世界的所有数据能够挖掘和展现出来,并将其沉淀在数据平台。数据的全面性包含了数据广度、数据深度、数据时效性、数据的关联性、数据的动态变化等等。 第二是数字化的逻辑推理能力,基于知识图谱和基于机器学习来把逻辑推理过程实现数字化,比如深度学习、神经网络等等,都是在试图将我们的逻辑推理进行数字化表现。 第三是流程与场景数字化,我们现在已经有了临床辅助决策,为什么在现实业务中还会遇到例如应该报警的没报警,不该报警的反而误报警等现象,主要原因是现在决策流程都是在各个业务系统内通过人工环节里面来体现的,使得我们并不了解实际业务中的决策流程,所以如果想要真正了解我们的决策是不是正确的、及时的,以及闭环地应用在业务里面,我们需要对业务的流程和场景进行数字化。 第四,对决策本身的绩效评价数字化。我们怎么知道决策是好的还是坏的,是最优的还是次优的?我们应该如何优化我们的决策,让决策能够变得越来越好?我们需要对决策进行可度量的数字化评价,评价的模型、指标,决策的效果等,对应这些我们也需要进行数字化。有了这些评价的结果,我们才能依据这些结果进行推理逻辑的改进,让决策更好。 数字化时代决策的四要素: 全面的数据、逻辑推理数字化、流程与场景数字化、绩效评价数字化 03 数字孪生——智慧医院的未来 这时我们就不难看出为什么数字孪生是智慧医院的未来方向了。 根据Gartner 对数字孪生的定义:“现实世界实体或系统的数字表达。数字孪生体的实现是一套封装的软件对象或模型,它反映了一个独特的物理对象、过程、组织、人或其他抽象概念。” 数字孪生作为现实世界全面的数字化表达,不是为了表达而表达,而是通过全面表达来实现决策方法、决策流程和决策评价的全面数字化。数字孪生是一个能够全面反映真实医院,并且跟实际业务紧密集成、互相打通的数字化世界,在数字孪生世界里很多决策可以由它来产生,不需要由真实世界来产生,数字孪生世界里的决策可以真实地进行模拟、验证、演练,从而最大程度降低我们的试错成本,同时还可以反馈给真实世界,形成一个统一的、闭环的数字——真实一体化的世界。所以数字孪生并不仅仅是一个简单的真实世界的镜像,而是一个更复杂的、跟现实世界有互通性、互操作性的数字世界。 数字孪生:现实+决策+流程+评价的全面数字化 04 InterSystems IRIS医疗版实现数字孪生医院的最佳数智底座 数字孪生的世界里我们有什么样的平台和技术来支撑它?对于数字孪生的智慧医院来说,我们认为应该有一个功能强大的数智基座能满足前面提到数字孪生时代决策的四个关键要素。 第一是数据的全面性。面对复杂的真实世界,我们需要多种数据建模方式,类似思维导图,对某一事物进行N维建模,同时可以随着我们的认知加深而不断扩展的建模方式,而不是简单的关系型二维表来描述我们的实际业务。同时这些数据是实时的、有关联性的,只有这样我们做的决策才能够发现或者反应事物的本质,才有价值。 第二是流程和场景的数字化。现在的流程大部分都是分散在各个业务系统和人工工作里面,相互割裂、理解起来很困难,合理性、是否需要优化、如何优化更是无从谈起。在数字孪生时代,我们需要对于整个流程来建立可视化的、直观的并且能够优化的流程,通过流程建模来建模,同时建立这个流程模型,在数字化的世界里是闭环的,也就是说可以把决策的结果实时反馈给真正在运营的流程,并且反馈给我们真实世界。这个流程是可以优化的,可以不断进行业务流程的再造。 第三,要实现逻辑推理的数字化。我们要借助机器学习、深度学习等技术来提升我们决策的科学水平,对于很多行业里面的非结构化的数据,我们借助自然语言处理来挖掘真实的非结构化数据真实的含义,当然还有许多通过知识图谱认知来分析来提升整体决策的能力。 最后,从决策的评价和改进层面来说,基于行业的模型行业的标准对于决策进行相应评价,通过数据深度的分析对于评价指标进行相应的计算,并且把结果实时反馈给业务流程,通过推理和逻辑来优化决策,提升决策水平。 以上这四个能力就是未来数字孪生智慧医院数智基座应该具有的能力。 InterSystems IRIS医疗版数据平台正是这样一个平台,可以帮助用户从各种数据源收集和编辑相应的数据,并且能够对真实世界使用不同的模型,真实反映真实世界全貌的数据模型,里面包含了我们很多方面的数据,例如说临床的、组学的、物联网的、社交的、健康管理等等数据。同时InterSystems IRIS医疗版数据平台提供完整的互操作平台,可以帮助我们业务流程进行可视化,实现可视化流程的建模——这种方式可以支撑我们对于这个流程的实时优化,同时通过实时数据分析把实时洞察反馈给业务流程,支撑用户做更智能的决策。 通过开放型分析,基于全面的数据,我们可以应用机器学习、自然语言处理、知识图谱等等对数据进行分析、决策和相应决策的评价来驱动业务持续改进。 InterSystems IRIS医疗版数据平台:数字孪生医院最佳数智基座 数字化时代,基于真实业务数据的决策智能和基于合理决策的业务执行闭环是实现智慧医院的核心,而数字孪生开启了一个全新的围绕着智慧决策的建模、管理行动的全新方式,能够帮助医院完整认知和理解医院业务,提高决策质量和效率、优化资源利用,从而提高医疗质量、优化患者体验以及降低运营成本,更重要的是可以支持医院实现可持续的高质量发展和数字化创新能力。 InterSystems IRIS数据平台作为全美排名前20医院、复旦百强中40%的医院以及全国数百家医院的共同选择,毫无疑问是实现数字孪生医院数智底座的最佳选择。
文章
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)
文章
Louis Lu · 四月 15, 2021

精华文章--访问IRIS数据平台的四种方式

IRIS 中支持的四种方式: SQL、Objects、REST 和 GraphQL 卡济米尔·马列维奇,《运动员》(1932) > > “你当然无法理解! 习惯了坐马车旅行的人怎么可能理解乘坐火车或者飞机旅行的人的感受和印象?”> > > > 卡济米尔·马列维奇 (1916)> ## 引言 我们已经讨论过为什么在主题领域建模使用对象类型优于使用 SQL。 当时得出的结论和总结的事实如今依然适用。 那么,我们为什么要退后到对象和类型之前的时代,讨论将对象的操作拖回到使用global的技术? 我们又为什么要鼓励面条式代码?难道是为了用它难以跟踪的错误考验开发者的技能熟练度? 目前有几种观点支持通过基于 SQL/REST/GraphQL 的 API 传输数据,而不是将其表示为类型/对象: 这些技术经过深入研究,相当易于部署。 知名度非常高,已在便捷的开源软件中广泛实现。 您通常别无选择,只能使用这些技术,尤其是在网络和数据库中。 最重要的是,API 仍然使用对象,因为它们提供了在代码中实现 API 的最适途径。 在讨论实现 API 之前,我们先来看一下底层的抽象层。 下图显示了数据在永久存储位置与处理并向应用程序用户呈现的位置之间的移动方式。 如今,数据存储在旋转硬盘驱动器 (HDD) 上,或者使用更现代的技术存储在 SSD 的闪存芯片中。 再使用由 HDD/SSD 上独立存储块组成的流完成数据的读取和写入。 分块并不是随机的, 而是由数据存储介质的物理学、力学和电子学特性决定。 在 HDD 中,这是旋转磁盘上的磁道/扇区。 在 SSD 中,这是可写硅芯片中的内存段。 它们本质上都是信息块,只有找到这些信息块并将其组合才能检索出需要的数据。 数据必须装配成将我们的数据模型/类型与查询时间对应值相匹配的结构。 数据的装配和检索过程由与操作系统中的文件子系统捆绑的 DBMS 负责。 我们可以直接寻址文件系统甚至 HDD/SSD,绕过 DBMS。 但这样一来就失去了两个极其重要的数据桥梁:存储块和文件流之间的桥梁,以及文件和数据库模型中的有序结构之间的桥梁。 换句话说,我们要负责开发所有处理块、文件和模型的代码,包括所有的优化、精细调试和长期可靠性测试。DBMS 使用可理解的模型和表示形式,给我们提供了以高级语言处理数据的绝好机会。 这也是这些系统的一大优势。 DBMS 和数据平台(如 InterSystems IRIS)提供了更多功能:能够以多种方式同时访问有序数据。 而在您的项目中,使用哪一种则取决于您自己。 利用 IRIS 提供的各种工具, 我们可以让代码更美观、更简洁。 我们将直接通过 ObjectScript 面向对象的语言来利用和开发 API。 也就是说,例如,我们会直接从 ObjectScript 软件内部调用 SQL 代码。 对于其他 API,我们将使用现成的库和内置的 ObjectScript 工具。 我们将以 SQLZoo Internet 项目为例,该项目提供了 SQL 的学习资源。 我们将在其他 API 示例中使用相同的数据。 如果您想了解 API 设计的各种方法并利用现成的解决方案,这里有一个有趣又实用的公共 API 集合,汇集到 GitHub 上的一个项目中。 SQL 最自然的方法莫过于从 SQL 开始。 这里没人对它不熟悉吧? SQL 的教程和书籍浩如烟海。 我们选择的是 SQLZoo。 这是一门很好的 SQL 入门课程,其中有示例,有操作步骤详解,也有语言引用。 我们将一些任务从 SQLZoo 带到 IRIS 平台,并使用各种方法解决。 您可以用多快的速度在您的电脑上访问 InterSystems IRIS? 最快的选择之一是在 Docker 中从现成的 InterSystems IRIS Community Edition 镜像部署容器。 InterSystems IRIS Community Edition 是 InterSystems IRIS Data Platform 的免费开发者版本。 其他方式:在学习门户中访问 InterSystems IRIS Community Edition 将数据从 SQLZoo 转移到我们自己的 IRIS 实例存储中。 需要执行以下操作: 打开管理门户(以我的为例,http://localhost:52773/csp/sys/UtilHome.csp)。 切换到 USER 命名空间 - 在 Namespace %SYS 中,点击“Switch”链接,选择 USER 转到 System > SQL - 依次打开 System Explorer、SQL,然后点击“Go”按钮。 右侧将打开“Execute query”标签页,带有“Execute”按钮,这就是我们需要的。 要详细了解如何通过管理门户网站使用 SQL,请参阅此文档。 在 Data 部分的描述中查看用于部署数据库和 SQLZoo 测试数据集的现成脚本。 这里有 world 表的几个直接链接: 用于创建 world 数据库的脚本 进入该表的数据 创建数据库的脚本可以在 IRIS 管理门户的 Query Executor 表单中执行。 CREATE TABLE world( name VARCHAR(50) NOT NULL ,continent VARCHAR(60) ,area DECIMAL(10) ,population DECIMAL(11) ,gdp DECIMAL(14) ,capital VARCHAR(60) ,tld VARCHAR(5) ,flag VARCHAR(255) ,PRIMARY KEY (name) ) 要加载 Query Executor 表单的测试集,首先转到 Wizards > Data Import 菜单。 请注意,在创建容器时,必须预先添加带有测试数据文件的目录,或者通过浏览器从计算机中加载。 该选项在数据导入向导的控制面板中可用。 在 Query Executor 表单中运行以下脚本,检查带有数据的表是否存在: SELECT * FROM world 接下来可以从 SQLZoo 网站访问示例和任务。 以下所有示例均要求您在第一次赋值中实现 SQL 查询: SELECT population FROM world WHERE name = 'France' 这样就可以将任务从 SQLZoo 转移到 IRIS 平台,保持与 API 的无缝协作。 请注意:我发现,SQLZoo 网站界面的数据与导出的数据不同。 至少在第一个示例中,法国和德国的人口值是不同的。 不必过度纠结。 使用[欧洲统计局数据](https://ec.europa.eu/eurostat/tgm/table.do?tab=table&language=en&pcode=tps00001&tableSelection=1&footnotes=yes&labeling=labels&plugin=1)作为参考。 另一种在 IRIS 中获得数据库 SQL 访问的便捷途径是 Visual Studio 代码编辑器与 SQLTools 插件和 SQLTools Driver for InterSystems IRIS。 这种解决方案很受开发者的欢迎,不妨一试。 为了顺利进行下一步并获得对我们数据库的对象访问权,让我们绕一个小弯,从“纯粹”的 SQL 查询转到[在 ObjectScript 中嵌入应用程序代码](https://docs.intersystems.com/iris20203/csp/docbook/Doc.View.cls?KEY=GSQL_esql)的 SQL 查询,ObjectScript 是 IRIS 内置的一种面向对象的语言。 如何在 VSCode 中设置 IRIS 访问并使用 ObjectScript 进行开发。 Class User.worldquery { ClassMethod WhereName(name As %String) { &sql( SELECT population INTO :population FROM world WHERE name = :name ) IF SQLCODE<0 {WRITE "SQLCODE error ",SQLCODE," ",%msg QUIT} ELSEIF SQLCODE=100 {WRITE "Query returns no results" QUIT} WRITE name, " ", population } } 在终端中查看结果: do ##class(User.worldquery).WhereName("France") 您应该会收到国家/地区名称和居民人数的响应。 对象 类型 接下来是 REST/GraphQL 的部分。 我们正在为 Web 协议实现一个 API。 大多数情况下,服务器端的后台源代码是以良好支持类型的语言开发的,甚至是完全面向对象的范式编写的。 包括:Java/Kotlin 中的 Spring、Python 中的 Django、Ruby on Rails、C# 中的 ASP.NET或者 TypeScript 中的 Angular。 当然,还有 IRIS 平台原生的 ObjectScript 中的对象。 为什么这很重要? 因为在发送时,代码中的类型和对象将简化为数据结构。 您需要考虑如何在程序中简化模型,这类似于考虑关系模型中的损失。 您还需要确保在 API 的另一侧,模型得到了充分的还原,可以不失真地投入使用。 这就带来了额外的负担:您作为程序员需要承担额外的责任。 在代码之外,除去转换器、编译器和其他自动工具的帮助,您还需要不断确保模型得到正确转移。 如果从另一个角度看待上述问题,我们还没有发现任何技术和工具可以用来轻松地将类型/对象从一种语言的程序转移到另一种语言的程序。 还剩下什么? 有 SQL/REST/GraphQL 的简化实现,以及大量用人类友好语言描述 API 的文档。 面向开发者的非正式(从计算机的角度)文档具体描述了应使用所有可用方法将哪些内容转换为正式代码,以便计算机处理。 程序员一直在开发不同的方法来解决上述问题。 其中一个成功的方法是 IRIS 平台对象 DBMS 中的跨语言范式。 下表应该可以帮助您理解 IRIS 中 OPP 与 SQL 模型之间的关系: 面向对象编程 (OOP) 结构化查询语言 (SQL) 包 Schema 类 表 属性 列 方法 存储过程 两个类之间的关系 外键约束,内置联接 对象(在内存中或磁盘上) 行(在磁盘上) 您可以从 [IRIS 文档]中详细了解如何显示对象和关系模型。 执行我们的 SQL 查询根据示例创建 world 表时,IRIS 将在名为 User.world 的类中自动生成对应对象的描述。 Class User.world Extends %Persistent [ ClassType = persistent, DdlAllowed, Final, Owner = {_SYSTEM}, ProcedureBlock, SqlRowIdPrivate, SqlTableName = world ] { Property name As %Library.String(MAXLEN = 50) [ Required, SqlColumnNumber = 2 ]; Property continent As %Library.String(MAXLEN = 60) [ SqlColumnNumber = 3 ]; Property area As %Library.Numeric(MAXVAL = 9999999999, MINVAL = -9999999999, SCALE = 0) [ SqlColumnNumber = 4 ]; Property population As %Library.Numeric(MAXVAL = 99999999999, MINVAL = -99999999999, SCALE = 0) [ SqlColumnNumber = 5 ]; Property gdp As %Library.Numeric(MAXVAL = 99999999999999, MINVAL = -99999999999999, SCALE = 0) [ SqlColumnNumber = 6 ]; Property capital As %Library.String(MAXLEN = 60) [ SqlColumnNumber = 7 ]; Property tld As %Library.String(MAXLEN = 5) [ SqlColumnNumber = 8 ]; Property flag As %Library.String(MAXLEN = 255) [ SqlColumnNumber = 9 ]; Parameter USEEXTENTSET = 1; /// Bitmap Extent Index auto-generated by DDL CREATE TABLE statement. 不要编辑此索引的 SqlName 。 Index DDLBEIndex [ Extent, SqlName = "%%DDLBEIndex", Type = bitmap ]; /// DDL Primary Key Specification Index WORLDPKey2 On name [ PrimaryKey, Type = index, Unique ]; } 您可以用这个模板来开发面向对象风格的应用程序。 您需要做的就是向 ObjectScript 中的类添加方法,ObjectScript 天生与数据库捆绑。 事实上,这个类的方法按 SQL 术语来说就是“存储过程”。 让我们用 SQL 实现之前完成的同一个示例。 将 WhereName 方法添加到 User.world 类中,充当输入的国家/地区名称的“Country information”对象设计器: ClassMethod WhereName(name As %String) As User.world { Set id = 1 While ( ..%ExistsId(id) ) { Set countryInfo = ..%OpenId(id) if ( countryInfo.name = name ) { Return countryInfo } Set id = id + 1 } Return countryInfo = "" } 在终端中查看: set countryInfo = ##class(User.world).WhereName("France") write countryInfo.name write countryInfo.population 从此示例可以看出,与 SQL 查询不同,为了通过国家/地区名称查找所需对象,我们需要手动对数据库记录进行逐一排序。 在最坏情况下,如果我们的对象位于列表末尾(或根本不在列表中),我们就不得不从头到尾将所有记录都整理一遍。 关于如何在 IRIS 中通过索引对象字段和自动生成类方法来加快搜索过程有一个单独的讨论。 有关更多详情,请参阅[文档]和[开发者社区门户]中的帖子。 例如,对于我们的类,了解 IRIS 从 WORLDPKey2 国家/地区名称生成的索引名称后,您可以使用单个快速查询从数据库中直接加载一个对象: set countryInfo = ##class(User.world).WORLDPKey2Open("France") 同时检查: write countryInfo.name write countryInfo.population 此文档中包含一些指导原则,可以帮助您决定使用对象还是 SQL 访问存储的内容。 当然,您也要始终牢记,您只能将其中之一完全用于您的任务。 此外,由于 IRIS 中提供了现成的二进制捆绑包,支持常见的 OOP 语言,例如 Java、Python、C、C#(.Net)、JavaScript,甚至是正在迅速普及的 Julia(参见 GitHub 和 OpenExchange),您总能找到最方便的语言开发工具。 接下来,让我们深入讨论 Web API 中的数据。 REST 或 RESTful Web API 跳出服务器和常见终端,转向一些更主流的接口:浏览器和类似应用程序。 这些应用程序依靠 HTTP 系列的超文本协议管理系统之间的交互。 IRIS 自带许多适合这个目的的工具,包括一个实实在在的数据库服务器和 Apache HTTP 服务器。 表述性状态转移 (REST) 是一种架构样式,用于设计分布式应用程序,尤其是 Web 应用程序。 REST 虽然流行,但它只是一套架构原则,而 SOAP 则是由 World Wide Web Consortium (W3C) 维护的标准协议,因此基于 SOAP 的技术具有标准支持。 REST 中的全局 ID 是 URL,当与数据库或后端应用程序交换时,由它定义每个连续的信息单元。 请参阅在 IRIS 中开发 REST 服务的文档。 在我们的示例中,基础标识符类似于 IRIS 服务器地址的基础 (http://localhost:52773),以及指向我们数据的 /world/ 子目录路径。 特别是我们的 /world/France 国家/地区目录。 在 Docker 容器中类似于: http://localhost:52773/world/France 如果要开发完整应用程序,务必查看 IRIS 文档推荐。 其中之一基于遵循 OpenAPI 2.0 规范的 REST API 描述。 让我们用简单的方法,手动实现 API。 在示例中,我们将创建最简单的 REST 解决方案,这个解决方案在 IRIS 中只需要两个步骤: 在 URL 内创建一个类路径监视器,它将继承 %CSP.REST 系统类 配置 IRIS web application时,添加对我们的监视器类的调用 第 1 步:创建类监视器 您应该清楚如何实现一个类。 按照文档中的说明手动创建REST。 Class User.worldrest Extends %CSP.REST { Parameter UseSession As Integer = 1; Parameter CHARSET = "utf-8"; XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ] { <Routes> <Route Url="/:name" Method="GET" Call="countryInfo" /> </Routes> } } 确保包括处理程序方法。 它执行的功能应该与先前示例中终端的调用完全相同: ClassMethod countryInfo(name As %String) As %Status { set countryInfo = ##class(User.world).WhereName(name) write "Country: ", countryInfo.name write "<br>" write "Population: ", countryInfo.population return $$$OK } 如您所见,以冒号开头的参数 :name 表示从传入的 REST 查询将监视器中被调用的处理程序方法的名称传递给参数。 第 2 步:配置 IRIS web application 在 System Administration > Security > Applications > Web Applications 下, 添加一个新的 web application,其 URL 入口地址为 /world,并添加以下处理程序:our worldrest monitor class。 配置完成后,转到 http://localhost:52773/world/France 时,web application应立即响应。 请记住,数据库区分大小写,因此在向方法参数传输请求数据时必须使用正确的字母大小写。 小诀窍: 根据需要使用调试工具。 您可以在这篇由两部分组成的文章中找到很好的描述(评论也值得一看)。 > 如果出现错误“401 Unauthorized”,并且您确定监视器类在服务器上,链接中也没有错误,请尝试在 Web 应用程序设置的 Application Roles 标签页添加 %All 角色。 这不是一种完全安全的方法,您需要了解允许访问所有角色可能带来的影响,但是这对于本地安装是可以接受的。 GraphQL 这是一个全新的领域,因为您在当前的 IRIS 文档中找不到关于使用 GraphQL 的 API 的任何内容。 然而,我们不能就这样停止使用这个出色的工具。 距 GraphQL 公开推出不过五年。 GraphQL 由 Linux Foundation 开发,是 API 的查询语言。 可以肯定地说,这是 REST 架构和各种 Web API 改进所带来的最好的技术。 这是是一篇简短介绍,供初学者参考。 并且,在 InterSystems 爱好者和工程师的努力下,IRIS 从 2018 年开始提供 GraphQL 支持。 > 这是一篇相关文章《为 InterSystems 平台实现 GraphQL》。 这里是 GraphQL 理解、解释和实现。 GraphQL 应用程序由两个模块组成:IRIS 侧的应用程序后端和在浏览器中运行的前端部分。 换句话说,您需要根据 GraphQL 和 GraphiQL Web 应用程序的说明进行配置。 例如,这是我的应用程序配置在 Docker 容器内的 IRIS 中的样子。 这些是 GraphQL Web 应用程序(充当 REST 监视器和数据库架构处理程序)的设置: 第二个 GraphQL 应用程序是浏览器的用户界面,使用 HTML 和 JavaScript 编写: 可前往 http://localhost:52773/graphiql/index.html 运行。 如果没有额外限制性设置,应用程序将立即拾取在安装区域中能找到的所有数据库schema。 这意味着我们的示例将立即开始工作。 另外,前端还提供了来自可用对象的一系列良好清晰的提示。 这是我们数据库的示例 GraphQL 查询: { User_world ( name: France ) { name population } } 这是匹配的响应: { "data": { "User_world": [ { "name": "France", "population": 65906000 } ] } } 在浏览器中是这样: 总结 技术名称 技术历史 查询示例 SQL 50 年 Edgar F. Codd SELECT population FROM world WHERE name = 'France' OOP 40 年 Alan Kay 和 Dan Ingalls set countryInfo = ##class(User.world).WhereName("France") REST 20 年 Roy Thomas Fielding http://localhost:52773/world/France GraphQL 5 年 Lee Byron { User_world ( name: France ) { name p opulation } } SQL、REST,可能还有 GraphQL 等技术均已获得大量投入。 它们的背后也都有着丰富的历史。 在 IRIS 平台内,这些技术都可以良好配合,创建数据处理程序。 虽然本文未提及,但 IRIS 也支持其他已充分实现的基于 XML (SOAP) 和 JSON 的 API。 除非您会特别注意,比如整理对象,否则,请记住通过 API 交换的数据仍然代表不完整的、精简的对象传输版本。 作为开发者,您(而不是代码)有责任确保正确地传输对象的数据类型信息。 尊敬的读者,请您提供反馈 本文的目的不仅仅是与现代 API 进行比较,更不是要回顾 IRIS 的基本功能。 它是为了帮助您了解,访问数据库时在 API 之间切换是多么容易。向 IRIS 迈出第一步,马上就能从任务中快速获得结果。 因此,我很想知道您的想法: 这种方式能否帮助您上手使用软件? 哪些流程步骤使您难以掌握用于在 IRIS 中使用 API 的工具? 您能说出一个您先前无法预见的障碍吗? 如果您认识仍在学习如何使用 IRIS 的用户,请让这些用户在下方发表评论。 这可以引起让所有参与者都能受益的讨论。
文章
姚 鑫 · 四月 2, 2021

第十五章 使用管理门户SQL接口(一)

# 第十五章 使用管理门户SQL接口(一) 本章介绍如何在InterSystems IRIS®数据平台管理门户上执行SQL操作。 管理门户界面使用动态SQL,这意味着在运行时准备和执行查询。 Management Portal界面旨在帮助针对小型数据集开发和测试SQL代码。 它不打算用作在生产环境中执行SQL的接口。 管理门户还提供了各种配置SQL的选项。 有关使用管理门户的一般信息,请选择左上角的Help按钮。 通过使用左上角的Contact按钮,可以从管理门户向InterSystems Worldwide Response Center (WRC)报告有关InterSystems软件的问题。 # 管理门户SQL工具 InterSystems IRIS允许使用SQL工具从InterSystems IRIS管理门户检查和操作数据。 此操作的起点是Management Portal System Explorer选项。 从这里选择SQL选项。 这将显示SQL接口,它允许: - 执行SQL查询—编写和执行SQL命令。 可以对现有的表和数据执行SQL查询,创建表,或插入、更新或删除表数据。 可以编写SQL代码直接转化为一个文本框(包括选择、插入、更新、删除、创建表和其他SQL语句),检索语句的SQL历史文本框,拖拽一个表到文本框来生成一个查询(`SELECT`语句),或构成一个查询(`SELECT`语句)使用`query Builder`接口。 - 过滤模式内容——在屏幕左侧显示当前名称空间的SQL模式或这些模式的过滤子集,以及每个模式的表、视图、过程和缓存查询。 可以选择单独的表、视图、过程或缓存查询来显示其目录详细信息。 - 向导—执行向导,以执行数据导入、导出或数据迁移。 执行向导以链接到表或视图,或链接到存储过程。 - Actions -定义一个视图; 打印一个表定义的详细信息; 通过运行调优表和/或重建索引提高查询的性能; 或者通过清除不需要的缓存查询和/或删除不需要的表、视图或过程定义来执行清理。 - 打开表格——以显示模式在表格中显示当前数据。 这通常不是表中的完整数据:记录的数量和列中的数据长度都受到限制,以提供可管理的显示。 - 工具——执行以下工具之一:SQL运行时统计、索引分析器、备用显示计划、生成报告、导入报告。 - 文档—允许查看SQL错误代码列表和SQL保留字列表。 如果选择了一个表,则允许显示类文档(该表的类引用页)。 ## 选择命名空间 所有SQL操作都会在特定名称空间中进行。因此,必须首先指定要通过单击SQL接口页面顶部的 **“开关switch”** 选项要使用的命名空间。这将显示可用名称空间列表,可以从中进行选择。 可以设置管理门户默认命名空间。从管理门户选择系统管理,安全性,用户。单击所需用户的名称。这允许编辑用户定义。从“常规”选项卡中,从下拉列表中选择“启动命名”空间。单击“保存”。如果未选择启动命名空间,则会默认为%SYS.。 ## 用户自定义 许多Management Portal SQL操作都是为每个用户自动定制的。 如果在Execute Query选项卡或SQL Statements选项卡中设置了筛选器、最大值、模式或其他选项,则此用户指定的值将保留以供将来使用。 当同一个用户激活管理门户时,将显示该用户先前的设置。 重新启动InterSystems IRIS返回所有选项为默认值。 没有自定义名称空间选择。 它恢复到用户定义启动名称空间。 # 执行SQL查询 从管理门户选择System Explorer,然后选择SQL。 在页面顶部选择带有Switch选项的名称空间; 这将显示可用名称空间的列表。 要执行SQL查询,有三个选项: - Execute Query:写并执行SQL命令。 SQL命令可以是一个`SELECT`查询,也可以是一个InterSystems SQL DDL或DML语句; 语句执行时在InterSystems IRIS服务器上验证。 - Show History:收回以前运行的SQL语句,然后重新运行它,或者修改它,然后运行它。 列出所有已执行的语句,包括未成功执行的语句。 - 查询生成器:调用SQL查询生成器(它专门用于创建SELECT语句)。 在SQL Query Builder中,通过选择表、列、`WHERE`子句谓词和其他查询组件来创建SQL `SELECT`查询。 然后,可以通过单击Execute query来运行查询。 ## 编写SQL语句 Execute Query文本框不仅允许编写`SELECT`和`CALL`查询,还允许编写大多数SQL语句,包括DDL语句(如`CREATE TABLE`)和DML语句(如`INSERT`、`UPDATE`和`DELETE`)。 可以在“执行查询”文本框中指定SQL代码: - 将SQL代码键入(或粘贴)到文本框中。 SQL代码区域不给SQL文本着色,也不提供任何语法或存在验证。 但是,它确实提供了自动拼写验证。 可以使用X图标删除文本框的内容。 - 使用Show History列表选择前面的SQL语句。 选中的语句将复制到文本框中。 执行时,该语句移到Show History列表的顶部。 注意,Show History列出了之前执行的所有语句,包括那些执行失败的语句。 - 使用表拖放在文本框中构造SQL代码。 - 可以使用Query Builder(而不是Execute Query文本框)来指定和执行`SELECT`查询。 使用查询生成器执行的选择查询不会显示在“执行查询”中,也不会列出在“显示历史”中。 Execute Query文本框中的SQL代码可以包括: - ?输入参数。如果指定输入参数,例如 `TOP ? or WHERE Age BETWEEN ? AND ?`,`Execute`按钮显示查询窗口的Enter参数值,其中每个输入参数的条目字段按查询中指定的顺序。 - 空白字符。可以指定多个空格,单个和多行返回。标签键已禁用;将代码复制到SQL代码区域时,现有选项卡将转换为单个空格。线返回和未保留多个空格。 - 注释。 SQL代码区域支持单行和多行注释。在Show历史显示中保留并显示注释。在Show Plan语句文本显示或缓存查询中未显示注释。 - 返回多个结果集的查询。 在文本框中编写SQL代码后,可以单击“显示计划”按钮查看SQL代码而不执行SQL代码。如果代码有效,则显示计划显示查询计划。如果代码无效,则显示计划显示SQLCode错误值和消息。还可以使用“显示计划”按钮显示最近执行的SQL代码的此信息。 要执行SQL代码,请单击“执行”按钮。 ### 表拖放 可以通过从屏幕左侧的表列表(或视图列表)拖动表(或视图)来生成查询,并将其丢弃到执行查询文本框中。这在表中生成了选择的选项列表,以及指定表的表中的所有非隐藏字段。然后,可以进一步修改此查询并使用Execute按钮执行它。 还可以从屏幕左侧的过程列表中拖放过程名称。 ## 执行查询选项 SQL执行界面具有以下选项: - 具有`SELECT`的“选择模式下拉列表”指定查询应用于提供数据值(例如,在WHERE子句中)的格式,并在查询结果集中显示数据值。选项是显示模式(默认值),ODBC模式和逻辑模式。 具有插入或更新的选择模式下拉列表允许指定输入数据是否将从显示格式转换为逻辑存储格式。对于此数据转换,必须使用选择运行时的选择模式编译SQL代码。在执行时间时,必须将“选择模式”下拉列表设置为逻辑模式。 选择模式对于数据类型是有意义的,其逻辑存储格式与所需的显示格式(显示或ODBC)不同,例如Intersystems Iris日期和时间和Objectscript`%List`结构化数据。 - 最大字段允许限制从查询返回的数量数量。它可以设置为任何正整数,包括0.一旦设置MAX,除非显式更改,否则将该值用于会话持续时间的所有查询。默认值为1000.最大值为100,000,如果输入没有值(将MAX设置为NULL),则输入大于100,000或非数值的值,这是默认值。还可以使用顶部子句限制要返回的数据行数。 MAX对其他SQL语句没有影响,例如删除。 如果单击“更多”选项,则SQL执行界面将显示以下其他选项: - 方言:SQL代码的方言。包括“IRIS”、“Sybase”和“MSSQL”。默认为IRIS。 在InterSystems Transact-SQL (TSQL)迁移指南中描述了Sybase和MSSQL。 请注意,下次访问管理门户时,选择的方言将成为用户自定义的默认语言。 - 行号:一个复选框,指定是否在结果集中显示的每一行中包含行计数号。 行号是分配给结果集中每一行的连续整数。它只是对返回的行进行编号,它既不对应rowwid也不对应`%VID`。行号列标题名是#。默认是显示行号。 所有这些选项都是用户自定义的。 ## 显示计划按钮 Show Plan按钮在页面的文本框中显示语句文本和查询计划,包括查询的当前查询计划的相对成本(开销)。可以从Execute查询或Show History接口调用Show Plan。查询计划是在准备(编译)查询时生成的; 当编写查询并选择Show Plan按钮时,就会发生这种情况。不必执行查询来显示其查询计划。Show Plan在为无效查询调用时显示`SQLCODE`和错误消息。 ## SQL语句的结果 在“执行查询”文本框中编写SQL代码之后,可以通过单击“执行”按钮来执行代码。这要么成功执行SQL语句并在代码窗口下面显示结果,要么SQL代码失败。如果SQL代码失败,它会在code窗口下面显示一条错误消息(红色); 按下Show Plan按钮将显示`SQLCODE`错误和错误消息。 执行查询SQL代码执行作为后台进程执行。在执行代码时,Execute按钮被Cancel按钮替换。这允许取消长时间运行的查询的执行。 ### 查询数据显示 如果选中了行号框,结果集将作为表返回,行计数器将显示为第一列(`#`)。 其余的列将按照指定的顺序显示。RowID (ID字段)可以显示或隐藏。每个列都由列名(如果指定了,也可以是列别名)标识。聚合、表达式、子查询、主机变量或文字选择项可以由列别名(如果指定)标识,或者由单词`Aggregate_`、`Expression_`、`Subquery_`、`HostVar_`或`Literal_`后跟选择项序列号(默认情况下)标识。 如果行列不包含数据(`NULL`),结果集将显示一个空白的表格单元格。 指定一个空字符串文本将显示一个`HostVar_`字段,其中包含一个空白的表格单元格。 指定`NULL`显示一个带有空白单元格的`Literal_`字段。 如果选择的字段是日期、时间、时间戳或%List编码的字段,则显示的值取决于显示模式。 以下显示特性是管理门户SQL接口独有的,执行查询结果显示和打开表数据显示: - 数据类型`%Stream.Globalcharacter`的流字段将实际数据(最多100个字符)作为字符串显示。如果流字段中的数据长于100个字符,则显示数据的前100个字符,后跟省略的省略号(`...`)。 - 数据类型`%Stream.GlobalBinary`作为的流字段。 - 字符串数据字段根据需要,以完整的方式显示实际数据。 - Integer字段在结果表单元格中右对齐。 `ROWID`,`NUMERIC`和所有其他字段都是左对齐的。 当使用动态SQL代码,SQL Shell或嵌入式SQL代码执行相同的查询时,不会发生这些结果显示功能。 如果指定的查询返回多个结果集,则执行查询将这些结果集显示为命名选项卡:`Result #1`, `Result #2`等。 ### 查询执行指标 如果成功,则执行查询显示性能信息和缓存查询例程的名称。如果显示数据以显示,则显示在性能信息下方。执行信息包括行计数,性能,缓存查询,显示缓存的查询名称,最后更新指定查询的最后一次执行的时间戳。 - Row count:对于`CREATE TABLE`这样的DDL语句,如果操作成功,则显示`Row count: 0`。 对于`INSERT`、`UPDATE`或`DELETE`等DML语句,显示受影响的行数。 对于`TRUNCATE TABLE`语句,快速`TRUNCATE`操作不能确定实际删除的行数,而是设置行数:-1。 对于`SELECT`,显示作为结果集返回的行数。注意,返回的行数由Max设置控制,它可能低于可以选择的行数。 对于多个结果集,列出每个结果集的行数,用/字符分隔。 指定一个或多个聚合函数(且没有选择字段)的查询总是显示`Row count: 1`,并返回表达式、子查询和聚合函数的结果,即使`FROM`子句表不包含行。 一个不指定聚合函数和不选择行的查询总是显示`Row count: 0`并且不返回结果,即使该查询只指定不引用`FROM`子句表的表达式和子查询。 带`no FROM`子句的查询总是显示`行数:1`,并返回表达式、子查询和聚合函数的结果。 - 性能:以运行时间(以秒为单位)、全局引用总数、执行的命令总数和磁盘读取延迟(以毫秒为单位)来衡量。 如果该查询存在缓存的查询,那么这些性能指标将用于执行缓存的查询。 因此,查询的第一次执行将比后续执行具有更高的性能指标。 如果指定的查询返回多个结果集,那么这些性能指标就是所有查询的总和。 要更深入地分析这些性能指标,可以运行`MONLBL`(逐行监视实用程序)并使用星号通配符`%sqlcq*`指定例程名称。 请参考使用`^%SYS.MONLBL`检查例程性能。 - 缓存查询:自动生成的缓存查询类名。 例如,`%sqlcq.USER.cls2`表示用户名称空间中的第二个缓存查询。 每个新的查询被分配一个新的缓存的查询名称,该名称具有下一个连续的整数。 通过单击此缓存查询名称,以显示关于缓存查询的信息,以及显示其显示计划或执行缓存查询的进一步链接。 关闭管理门户或停止InterSystems IRIS不会删除缓存的查询或重置缓存的查询编号。 要从当前命名空间中清除缓存的查询,请调用%`SYSTEM.SQL.Purge()`方法。 并不是所有的SQL语句都会导致缓存的查询。与现有缓存查询相同的查询,除了文字替换值(例如`TOP`子句值和谓词文字)之外,不会创建新的缓存查询。有些SQL语句是不缓存的,包括DDL语句和权限分配语句。 非查询SQL语句,如`CREATE TABLE`,也会显示缓存的查询名。 然而,这个缓存的查询名称被创建然后立即删除; 下一个SQL语句(查询或非查询)重用相同的缓存查询名称。 - 最后一次更新:最后一次执行查询(或其他SQL操作)的日期和时间。 这个时间戳在每次执行查询时都被重置,即使在重复执行相同的查询时也是如此。 - 成功执行还提供了一个打印链接显示打印查询窗口,它给你选择打印或导出到一个文件中查询文本和/或查询的结果集。点击查询和结果切换使可以显示或隐藏文本或查询结果集的查询,查询结果集显示包含名称空间的名字,结果集的数据行数,一个时间戳,缓存的查询名称。 (注意,时间戳是调用Print查询窗口的时间,而不是执行查询的时间。) “打印查询”按钮用于打印查询窗口的屏幕截图。 “导出到文件”复选框显示指定导出文件格式(`xml、hdml、pdf、txt、csv`)和导出文件路径名的选项。 Export选项忽略查询和结果切换,并始终只导出结果集数据(默认为`:exportQuery.pdf`)和行数(默认为`:exportQueryMessages.pdf`); 不包括查询文本、名称空间、时间戳和缓存的查询名称。 如果不成功,则Execute Query显示错误消息。 可以单击Show Plan按钮来显示相应的`SQLCODE`错误值和消息。 ## 显示历史 单击“显示历史记录”可列出当前会话期间执行的SQL语句。 Show History列出从该接口调用的所有SQL语句,包括那些成功执行和那些执行失败的语句。 默认情况下,SQL语句按执行时间列出,最近执行的语句出现在列表的顶部。可以单击任何列标题,根据列值按升序或降序排列SQL语句。从Show History列表中执行SQL语句将更新其执行时间(本地日期和时间戳),并增加其计数(执行次数)。 可以过滤Show History列表,如下所示:在过滤框中指定一个字符串,然后按Tab键。只有包含该字符串的历史项才会包含在刷新后的列表中。 筛选器字符串可以是在SQL语句列中找到的字符串(比如表名),也可以是在执行时间列中找到的字符串(比如日期)。 过滤字符串不区分大小写。 在显式地更改过滤器字符串之前,它将一直有效。 通过选择语句,可以在“Show History”中修改和执行SQL语句,该语句将显示在“execute Query”文本框中。 在“执行查询”中,可以修改SQL代码,然后单击“执行”。 对从Show History中检索到的SQL语句进行任何更改,都会将其作为新语句存储在Show History中; 这包括不影响执行的更改,如更改字母大小写、空格或注释。 空格不会显示在Show History中,但是当从Show History中检索SQL语句时,会保留空格。 通过单击Show History列表中SQL语句右侧的execute按钮,可以直接从Show History列表中执行(重新运行)未修改的SQL语句。 注意,Show History列表与缓存查询列表不同。 Show History列出当前会话中调用的所有SQL语句,包括那些在执行过程中失败的语句。 ## 其他SQL接口 InterSystems IRIS支持许多其他编写和执行SQL代码的方法,在本手册的其他章节中有描述。 这些包括: - 嵌入式SQL:嵌入ObjectScript代码中的SQL代码。 - 动态SQL:使用%SQL。 语句类方法(或其他结果集类方法)用于从ObjectScript代码中执行SQL语句。 - SQL Shell:在终端使用SQL Shell接口执行动态SQL。
文章
姚 鑫 · 四月 14, 2021

第二章 定义和构建索引(二)

# 第二章 定义和构建索引(一) # 定义索引 ### 使用带有索引的Unique、PrimaryKey和IdKey关键字 与典型的SQL一样,InterSystems IRIS支持惟一键和主键的概念。 InterSystems IRIS还能够定义`IdKey`,它是类实例(表中的行)的唯一记录ID。 这些特性是通过`Unique`、`PrimaryKey`和`IdKey`关键字实现的: - `Unique` -在索引的属性列表中列出的属性上定义一个唯一的约束。 也就是说,只有这个属性(字段)的唯一数据值可以被索引。 唯一性是根据属性的排序来确定的。 例如,**如果属性排序是精确的,则字母大小写不同的值是唯一的; 如果属性排序是`SQLUPPER`,则字母大小写不同的值不是唯一的。** 但是,请注意,对于未定义的属性,不会检查索引的惟一性。 根据SQL标准,未定义的属性总是被视为唯一的。 - `PrimaryKey` -在索引的属性列表中列出的属性上定义一个主键约束。 - `IdKey` -定义一个唯一的约束,并指定哪些属性用于定义实例(行)的唯一标识。 `IdKey`总是具有精确的排序规则,即使是数据类型为`string`时也是如此。 这些关键字的语法出现在下面的例子中: ```java Class MyApp.SampleTable Extends %Persistent [DdlAllowed] { Property Prop1 As %String; Property Prop2 As %String; Property Prop3 As %String; Index Prop1IDX on Prop1 [ Unique ]; Index Prop2IDX on Prop2 [ PrimaryKey ]; Index Prop3IDX on Prop3 [ IdKey ]; } ``` **注意:`IdKey`、`PrimaryKey`和`Unique`关键字只在标准索引下有效。 不能将它们与位图或位片索引一起使用。** 同时指定`IdKey`和`PrimaryKey`关键字也是有效的语法,例如: ``` Index IDPKIDX on Prop4 [ IdKey, PrimaryKey ]; ``` 这个语法指定`IDPKIDX`索引既是类(表)的`IdKey`,也是它的主键。 这些关键字的所有其他组合都是多余的。 对于使用这些关键字之一定义的任何索引,都有一个方法允许打开类的实例,其中与索引关联的属性有特定的值; ### 定义SQL搜索索引 可以在表类定义中定义SQL搜索索引,如下所示: ```java Class Sample.TextBooks Extends %Persistent [DdlAllowed] { Property BookName As %String; Property SampleText As %String(MAXLEN=5000); Index NameIDX On BookName [ IdKey ]; Index SQLSrchIDXB On (SampleText) As %iFind.Index.Basic; Index SQLSrchIDXS On (SampleText) As %iFind.Index.Semantic; Index SQLSrchIDXA On (SampleText) As %iFind.Index.Analytic; } ``` ### 用索引存储数据 **可以使用index Data关键字指定一个或多个数据值的副本存储在一个索引中:** ```java Class Sample.Person Extends %Persistent [DdlAllowed] { Property Name As %String; Property SSN As %String(MAXLEN=20); Index NameIDX On Name [Data = Name]; } ``` 在本例中,索引`NameIDX`的下标是各种`Name`值的排序(大写)值。名称的实际值的副本存储在索引中。当通过SQL更改`Sample.Person`表或通过对象更改对应的`Sample.Person`类或其实例时,将维护这些副本。 在经常执行选择性(从许多行中选择一些行)或有序搜索(从许多列中返回一些列)的情况下,在索引中维护数据副本会很有帮助。 例如,考虑以下针对`Sample.Person`表的查询: SQL引擎可以通过读取`NameIDX`而从不读取表的主数据来决定完全满足此请求。 ![image](/sites/default/files/inline/images/1_34.png) **注意:不能使用位图索引存储数据值。** ### 索引null 如果一个索引字段的数据为`NULL`(没有数据存在),相应的索引使用索引`NULL`标记来表示这个值。 **默认情况下,索引空标记值为`-1E14`。** 使用索引空标记可以使空值排序在所有非空值之前。 `%Library.BigInt`数据类型存储小于`-1E14`的小负数。默认情况下,`%BigInt`索引空标记值为`-1E14`,因此与现有`BigInt`索引兼容。如果索引的`%BigInt`数据值可能包括这些极小的负数,则可以使用`INDEXNULLMARKER`属性参数更改特定字段的索引`NULL`标记值,作为特性定义的一部分,如下例所示: ```java Property ExtremeNums As %Library.BigInt(INDEXNULLMARKER = "-1E19"); ``` 还可以在数据类型类定义中更改索引`NULL`标记的默认值。 **此参数属性在IRIS里有,Cache里没有。** ### 索引集合 为属性编制索引时,放在索引中的值是整个已整理属性值。对于集合,可以通过将(`Elements`)或(`Key`)附加到属性名称来定义与集合的元素和键值相对应的索引属性。(元素)和(键)允许指定从单个属性值生成多个值,并对每个子值进行索引。当属性是集合时,`Elements`令牌通过值引用集合的元素,`Key`令牌通过位置引用它们。当元素和键都出现在单个索引定义中时,索引键值包括键和关联的元素值。 例如,假设有一个基于`Sample.Person`类的`FavoriteColors`属性的索引。对此属性集合中的项进行索引的最简单形式是以下任一种: ```java INDEX fcIDX1 ON (FavoriteColors(ELEMENTS)); ``` 或 ```java INDEX fcIDX2 ON (FavoriteColors(KEYS)); ``` 其中,`FavoriteColor(Elements)`是指`FavoriteColors`属性的元素,因为它是一个集合。一般形式是`PropertyName`(元素)或`PropertyName`(键),其中该集合的内容是定义为某个数据类型的列表或数组的属性中包含的一组元素)。 若要索引文本属性,可以创建一个由`PropertyNameBuildValueArray()`方法生成的索引值数组(在下一节中介绍)。与集合本身一样,(Elements)和(Key)语法对索引值数组有效。 如果属性集合被投影为数组,则索引必须遵守以下限制才能被投影到集合表。索引必须包括(键)。索引不能引用集合本身和对象ID值以外的任何属性。如果投影索引还定义了要存储在索引中的数据,则存储的数据属性也必须限制为集合和ID。否则,不会投影索引。此限制适用于投影为数组的集合属性上的索引;不适用于投影为列表的集合上的索引。 与集合的元素或键值对应的索引还可以具有所有标准索引功能,例如将数据与索引一起存储、特定于索引的排序规则等。 InterSystems SQL可以通过指定`FOR SOME%ELEMENT`谓词来使用集合索引。 ### 使用(Elements)和(Key)索引数据类型属性 为了索引数据类型属性,还可以使用`BuildValueArray()`方法创建索引值数组。此方法将属性值解析为键和元素的数组;它通过生成从与其关联的属性的值派生的元素值集合来实现这一点。使用`BuildValueArray()`创建索引值数组时,其结构适合索引。 `BuildValueArray()`方法的名称为`PropertyNameBuildValueArray()`,其签名为: ```java ClassMethod propertynameBuildValueArray(value, ByRef valueArray As %Library.String) As %Status ``` - `BuildValueArray()`方法的名称以组合方法的典型方式派生于属性名。 - 第一个参数是属性值。 - 第二个参数是通过引用传递的数组。 这是一个包含键-元素对的数组,键下标的数组等于元素。 - 该方法返回一`%Status` 值。 这个例子: ```java /// DescriptiveWords是一个以逗号分隔的单词字符串 Property DescriptiveWords As %String; /// 基于描述词的索引 Index dwIDX On DescriptiveWords(ELEMENTS); /// 方法的作用是:演示如何在属性的子值上建立索引。 /// /// (如果DescriptiveWords被定义为一个集合,则不需要此方法。) ClassMethod DescriptiveWordsBuildValueArray( Words As %Library.String = "", ByRef wordArray As %Library.String) As %Status { If Words '= "" { For tPointer = 1:1:$Length(Words,",") { Set tWord = $Piece(Words,",",tPointer) If tWord '= "" { Set wordArray(tPointer) = tWord } } } Else { Set wordArray("TODO") = "Enter keywords for this person" } Quit $$$OK } ``` 在本例中,`dwIDX`索引基于`DescriptiveWords`属性。 `DescriptiveWordsBuildValueArray()`方法接受由`Words`参数指定的值,基于该值创建一个索引值数组,并将其存储在`wordArray`中。 InterSystems IRIS在内部使用`BuildValueArray()`实现; 不调用此方法。 注意:没有必要将任何元素/键值建立在属性值的基础上。 唯一的建议是,每次向该方法传递给定值时,都创建相同的元素和键数组。 为各种实例的描述性词所属性设置值和检查这些值的属性涉及活动(如以下): ```java SAMPLES>SET empsalesoref = ##class(MyApp.Salesperson).%OpenId(3) SAMPLES>SET empsalesoref.DescriptiveWords = "Creative" SAMPLES>WRITE empsalesoref.%Save() 1 SAMPLES>SET empsalesoref = ##class(MyApp.Salesperson).%OpenId(4) SAMPLES>SET empsalesoref.DescriptiveWords = "Logical,Tall" SAMPLES>WRITE empsalesoref.%Save() 1 ``` 这 `sample index`内容,例如: DescriptiveWords(ELEMENTS) | ID | Data ---|---|--- " CREATIVE" |3 |"" " ENTER KEYWORDS FOR THIS PERSON"| 1 |"" " ENTER KEYWORDS FOR THIS PERSON"| 2| "" " LOGICAL" |4| "" " TALL"| 4| "" 注意:此表显示抽象中的索引内容。磁盘上的实际存储形式可能会有所变化。 ### 将数组(元素)上的索引投影到子表 要在嵌入式对象中索引属性,需要在引用该嵌入式对象的持久化类中创建索引。 属性名必须指定表(`%Persistent`类)中的引用字段的名称和嵌入对象(`%SerialObject`)中的属性的名称,如下面的示例所示: ```java Class Sample.Person Extends (%Persistent) [ DdlAllowed ] { Property Name As %String(MAXLEN=50); Property Home As Sample.Address; Index StateInx On Home.State; } ``` 此处`Home`是`Sample.Person`中引用嵌入对象`Sample.Address`的属性,该对象包含`State`属性,如下例所示: ```java Class Sample.Address Extends (%SerialObject) { Property Street As %String; Property City As %String; Property State As %String; Property PostalCode As %String; } ``` **只有与持久类属性引用相关联的嵌入对象的实例中的数据值被索引。不能直接索引`%SerialObject`属性**。`%Library.SerialObject`(以及`%SerialObject`的所有未显式定义`SqlCategory`的子类)的`SqlCategory`为字符串。 还可以使用`SQL CREATE INDEX`语句在嵌入式对象属性上定义索引,如下例所示: ```sql CREATE INDEX StateIdx ON TABLE Sample.Person (Home_State) ``` ### 类中定义的索引注释 当在类定义中使用索引时,需要记住以下几点: - **索引定义仅从主(第一个)超类继承。** - **如果使用Studio添加(或删除)数据库中存储数据的类的索引定义,则必须使用“构建索引”中描述的过程之一来手动填充索引。** ## 使用DDL定义索引 如果你使用DDL语句来定义表,也可以使用以下DDL命令来创建和删除索引: - `CREATE INDEX` - `DROP INDEX` `DDL index`命令执行以下操作: 1. 它们更新在其上添加或删除索引的相应类和表定义。 重新编译修改后的类定义。 2. 它们根据需要在数据库中添加或删除索引数据:`CREATE index`命令使用当前存储在数据库中的数据填充索引。 类似地,`DROP INDEX`命令从数据库中删除索引数据(即实际索引)。
文章
姚 鑫 · 三月 28, 2021

第十三章 使用动态SQL(六)

# 第十三章 使用动态SQL(六) ### 用`%ObjectSelectMode = 1` Swizzling字段名称属性 下面的示例使用`%ObjectSelectMode = 1`进行准备,当使用字段名称属性返回值时,其类型类别为可Swizzle类型的字段(持久性类,序列类或流类)将自动发生Swizzle。转换字段值的结果是相应的对象参考(oref)。使用`%Get()`或`%GetData()`方法访问字段时,InterSystems IRIS不会执行此筛选操作。在此示例中,`rset.Home`处于Swizzle状态,而引用同一字段的`rset.%GetData(2)`处于not swizzled状态: ```java /// d ##class(PHA.TEST.SQL).PropSQL2() ClassMethod PropSQL2() { SET myquery = "SELECT TOP 5 Name,Home FROM Sample.Person" SET tStatement = ##class(%SQL.Statement).%New(0) SET tStatement.%ObjectSelectMode=1 WRITE !,"set ObjectSelectMode=",tStatement.%ObjectSelectMode,! SET qStatus = tStatement.%Prepare(myquery) IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } SET rset = tStatement.%Execute() WHILE rset.%Next() { WRITE "Row count ",rset.%ROWCOUNT,! WRITE rset.Name WRITE " ",rset.Home,! WRITE rset.%GetData(1) WRITE " ",$LISTTOSTRING(rset.%GetData(2)),!! } WRITE !,"End of data" WRITE !,"Total row count=",rset.%ROWCOUNT } ``` ```java DHC-APP> d ##class(PHA.TEST.SQL).PropSQL2() set ObjectSelectMode=1 Row count 1 yaoxin 5@Sample.Address yaoxin 889 Clinton Drive,St Louis,WI,78672 Row count 2 xiaoli 5@Sample.Address xiaoli Row count 3 姚鑫 5@Sample.Address 姚鑫 Row count 4 姚鑫 5@Sample.Address 姚鑫 ``` 下面的示例使用`%ObjectSelectMode = 1`从唯一记录ID(`%ID`)导出所选记录的`Home_State`值。请注意,在原始查询中未选择`Home_State`字段: ```java /// d ##class(PHA.TEST.SQL).PropSQL2() ClassMethod PropSQL2() { SET myquery = "SELECT TOP 5 %ID AS MyID,Name,Age FROM Sample.Person" SET tStatement = ##class(%SQL.Statement).%New() SET tStatement.%ObjectSelectMode=1 SET qStatus = tStatement.%Prepare(myquery) IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT} SET rset = tStatement.%Execute() WHILE rset.%Next() { WRITE rset.Name WRITE " Home State:",rset.MyID.Home.State,! } WRITE !,"End of data" WRITE !,"Total row count=",rset.%ROWCOUNT } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).PropSQL2() yaoxin Home State:WI xiaoli Home State: 姚鑫 Home State: 姚鑫 Home State: 姚鑫 Home State: End of data Total row count=5 ``` 如果已配置,则如果配置了swizzled属性,但系统无法生成引用,则系统会生成``错误。如果引用的属性已从磁盘中意外删除或被另一个进程锁定,则会发生这种情况。要确定SWIZZLE失败的原因,请在``错误之后立即在`%objlasterror`中查找并解码此`%Status`值。 默认情况下,未配置``。可以通过设置`SET ^%SYS("ThrowSwizzleError")=1`或使用InterSystems IRIS管理门户来全局设置此行为。在“系统管理”中,选择“配置”,然后选择“ SQL和对象设置”,然后选择“对象”。在此屏幕上,可以设置``选项。 ## %Get("fieldname")方法 可以使用`%Get(“ fieldname”)`实例方法按字段名称或字段名称别名返回数据值。 Dynamic SQL根据需要解析字母大小写。如果指定的字段名称或字段名称别名不存在,系统将生成``错误。 下面的示例从查询结果集中返回`Home_State`字段和`Last_Name`别名的值。 ```java /// d ##class(PHA.TEST.SQL).PropSQL4() ClassMethod PropSQL4() { SET myquery = "SELECT TOP 5 Home_State,Name AS Last_Name FROM Sample.Person" SET tStatement = ##class(%SQL.Statement).%New(2) SET qStatus = tStatement.%Prepare(myquery) IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } SET rset = tStatement.%Execute() WHILE rset.%Next() { WRITE rset.%Get("Home_State")," : ",rset.%Get("Last_Name"),! } WRITE !,"End of data" WRITE !,"Total row count=",rset.%ROWCOUNT } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).PropSQL4() WI : yaoxin : xiaoli : 姚鑫 : 姚鑫 : 姚鑫 End of data Total row count=5 ``` 必须使用`%Get("fieldname")`实例方法从使用`%PrepareClassQuery()`准备的现有查询中按字段属性名称检索单个数据项。如果字段属性名称不存在,则系统会生成``错误。 下面的示例从内置查询中按字段属性名称返回Nsp(命名空间)字段值。因为此查询是现有的存储查询,所以此字段检索需要使用`%Get("fieldname") `方法。请注意,由于`“Nsp”`是属性名称,因此区分大小写: ```java /// d ##class(PHA.TEST.SQL).PropSQL5() ClassMethod PropSQL5() { SET tStatement = ##class(%SQL.Statement).%New(2) SET qStatus = tStatement.%PrepareClassQuery("%SYS.Namespace","List") IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } SET rset = tStatement.%Execute() WHILE rset.%Next() { WRITE "Namespace: ",rset.%Get("Nsp"),! } WRITE !,"End of data" WRITE !,"Total row count=",rset.%ROWCOUNT } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).PropSQL5() Namespace: %SYS Namespace: DHC-APP Namespace: DHC-CHSSWEB Namespace: DHC-CSM Namespace: DHC-DATA Namespace: DHC-DWR Namespace: DHC-EKG Namespace: DHC-HEIS Namespace: DHC-HR Namespace: DHC-LISDATA Namespace: DHC-LISSRC Namespace: DHC-MEDSRC Namespace: DHC-MRQ Namespace: DOCBOOK Namespace: FDBMS Namespace: PACS Namespace: PIS Namespace: RIS Namespace: SAMPLES Namespace: USER End of data Total row count=20 ``` 重复名称:如果名称解析为相同的属性名称,则它们是重复的。重复名称可以是对同一字段的多个引用,对表中不同字段的引用或对不同表中字段的引用。如果`SELECT`语句包含相同字段名称或字段名称别名的多个实例,则`%Get(“fieldname”)`始终返回查询中指定的重复名称的最后一个实例。这与`rset.PropName`相反,后者返回查询中指定的重复名称的第一个实例。在下面的示例中显示: ```java /// d ##class(PHA.TEST.SQL).PropSQL6() ClassMethod PropSQL6() { SET myquery = "SELECT c.Name,p.Name FROM Sample.Person AS p,Sample.Company AS c" SET tStatement = ##class(%SQL.Statement).%New() SET qStatus = tStatement.%Prepare(myquery) IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } SET rset = tStatement.%Execute() WHILE rset.%Next() { WRITE "Prop=",rset.Name," Get=",rset.%Get("Name"),! } WRITE !,rset.%ROWCOUNT," End of data" } ``` ## %GetData(n)方法 `%GetData(n)`实例方法返回由结果集的整数计数列号索引的当前行的数据您可以将`%GetData(n)`与使用`%Prepare()`准备的指定查询或使用`%PrepareClassQuery()`准备的存储查询一起使用。 使用`%PrepareClassQuery()`准备。 整数n对应于查询中指定的选择项列表的序列。除非在选择项列表中明确指定,否则不会为`RowID`字段提供整数n值。如果n大于查询中的选择项数,或者为0,或者为负数,则Dynamic SQL不返回任何值,也不发出错误。 `%GetData(n)`是返回特定重复字段名称或重复别名的唯一方法; `rset.Name`返回第一个重复项,`%Get(“Name”)`返回最后一个重复项。 ```java /// d ##class(PHA.TEST.SQL).PropSQL7() ClassMethod PropSQL7() { SET myquery="SELECT TOP 5 Name,SSN,Age FROM Sample.Person" SET tStatement = ##class(%SQL.Statement).%New() SET qStatus = tStatement.%Prepare(myquery) IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } SET rset = tStatement.%Execute() WHILE rset.%Next() { WRITE "Years:",rset.%GetData(3)," Name:",rset.%GetData(1),! } WRITE "End of data" WRITE !,"Total row count=",rset.%ROWCOUNT } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).PropSQL7() Years:30 Name:yaoxin Years: Name:xiaoli Years:7 Name:姚鑫 Years:7 Name:姚鑫 Years:43 Name:姚鑫 End of data Total row count=5 ``` # 返回多个结果集 `CALL`语句可以将多个动态结果集作为一个集合返回,称为结果集序列(RSS)。 下面的示例使用`%NextResult()`方法分别返回多个结果集: ```java /// d ##class(PHA.TEST.SQL).PropSQL8() ClassMethod PropSQL8() { SET mycall = "CALL Sample.CustomSets()" SET rset = ##class(%SQL.Statement).%ExecDirect(,mycall) IF rset.%SQLCODE'=0 { WRITE !,"ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message QUIT } SET rset1=rset.%NextResult() DO rset1.%Display() WRITE !,"End of 1st Result Set data",!! SET rset2=rset.%NextResult() DO rset2.%Display() WRITE !,"End of 2nd Result Set data" } ``` ```java DHC-APP> d ##class(PHA.TEST.SQL).PropSQL8() ID Name Street City State Spouse 3 Davis,Robert I. 4177 Franklin Court Fargo WY 86 2 Hanson,Roberta O. 9840 Ash Drive Boston KS 155 4 Huff,Olga M. 1902 Franklin Avenue Vail DE 150 1 Woo,Jocelyn A. 9932 Clinton Avenue Queensbury NM 14 5 Zubik,George T. 8102 First Drive Denver VA 110 5 Rows(s) Affected End of 1st Result Set data ID Name Street City State Spouse 5 Campos,Alvin N. 1847 Franklin Drive Ukiah WY 206 1 Fripp,Kristen A. 1487 Ash Place Islip NC 133 3 Jafari,Christen K. 7384 Washington Court Newton CO 168 4 Kratzmann,Mark V. 9573 Second Blvd Chicago OR 43 2 O'Donnell,George H. 3413 Main Drive Newton RI 143 7 Ravazzolo,Danielle Y. 2898 Clinton Blvd Tampa HI 133 10 Rodriguez,Sophia U. 4766 Clinton Avenue Ukiah AR 202 6 Sverdlov,Phyllis J. 5010 Oak Place Fargo VT 214 8 Uhles,Andrew O. 4931 Madison Street Bensonhurst IA 129 9 Xerxes,Mo C. 49 Main Drive Vail CA 151 10 Rows(s) Affected End of 2nd Result Set data ```
文章
Claire Zheng · 九月 9, 2021

优秀案例:吉大中日联谊医院“通关”互联互通五级乙等

在医院信息化领域,中日联谊医院最近一次站在行业瞩目的聚光灯下,是在2021年7月。在国家卫生健康委统计信息中心发布的《关于2020年度国家医疗健康信息互联互通标准化成熟度测评结果公示的通知》中,9家医院获评五级乙等,中日联谊医院名列其中。这也是目前为数不多的“通关”互联互通五乙的医院之一。2021年是《国家医疗健康信息医院信息互联互通标准化成熟度测评方案(2020年版)》(以下简称新版测评方案)施行的第一个年头。由于测评方案的细化、参评机构的数量增多,业界普遍反映医院互联互通测评的整体难度正在逐年增大。而五级乙等,已是医疗机构目前所能取得的最高级别。 在互联互通新版测评方案中,对互联互通功能的“实际落地”与“有效应用”提出了很高的要求。在五级乙等的分级要求中就明确提出:“实现公众服务应用功能数量不少于27个、医疗服务应用功能数量不少于30个;提供较为完善的互联网诊疗服务,初步实现基于平台的临床决策支持、闭环管理、大数据应用。” 医院信息化建设立足于实际应用,为医院的医疗服务与运营管理提供助力,这是中日联谊医院能够获评五级乙等的重要原因之一。 医院在参与测评的过程中,遇到的首要问题即异构系统的接入问题。互联互通新版测评方案对医院信息系统内外联通业务的数量提出了明确要求,其中对五级乙等的分级要求是:集成平台至少需要接入54个内部业务系统,以及不少于7个外部机构数量,实现基于平台的内部与外部联通业务。 为达到上述要求,医疗机构需有效解决异构系统间的数据交互问题,完成交互服务接口和闭环管理改造。中日联谊医院采用了“东华医为医院信息平台产品”,该产品依托InterSystems技术实现,是互联互通标准化成熟度测评工作的基础。InterSystems数据平台技术不仅稳定可靠,有效支撑数百万级的大规模消息吞吐交换,而且以其强大的可扩展性、易用性和互操作能力,使各异构系统的直接连接更简单,降低了交互服务接口开发的时间和成本,帮助医院高质、高效地推进了业务联通工作。 点击此次,阅读全文
文章
Michael Lei · 六月 6, 2023

2023 年全球峰会初体验

大家好! 我相信很多人都希望出席 2023 年全球峰会,但出于某种原因无法出席。以下是对 6 月 4 日和 5 日发生的事情的简要回顾。 尽管正式的开幕式要到周日晚上才会举行,但峰会前的活动还是很精彩的。亮点之一(特别是对于这里的体育爱好者而言)是早上的高尔夫锦标赛和足球(又名足球)比赛。不能说关于足球的任何事情,因为我不在场(如果你在场,请在评论部分发表评论和照片!),但据我所知,这是一场有趣的比赛。我可以告诉你关于高尔夫的事! 比赛从早上 6 点开始,一直持续到下午 2 点左右。一开始天气阴沉,雨刚下完。但几个小时后,太阳出现了,防晒霜也随之出现。让我告诉你,50SPF 感觉不够! 为了让它更有趣,当地的动物群决定也出去玩。 在宣布获奖者并享用午餐后,我们返回 The Diplomat Beach Hotel 参加其他活动。 例如,下午有一场女性活动。 这导致周日的主要活动 - 欢迎酒会!这是看到熟悉的面孔并与同事和朋友取得联系的最佳时机。 例如,英语社区的版主(@Muhammad.Waseem、@Dmitry.Maslennikov、@Irène.Mykhailova): 和法语社区(@Lorenzo.Scalese、@Irène.Mykhailova、@Dmitry.Maslennikov、@Guillaume.Rongier7183): 这是“准备”日。主要内容从周一开始! Terry Ragon 开启了峰会,在他的演讲中提醒大家任何成功的公司都必须创新才能保持相关性。 InterSystems 就是这样的公司之一! 此外,他祝贺大家 InterSystems 成立 45 周年和第 30 届全球峰会!这不是令人兴奋吗?不知道你怎么想,公司成立的时候我还没有出生呢! 开场白之后,第一个主题演讲是关于医疗保健的。其中一点 @Donald.Woodlock 谈到了AI 以及它如何拉近开发人员与客户的距离,而不是相反。 在主题演讲(和午餐)之后,每个人都分散到不同的会议、演讲、会议等。我回到了 Tech Exchange 的开发者社区展位。 如果你在峰会上,别忘了顺路过来——我很乐意给你一些礼物作为简单行动的回报 😘 如果你认识一些还不是社区成员的人,鼓励他们过来也得到一些赃物。此外,请查看全球大师赛- 峰会期间可兑换一些奖励: 还有一些新的(和旧的最爱)奖励!有传言说,我们甚至很快就会有笔记本电脑贴纸! 无论如何,在 DevCom 展位让我有机会与新朋友交谈,并结识其他有空的版主。 @Guillaume.Rongier7183 和@Dmitry.Maslennikov @Lorenzo.Scalese、@Dmitry.Maslennikov 和@Francisco.López1549 如果您对背景中的演示文稿感兴趣,请看这里。 此外,参加 Tech Exchange 意味着我可以关注那里正在进行的所有演示。例如,了解容器 😉 在所有这些兴奋之后,我们都去吃晚餐和演示。 玩游戏,玩得很开心。 这是 2023 年全球峰会的第一个官方日。敬请期待接下来的活动!
文章
Lele Yang · 一月 30, 2022

FAQ 常见问题系列 -- 系统管理篇 Linux OOM Killer问题

Linux内核机制OOM Killer,也即Out of Memory Killer, 顾名思义,该机制的主要职能就是当内存不足时,选择并杀掉一些进程,以使系统继续运行。 Caché/Ensemble/IRIS的多个客户曾经遇到过与此相关的系统宕机,宕机的直接原因是数据库核心写进程Write Daemon被OOM Killer选中并杀掉了,在我们的日志文件中可以看到如下信息, 06/15/21-10:50:31:035 (13579) 3 Daemon WRTDMN (pid 13588) died. Freezing system 06/15/21-10:52:25:940 (13601) 2 System Process 'WRTDMN' terminated abnormally (pid 13588) 与之对应,在操作系统的日志文件中可以看到如下记录, Jun 15 10:50:34 localhost kernel: Free swap = 0kB Jun 15 10:50:34 localhost kernel: Total swap = 20479996kB Jun 15 10:50:34 localhost kernel: 16777102 pages RAM Jun 15 10:50:34 localhost kernel: 0 pages HighMem/MovableOnly Jun 15 10:50:34 localhost kernel: 324506 pages reserved Jun 15 10:50:30 localhost kernel: cache invoked oom-killer: gfp_mask=0x42d0, order=3, oom_score_adj=0 Jun 15 10:50:35 localhost kernel: Out of memory: Kill process 13588 (cache) score 127 or sacrifice child InterSystems在后续的IRIS版本中(从IRIS2021.1.0开始)已经对此做了优化,以使Write Daemon不那么容易被OOM Killer选中。但是要从根本上解决该问题,还是应当重新审视系统的内存分配,如Huge Page,共享内存等,检查Linux内存相关参数,如vm.swappiness,vm.dirty_background_ratio,vm.dirty_ratio等,以使系统可以在内存使用方面达到最大的效用。
公告
Claire Zheng · 一月 25, 2022

恭喜Louis喜获重磅证书——HL7 FHIR R4 Proficiency Exam

亲爱的开发者们, 很高兴同大家分享一个好消息!我们中文社区版主、InterSystems高级销售工程师Louis(@Louis Lu)于近日顺利通过“HL7 FHIR R4 Proficiency Exam”并取得资格认证证书! HL7 FHIR(R4)能力证书可以证明在最新和最热门的HL7标准方面达到行业公认的专业水平。考试涵盖了以下内容:FHIR原则;FHIR资源的基本概念;交换机制;一致性和实施指导;如何使用术语;如何建立安全和可靠的FHIR解决方案;FHIR维护过程;以及如何使用和处理FHIR许可和知识产权(IP)。 FHIR®(快速医疗互操作性资源)是HL7的下一代标准框架,它建立并结合了HL7第二版(V2)、第三版(V3)和临床文档架构(CDA®)产品线的最佳功能,同时利用了最新的网络标准并注重于实际实施的能力。FHIR适合在各种情况下使用,如手机应用程序、云通信、基于电子健康记录(EHR)的数据共享、大型医疗机构间服务器通信等等。FHIR还可以支持使用一个标准涵盖尽可能多的应用场景,实现医疗信息化真正意义的互操作性,是智慧医院数字化转型,实现组装式应用创新的重要基础。 一起来恭喜Louis(@Louis Lu)吧✿✿ヽ(°▽°)ノ✿✿✿ヽ(°▽°)ノ✿(*^▽^*) 恭喜🎉🎉
文章
Louis Lu · 一月 7, 2021

FAQ 常见问题系列--RHEL V7.2 上的 Caché 进程故障

**RHEL V7.2 上的 Caché 进程故障** InterSystems WRC 处理了几个有关进程错误引发的问题,这些问题可以归因于 Red Hat Linux 最近的一次更新。 RHEL V7.2 (systemd-219-19.el7.x86_64) 中实现的一个新功能可能导致操作系统 IPC(进程间通信)信号量在 非系统用户注销时被解除分配(系统用户,即 UID 编号小于 1000 的用户除外)。 Caché 在内部利用 IPC 信号量来控制 Caché 进程的运行(例如,当尝试唤醒 Caché 进程时)。 这通过“semop”系统服务来实现,如果操作系统意外删除了 Caché 用于进行 IPC 通讯的信号量,则进程可能会出现错误。 如果发生这种情况,在 cconsole.log 中会找到以下证据: “System error while trying to wake-up a process, code = 22”(尝试唤醒进程时系统出错,代码 = 22) 以及在 Caché SYSLOG 中也会记录相应的错误,例如以下典型示例: Err   Process    Date/Time           Mod Line  Routine            Namespace 22    39761      09/29/2016 04:41:27PM 61  359   BF0+1359^Ens.Queue.1 HSBUS 这最终可能导致 Caché 的运行实例处于挂起状态。 以下是 Redhat 提供的一篇文章的链接,文中给出了有关此功能的详细信息以及禁用该功能的方法: [https://access.redhat.com/solutions/2062273](https://access.redhat.com/solutions/2062273 "Follow link")   此问题已在 systemd-219-19.el7_2.4(通过 RHBA-2016-0199 发布 ()中修复。
文章
Hao Ma · 一月 15, 2021

npm-iris概览

什么是npm-iris? NPM是“No Project Mess(项目不乱)”的缩写。 NPM是使用Intersystems IRIS和Bootstrp 4建成的项目和任务管理应用程序。 NPM的创建初衷是通过一个简单直观的项目和任务管理软件,帮助开发者和小型商业公司降低日常问题的复杂度。 它能提供不同的任务视图,包括电子表格、看板、日历,甚至甘特图! 为什么? 在不同的团队中工作,您会发现不同的人喜欢不同的工具。 所以,有时您会在一个项目中使用甘特图,在另一个项目中使用看板,在其他项目中使用纸上的列表…… NPM专注于任务。无论您和您的团队喜欢以哪种方式查看。只需单击并更改您的视图。 功能 初始安装 项目 用户 任务 - 创建和管理任务 调度程序 - 任务的日历视图 看板 - 用看板风格管理您的任务 甘特图 - 使用甘特图查看截止日期、里程碑和进度 新特性/改进的路线图 OAuth2身份验证 项目/团队/用户的安全性 时间跟踪 自定义日历(假期) 支持附件 利用AppS.REST框架 Vue.js版本 Home面板,可以查看活动的概况 试一下这款应用程序! http://npm-iris.eastus.cloudapp.azure.com:52773/npm/home.csp 如果您喜欢这个应用程序,并认为我值得您的投票,请投票给npm-iris! https://openexchange.intersystems.com/contest/current