搜索​​​​

文章
Jingwei Wang · 七月 21, 2022

InterSystems SQL 的使用 - 第四部分 - 视图

视图为存储查询,提供了物理表的所有灵活性和安全权限。所有的视图都是可更新的或只读的。 注意:不能对只读的数据库中的数据创建视图。不能对存储在通过ODBC或JDBC网关连接的Informix表中的数据创建视图。这是因为InterSystems IRIS查询转换在FROM子句中使用子查询,而Informix不支持FROM子句的子查询。 创建视图 视图名称可以是合格的的或不合格的。一个没有限定的视图名称是一个简单的标识符。MyView。一个合格的视图名称由两个简单的标识符组成,一个schema名称和一个视图名称,用句号隔开, 例如MySchema.MyView。视图名和表名遵循相同的命名规则,并对未限定的名称执行相同的schema名称解析。同一模式中的视图和表不能有相同的名称。 你可以通过几种方式定义视图: 使用SQL CREATE VIEW命令(在DDL脚本中或通过JDBC或ODBC)。 CREATE VIEW MySchema.MyView (ViewCol1, ViewCol2, ViewCol3) AS SELECT TableCol1, TableCol2, TableCol3 FROM MyTable 使用管理门户的创建视图界面。 系统操作 -> SQL -> 操作 -> 创建视图 这将显示 "创建视图 "窗口,如图所示: schema:您可以决定将视图包含在现有schema中,或者创建一个新的schema。 视图名称:一个有效的视图名称。在同一模式中,你不能对表和视图使用相同的名称。 带检查选项:选项有READONLY、LOCAL、CASCADED。 授予视图的所有权限给_PUBLIC:如果选择了这个选项,这个选项会给所有用户执行这个视图的权限。默认是不给所有用户访问该视图的权限。 查看文本:你可以通过以下三种方式中的任何一种指定视图文本。 在 "查看文本 "区域键入一个SELECT语句。 使用查询生成器创建一个SELECT语句,然后按确定将此查询提供给视图文本区。 如果你在管理门户SQL界面左侧选择一个缓存查询名称(例如%sqlcq.USER.cls4),然后调用创建视图,这个缓存查询就会提供给视图文本区。注意,在视图文本区,你必须在保存视图文本之前用实际值替换变量引用(问号)。 使用Objectscript执行DDL ClassMethod CreateTable() As %String { &sql(CREATE VIEW Sample.VSrStaff AS SELECT Name AS Vname,Age AS Vage FROM Sample.Person WHERE Age>75) IF SQLCODE=0{ WRITE "Created a view"} ELSEIF SQLCODE=-201 { WRITE "View already exists" RETURN SQLCODE} ELSE { WRITE "Serious SQL Error, returing SQLCODE " RETURN SQLCODE_" "_%msg} } $SYSTEM.SQL.Schema.ViewExists()方法可以用来确定一个视图名称是否已经存在。 查看视图定义 视图信息 SELECT * FROM INFORMATION_SCHEMA.VIEWS 视图的依赖表 NFORMATION.SCHEMA.VIEWTABLEUSAGE持久化类显示当前命名空间中的所有视图和它们所依赖的表。 通过SQL脚本显示视图依赖表 SELECT View_Schema,View_Name, Table_Schema,Table_Name FROM INFORMATION_SCHEMA.VIEW_TABLE_USAGE 通过ObjectScript显示依赖表 SET statemt = ##class(%SQL.Statement).%New() SET cqStatus = statemt.%PrepareClassQuery("%Library.SQLCatalog","SQLViewDependsOn") IF cgStatus '= 1 { WRITE "PrepareClassQuery failed:" DO $System.Status.DisplayError(cgStatus) QUIT } SET rset = statemt.%Execute("vschema.vname") DO rset.%Display() 如果调用者没有视图所依赖的表的权限,该表及其模式将被列为NOT PRIVILEGED。这允许没有表权限的调用者确定视图所依赖的表的数量,但不能确定表的名称。 View ID: %VID InterSystems IRIS为视图或FROM子句返回的每条记录分配一个整数的视图ID(%VID)。与表的行ID号一样,这些视图的行ID号也是系统分配的、唯一的、非空的、非零的和不可修改的。这个%VID通常对用户来说是不可见的,只有在明确指定时才会返回。它作为数据类型INTEGER返回。因为%VID值是连续的整数,如果视图返回有序的数据,它们就更有意义;视图只有在与TOP子句配对时才能使用ORDER BY子句。 CREATE VIEW Sample.VSrStaff AS SELECT TOP ALL Name AS Vname,Age AS Vage FROM Sample.Person WHERE Age>75 ORDER BY Name 下面的例子返回VSrStaff视图定义的所有数据(使用SELECT *),并且还指定要返回每一行的视图ID。与表的行ID不同,视图的行ID在使用星号语法时不会显示;只有在SELECT中明确指定时才会显示。 SELECT *,%VID AS ViewID FROM Sample.VSrStaff %VID可以用来进一步限制SELECT从视图返回的行数,如下面的例子所示。 SELECT *,%VID AS ViewID FROM Sample.VSrStaff WHERE %VID BETWEEN 5 AND 10 因此,%VID可以用来代替TOP(或者作为TOP的补充)来限制查询返回的记录的数量。一般来说,TOP子句用于返回数据记录的一个小子集;%VID用于返回大部分或全部数据记录,将记录返回到小子集的中。这个功能可能很有用,特别是对于Oracle查询(%VID很容易映射到Oracle ROWNUM)。然而,用户应该意识到,与TOP相比,使用%VID有一些性能限制。 %VID不执行time-to-first-row的优化。TOP的优化是尽可能快地返回第一行数据。%VID的优化是为了尽可能快地返回完整的数据集。 如果查询指定了排序的结果,%VID不会执行有限的排序(这是由TOP执行的特殊优化)。查询首先对完整的数据集进行排序,然后使用%VID对返回的数据集进行限制。TOP是在排序前应用的,所以SELECT执行的是有限的排序,只涉及一个受限制的行子集。 为了保持对第一行的优化时间和有限的排序优化,你可以使用一个带有TOP和%VID组合的FROM子句子查询。 SELECT *,%VID AS SubQueryID FROM (SELECT TOP 10 Name,Age FROM Sample.Person WHERE Age > 75 ORDER BY Name) WHERE %VID > 4 不能对指定了%VID的查询进行并行执行,即使明确指定了%PARALLEL关键字。
文章
Louis Lu · 一月 19

InterSystems IRIS 2022.3 版本下的列存储

您可能还记得在 InterSystems 2022年全球峰会以及 2022.2 版本发布的网络研讨会上,我们发布了一项令人兴奋的新功能——列存储,它可以纳入您 InterSystems IRIS 的解决方案中。 列存储引入了一种存储SQL表数据的替代方法,它为分析查询提供了数量级的加速。 最新的2022.3开发预览版在原有的基础上包括一系列我们认为值得在这里宣布的更新。 快速回顾 如果您不熟悉InterSystems IRIS 的列存储,请观看这段简短的介绍视频以及相关该主题的2022全球峰会内容。 简而言之,我们使用新的$vector数据类型将表数据编码为每列 64k 个值的块。 $vector是一种仅限内部使用的数据类型(目前),它利用自适应编码方案来实现稀疏和密集数据的高效存储。 编码还针对一系列专用$vector操作进行了优化,例如一次计算64k 值的整个块的聚合、分组和过滤,并在可能的情况下利用芯片的SIMD指令。 在SQL查询时,我们构建对这些块直接进行操作的查询计划,正如您所想象的,与传统的逐行处理相比,这大大减少了执行查询所需的IO量和ObjectScript指令数。 当然,与面向行且对于单值的操作相比,这个的IO消耗更大,$vector的操作也更重一些,但收益是巨大的。 我们使用一个专业术语矢量化查询计划来表示处理$vector数据的执行策略,通过一系列快速的对于整个块的操作来提高整体的效率。 就一个字“快” 最重要的是,事情变得更快了。 我们扩展了优化器对列索引的理解,现在您将看到更多的查询使用列索引,即使某些请求的字段没有存储在列索引或数据映射中。 此外,您将看到它在许多情况下联合收割机了列索引和位图索引,如果您是从向现有模式添加列索引开始的,那么这将非常有用。 新工具包还包括一系列跨堆栈的更改,以提高性能,从优化到一些查询处理增强功能上的低级$vector操作,以及一组更广泛的可并行化的矢量化查询计划。 加载数据的某些方法,如通过INSERT.. SELECT语句,现在还将使用我们已经用于构建索引的缓冲模型,并且现在能够以真正高的性能构建整个表。 向量化的 JOIN 操作 我们在此版本中添加的最令人兴奋的功能是支持以矢量化方式连接列数据。 在Python 2022.2中,当您希望在一个查询中联合收割机两个表中的数据时,我们仍然会求助于健壮的逐行JOIN策略,该策略同样适用于按列和按行组织的数据。 现在,当JOIN的两端都以列格式存储时,我们使用一个新的内核API在内存中对它们进行JOIN,同时保留它们的$vector格式。 这是实现完全矢量化查询计划的又一重要步骤,即使对于最复杂的查询也是如此。 下面是一个利用新函数的查询示例,该查询对纽约Taxi数据集执行 SELECT COUNT(*), MAX(r1.total_amount - r2.total_amount) FROM NYTaxi.Rides r1, NYTaxi.Rides r2 WHERE r1.DOLocationID = r2.PULocationID AND r1.tpep_dropoff_datetime = r2.tpep_pickup_datetime AND r2.DOLocationID = r1.PULocationID AND r1.passenger_count > 2 AND r2.passenger_count > 2 此查询查找有两名以上乘客的旅行对,第二次旅行在第一次旅行结束的地方开始,在完全相同的时间,并且第二次旅行将乘客带回第一次旅行开始的地方。 这不是一个非常有用的分析,但是我在这个模式中只有一个真正的表,复合JOIN键使这个分析变得不那么琐碎。 在此语句的查询计划中,您将看到类似Apply vector operation %VHASH(用于构建复合JOIN键)和Read vector-join temp-file A的片段,它们表明我们的新矢量化连接器正在工作! 这听起来像是一个冗长的查询计划中的一个微不足道的小问题,但它涉及到内部的大量智能工程,而且有相当多的知名柱状数据库供应商根本不允许这样做,并对您的模式布局施加了严格的约束,所以请加入我们,一起来享受这个! :—) 当查询计划继续读取该临时文件时,您可能会注意到在连接后的工作中仍有一些逐行处理,这就把我们带到了...... 接下来我们会... 列存储在2022.3版本中仍被标记为"实验性",但我们正在接近生产就绪状态,并为多表查询提供完整的端到端矢量化操作。 这包括上面提到的Post-Join 工作、查询优化器中更广泛的支持、更快地加载列式表以及对连接器的进一步增强(如共享内存支持)。 简而言之:现在是使用 InterSystems IRIS 2022.3社区版在纽约出租车数据集上进行第一次尝试的好时机,并且在我们发布2023.1时,您只需按下"运行"按钮就可以! 如果您对如何将列式存储用于您自己的数据和查询感兴趣,请直接联系 InterSystems 客户团队,也许我们会在InterSystems 2023年全球峰会上见面;-).
文章
Claire Zheng · 一月 30

如何将 InterSystems 认证添加到您的社区资料(Profile)中?

亲爱的社区开发者们, 有一些开发者已经通过了InterSystems 官方认证,并希望在个人资料头像上有一个漂亮的绿色标识 以及您社区中的所有证书,以便其他人更好地了解你的能力…… 那么,要将认证添加到您的社区资料中,您需要执行 3 个简单的步骤: 1️⃣ 打开社区个人主页(点击右上角头像即可) 2️⃣ 找到InterSystems 认证( InterSystems Certification) 3️⃣ 点击加载我的认证(Load my certification) 就这么简单! 系统将通过您社区关联的电子邮件将请求发送到 Credly。如果您的证书链接到同一电子邮件,您的证书将会自动加载: 如果没有,请按照页面上描述的详细步骤操作: 完成后,每个人都可以看到你的认证资质哦 ;) 预祝每位社区成员成功添加认证并通过!
文章
Michael Lei · 五月 15, 2022

InterSystems 最佳实践之 掌握 %SYSTEM.Encryption 加密类

InterSystems IRIS对加密、解密和哈希操作有很好的支持。在%SYSTEM.Encryption(https://docs.intersystems.com/iris20212/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&PRIVATE=1&CLASSNAME=%25SYSTEM.Encryption)类中,有市场上主要算法的类方法。 IRIS算法和加密/解密类型 正如你所看到的,这些操作是基于密钥的,包括3个类: 对称密钥: 进行加密和解密操作的部分共享同一个秘密密钥。 非对称密钥: 进行加密和解密操作的部分共享相同的秘密密钥进行加密。然而,对于解密,每个部分都有一个私人密钥。这个密钥不能与其他人共享,因为它是一个身份证明。 哈希: 当你不需要解密,而只需要加密时使用。当涉及到存储用户密码时,这是一种常见的方法。 对称和非对称加密的区别 对称加密使用一个单一的密钥,需要在需要接收信息的人之间共享,而非对称加密在通信时使用一对公共密钥和一个私人密钥来加密和解密信息。 对称加密是比较老的技术,而非对称加密则相对较新。 非对称加密的出现是为了补充对称加密模式中需要共享密钥的固有问题,通过使用一对公共-私有密钥来消除共享密钥的需要。 非对称加密比对称加密花费的时间相对较多。 主要区别 对称加密 非对称加密 密码文本大小 比原始纯文本文件更小的密码文本 比原始纯文本文件更大的密码文本 数据量 用于传输大数据. 用于传输小数据. 资源消耗 较低. 较高 密钥长度 128 或 256 位 RSA 2048位或者更长. 安全性 由于使用单一密钥进行加密,安全性较低。 由于在加密和解密中涉及两个不同的密钥,因此要安全得多。 密钥数量 加密和解密用单一密钥 加密和解密使用两个不同的密钥 技术 较老. 较新. 保密性 单一密钥,有可能被破解. 两个密钥分别用于加密和解密,从而消除了共享密钥的需要. 速度 较快 较慢. 算法 RC4, AES, DES, 3DES, QUAD. RSA, Diffie-Hellman, ECC . 来源: https://www.ssl2buy.com/wiki/symmetric-vs-asymmetric-encryption-what-are-differences 使用 %SYSTEM.Encryption 类来实现加密、解密与哈希 要行使IRIS对加密、解密和哈希操作的支持,请访问https://github.com/yurimarx/cryptography-samples,并遵循以下步骤: 1. Clone/git 将 repo 拉入任何本地目录 $ git clone https://github.com/yurimarx/cryptography-samples.git 2. 在目录中打开 Docker 终端并执行: $ docker-compose build 3. 启动 IRIS 容器: $ docker-compose up -d 4. 打开 IRIS 终端: $ docker-compose exec iris iris session iris -U IRISAPP IRISAPP> 5. 实现非对称RSA 加密: IRISAPP>Set ciphertext = ##class(dc.cryptosamples.Samples).DoRSAEncrypt("InterSystems") IRISAPP>Write ciphertext Ms/eR7pPmE39KBJu75EOYIxpFEd7qqoji61EfahJE1r9mGZX1NYuw5i2cPS5YwE3Aw6vPAeiEKXF rYW++WtzMeRIRdCMbLG9PrCHD3iQHfZobBnuzx/JMXVc6a4TssbY9gk7qJ5BmlqRTU8zNJiiVmd8 pCFpJgwKzKkNrIgaQn48EgnwblmVkxSFnF2jwXpBt/naNudBguFUBthef2wfULl4uY00aZzHHNxA bi15mzTdlSJu1vRtCQaEahng9ug7BZ6dyWCHOv74O/L5NEHI+jU+kHQeF2DJneE2yWNESzqhSECa ZbRjjxNxiRn/HVAKyZdAjkGQVKUkyG8vjnc3Jw== 6. 实现非对称 RSA 解密: IRISAPP>Set plaintext = ##class(dc.cryptosamples.Samples).DoRSADecrypt(ciphertext) IRISAPP>Write plaintext InterSystems 7. 实现对称 AES CBC 加密: IRISAPP>Do ##class(dc.cryptosamples.Samples).DoAESCBCEncrypt("InterSystems") 8sGVUikDZaJF+Z9UljFVAA== 8. 实现对称AES CBC 解密: IRISAPP>Do ##class(dc.cryptosamples.Samples).DoAESCBCDecrypt("8sGVUikDZaJF+Z9UljFVAA==") InterSystems 9. 实现MD5 哈希: IRISAPP>Do ##class(dc.cryptosamples.Samples).DoHash("InterSystems") rOs6HXfrnbEY5+JBdUJ8hw== 10. 实现SHA 推荐哈希: IRISAPP>Do ##class(dc.cryptosamples.Samples).DoSHAHash("InterSystems") +X0hDlyoViPlWOm/825KvN3rRKB5cTU5EQTDLvPWM+E= 11. 退出终端: Enter HALT or H (大小写不敏感) 关于源代码 1. 关于对称密钥 # to use with symmetric encrypt/decrypt ENV SECRETKEY=InterSystemsIRIS 在Dockerfile创建了一个环境密钥,作为对称操作的秘钥。 2. 关于非对称密钥 # to use with asymmetric encrypt/decrypt RUN openssl req -new -x509 -sha256 -config example-com.conf -newkey rsa:2048 -nodes -keyout example-com.key.pem -days 365 -out example-com.cert.pem 在Dockerfile创建了一个公钥和一个私钥用作非对称操作 3. 对称加密 // Symmetric Keys sample to encrypt ClassMethod DoAESCBCEncrypt(plaintext As %String) As %Status { // convert to utf-8 Set text=$ZCONVERT(plaintext,"O","UTF8") // set a secret key Set secretkey = $system.Util.GetEnviron("SECRETKEY") Set IV = $system.Util.GetEnviron("SECRETKEY") // encrypt a text Set text = $SYSTEM.Encryption.AESCBCEncrypt(text, secretkey, IV) Set ciphertext = $SYSTEM.Encryption.Base64Encode(text) Write ciphertext } AES CBC 加密主要用于加密文本。Base64编码将结果作为一个可读的文本返回给用户。. 4. 对称解密 // Symmetric Keys sample to decrypt ClassMethod DoAESCBCDecrypt(ciphertext As %String) As %Status { // set a secret key Set secretkey = $system.Util.GetEnviron("SECRETKEY") Set IV = $system.Util.GetEnviron("SECRETKEY") // decrypt a text Set text=$SYSTEM.Encryption.Base64Decode(ciphertext) Set text=$SYSTEM.Encryption.AESCBCDecrypt(text,secretkey,IV) Set plaintext=$ZCONVERT(text,"I","UTF8") Write plaintext } AES CBC 解密用于解密文本.Base64解码将加密后的文本返回为二进制文本,可以用来解密。 5. 非对称加密 // Asymmetric Keys sample to encrypt ClassMethod DoRSAEncrypt(plaintext As %String) As %Status { // get public certificate Set pubKeyFileName = "/opt/irisbuild/example-com.cert.pem" Set objCharFile = ##class(%Stream.FileCharacter).%New() Set objCharFile.Filename = pubKeyFileName Set pubKey = objCharFile.Read() // encrypt using RSA Set binarytext = $System.Encryption.RSAEncrypt(plaintext, pubKey) Set ciphertext = $SYSTEM.Encryption.Base64Encode(binarytext) Return ciphertext } 必须获得公钥文件内容,用RSA进行加密。RSA加密是用来加密文本的。 6. 非对称解密 // Asymmetric Keys sample to decrypt ClassMethod DoRSADecrypt(ciphertext As %String) As %Status { // get private key Set privKeyFileName = "/opt/irisbuild/example-com.key.pem" Set privobjCharFile = ##class(%Stream.FileCharacter).%New() Set privobjCharFile.Filename = privKeyFileName Set privKey = privobjCharFile.Read() // get ciphertext in binary format Set text=$SYSTEM.Encryption.Base64Decode(ciphertext) // decrypt text using RSA Set plaintext = $System.Encryption.RSADecrypt(text, privKey) Return plaintext } 要想用RSA解密,必须得到私钥文件内容。RSA解密的操作来解密文本。 7. 使用MD5的哈希文本(老方法) // Hash sample ClassMethod DoHash(plaintext As %String) As %Status { // convert to utf-8 Set text=$ZCONVERT(plaintext,"O","UTF8") // hash a text Set hashtext = $SYSTEM.Encryption.MD5Hash(text) Set base64text = $SYSTEM.Encryption.Base64Encode(hashtext) // convert to hex text to following best practices Set hextext = ..GetHexText(base64text) // return using lowercase Write $ZCONVERT(hextext,"L") } MD5 哈希加密文本,并且不能被解密Hash will encrypt the text, and it will not be possible to decrypt it.新项目不建议使用MD5的哈希值,因为它被认为是不安全的。这就是为什么它被SHA所取代。InterSystems的IRIS支持SHA(我们的下一个例子将演示它)。 8. 使用SHA的Hash文本 (推荐) 我们将在这个样本中使用SHA-3哈希方法。根据InterSystems公司的文档,该方法使用美国安全哈希算法-3之一生成哈希值((更多信息见联邦信息处理标准出版物202)。 // Hash using SHA ClassMethod DoSHAHash(plaintext As %String) As %Status { // convert to utf-8 Set text=$ZCONVERT(plaintext,"O","UTF8") // hash a text Set hashtext = $SYSTEM.Encryption.SHA3Hash(256, text) Set base64text = $SYSTEM.Encryption.Base64Encode(hashtext) // convert to hex text to following best practices Set hextext = ..GetHexText(base64text) // return using lowercase Write $ZCONVERT(hextext,"L") } 对于SHA方法,可以设置哈希操作中使用的比特长度。位数越多,破解哈希就越困难。然而,哈希过程也会变慢。在这个例子中,我们使用了256位。你可以选择以下长度: 224 (SHA-224) 256 (SHA-256) 384 (SHA-384) 512 (SHA-512)
文章
Jingwei Wang · 七月 28, 2022

InterSystems SQL 的使用 - 第七部分 - Stored Procedures

定义 Stored Procedures 可以使用以下方式定义stored procedures 使用DDL定义存储过程 使用类方法定义存储过程 使用DDL定义存储过程 CREATE PROCEDURE 可以创建一个查询,它总是作为一个存储过程被预测。一个查询可以返回一个单一的结果集。 CREATE PROCEDURE AgeQuerySP(IN topnum INT 10, IN minage INT 20) BEGIN SELECT TOP :topnum Name, Age FROM Sample.Person WHERE Age > :minage; END 列表中的每个参数声明包括(按顺序)。指定参数模式是IN(输入值),OUT(输出值),还是INOUT(修改值)。如果省略,默认的参数模式是IN,参数名称是区分大小写的。 CREATE QUERY 可以创建一个可以选择作为存储过程投射的查询。一个查询可以返回一个单一的结果集,这个查询可能是也可能不是作为存储过程公开的。要创建一个作为存储过程公开的查询,你必须指定PROCEDURE关键字作为其特征之一。你也可以使用CREATE PROCEDURE语句来创建一个作为存储过程公开的查询。 为了创建一个查询,你必须拥有%CREATE_QUERY管理权限,正如GRANT命令所指定的那样。如果类的定义是一个已部署的类,你不能在该类中创建一个查询。 CREATE QUERY AgeQuery(IN topnum INT DEFAULT 10,IN minage INT 20) PROCEDURE BEGIN SELECT TOP :topnum Name,Age FROM Sample.Person WHERE Age > :minage ; END 使用类定义存储过程 要定义一个方法存储过程,只需定义一个类方法并设置其SqlProc关键字。 Class MyApp.Person Extends %Persistent [DdlAllowed] { ​ /// This procedure finds total sales for a territory ClassMethod FindTotal(territory As %String) As %Integer [SqlProc] { // use embedded sql to find total sales &sql(SELECT SUM(SalesAmount) INTO :total FROM Sales WHERE Territory = :territory ) ​ Quit total } } 这个类被编译后,FindTotal()方法将作为存储过程MyApp.Person_FindTotal()投射到SQL。你可以使用该方法的SqlName关键字改变SQL对存储过程的命名。 该方法使用一个存储过程上下文处理程序,在存储过程和其调用者(例如,ODBC服务器)之间来回传递存储过程上下文。这个过程上下文处理程序是由InterSystems IRIS使用%sqlcontext对象自动生成的(作为%qHandle:%SQLProcContext)。 %sqlcontext由SQLCODE错误状态、SQL行数、错误信息等属性组成,使用相应的SQL变量进行设置,如下所示。 SET %sqlcontext.%SQLCODE=SQLCODE SET %sqlcontext.%ROWCOUNT=%ROWCOUNT SET %sqlcontext.%Message=%msg 使用 Stored Procedures 当执行一个以SQL函数为参数的存储过程时,使用CALL来调用该存储过程,如下面的例子. CALL sp.MyProc(CURRENT_DATE) SELECT查询不支持执行带有SQL函数参数的存储过程。SELECT支持用一个SQL函数参数执行一个存储函数。 xDBC不支持使用SELECT或CALL来执行一个带有SQL函数参数的存储过程。 你可以在一个SQL查询中使用一个存储函数,就像它是一个内置的SQL函数一样。函数的名称是存储函数的SQL名称(在这里是 "Square"),由定义它的schema(包)名称限定(在这里是 "MyApp")。 下面的查询使用了Square函数。 SELECT Cost, MyApp.Utils_Square(Cost) As SquareCost FROM Products 查询Stored Procedures 信息 INFORMATION.SCHEMA.ROUTINES持久化类显示关于当前命名空间中所有routine和程序的信息。 下面的例子返回routine名称,方法或查询名称,routine类型(PROCEDURE或FUNCTION),routine主体(SQL=带SQL的类查询,EXTERNAL=不带SQL的类查询),返回数据类型,以及当前名称空间中模式 "Sample "中所有routine的routine定义。 SELECTROUTINE_NAME,METHOD_OR_QUERY_NAME,ROUTINE_TYPE,ROUTINE_BODY,SQL_DATA_ACCESS,IS_USER_DEFINED_CAST, DATA_TYPE||' '||CHARACTER_MAXIMUM_LENGTH AS Returns,NUMERIC_PRECISION||':'||NUMERIC_SCALE ASPrecisionScale, ROUTINE_DEFINITION FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='Sample' INFORMATION.SCHEMA.PARAMETERS持久化类显示当前命名空间中所有routine和程序的输入和输出参数的信息。 下面的例子返回了当前命名空间中模式 "Sample "中所有routine的routine名称、参数名称、是输入参数还是输出参数,以及参数数据类型信息。 SELECT SPECIFIC_NAME,PARAMETER_NAME,PARAMETER_MODE,ORDINAL_POSITION, DATA_TYPE,CHARACTER_MAXIMUM_LENGTH AS MaxLen,NUMERIC_PRECISION||':'||NUMERIC_SCALE AS PrecisionScale FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_SCHEMA='Sample'
文章
Jingwei Wang · 七月 28, 2022

InterSystems SQL 的使用 - 第八部分 - 存储和使用流数据(BLOBs和CLOBs)

InterSystems SQL支持在InterSystems IRIS数据平台数据库中将流数据存储为BLOB(Binary Large Objects 二进制大对象)或CLOB(Character Large Objects字符大对象)的能力。 InterSystems SQL支持两种流字段: 字符流:用于大量的文本。 二进制流:用于图像、音频或视频。 BLOBs和CLOBs可以存储多达4GB的数据(JDBC和ODBC规范规定的限制)。除了在通过ODBC或JDBC客户端访问时如何处理字符编码转换(如Unicode到多字节)外,BLOB和CLOB的操作在各方面都是相同的:BLOB中的数据被视为二进制数据,决不转换为其他编码,而CLOB中的数据被视为字符数据,在必要时进行转换。 如果一个二进制流文件(BLOB)包含单一的非打印字符$CHAR(0),它被认为是一个空的二进制流。它相当于""空二进制流值:它存在(不是空的),但长度为0。 从对象的角度来看,BLOB和CLOBs被表示为流对象。更多信息请参见定义和使用类中的 "与流合作 "一章。 定义流数据字段 InterSystems SQL支持流字段的各种数据类型名称。这些InterSystems的数据类型名称是对应于以下的同义词。 字符流:数据类型LONGVARCHAR,映射到%Stream.GlobalCharacter类和ODBC/JDBC数据类型-1。 字符流:数据类型LONGVARBINARY,映射到%Stream.GlobalBinary类和ODBC/JDBC数据类型-4。 下面的例子定义了一个包含两个流字段的表。 CREATE TABLE Sample.MyTable ( Name VARCHAR(50) NOT NULL, Notes LONGVARCHAR, Photo LONGVARBINARY) 流字段的限制: 一个流字段可以被定义为NOT NULL。 一个流字段可以取一个DEFAULT值,一个ON UPDATE值,或者一个COMPUTECODE值。 一个流字段不能被定义为UNIQUE,一个主键字段,或一个IdKey。试图这样做会导致一个SQLCODE -400的致命错误,并带有%msg,如以下。ERROR #5414: 无效的索引属性。Sample.MyTable::MYTABLEUNIQUE2::Notes, Stream属性在唯一/主键/idkey索引中是不允许的 > ERROR #5030: 在编译'Sample.MyTable'类时发生错误。 不能用指定的COLLATE值定义一个流字段。试图这样做会导致一个SQLCODE -400的致命错误,并带有%msg,如以下。ERROR #5480: 属性参数没有声明。Sample.MyTable:Photo:COLLATION > ERROR #5030: 在编译'Sample.MyTable'类时发生错误。 在流数据字段中插入数据 有三种方法可以将数据插入到流字段。 %Stream.GlobalCharacter字段:你可以直接插入字符流数据。例如: INSERT INTO Sample.MyTable (Name,Notes) VALUES ('Fred','These are extensive notes about Fred') %Stream.GlobalCharacter和%Stream.GlobalBinary字段:你可以使用OREF插入流数据。你可以使用Write()方法将一个字符串追加到字符流中,或者使用WriteLine()方法将一个带有行终止符的字符串追加到字符流中。默认情况下,行结束符是$CHAR(13,10)(回车/换行);你可以通过设置LineTerminator属性改变行结束符。 在下面的例子中,第一部分创建了一个由两个字符串和它们的终止符组成的字符流,然后使用嵌入式SQL将其插入到一个流字段中。例子的第二部分返回字符流的长度并显示字符流数据,显示终止符。 ​ ClassMethod CreateAndInsertCharacterStream() { Set gcoref = ##class(%Stream.GlobalCharacter).%New() DO gcoref.WriteLine("First Line") Do gcoref.WriteLine("Second Line") &sql(INSERT INTO Sample.MyTable(Name, Notes) VALUES('Fred',:gcoref)) IF SQLCODE<0 {WRITE "SQLCODE ERROR:"_SQLCODE_" "_%msg QUIT} ELSE {WRITE "Insert successful",!} do ..DisplayTheCharacterStream(gcoref) } ​ ClassMethod DisplayTheCharacterStream(gcoref As %Stream.GlobalCharacter) { KILL ^CacheStream WRITE gcoref.%Save(),! ZWRITE ^CacheStream } %Stream.GlobalCharacter和%Stream.GlobalBinary字段:你可以通过从文件中读取数据来插入流数据。比如说 ClassMethod InsertDataFromImage() { Set myf = "C:\Temps\IMG.png" OPEN myf:("RF"):10 USE myf:0 READ x(1):10 &sql(INSERT INTO Sample.MyTable (Name,Photo) VALUES ('George',:x(1))) IF SQLCODE <0 {WRITE "INSERT Failed:"_SQLCODE_" "_%msg QUIT} ​ CLOSE myf } 作为DEFAULT值或计算值插入的字符串数据将以适合于流字段的格式存储。 查询流字段数据 二进制流字段返回字符串<binary>。 SELECT Name,Photo,Notes FROM Sample.MyTable WHERE Photo IS NOT NULL DISTINCT, GROUP BY, 和 ORDER BY 每个流数据字段的OID值都是唯一的,即使数据本身包含重复的内容。这些SELECT子句对流的OID值进行操作,而不是数据值。因此,当应用于查询中的流字段时。 DISTINCT子句对重复的流数据值没有影响。DISTINCT子句将流字段为NULL的所有记录记录减少到一个NULL记录。DISTINCT对流字段的OID进行操作,而不是它的实际数据。 GROUP BY子句对重复的流数据值没有影响。GROUP BY子句将流字段为NULL的所有记录数减少到一个NULL记录。GROUP BY StreamField的操作对象是一个流字段的OID,而不是它的实际数据。 ORDER BY子句根据流数据值的OID值,而不是数据值来排序。ORDER BY子句在列出有流字段数据值的记录之前,先列出流字段为NULL的记录。 predicate 条件和流 IS [NOT] NULL 可以应用于流字段的数据值,如下面的例子中所示。 SELECT Name,Notes FROM Sample.MyTable WHERE Notes IS NOT NULL BETWEEN, EXISTS, IN, %INLIST, LIKE, %MATCHES, 和 %PATTERN谓词可以应用于流对象的OID值,如下面的例子所示。 SELECT Name,Notes FROM Sample.MyTable WHERE Notes %MATCHES '*1[0-9]*GlobalChar*' 。 试图在一个流字段上使用任何其他的predicate条件会导致SQLCODE -313错误。 聚合函数和流 COUNT聚合函数接收一个流字段,并对包含该字段非空值的记录进行计数,如下面的例子所示: SELECT COUNT(Photo) AS PicRows,COUNT(Notes) AS NoteRows FROM Sample.MyTable 然而,COUNT(DISTINCT)不支持流字段。对流字段不支持其他聚合函数。试图用任何其他聚合函数来使用流字段会导致SQLCODE -37错误。 标量函数和流 除了%OBJECT、CHARACTER_LENGTH(或CHAR_LENGTH或DATALENGTH)、SUBSTRING、CONVERT、XMLCONCAT、XMLELEMENT、XMLFOREST和%INTERNAL函数外,InterSystems SQL不能将任何函数应用到流字段。试图使用流字段作为任何其他SQL函数的参数会导致SQLCODE -37错误。 %OBJECT函数打开一个流对象(接受一个OID),并返回oref(对象引用),如以下例子所示: SELECT Name,Notes,%OBJECT(Notes) AS NotesOref FROM Sample.MyTable WHERE Notes IS NOT NULL CHARACTER_LENGTH,CHAR_LENGTH和DATALENGTH函数取一个流字段,并返回实际的数据长度,如下面的例子所示。 SELECT Name,DATALENGTH(Notes) AS NotesNumChars FROM Sample.MyTable WHERE Notes IS NOT NULL SUBSTRING函数接收一个流字段,并返回流字段实际数据值的指定子串,如下面的例子所示。 SELECT Name,SUBSTRING(Notes,1,10) AS Notes1st10Chars FROM Sample.MyTable WHERE Notes IS NOT NULL 当从管理门户的SQL执行界面发出时,SUBSTRING函数最多返回流字段数据的100个字符的子串。如果指定的流数据子串长于100个字符,会在第100个字符后面用省略号(...)表示。 CONVERT函数可以用来将流数据类型转换为VARCHAR,如下面的例子所示。 SELECT Name,CONVERT(VARCHAR(100),Notes) AS NotesTextAsStr FROM Sample.MyTable WHERE Notes IS NOT NULL CONVERT(datatype,expression)语法支持流数据转换。如果VARCHAR精度小于实际流数据的长度,它将返回值截断为VARCHAR精度。如果VARCHAR精度大于实际流数据的长度,返回值就有实际流数据的长度。不进行填充。 {fn CONVERT(expression,datatype)}语法不支持流数据转换;它发出SQLCODE -37错误。 %INTERNAL函数可以在流字段上使用,但不执行任何操作。 流字段并发锁定 InterSystems IRIS通过在流数据上加锁来保护流数据值不受另一个进程的并发操作。 InterSystems IRIS在执行写操作之前会拿出一个独占锁。写操作完成后,独占锁会立即释放。 InterSystems IRIS在第一次读操作发生时取出一个共享锁。只有在实际读取流时才会获得共享锁,并且在整个流从磁盘读入内部临时输入缓冲区后立即释放。 在InterSystems IRIS方法中使用流字段 你不能在InterSystems IRIS方法中直接使用嵌入式SQL或动态SQL来使用BLOB或CLOB值;而是要使用SQL来找到BLOB或CLOB的流标识符,然后创建%AbstractStream对象的实例来访问数据。 从ODBC使用流字段 ODBC规范没有为BLOB和CLOB字段提供任何识别或特殊处理。 InterSystems SQL在ODBC中表示CLOB字段为LONGVARCHAR (-1)类型。BLOB字段被表示为LONGVARBINARY类型(-4)。 从JDBC使用流字段 在一个Java程序中,你可以使用标准的JDBC BLOB和CLOB接口从BLOB或CLOB中检索或设置数据。比如说 Statement st = conn.createStatement()。 ResultSet rs = st.executeQuery("SELECT MyCLOB,MyBLOB FROM MyTable")。 rs.next(); // 取出Blob/Clob ​ java.sql.Clob clob = rs.getClob(1); java.sql.Blob blob = rs.getBlob(2); ​ // Length System.out.println("Clob length = " + clob.length())。 System.out.println("Blob length = " + blob.length())。 ​ // ... 注意:当完成对BLOB或CLOB的处理时,你必须明确地调用free()方法来关闭Java中的对象,并向服务器发送消息以释放流资源(对象和锁)。
文章
Jingwei Wang · 七月 14, 2022

精华文章 InterSystems SQL 的使用 - 第一部分 - 架构及特性介绍

InterSystems SQL为存储在IRIS数据库中的数据提供不折不扣的、标准的关系型访问。 InterSystems SQL具有以下优点。 高性能和可扩展性 - InterSystems SQL的性能和可扩展性优于其他关系型数据库产品。 与IRIS对象技术的集成 - InterSystems SQL与IRIS对象技术紧密集成。你可以混合使用关系型和对象型的数据访问,而不牺牲任何一种方法的性能。 低维护 - 与其他关系型数据库不同,IRIS应用程序不需要在部署的应用程序中重建索引和压缩表。 支持标准SQL查询 - InterSystems SQL支持SQL-92标准语法和命令。 你可以将InterSystems SQL用于许多目的,包括。 基于对象和Web应用程序 - 你可以在InterSystems Object和Caché Server Page应用程序中使用SQL查询,以执行强大的数据库操作,如查询和搜索。 在线事务处理 - InterSystems SQL为插入和更新操作以及通常在事务处理应用程序中发现的查询类型提供出色的性能。 BI和数据仓库 - IRIS多维数据库引擎和位图索引技术的结合使其成为数据仓库式应用的绝佳选择。 点对点查询和报告 - 你可以使用InterSystems SQL包含的全功能ODBC和JDBC驱动来连接到流行的报告和查询工具。 企业应用集成 - InterSystems SQL网关使你能以SQL方式无缝访问存储在符合ODBC或JDBC标准的外部关系型数据库中的数据。这使得在IRIS应用程序中整合各种来源的数据变得很容易。 架构 IRIS SQL的核心由以下部分组成。 统一数据字典 - 所有元信息的存储库,以一系列的类定义的形式存储。自动为每个存储在统一字典中的持久类创建关系访问(表)。 SQL处理器和优化器 - 是一套解析和分析SQL查询的程序,为给定的查询确定最佳搜索策略,并生成执行查询的代码。 InterSystems SQL Server - 一组InterSystems IRIS服务器进程,负责与InterSystems ODBC和JDBC驱动的所有通信。它还管理着一个常用查询的缓存;当同一个查询被多次执行时,它的执行计划可以从查询缓存中检索出来,而不必由优化器再次处理。 特性 InterSystems SQL包括一整套标准的关系型功能。这些功能包括。 定义表和视图的能力(DDL或数据定义语言)。 对表和视图执行查询的能力(DML或数据操作语言)。 执行事务的能力,包括INSERT、UPDATE和DELETE操作。当执行并发操作时,InterSystems SQL使用行级锁。 能够定义和使用索引以提高查询效率。 能够使用各种各样的数据类型,包括用户定义的类型。 能够定义用户和角色并给他们分配权限。 能够定义外键和其他完整性约束。 能够定义INSERT, UPDATE, 和DELETE触发器。 定义和执行存储程序的能力。 能够以不同的格式返回数据。ODBC模式用于客户端访问;显示模式用于基于服务器的应用程序(如CSP页面)。 SQL-92标准 InterSystems SQL支持完整的入门级SQL-92标准,但有以下例外: 不支持在表定义中添加额外的CHECK约束。 不支持SERIALIZABLE隔离级别。 分隔标识符不区分大小写;标准规定它们应该区分大小写,例如create table "user info" (id number); 和 create table "USER INFO" (id number); 在InterSystems SQL中不区分大小写。 在包含HAVING子句的子查询中,人们应该能够引用HAVING子句中 "可用 "的min(),max(),avg()。这不被支持。即 aggregate函数无法在 WHERE 或 GROUP BY 子句中使用。例如Select DeptID , Avg(Salary) From Employee GROUP BY DeptID, Avg(Salary) HAVING Avg(Salary) > 1000, 但是可以使用Select DeptID , Avg(Salary) From Employee GROUP BY DeptID HAVING Avg(Salary) > 1000 SQL-92标准在算术运算符优先级方面是不精确的;关于这个问题的假设在不同的SQL实现中是不同的。InterSystems SQL支持两种系统范围内的SQL算术运算符优先级的选择。 默认情况下,InterSystems SQL严格按照从左到右的顺序解析算术表达式,没有运算符优先级。这与ObjectScript中使用的惯例相同。因此,3+3*5=30。你可以使用圆括号来强制执行所需的优先级。因此,3+(3*5)=18。 你可以配置InterSystems SQL使用ANSI优先级来解析算术表达式,它给乘法和除法运算符的优先级高于加法、减法和连接运算符。因此,3+3*5=18。如果需要,你可以使用圆括号来覆盖这个优先级。因此,(3+3)*5=30。 扩展功能 InterSystems SQL支持一些有用的扩展。其中许多都与InterSystems IRIS同时提供对象和关系访问数据的事实有关。 其中一些扩展包括: 支持用户定义的数据类型和函数。 用于跟踪对象引用的特殊语法。 支持子类化和继承。 支持对存储在其他数据库中的外部表的查询。 控制用于表的存储结构的一些机制,以实现最大的性能。 支持ODBC,JDBC 使用JDBC驱动和ODBC驱动 嵌入式SQL 在ObjectScript中,InterSystems SQL支持嵌入式SQL:在方法(或其他代码)的内部放置SQL语句的能力。 使用嵌入式SQL,你可以查询一条记录,或者定义一个游标并使用它来查询多条记录。 嵌入式SQL是被编译的;它可以与ObjectScript例程同时被编译(默认),或者你可以将嵌入式SQL的编译推迟到运行时。 当与IRIS的对象访问能力结合使用时,嵌入式SQL是相当强大的。例如,下面的方法可以找到具有给定Name值的记录的RowID。 ClassMethod FindByName(fullname AS %String) { &sql(SELECT %ID INTO :id FROM Sample.MyTable WHERE name= :fullname) IF SQLCODE <0 {SET baderr="SQLCODE ERROR:"_SQLCODE_" "_%msg RETURN baderr} ELSEIF SQLCODE=100 {SET nodata="Query returns no data" RETURN nodata} RETURN "RowID="_id } Dynamic SQL 作为标准库的一部分,InterSystems IRIS提供了一个%SQL.Statement类,你可以用它来执行动态(即在运行时定义)的SQL语句。你可以在ObjectScript方法中使用动态SQL。例如,下面的方法查询指定数量的21世纪出生的人。该查询选择所有在1999年12月31日以后出生的人,按出生日期对所选记录进行排序,然后选择前 x 条记录。 ClassMethod Born21stC(x) [language=objectscript] { set myquery =2 set myquery(1) = "SELECT TOP ? Name, %EXTERNAL(DOB) FROM Sample.Person " set myquery(2) = "WHERE DOB >58073 ORDER BY DOB" 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(x) Do rset.%Display() WRITE !, "End of data" } 当你prepare一个查询时,该查询的优化版本被存储为一个缓存的查询。这个缓存的查询在随后的查询调用中被执行,避免了每次执行查询时重新优化的开销。 限制 注意InterSystems SQL的以下限制: NLS可以用来指定$ORDER的行为,用于特定的国家地区语言行为的Global,以及当前运行进程中的本地变量。InterSystems SQL可以在任何国家语言区划内使用,并且运行良好。然而,目前InterSystems SQL的一个限制是,对于任何特定的进程,它所引用的所有相关的globals都必须使用与当前进程locale相同的国家locale。
文章
Jingwei Wang · 七月 21, 2022

InterSystems SQL 的使用 - 第五部分 - 导入SQL Code

本章介绍了如何将SQL code从文本文件导入InterSystems SQL。当你导入SQL code时,InterSystems IRIS 数据平台使用动态SQL准备并执行每一行的SQL。如果遇到无法解析的SQL code行,SQL导入会跳过该行code,继续准备和执行后续的code行,直到到达文件的末端。所有的SQL code导入操作都会导入到当前的命名空间。 SQL导入主要用于导入数据定义语言(DDL)命令,如CREATE TABLE,并使用INSERT、UPDATE和DELETE命令来填充表。SQL导入可以准备和执行SELECT查询,但不创建结果集。 SQL导入可以用来导入InterSystems的SQL code。它也可以用于code迁移,从其他供应商(FDBMS、Informix、InterBase、MSSQLServer、MySQL、Oracle、Sybase)导入SQL。来自其他供应商的code被转换为InterSystems的SQL并执行。SQL导入不能将所有的SQL命令导入到InterSystems SQL中。它导入的是那些与InterSystems IRIS实现的SQL标准兼容的命令和条款。不兼容的特征通常会被解析,但会被忽略。 SQL导入可以成功地准备一个SQL查询--在适当的时候创建一个相应的缓存查询--但它不会执行查询。 你通过调用%SYSTEM.SQL.Schema类中的适当方法来执行SQL code导入。当导入SQL code时,这些方法可以创建另外两个文件:一个是Errors.log文件,它记录了解析SQL命令的错误;另一个是Unsupported.log文件,它包含了该方法不能识别为SQL命令的行的字面文本。 导入 InterSystems SQL Code 你可以使用%SYSTEM.SQL.Schema方法从一个文本文件中导入InterSystems的SQL code。 ImportDDL()是一个通用的SQL导入方法。该方法作为一个后台(非交互式)进程运行。如下面的例子中所示,示例中第一个参数mysqlcode.txt为SQL命令文件,第二个参数是默认的错误日志文件mysqlcode_Errors.log,第三个参数指定为 "IRIS "。 DO $SYSTEM.SQL.Schema.ImportDDL("c:\InterSystems\mysqlcode.txt",,"IRIS") 注意:这种SQL DDL代码的导入和执行不应该与管理门户SQL界面的导入语句操作相混淆。管理门户SQL界面的导入语句是以XML格式导入SQL语句。 这个mysqlcode.txt文本文件必须是一个没有格式化的文件,如.txt文件。每条SQL命令必须在自己的行中开始。一个SQL命令可以分成多行,允许缩进。每条SQL命令后面必须有一个GO语句,并在自己的行上,下面是一个有效的InterSystems SQL导入文件文本的例子: CREATE TABLE Sample.MyStudents (StudentName VARCHAR(32),StudentDOB DATE) GO CREATE INDEX NameIdx ON TABLE Sample.MyStudents (StudentName) GO INSERT INTO Sample.MyStudents (StudentName,StudentDOB) SELECT Name, DOB FROM Sample.Person WHERE Age <= '21' GO INSERT INTO Sample.MyStudents (StudentName,StudentDOB) VALUES ('Jones,Mary',60123) GO UPDATE Sample.MyStudents SET StudentName='Smith-Jones,Mary' WHERE StudentName='Jones,Mary' GO DELETE FROM Sample.MyStudents WHERE StudentName %STARTSWITH 'A' GO Run() 是一个InterSystems SQL导入方法。这个方法在终端以交互方式运行。它提示你指定导入文本文件的位置,创建Errors.log文件和Unsupported.log文件的位置,以及其他信息。 支持的SQL命令 不是所有有效的InterSystems SQL命令都能被导入。以下是支持的InterSystems SQL命令的列表。 CREATE TABLE, ALTER TABLE, DROP TABLE ​ CREATE VIEW, ALTER VIEW, DROP VIEW ​ CREATE INDEX all index types, except bitslice ​ CREATE USER, DROP USER ​ CREATE ROLE ​ GRANT, REVOKE ​ INSERT, UPDATE, INSERT OR UPDATE, DELETE ​ SET OPTION ​ SELECT for optimizer plan mode only 导入非 InterSystems SQL Code InterSystems SQL 支持导入其他供应商使用的SQL。来自其他供应商的code被转换为InterSystems的SQL code并执行,我们提供了以下方法。 ImportDDL() 也可以用来导入非InterSystems SQL。 要导入特定格式的SQL,你需要指定该格式的名称作为一个参数,例如 FDBMS, Informix, InterBase, MSSQLServer (或MSSQL), MySQL, Oracle, 或Sybase。下面的例子导入了MSSQL代码文件mssqlcode.txt,在当前命名空间中执行该文件中列出的SQL命令。 DO $SYSTEM.SQL.Schema.ImportDDL($lb("C:\temp\somesql.sql","UTF8"),,"MSSQL") 注意,如果第三个参数是MSSQL、Sybase、Informix或MySQL,第一个参数可以是一个SQL code文件路径名或一个双元素%List,第一个元素是SQL code文件路径名,第二个元素是要使用的I/O转换表。 在%SYSTEM.SQL.Schema中提供了单独的交互式方法来导入以下类型的SQL。LoadFDBMS(), LoadInformix(), LoadInterBase(), LoadMSSQLServer(), LoadOracle(), and LoadSybase()。这些方法从终端交互式地运行。它提示你指定导入文本文件的位置,创建Errors.log文件和Unsupported.log文件的位置,以及其他信息。 ImportDDLDir()允许你从一个目录中的多个文件导入SQL code。该方法作为一个后台进程运行。它支持Informix、MSSQLServer和Sybase。所有要导入的文件必须有一个.sql**的后缀。 ImportDir()允许你从一个目录中的多个文件导入SQL code。比ImportDDLDir()提供更多的选项。该方法作为一个后台进程运行。它支持MSSQLServer,和Sybase。你可以指定一个允许的文件扩展名后缀列表。
文章
Qiao Peng · 四月 25, 2022

InterSystems互操作进阶 - 第一篇:InterSystems流程自动化与工作流引擎

InterSystems流程自动化与工作流引擎 InterSystems工作流程引擎的主要功能 2 使用InterSystems工作流程引擎 3 场景描述 3 环境配置与测试 5 任务管理 15 任务API和自定义任务用户界面 16 展望 17 15 集成平台除了集成业务系统,打通数据与业务流程外,另一个核心的功能就是流程自动化(BPA)。 流程自动化涉及几个重要的特性: 流程建模 流程协同 决策自动化 低代码工作流程自动化 任务协同与任务管理 其中第4和5点都是和工作流程相关的。 什么是工作流程(Workflow)?它和业务流程(Business Process)有何区别?为何集成平台要涉及对工作流程的管理? 工作流程是对人工工作任务的流程及其各操作步骤之间业务规则的抽象、概括描述。所以它针对的是人工工作任务,而非业务系统的接口。业务流程与工作流不同,业务流程描述的是特定的业务在各个IT系统间和人工任务间的流程过程和业务抽象。也就是说业务流程范围比工作流程大,是包括工作流程的。 为何要在集成平台里提供工作流程建模和管理的能力?其实医院的所有业务系统都是执行人工任务的,例如医生在HIS中给患者录入诊断、下达医嘱。但并非所有的人工任务和流程在现有的业务系统中都有,例如越来越多的辅助决策系统会提供给医护人员决策建议,这些建议需要医护人员确认才能被采纳。这些辅助决策系统需要被集成平台持续集成:拿到辅助决策的上下文数据,并实时反馈决策建议给业务系统。但它的决策建议缺很难集成进业务系统的工作流程中,这涉及对业务系统的改造 – 改造现有业务系统的流程和用户界面,时间和费用成本高昂。随着辅助决策使用的范围与深度的扩大,通过改造业务系统以纳入对不断涌现的辅助决策支持内容变得越发难以为继。 工作流程引擎可以帮助解决这样的需求,快速满足业务流程优化和再造的需要,创造持续集成的价值。 InterSystems工作流程引擎的主要功能 InterSystems IRIS数据平台、Health Connect医疗集成平台和Ensemble集成平台都内建有工作流引擎。工作流程引擎具有以下功能: 任务角色和用户管理 – 对工作任务的角色定义和角色用户的管理 任务抽象与建模 – 对工作任务的上下文数据模型和任务动作的建模 任务列表 – 对任务进行管理的 任务分配 – 对工作任务进行分配与管理,例如按什么顺序分配任务?任务退回后如何重分配? 任务流程建模和自动化 – 通常工作流程是业务流程的一部分,按业务流程图建立工作流程模型,并自动化执行 任务门户 – 提供给用户的任务界面,用以查看、接受、执行或退回任务 任务API – 提供给第三方系统用以集成的任务查询、接受、执行、退回的API InterSystems数据平台提供了一个针对工作任务的标准业务操作类 -EnsLib.Workflow.Operation,将这个业务操作加入到业务流程即可。它有对应的任务请求消息 –EnsLib.Workflow.TaskRequest 任务响应消息 - EnsLib.Workflow.TaskResponse。这些类都无需修改,直接使用。 使用InterSystems工作流程引擎 场景描述 我们以一个简化的示例为例,说明如何使用工作流。这个示例不需要写代码,完全通过图形化工具和配置工具完成。场景如下: 医生通过医生站下达药嘱后,药嘱发送给药房系统。现在医院上了一套基于机器学习的药品知识库,通过患者的年龄信息、诊断和药嘱,判断药嘱是否有风险。但药房系统尚无法与药品知识库做流程集成,因此我们用InterSystems工作流来做对药剂师的药嘱风险进行提示。整体业务流程图如下: 医生站会发出HL7 V2的药嘱消息OMG_O19,药房系统也接受HL7 V2的药嘱消息OMG_O19。而药品知识库提供服务,需要的请求消息,包含患者诊断、药嘱,并返回警告级别和警告内容。 我们的用例中,医生为控制患者血压开了美托洛尔,但患者有糖尿病,美托洛尔是β受体阻滞剂药物,会影响血糖和血脂的代谢。因此药品知识库会给出药品风险提示。 环境配置 演示环境安装和配置 初始的演示环境在这里下载。 将它导入您的IRIS或HealthConnect平台,如果还没有IRIS,可以下载免费的社区版。 导入后,会看到有Demo.BP.Workflow这个非常简单的业务流程: Production中只有一个业务服务,用来通过文件接收HL7 V2消息,默认的接收文件目录是 C:\Temp\hl7v2\,处理过的HL7文件会保存在C:\Temp\hl7v2\Archive。请配置这2个目录到你本地的可用且有权限的目录。 下载包中提供了样例HL7 V2消息文件,测试时,将其拷贝到接收文件目录即可。 现在我们增加“药剂师复核”的工作任务 向Production中增加操作,弹出页面中: 类名称 选择 EnsLib.Workflow.Operation 操作名称 可以填写“药剂师” 自动创建角色 选择 是 。这样系统会自动检查任务角色名称,如果没有“药剂师”任务角色,会自动帮我们创建 修改业务流程,增加对药剂师角色的任务流程调度 将“待实现药剂师任务”的<empty>流程节点删除,并在原节点上增加“调用(<call>)”流程节点。选择目标为“药剂师”业务操作;取消选中“异步”;给它的名称设置为“提示药剂师药品风险”。 配置药剂师任务 4.1配置任务请求消息 – 应选择EnsLib.Workflow.TaskRequest。这个消息中有这些属性: %Actions:字符串类型,用来让用户执行的操作,如果有多个操作,用逗号分隔。这里我们给药剂师二个操作选项:"取消药嘱,忽略提示"。 %Subject: 字符串类型,任务的主题。今后可用于任务的分析、分配。这里我们设置为"药嘱风险处理"。 %Message:字符串类型,任务的描述。这里我们不赋值。 %Priority: 整数型,任务优先级,1为最高,默认值为3。我们将任务优先级设置为药品风险级别: context.DrugAlertLevel。 %UserName:字符串类型,用于指定任务分配到的具体用户。这里我们计划分配给角色,而不是具体的用户(药剂师),因此不用赋值。 %Title:字符串类型,任务的名称。它和主题不同,是任务的具体名称。这里我们不赋值。 %TaskHandler:字符串类型,任务句柄,设置为响应消息的类名。不用设置。 %Command: 字符串类型,用于向任务句柄传递参数。不用设置。 %FormTemplate: 字符串类型,用于设置用户自定义任务界面的CSP网页名。不用设置。 %FormFields: 字符串类型,用于设置任务用户界面的显示项目名称,多个显示项目名称间用逗号分隔。我们需要显示患者姓名、药品名称、患者诊断、药品风险级别和药品风险信息,设置为"患者姓名,药品名称,患者诊断,药品风险级别,药品风险"。 %FormValues:字符串数组类型,对应%FormFields每个显示项目的值。%FormValues的下标(Key)就是对应的%FormFields的每个显示项目的名称。 其中Key为"患者姓名"的数据从请求HL7消息的PID段的PatientName字节获取,因此值设置为request.{PIDgrp.PID:PatientName}; Key为"药品名称"的数据从上下文变量context的Msg4DrugDB属性的Drug属性中获取,因此值拖拽为context.Msg4DrugDB.Drug; Key为"患者诊断"的数据从上下文变量context的Msg4DrugDB属性的Diagnoses属性中获取,因此值拖拽为context.Msg4DrugDB.Diagnoses; Key为"药品风险级别"的数据从上下文变量context的DrugAlertLevel属性中获取,因此值拖拽为context.DrugAlertLevel; Key为"药品风险"的数据从上下文变量context的DrugAlert属性中获取,因此值拖拽为context.DrugAlert。 4.2配置任务响应消息 – 应配置为EnsLib.Workflow.TaskResponse。这个消息中有这些属性: %Action: 记录了药剂师完成任务时选择了哪个操作。我们需要将它保存到上下文中。直接拖拽callresponse的%Action到context的PharmacistDecision: %Priority:任务优先级,是从任务请求消息拷贝来的。我们无需处理它。 %UserName: 记录哪个用户执行了该任务。我们需要将它保存到上下文中。 %UserTitle:记录执行该任务的用户头衔。我们无需处理它。 %UserRanking: 记录执行该任务的用户在该角色组中的排序。我们无需处理它。 %RoleName:记录执行该任务的角色名称。我们无需处理它。 %Subject:任务的主题,是从任务请求消息拷贝来的。我们无需处理它。 %Message:任务的描述,是从任务请求消息拷贝来的。我们无需处理它。 %Actions:用来让用户执行的操作,是从任务请求消息拷贝来的。我们无需处理它。 %FormTemplate: 用户自定义任务界面的CSP网页名,是从任务请求消息拷贝来的。我们无需处理它。 %FormFields: 任务用户界面的显示项目名称,是从任务请求消息拷贝来的。我们无需处理它。 %FormValues:任务用户界面的项目值。我们无需处理它。 %Status:任务状态,用于查询任务状态。我们无需处理它。 %TaskStatus: 任务状态,用于工作流引擎分配和管理任务。我们无需处理它。 添加业务流程分支,以响应药剂师不同的任务执行结果 在“提示药剂师药品风险”流程节点后面增加对任务执行结果判断,这里需要用“分支<branch>”,而不是“if” - “分支<branch>”可以返回到任何的“标签<label>”节点。 对“分支<branch>”节点,需要设置其条件和标签,在满足条件时转到标签继续执行。因此条件设置为药剂师没有选择“取消医嘱”: context.PharmacistDecision'="取消医嘱";标签选择“药房”。 编译并启动Production 祝贺大家看到这里了,主要工作已经完成,我们没有写任何一行代码、完全通过配置,已经有了如下完整的业务流程图,保存并编译它。 启动Production。 配置工作流角色与工作流用户 启动Production后,系统会自动帮助我们创建“药剂师”工作流角色。可以到管理门户>Interoperability>管理>工作流>工作流角色 确认。 现在要增加一个工作流用户。可以使用你正在使用的IRIS账户作为药剂师用户账户。在管理门户>Interoperability>管理>工作流>工作流用户 页面中选择你的IRIS账户,我这里选择的是SuperUser;并给他一个全名,然后保存。 然后将这个工作流用户加入“药剂师”工作流角色:在工作流角色管理界面点击添加,在用户名中选中刚才创建的工作流用户,其它不用选中,点击确定。现在我们有了可用于测试的工作流用户。 测试 现在我们开始测试: 8.1 检查一下Production是否处于启动状态;然后将示例HL7 V2文件拷贝到接收HL7目录,应该看到这个文件很快就消(处)失(理)了。 8.2登录到任务门户:以药剂师用户帐号登录到管理门户>Analytics>用户门户>工作流收件箱。 可以看到有一个新的“药嘱风险处理”任务,“已分配给”字段时空的,也就是说这个给药剂师角色的人物还没有分配给任何用户。 8.3 这时,我们是看不到任务详情的,但有一个“接受”按钮。点击它就会接受该任务。当任务被接受,其他用户就看不到该任务了。 现在,就可以看到任务详情了:我们设置的任务上下文信息,例如患者姓名、药品名称、药品风险提示都可以看到了。同时,页面上面有4个按钮,其中2个“取消药嘱”、“忽略提示”是我们设置给药剂师的操作。 另外2个按钮是什么? 用户接受了、或被分配了任务,但可以点击“放弃”以退回任务,这样任务又称为“未分配”状态,其他用户就可以看到它并点击“接受”以接受任务。 “保存”是用于未完成任务,但中途需要保存信息时使用。例如任务需要用户书写记录,记录书写到一半离开去忙别的事情,可以点击“保存”以保存已经书写的内容。注意,这时任务并没有完成。 8.4 现在我们让药剂师忽略药嘱风险提示,点击“忽略提示”继续发药流程。 8.5 现在让我们回顾一下完整的业务流程: 在可视化追踪中查看刚才的业务,我们可以看到业务流程按照设计,在药品知识库有提示的情况下,启动了药剂师药嘱风险决策支持任务,并在药剂师忽略提示后,将药嘱发送给药房系统。 大家可以更改一下药剂师的决策,看看流程有什么变化。 任务管理 平台管理员可以查看、分配、重分配、修改优先级和取消任务。 以管理员身份登录到管理门户>Interoperability>管理>工作流>工作流任务,可以查看包括完成的任务在内的所有任务,并可按不同的条件排序。 如果要分配任务,在“用户名”中选择要分配给的用户; 如果要调整任务优先级,选择“优先级”; 如果要取消任务,选中“是否取消”选择框,即可取消。 另外,任务管理员可以将已取消或已完成的任务重新激活。 任务API和自定义任务用户界面 前面介绍了工作流建模、使用工作流门户管理工作任务。如何使用自己的应用或界面管理工作流任务? 有几种方法: 管理门户是网页,它可以被嵌入到其它应用中。即其它应用可以直接通过单点登录,登录到管理门户进行操作。 使用InterSystems平台的任务管理API,然后自己开发任务用户界面或直接在自己的应用中调用。 我个人推荐方法2: 社区里有一篇非常棒的文章,介绍任务管理API和如何使用这些API和Angular进行自定义的任务用户界面开发。 其中任务管理API可以在此下载 自定义任务管理界面可以在此下载 展望 现在的IT应用都是“复合应用”:当今所有应用系统建设即不是原来的纯单体应用开发项目、也不是仅与其它系统整合的集成项目,而是一个复合应用项目 – 每个新业务都需要快速开发并和别的应用集成。 InterSystems工作流引擎为人工工作流程建模提供了低代码/免代码开发的支持。同时,它也赋能复合应用开发: 使用InterSystems工作流和业务流程建模、自动化机器学习引擎或第三方机器学习引擎,可以将基于机器学习的辅助决策支持整合到业务流程中,形成决策过程闭环和机器学习优化闭环。 基于InterSystems工作流,可以将传统编码开发实现的人工工作流程开发通过低代码的业务流程建模方式实现,赋能业务团队高度参与到业务流程梳理、建模与优化中;它将业务流程逻辑与用户界面分离,从而提高业务团队参与度、降低开发成本、快速满足业务进化需求、提高架构灵活性。
文章
Claire Zheng · 二月 13, 2022

2021 InterSystems热门直播链接

在刚刚过去的2021年,InterSystems举办了多场线上直播,我们汇总了多场重要直播,您可以点击链接或扫描相应二维码进入直播间,即可观看精彩回放! 直播一 聚焦InterSystems数据平台与WRC服务 直播二 InterSystems Caché系统运维培训 直播三 集成平台赋能智慧医院建设 直播四 国内外互联互通标准解读与实践 直播五 Health Connect系统运维培训 直播六 高效实施,加速互联互通标准化成熟度测评 直播七 InterSystems 2021全球线上峰会 直播八 InterSystems TrakCare助力医疗机构运营优化和质量提升
文章
Jingwei Wang · 六月 6, 2022

windows版Intersystems IRIS安装步骤

双击安装文件 选择同意协议,下一步 实例名称默认IRISHEALTH,不需要修改,直接下一步 安装路径,可修改,但不可使用中文路径 安装类型选择Development,点击下一步 选择Unicode,下一步 选择Normal,下一步 默认选项,不做修改,下一步 输入IRIS管理账户密码 输入CSP服务管理密码,和上一步密码保持一致。 点击安装,等待安装成功
文章
Jingwei Wang · 七月 21, 2022

InterSystems SQL 的使用 - 第六部分 - SQL 数据的导入、导出

InterSystems IRIS数据平台管理门户中,有一些工具用于导入和导出数据。这些工具使用动态SQL,这意味着查询是在运行时准备和执行的。可以导入或导出的行的最大尺寸是3,641,144个字符。 你也可以使用%SQL.Import.Mgr类或LOAD DATA SQL命令导入数据,并使用%SQL.Export.Mgr类导出数据。 从文本文件中导入数据(.csv 和.txt) 你可以从一个文本文件中导入数据到一个合适的InterSystems IRIS类。当你这样做时,系统会在该类的表中创建并保存新的行。该类必须已经存在并且必须被编译。 步骤如下: 从管理门户中 选择系统资源管理器,然后选择SQL。用页面顶部的切换选项选择一个命名空间;这会显示可用的命名空间的列表。 在页面顶部,点击向导下拉列表,并选择数据导入。 在向导的第一页,首先指定外部文件的位置。对于导入文件所在的位置,点击要使用的服务器的名称。 然后输入文件的完整路径和文件名,文件可以是.csv 和 .txt格式。 然后选择你想要导入到schema的名称。 选择表名。 然后点击下一步。 在向导的第二页,选择需要导入数据的列。 然后点击下一步。 在向导的第三页,描述外部文件的格式。 在 "您的列所使用的分隔符? "中,点击与导入文件中的分隔符相对应的选项。 如果文件的第一行不包含数据,请点‘第一行是否包含列标题?’复选框。 对于字符串引号,点击表示该文件用于开始和结束字符串数据的引号定界符的选项。 对于日期格式,单击表示此文件中的日期格式的选项。 对于时间格式,点击表示此文件中的时间格式的选项。 对于时间戳格式,点击表示此文件中的时间戳格式的选项。 如果你不希望向导在导入时验证数据,请点击禁用验证复选框。 如果您不希望向导在导入时重建索引,请点击 ‘推迟 %SortBegin/%SortEnd 的索引构建?’ 复选框。如果勾选了 "延迟建立索引",向导会在将导入的数据插入到表中之前调用类的%SortBegin方法。当导入完成后,向导会调用%SortEnd方法。没有进行验证(与带有%NOCHECK的INSERT相同)。这是因为当使用%SortBegin/%SortEnd时,在SQL插入过程中不能检查索引的唯一性。 可以选择点击预览数据,看看向导将如何解析这个文件中的数据。 点击 "下一步"。 审查你的条目并点击完成。该向导显示数据导入结果对话框。 点击关闭。或者点击给定的链接,查看背景任务页面。在这两种情况下,向导会启动一个后台任务来完成导入工作。 导出数据到文本文件 你可以将一个给定类的数据导出到一个文本文件。 步骤如下: 从管理门户中 选择系统资源管理器,然后选择SQL。用页面顶部的切换选项选择一个命名空间;这将显示可用的命名空间的列表。 在页面的顶部,点击向导下拉列表,选择数据导出。 在向导的第一页。 输入你要创建的文件的完整路径和文件名,以保存导出的数据。 从下拉列表中,选择你要导出数据的命名空间、模式名称和表名称。 可以选择从字符集下拉列表中选择一个字符集;默认是设备默认值。 然后点击下一步。 在向导的第二页,选择要导出的列。然后点击下一步。 在向导的第三页,描述外部文件的格式。 在 "用什么分隔符分隔你的列?"中,单击与该文件中的分隔符相对应的选项。 如果你想把列头作为文件的第一行导出,请点击 ‘是否导出列标题?’ 复选框。 对于字符串引号,点击一个选项来表示如何在这个文件中开始和结束字符串数据。 对于日期格式,点击一个选项来表示在这个文件中使用的日期格式。 对于时间格式,点击一个选项来指示在此文件中使用的时间格式。 可以选择点击预览数据,看看结果会是什么样子。 然后点击下一步。 审查你的条目并点击完成。向导会显示 "数据导出结果 "对话框。 点击关闭。或者点击给定的链接,查看背景任务页面,向导会启动一个后台任务来完成导出工作。
文章
Jingwei Wang · 八月 30, 2022

使用Python DB-API连接InterSystems IRIS

InterSystems IRIS 允许从任何符合DB-API的Python应用程序对InterSystems IRIS 进行快速、无缝地访问。Python DB-API驱动是对PEP 249 v2.0(Python数据库API规范 v2.0)的完整兼容。 步骤 前提是要有一个Python的开发环境。 本示例使用vs code 如下所示创建一个dbapi.py文件。 dbapi.py : # Embedded Python examples from summer 2022 import iris as dbapi ​ mytable = "mypydbapi.test_things" conn = dbapi.connect(hostname='localhost', port=1972, namespace='IRISAPP', username='superuser', password='iris') ​ # Create table cursor = conn.cursor() try: cursor.execute(f"CREATE TABLE {mytable} (myvarchar VARCHAR(255), myint INTEGER, myfloat FLOAT)") except Exception as inst: pass cursor.close() conn.commit() ​ # Create some data to fill in chunks = [] paramSequence = [] for row in range(10): paramSequence.append(["This is a non-selective string every row is the same data", row%10, row * 4.57292]) if (row>0 and ((row % 10) == 0)): chunks.append(paramSequence) paramSequence = [] chunks.append(paramSequence) ​ query = f"INSERT INTO {mytable} (myvarchar, myint, myfloat) VALUES (?, ?, ?)" ​ for chunk in chunks: cursor = conn.cursor() cursor.executemany(query, chunk) cursor.close() conn.commit() # conn.close() ​ sql = f"select * from {mytable}" rowsRead = 0 cursor = conn.cursor() cursor.arraysize = 20 ​ cursor.execute(sql) rc = cursor.rowcount rows = cursor.fetchall() for row in rows: print(row) rowsRead += len(rows) ​ cursor.close() conn.close() 安装 DB-API驱动,点击此链接下载DB-API驱动 pip install intersystems_irispython-version-py3-none-any.whl 配置Connection String - 按照Intersystems IRIS的服务器,在dbapi.py文件中配置 hostname port namespace username password 在InterSystems IRIS管理门户中创建IRISAPP命名空间。 在VS code中运行dbapi.py文件,运行结果如下,说明数据成功导入。 ['This is a non-selective string every row is the same data', 0, 0.0] ['This is a non-selective string every row is the same data', 1, 4.57292] ['This is a non-selective string every row is the same data', 2, 9.14584] ['This is a non-selective string every row is the same data', 3, 13.71876] ['This is a non-selective string every row is the same data', 4, 18.29168] ['This is a non-selective string every row is the same data', 5, 22.8646] ['This is a non-selective string every row is the same data', 6, 27.43752] ['This is a non-selective string every row is the same data', 7, 32.01044] ['This is a non-selective string every row is the same data', 8, 36.58336] ['This is a non-selective string every row is the same data', 9, 41.156279999999995] 在IRISAPP命名空间下,查看InterSystems IRIS 数据库,可以看到数据,说明数据导入成功InterSystems IRIS。 SELECT myvarchar, myint, myfloat FROM mypydbapi.test_things
文章
Michael Lei · 六月 11, 2022

InterSystems 最佳实践系列文章--系统性能组件SystemPerformance (原 pButtons) API和UI示例

在检查我们的^pButtons(在IRIS中改名为^SystemPerformance)性能监控工具的文档时,一位客户告诉我。"我理解所有内容,但我希望它能更简单......更容易定义配置文件,管理它们等等"。 在这次会议之后,我认为尝试为其提供一些更简单的人机界面是一个不错的试验。 这方面的第一步是在现有的pButtons例程上包裹一个基于类的API。 我还能够添加一些更多的 "功能",比如显示当前正在运行的配置文件,它们剩余的运行时间,以前运行的进程等等。 下一步是在这个API的基础上添加一个REST API类。 有了这个工件(pButtons REST API),人们就可以在上面建立一个比较时髦的用户界面。 举个🌰: - 我在这里分享这个方法的几个步骤: 两个类是 "Basic "API – 以及REST API(包括一些单元测试类来测试这些)。 一个用于REST API的Swagger JSON。为了构建这个,我使用了当时(2017年)InterSystems IRIS中尚未发布的REST管理功能。在InterSystems IRIS提供的基本Swagger JSON的基础上,我添加了更多的信息。 以及一个更加简单的angular UI界面 (基于 http://websystique.com/angularjs/angularjs-crud-application-using-ngresource/) 几个重点提示: 大多数的 "Basic "API方法都使用有文档的和官方支持的pButtons/SystemPerformance rountine的入口点。但有些方法是访问由pButtons工具管理的内部结构。这些方法没有被记录下来,也不被支持,这些方法在升级后可能会停止工作而不被通知。 这段代码不应该作为使用InterSystems IRIS构建基于REST的Angular应用程序的 "最佳实践 "范例。UI部分只是作为一个例子/"预告 "和示例的起点,[例如,它并不完整--"常规部分"(例如,日志文件夹位置管理的占位符)没有实现;刷新内容并不完全工作,以及其他一些 "已知问题..."] 这段代码最早是在很久之前写的--所以: (a) 我重新检查/测试/修改确保能在IRIS上运行 (包括改些名字等等). (b) REST API并没有使用需求/设计优先的方法--我最初确实玩过(当时)IRIS-beta对生成Swagger的支持,现在用它把这个变成了需求/设计优先的方法. (c) 这个可能没有用到最新的特性. (d) 我现在还增加了对在Docker容器中运行的支持(有相关的Docker文件等)。 (e) 我还增加了能作为ZPM包安装的支持。 应用和源代码:https://openexchange.intersystems.com/package/sys-perf-restapi
文章
Jingwei Wang · 二月 14, 2022

使用嵌入式Python实现InterSystems IRIS 互操作性

# 1. 互操作性-嵌入式Python(interoperability-embedded-python) 这个概念旨在展示**iris互操作性框架**如何与嵌入的python一起使用。 ## 1.2. 示例代码 ``` import grongier.pex import iris import MyResponse class MyBusinessOperation(grongier.pex.BusinessOperation): def OnInit(self): print("[Python] ...MyBusinessOperation:OnInit() is called") self.LOGINFO("Operation OnInit") return def OnTeardown(self): print("[Python] ...MyBusinessOperation:OnTeardown() is called") return def OnMessage(self, messageInput): if hasattr(messageInput,"_IsA"): if messageInput._IsA("Ens.StringRequest"): self.LOGINFO(f"[Python] ...This iris class is a Ens.StringRequest with this message {messageInput.StringValue}") self.LOGINFO("Operation OnMessage") response = MyResponse.MyResponse("...MyBusinessOperation:OnMessage() echos") return response ``` ## 1.3. 注册一个组件 **不需要 ObjectScript 代码**. 多亏Grongier.PEX.Utils.RegisterComponent()方法。 启动一个嵌入式的Python shell: ``` /usr/irissys/bin/irispython ``` 然后使用这个类方法将一个新的py文件添加到组件列表中,以实现互操作性。 ``` iris.cls("Grongier.PEX.Utils").RegisterComponent(,,,,) ``` 例如 : ``` iris.cls("Grongier.PEX.Utils").RegisterComponent("MyCombinedBusinessOperation","MyCombinedBusinessOperation","/irisdev/app/src/python/demo/",1,"PEX.MyCombinedBusinessOperation") ``` # 2. 演示 Production有四个Python组件: - 两个业务服务组件 : - 用Grongier.PEX.MyCombinedBusinessService持续的给业务操作组件发送同步消息 - 这些消息是JSON格式的Python对象,存储于Grongier.PEX.Message类 - Thoses messages are python objects casted in JSON and stored in Grongier.PEX.Message. - Python代码 : src/python/demo/MyCombinedBusinessService.py - Grongier.PEX.MyBusinessService是一个用于写消息日志的原始业务服务组件,无其他作用。 - Python 代码 : src/python/demo/MyBusinessService.py - 两个业务操作组件: - Grongier.PEX.BusinessOperation是用于接收业务服务组件Grongier.PEX.MyCombinedBusinessService的消息 - Python 代码 : src/python/demo/MyBusinessOperation.py - Grongier.PEX.CombinedBusinessOperation可以接收Ens.StringRequest消息,并返回Ens.StringResponse消息 - Python 代码 : src/python/demo/MyCombinedBusinessOperation.py 为Python本地消息新增json跟踪: # 3. 前置需求 Git和Docker必须已安装 # 4. 使用Docker安装 从git拉取repo到本地 ``` git clone https://github.com/grongierisc/interpeorability-embedded-python ``` 在安装路径打开terminal,并运行: ``` docker-compose build ``` 使用IRIS容器运行你的项目: ``` docker-compose up -d ``` # 5. 无Docker安装 在iris 实例上安装 grongier_pex-1.0.0-py3-none-any.whl : ``` /usr/irissys/bin/irispython -m pip install grongier_pex-1.0.0-py3-none-any.whl ``` 然后,加载ObjectScript类: ``` do $System.OBJ.LoadDir("/opt/irisapp/src","cubk","*.cls",1) ``` # 6. 运行How to Run the Sample 打开production,并运行。 示例代码会开始运行。 # 7. repo中包含什么内容What's inside the repository ## 7.1. Dockerfile Dockerfile包含在容器中安装一些python的依赖项,例如pip, venv和sudo 然后创建dev目录将git仓库复制进入目录 Dockerfile启动IRIS并导入Titanics csv文件,然后为Python Shell激活**%Service_CallIn**。 可以使用使用相关的docker-compose.yml来轻松地设置额外的参数,如端口号和映射键和主机文件夹的位置。 这个dockerfile以安装python模块的需求结束。 最后一部分是关于安装jupyter notebook和它的内核。 使用.env/文件来调整在docker-compose中使用的dockerfile。 ## 7.2. .vscode/settings.json VSCode配置文件 [VSCode ObjectScript plugin](https://marketplace.visualstudio.com/items?itemName=daimor.vscode-objectscript) ## 7.3. .vscode/launch.json VSCode ObjectScript 调试配置文件 [获取更多相关信息](https://community.intersystems.com/post/dockerfile-and-friends-or-how-run-and-collaborate-objectscript-projects-intersystems-iris) ## 7.4. .vscode/extensions.json 如果你想在容器中用VSCode运行,可以添加扩展。 [获取更多相关信息](https://code.visualstudio.com/docs/remote/containers) 对于使用嵌入式python非常有用。 ## 7.5. src 文件夹 ``` src ├── Grongier │ └── PEX // ObjectScript classes that wrap python code │ ├── BusinessOperation.cls │ ├── BusinessProcess.cls │ ├── BusinessService.cls │ ├── Common.cls │ ├── Director.cls │ ├── InboundAdapter.cls │ ├── Message.cls │ ├── OutboundAdapter.cls │ ├── Python.cls │ ├── Test.cls │ └── Utils.cls ├── PEX // Some example of wrapped classes │ ├── MyBusinessOperationWithAdapter.cls │ ├── MyBusinessOperationWithIrisAdapter.cls │ ├── MyBusinessOperationWithPythonAdapter.cls │ ├── MyBusinessService.cls │ ├── MyOutboundAdapter.cls │ └── Production.cls └── python ├── demo // Actual python code to run this demo │ ├── MyBusinessOperation.py │ ├── MyBusinessOperationWithAdapter.py │ ├── MyBusinessOperationWithIrisAdapter.py │ ├── MyBusinessProcess.py │ ├── MyBusinessService.py │ ├── MyCombinedBusinessOperation.py │ ├── MyCombinedBusinessProcess.py │ ├── MyCombinedBusinessService.py │ ├── MyInboundAdapter.py │ ├── MyLoggingOperation.py │ ├── MyNonPollingStarter.py │ ├── MyOutboundAdapter.py │ ├── MyRequest.py │ ├── MyResponse.py │ ├── MySyncBusinessProcess.py │ └── SimpleObject.py ├── dist // Wheel used to implement python interoperability components │ └── grongier_pex-1.0.0-py3-none-any.whl ├── grongier │ └── pex // Helper classes to implement interoperability components │ ├── _BusinessHost.py │ ├── _BusinessOperation.py │ ├── _BusinessProcess.py │ ├── _BusinessService.py │ ├── _Common.py │ ├── _Director.py │ ├── _InboundAdapter.py │ ├── _Message.py │ ├── _OutboundAdapter.py │ └── __init__.py └── setup.py // setup to build the wheel ``` # 8. 如何增加一个新组件 ## 8.1. 入站适配器InboundAdapter 使用python执行入站适配器,使用grongier.pex.InboundAdapter子类,覆盖OnTask()方法。 ## 8.2. 出站适配器OutboundAdapter 使用python执行出站适配器,使用grongier.pex.OutboundAdapter子类,实现所需action方法。 ## 8.3. 业务服务组件BusinessService 使用python执行业务服务组件,使用grongier.pex.BusinessService子类,覆盖OnProcessInput()方法。 ## 8.4. 业务流程组件BusinessProcess 使用python执行业务流程组件,使用grongier.pex.BusinessProcess子类,覆盖 OnRequest(), OnResponse() and OnComplete()方法。 ## 8.5. 业务操作BusinessOperation 使用python执行业务流程组件,使用grongier.pex.BusinessOperation子类,覆盖 OnMessage()方法。 ## 8.6. 注册一个组件 启动一个嵌入式python shell: ``` /usr/irissys/bin/irispython ``` 然后使用这个类方法将一个新的py文件添加到组件列表中,以实现互操作性。 ``` iris.cls("Grongier.PEX.Utils").RegisterComponent(,,,,) ``` 例如 : ``` iris.cls("Grongier.PEX.Utils").RegisterComponent("MyCombinedBusinessOperation","MyCombinedBusinessOperation","/irisdev/app/src/python/demo/",1,"PEX.MyCombinedBusinessOperation") ``` ## 8.7. 直接使用Grongier.PEX 如果你不想使用RegisterComponent,你可以添加一个Grongier.PEX.Business*组件并配置其属性 - %module : - 你的python代码的模块名称 - %classname : - 你的组件的类名 - %classpaths - 你的组件所在的路径。 - 除了PYTHON_PATH之外,可以有一个或多个Classpaths(用'|'字符分隔) 例如 : # 9. 其他工作 - 仅业务服务组件和业务操作组件可以被测试 - 在适配器上工作 # 10. 认证 大部分代码来自Mo Cheng和Summer Gerry的PEX for Python。 注册部分来自于尚未发布的IRIS 2021.3的功能。