清除过滤器
文章
Claire Zheng · 二月 1, 2021
Hi, 大家好!
我们正在努力改进本网站,让大家可以在 InterSystems 开发者社区舒适地阅读、贡献、分享和获取答案!
虽然有一些 UI 问题还没有得到修复,但本帖提供了一些关于如何使用开发者社区的简单回答。
如何添加帖子?
打开社区页面, 选择所需产品,例如 Caché 然后点击 "创建新帖"。
如何订阅帖子的更新?
要订阅帖子的更新并通过电子邮件获取它们,请点击帖子摘要下方的“收藏”,例如:
如何管理帖子的标题?
假设您已经创建文章,并且想要在其注释中添加几句话。
为此,将编辑器切换到“筛选的 HTML”模式,然后在注释末尾输入<!--break--> 标签。
否则注释会被修剪为一两句话。 就像这样:
未完待续。 敬请期待!
文章
Michael Lei · 六月 1, 2022

大家好!
在这里跟大家分享一下我在大奖赛上的项目 :)
FHIR病人查看器是一个建立在Vue.js上的单页、反应式渲染工具,它以对人友好的方式显示从对InterSystems FHIR服务器的/Patient/{id}/$everything调用返回的数据。在自述文件中,包括3个主要内容:
1. 一个视频演示,将FHIR患者浏览器连接到一个沙盒IRIS FHIR服务器上(这是测试它的最快方法);
2.第二个视频显示我如何在生产环境中使用FHIR患者浏览器(使用一个定制的后端来处理API调用,在我的例子中用PHP/Laravel编写,但可以转移到其他语言/框架);
3.修改组件的说明,创建你自己版本的工具,并建立你自己的dist文件。
谢谢大家! 本次大赛的参赛作品质量很好!
Dan
问题
Johnny Wang · 二月 24, 2022
请教各位老师:
Cache数据日志怎么读取?
如果是想写代码去读取,应该怎么操作?如果是不知道数据日志在哪,应该在哪可以获取? 不知道你问题里的日志是不是指的journal文件。
没有直接读取journal的方法,只有类似MirrorDejournal的方法对写入异步镜像成员的journal进行操作,
比如将内容写入到你自己的一个数据global中。
在写journal的同时将journal中记录的内容存放在另一个地方。
具体做法可以参考社区里的另一篇文章:CDC系列之一 :使用Dejournal Filter在InterSystems IRIS/Caché上通过Mirroring实现CDC功能
对于较早版本还没有镜像Mirror的配置,则可以使用Shadow服务器的Dejournaling filter routine。
可以参考:CDC系列之二 :使用Dejournaling filter routine在Caché上通过Shadow实现CDC
文章
Michael Lei · 十一月 9, 2021
https://www.appeon.com/products/powerbuilder
Appeon PowerBuilder 是一个企业级开发工具,可以用来建立数据驱动的商业应用程序和组件。它是Appeon产品套件之一,同时提供了开发C/S、Web、移动和分布式应用程序的工具。
在这篇文章中,我将展示通过使用ODBC用Appeon PowerBuilder连接Caché的步骤。
步骤1 :确保在安装IRIS时选择ODBC驱动程序选项。
步骤2:通过使用ODBC数据源管理器配置ODBC IRIS数据源
步骤 3: 配置InterSystems ODBC 数据源
步骤 4: 测试连接 (确保 IRIS 实例在运行)
步骤 5: 从 PowerBuilder 打开数据库Profiles, 在列表选中ODB ODBC并单击“新建 New”... 按钮
步骤 6: 选择我们已经用ODBC管理器创建的 "IRISHealth User"数据源
步骤 7: 点击“测试连接Test Connection” 按钮,在Preview页面下测试连接
恭喜! 我们已经成功建立了IRIS链接。现在我们可以用PowerBuilder database painter来看表和数据了
谢谢
问题
Luo Haimianbaobao · 四月 13, 2023
问题如标题。尝试过改变java网关端口,还是会经常出现报错,错误如下图: 这个java Gateway 报错的引起的原因可能很多,如果是生产环境,建议开一个WRC工单,如果是开发测试环境可以InterSystems 的se 可以将Java Gateway Service的日志打开,设置方法是在Production管理页面选中该组件上,在设置中设置日志文件(包括路径和文件名称)。如果问题再次出现,我们可以对日志文件进行分析,开启之后请注意该文件的大小增长。
另外,linux的Dynamic TCP port范围是32768~60999,可通过下面的命令进行查询,例如(在RedHat7.9下),
sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999
在这个范围内的tcp端口号可能会被系统动态分配给其他进程使用,所以建议咱们更改一个不在此范围内的端口号。
文章
Meng Cao · 十一月 23, 2023
前言
随着网络安全日益被重视,特别是等级保护制度的大环境下,SSL加密传输越来越被重视,本文介绍如何使用支持SSL的ODBC连接IRIS数据库。
数据库版本:IRISHealth-2023.1
1. 服务器端配置
1)新建SSL服务器配置。
2)开启超级端口的SSL支持,这里我们选择启用即可,如所有超级端口的连接都支持SSL可选要求。
2.Windows客户端配置
1)创建 SSLDefs.ini 配置文件,并编辑内容:
[TLS to an InterSystems IRIS instance] Address=127.0.0.1 Port=51773 SSLConfig=DHCC
[DHCC] TLSMinVersion=8 TLSMaxVersion=16 CipherList=ALL:!aNULL:!eNULL:!EXP:!SSLv2 KeyType=2 VerifyPeer=1 Password= CertFile=E:\ADSSL\ADSSL.crt KeyFile=E:\ADSSL\ADSSL.key CAfile=E:\ADSSL\ADSSL.crt
2)设置系统环境变量 ISC_SSLconfigurations ,指向 SSLDefs.ini 配置文件。
3.创建ODBC源进行测试。
很棒哦,加油加油加油! 学到了,大佬棒棒 学到了 学到了 学到了,赞👍 👍👍👍
文章
Louis Lu · 十一月 22, 2024
当我们将 InterSystems IRIS 安装在Windows 系列操作系统中时,往往同时使用IIS 作为其Web 服务器。
但这时候我们却发现,由RESTful返回的错误信息却不能正常显示出来了,特别是在调试程序的时候,非常不方便。
比如在测试 FHIR Post/Put 请求时,我们的请求消息体中包含不正确的内容,这时我可以看到返回的是HTTP 400 Bad Request的返回内容.
我无法具体知道请求消息体哪句出错了,这对我调试程序造成很大困扰。
解决方法:
1. 打开IIS的管理页面,双击 错误页
2. 选择右侧 编辑功能设置
3. 选择错误响应下的 详细错误 选项。
我们再次请求,此时可以看到页面上显示了详细的错误信息
相关详细信息可以查看文档:Configuring IIS to Return SOAP Fault Details 以及 HTTP Errors
文章
bai hongtao · 十月 7, 2022
在Cache 2018之前的版本中,数据库的高可用是通过第三方HA软件保障的,Cache数据库在2018以后及IRIS支持MIRROR技术,通过MIRROR可以保障数据库的高可用及数据的冗余,那么在新版本中,第三方HA软件与MIRROR是否可以同时使用以实现更高的数据库可用性?使用起来有哪些需要注意的?本文重点介绍探讨上述两个问题。
为得出正确结论,我们搭建了如下实验环境:
我们采用3个服务器节点A、B、C分别部署IRIS 2021.1数据库,其中A节点、B节点部署第三方HA软件组成数据库高可用主备集群(本例中,采用的是基于POWER平台的PowerHA),该集群中定义A节点为主节点,B节点为备用节点,HA集群的共享资源组存放在共享SAN存储上,通过HA,生成HA集群的对外服务IP,即我们通常说的Service ip,保证在生产节点发生网络故障、主机故障、以及操作系统故障、手动切换等情况下,IRIS服务、共享资源组、以及HA的Service IP可自动切换至另外一台服务器,保障IRIS高可用,经测试,HA集群内部节点间服务切换时间约为30秒。
搭建IRIS的MIRROR集群,其中A、B两个节点组成的HA集群通过Service IP添加至MIRROR集群中,做为MIRROR集群的一个Failover member,C节点做为MIRROR集群的另外一个Failover member或者DR节点,镜像服务质量超时时间保持默认(8000ms),D节点只部署ISC Agent,做为Arbiter(Mirror的仲裁程序节点),整个集群通过Mirror集群的VIP对外提供服务。
测试环境的架构视图下所示:
首先,我们将HA集群做为Mirror的主Failover Member,C为Mirror的备Failover Member,此时HA Service IP及Mirror VIP均位于A节点上,数据库通过A节点向外提供服务,此时,我们在默认的镜像服务质量超时时间(8s)及调试后的镜像服务质量超时时间(80s)下,分别模拟A节点故障、A节点B节点几秒内先后故障、通过HA执行数据库服务从A节点向B节点手动切换三种场景,测试中观察到的现象及最终结果如下表所示:
镜像服务质
量超时时间
测试项
现象观察
最终结果
默认(8s)
A节点故障
A节点故障约8秒后MIRROR VIP切换至C节点,数据库服务恢复正常
Mirror集群报告Member Fail,HA集群开始内部切换
约30秒后HA Service IP及集群切换至B节点,B节点接替A节点加入Mirror集群
HA集群单节点故障
Mirror集群正常
A节点,B节点几秒内先后故障
A节点故障约8秒后Mirror VIP切换至C节点,数据库服务恢复正常
HA集群故障不可用
Mirror集群报告Member Fail
通过HA,执行数据库服务从A节点向B节点手动切换
开始执行手动切换约8秒后Mirror VIP切换至C节点,数据库服务恢复正常
约20秒后HA Service IP切换至B节点
HA集群正常
Mirror集群正常
调整为(80s)
A节点故障
A节点故障约30秒后Mirror VIP及HA Service IP切换至B节点,B节点接替A节点加入Mirror集群
HA集群单节点故障
Mirror集群正常
A节点,B节点几秒内先后故障
A节点故障约80秒后Mirror VIP切换至C节点
HA集群故障不可用
Mirror集群报告Member Fail
通过HA,执行数据库服务从A节点向B节点手动切换
约20秒后HA Service IP及Mirror VIP切换至B节点
HA集群正常
Mirror集群正常
测试结论:
IRIS 数据库服务在Mirror 集群的Failover Member之间切换,在journal一致的前提下,只需要进行Mirror VIP漂移就可完成,而第三方HA集群切换则至少需要进行故障节点共享存储资源释放、接管节点的存储资源装载、接管节点数据库服务启动、HA Service IP漂移等一系列动作,故Mirror集群的内部切换要快于第三方HA集群的内部切换
可调整镜像服务质量超时时间,实现不同的切换效果,当镜像服务质量超时时间设置得长于HA的切换时间时,HA将先于Mirror切换,数据库服务中断时间相对长,切换逻辑相对简单。当镜像服务质量超时时间设置得短于HA的切换时间时,Mirror将先于HA切换,数据库服务中断时间短,切换逻辑相对复杂。
IRIS中Mirror 集群的Failover节点数量为1-2个,可以通过第三方HA软件为Failover节点提供“热备”,提升IRIS数据库的可用性及业务连续性。
文章
姚 鑫 · 十二月 4, 2021
# 第五章 SQL谓词 BETWEEN
# 大纲
```
scalar-expression BETWEEN lowval AND highval
```
# 参数
- `scalar-expression` - 一种标量表达式(最常见的是数据列),将其值与低值和高值(包括高值)之间的值范围进行比较。
- `lowval` - 解析为低排序规则序列值的表达式,指定与标量表达式中的每个值匹配的值范围的开始。
- `highval` - 解析为高排序规则序列值的表达式,指定要与标量表达式中的每个值匹配的值范围的末尾。
# 描述
`BETWEEN`谓词允许选择`lowval`和`highval`指定范围内的数据值。
这个范围包括低值和高值本身。
这等价于一对大于或等于操作符和一对小于或等于操作符。
下面的例子展示了这种比较:
```sql
SELECT Name,Age FROM Sample.Person
WHERE Age BETWEEN 18 AND 21
ORDER BY Age
```
这将返回`Sample`中的所有记录。
年龄值介于`18`到`21`之间的人员表,包括这些值。
注意,必须按升序指定`BETWEEN`值;
例如`BETWEEN 21 AND 18`这样的谓词将返回空字符串。
如果标量表达式的值都不在指定的范围内,则`BETWEEN`返回空字符串。
与大多数谓词一样,`BETWEEN`可以使用`NOT`逻辑运算符进行反转。
`BETWEEN`和`NOT BETWEEN`都不能用于返回`NULL`字段。
返回`NULL`字段使用`IS NULL`。
`NOT BETWEEN`的示例如下:
```sql
SELECT Name,Age FROM Sample.Person
WHERE Age NOT BETWEEN 20 AND 55
ORDER BY Age
```
这将返回`Sample`中的所有记录。
年龄值小于`20`或大于`55`的人表,不包括这些值。
# 排序类型
`BETWEEN`通常用于按数字顺序排序的数值范围。
但是,`BETWEEN`可用于任何数据类型值的排序规则序列范围。
`BETWEEN`使用与它所匹配的列相同的排序规则类型。
默认情况下,字符串数据类型排序为`SQLUPPER`,这是不区分大小写的。
如果查询为列分配了不同的排序规则类型,则还必须将此排序规则类型应用于`BETWEEN`子字符串。
下面的例子说明了这一点:
在下面的示例中,`BETWEEN`使用字段的默认字母大小写排序规则`SQLUPPER`,它不区分大小写。
它返回`Name`的字母顺序比`Home_State`高,`Home_State`的字母顺序比`Home_City`高的记录:
```sql
SELECT Name,Home_State,Home_City
FROM Sample.Person
WHERE Home_State BETWEEN Name AND Home_City
ORDER BY Home_State
```
在下例中,`BETWEEN`字符串比较不区分大小写,因为`Home_State`字段被定义为`SQLUPPER`。
这意味着低`val`和高`val`在功能上是相同的,在任何字母中选择`'MA'`:
```sql
SELECT Name,Home_State FROM Sample.Person
WHERE Home_State
BETWEEN 'MA' AND 'Ma'
ORDER BY Home_State
```
在下面的示例中,`%SQLSTRING`排序函数使`BETWEEN`字符串比较区分大小写。
它选择那些`Home_State`值为`'MA'`到`'MA'`的记录,在这个数据集中包括`'MA'`, `'MD'`, `'ME'`, `'MO'`, `'MS'`和`'MT':`
```sql
SELECT Name,Home_State FROM Sample.Person
WHERE %SQLSTRING(Home_State)
BETWEEN %SQLSTRING('MA') AND %SQLSTRING('Ma')
ORDER BY Home_State
```
在以下示例中,`BETWEEN`字符串比较不区分大小写,并且忽略空格和标点符号:
```sql
SELECT Name FROM Sample.Person
WHERE %STRING(Name) BETWEEN %SQLSTRING('OA') AND %SQLSTRING('OZ')
ORDER BY Name
```
下面的示例显示了在内部连接操作`ON`子句中使用的BETWEEN。
它正在执行一个不区分大小写的字符串比较:
```sql
SELECT P.Name AS PersonName,E.Name AS EmpName
FROM Sample.Person AS P INNER JOIN Sample.Employee AS E
ON P.Name BETWEEN 'an' AND 'ch' AND P.Name=E.Name
```
# %SelectMode
如果`%SelectMode`设置为逻辑格式以外的值,那么`BETWEEN`谓词值必须以`%SelectMode`格式(`ODBC`或`Display`)指定。
这主要适用于日期、时间和 IRIS格式列表(`%List`)。
以逻辑格式指定谓词值通常会导致`SQLCODE`错误。
例如,`SQLCODE -146`“无法将日期输入转换为有效的逻辑日期值”。
在下面的动态SQL示例中,BETWEEN谓词必须以`%SelectMode=1` (ODBC)的格式指定日期:
```sql
ClassMethod Between()
{
s q1 = "SELECT Name,DOB FROM Sample.Person "
s q2 = "WHERE DOB BETWEEN '1950-01-01' AND '1960-01-01'"
s myquery = q1_q2
s tStatement = ##class(%SQL.Statement).%New()
s tStatement.%SelectMode=1
s qStatus = tStatement.%Prepare(myquery)
if qStatus'=1 {
w "%Prepare failed:"
d $System.Status.DisplayError(qStatus)
q
}
s rset = tStatement.%Execute()
d rset.%Display()
w !,"End of data"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQLCommand).Between()
Name DOB
Houseman,Martin D. 1955-09-25
Ingrahm,Yan S. 1954-06-15
Smith,Elvis Y. 1955-06-29
Gore,Alfred M. 1958-09-15
Yoders,Liza U. 1959-06-05
Ng,Liza Z. 1955-10-05
Yeats,Debby G. 1951-12-06
Zweifelhofer,Zelda J. 1954-02-19
Solomon,Emily D. 1953-01-28
Isaacs,Elvis V. 1952-04-05
Pantaleo,Robert U. 1950-03-29
Zampitello,Josephine Q. 1953-08-14
Xiang,Molly F. 1953-03-21
Nichols,Heloisa M. 1957-07-19
Hertz,Uma C. 1954-07-25
LaRocca,David X. 1956-01-11
Houseman,Alice R. 1957-12-07
Alton,Phil T. 1953-02-25
Davis,Jane E. 1953-07-28
Vanzetti,Alexandra O. 1953-12-29
Uhles,Dmitry P. 1951-08-23
Jafari,Christine Z. 1950-04-11
22 Rows(s) Affected
End of data
```
文章
姚 鑫 · 四月 29, 2021
# 第九章 冻结计划
大多数SQL语句都有一个关联的查询计划。查询计划是在准备SQL语句时创建的。默认情况下,添加索引和重新编译类等操作会清除此查询计划。下次调用查询时,将重新准备查询并创建新的查询计划。冻结计划使可以跨编译保留(冻结)现有查询计划。查询执行使用冻结的计划,而不是执行新的优化并生成新的查询计划。
对系统软件的更改也可能导致不同的查询计划。通常,这些升级会带来更好的查询性能,但软件升级可能会降低特定查询的性能。冻结计划使可以保留(冻结)查询计划,以便查询性能不会因系统软件升级而改变(降级或提高)。
# 如何使用冷冻计划
使用冻结计划有两种策略-乐观策略和悲观策略:
- 乐观:如果假设更改系统软件或类定义会提高性能,请使用此策略。运行查询并冻结计划。导出(备份)冻结的计划。解冻该计划。更改软件。重新运行查询。这会产生一个新的计划。比较这两个查询的性能。如果新计划没有提高性能,可以从备份文件中导入先前冻结的计划。
- 悲观:如果假设系统软件或类定义的更改可能不会提高特定查询的性能,请使用此策略。运行查询并冻结计划。更改软件。使用`%NOFPLAN`关键字重新运行查询(这会导致冻结的计划被忽略)。比较这两个查询的性能。如果忽略冻结的计划没有提高性能,请保持冻结该计划并从查询中删除`%NOFPLAN`。
# 软件版本升级自动冻结计划
将InterSystems IRIS®Data Platform升级到新的主要版本时,现有的查询计划将自动冻结。这可确保重大软件升级永远不会降低现有查询的性能。升级软件版本后,对性能关键型查询执行以下步骤:
1. 执行计划状态为冻结/升级的查询,并监控性能。这是在软件升级之前创建的优化查询计划。
2. 将`%NOFPLAN`关键字添加到查询中,然后执行并监视性能。这将使用软件升级提供的SQL优化器优化查询计划。它不会解冻现有的查询计划。
3. 比较性能指标。
- 如果`%NOFPLAN`性能更好,则软件升级改进了查询计划。解冻查询计划。删除`%NOFPLAN`关键字。
- 如果`%NOFPLAN`性能较差,则软件升级会使查询计划降级。保持查询计划冻结状态,将查询计划从冻结/升级升级为冻结/显式。删除`%NOFPLAN`关键字。
4. 测试性能关键型查询后,可以解冻所有剩余的冻结/升级计划。
当在比最初创建计划时使用的InterSystems软件版本更新的InterSystems软件版本下准备/编译查询时,会发生这种自动冻结。例如,考虑一条在系统软件版本xxxx.1下准备/编译的SQL语句。随后升级到版本xxxx.2,再次准备/编译SQL语句。系统将检测到这是SQL语句在新版本上的第一次准备/编译,并自动将计划状态标记为冻结/升级,并将现有计划用于新的准备/编译。这确保使用的查询计划不会比以前版本的查询计划差。
只有主要版本的InterSystems系统软件升级才会自动冻结现有查询计划。维护发布版本升级不会冻结现有查询计划。例如,主要版本升级(如从2018.1升级到2019.1)将执行此操作。维护版本升级(如2018.1.0到2018.1.1)不执行此操作。
在管理门户SQL界面中,SQL语句计划状态列将这些自动冻结的计划指示为冻结/升级,计划版本指示原始计划的系统间软件版本。
可以使用`INFORMATION.SCHEMA.STATEMENTS` `Frozen=2`属性列出当前命名空间中的所有冻结/升级计划。
可以使用以下`$SYSTEM.SQL.Statement`方法冻结单个查询计划或多个查询计划:`FreezeStatement()`用于单个计划;`FreezeRelation()`用于关系的所有计划;`FreezeSchema()`用于架构的所有计划;`FreezeAll()`用于当前命名空间中的所有计划。有相应的解冻方法。
- 冻结方法可以提升(“冻结”)标记为冻结/升级到冻结/显式的查询计划。通常,可以使用此方法有选择地将适当的冻结/升级计划升级为冻结/显式,然后解冻所有剩余的冻结/升级计划。
- 解冻方法可以解冻指定范围内的冻结/升级查询计划:命名空间、架构、关系(表)或单个查询。
# 冻结计划界面
冻结计划界面有两种,用途不同:
- Management Portal SQL语句界面,用于冻结(或解冻)单个查询的计划。
- `$SYSTEM.SQL.Statement`冻结和解冻方法,用于冻结或解冻命名空间、架构、表或单个查询的所有计划。
在Management Portal SQL界面中,选择`Execute Query`选项卡。编写查询,然后单击显示计划按钮以显示当前查询执行计划。如果计划被冻结,则查询计划部分的第一行是“冻结计划”。
在管理门户SQL界面中,选择SQL语句选项卡。这将显示SQL语句列表。此列表的计划状态列指定解冻、解冻/并行、冻结/显式或冻结/升级。(如果语句没有关联的查询计划,则计划状态列为空。)
可以使用`INFORMATION.SCHEMA.STATEMENTS` Frozen属性值列出当前命名空间中所有SQL语句的计划状态:`UNFRECTED(0)`、`Frozen/EXPLICIT(1)`、`Frozen/Upgrade(2)`或`UNFORMATED/PARALLEL(3)`。
要冻结或解冻计划,请在SQL语句文本列中选择SQL语句。这将显示“SQL语句详细信息”框。在此框的底部显示对帐单文本和查询计划。如果计划未冻结,则这些横断面的背景颜色为绿色,如果计划已冻结,则背景颜色为蓝色。在其正上方的对帐单操作下,可以根据需要选择冻结计划或解冻计划按钮。然后选择关闭。
- 冻结计划按钮:单击此按钮将冻结此语句的查询优化计划。冻结计划并编译该SQL语句时,SQL编译将使用冻结的计划信息并跳过查询优化阶段。
- 解冻计划按钮:点击该按钮将删除该语句冻结的计划,该语句的新编译将进入查询优化阶段,以确定要使用的最佳计划。
还可以使用`$SYSTEM.SQL.Statement`冻结和解冻方法冻结或解冻一个或多个计划。通过指定适当的方法,可以指定冻结或解冻操作的范围:单个计划的`FreezeStatement()`;关系的所有计划的`FreezeRelation()`;架构的所有计划的`FreezeSchema()`;当前命名空间中的所有计划的`FreezeAll()`。有相应的解冻方法。
## 权限
用户只能查看他们具有`EXECUTE`权限的那些SQL语句。这既适用于Management Portal SQL语句列表,也适用于`INFORMATION.SCHEMA.STATEMENTS`类查询。
管理门户SQL语句访问要求对`%Development`资源具有`“USE”`权限。任何可以在管理门户中看到SQL语句的用户都可以冻结或解冻该语句。
对于SQL语句的目录访问,如果您具有执行该语句的权限或对`%Development`资源具有`“Use”`权限,则可以看到这些语句。
对于`$SYSTEM.SQL.Statement`冻结或解冻方法调用,必须对`%Developer`资源拥有`“U”`权限。
## 冻结计划不同
如果计划被冻结,可以确定解冻该计划是否会导致不同的计划,而无需实际解冻该计划。此信息可以帮助您确定哪些SQL语句值得使用`%NOFPLAN`进行测试,以确定解冻计划是否会带来更好的性能。
可以使用`INFORMATION.SCHEMA.STATEMENTS` `FrozenDifferent`属性列出当前命名空间中此类型的所有冻结计划。
冻结的计划可能会因以下任一操作而与当前计划不同:
- 重新编译该表或该表引用的表
- 使用`SetMapSelecability()`激活或停用索引
- 在表上运行`TuneTable`
- 升级InterSystems软件版本
重新编译会自动清除现有的缓存查询。对于其他操作,必须手动清除现有缓存查询才能使新查询计划生效。
这些操作可能会也可能不会产生不同的查询计划。有两种方法可以确定它们是否这样做:
- 手工检查个别冻结计划
- 每天自动扫描所有冻结计划
如果计划尚未由这两个操作中的任何一个检查,或者计划未冻结,则列出新计划的SQL语句列为空。解冻选中的冻结计划会将新建计划列重置为空。
## 手动冻结计划检查
在冻结计划的SQL语句详细资料页的顶部有一个检查冻结按钮。按此按钮将显示解冻不同计划复选框。如果选中此框,则解冻计划将导致不同的查询计划。
对冻结计划执行此检查冻结测试后:
- 如果选中解冻计划不同框,则列出新计划的SQL语句列包含“1”。这表明解冻计划将导致不同的计划。
- 如果未选中解冻计划不同框,则列出新计划的SQL语句列将包含“0”。这表明解冻计划不会产生不同的计划。
- 已冻结的缓存查询的New Plan为“0”;清除缓存查询,然后解冻该计划会导致SQL语句消失。
- 已冻结的`Natura`l查询在New Plan列中为空。
执行此测试后,检查冻结按钮消失。如果要重新测试冻结的计划,请选择刷新页面按钮。这将重新显示检查冻结按钮。
## 日冻结计划自动检查
InterSystems SQL每晚`2:00`自动扫描SQL语句清单中的所有冻结语句。这次扫描最多持续一个小时。如果扫描未在一小时内完成,系统会记下它停止的位置,并从该点继续进行下一次每日扫描。可以使用管理门户监视此每日扫描或强制其立即扫描:选择系统操作、任务管理器、任务计划,然后选择扫描冻结计划任务。
此扫描检查所有冻结的计划:
- 如果冻结的计划具有与当前版本相同的InterSystems软件版本,InterSystems IRIS®Data Platform将计算两个计划的引用表和时间戳的散列,以创建可能已更改的内部计划列表。对于这个子集,它然后执行两个计划的逐个字符串比较,以确定哪些计划实际上不同。如果两个计划之间有任何不同(无论有多小),它都会在列`出New Plan`列的SQL语句中用`“1”`标记SQL语句。这表明解冻计划将导致不同的查询计划。
- 如果冻结的计划具有与当前版本相同的InterSystems IRIS版本,并且两个计划的逐字符串比较完全匹配,则它会将列出新计划的SQL语句列中的SQL语句标记为`“0”`。这表明解冻计划不会导致不同的查询计划。
- 如果冻结的计划具有与当前版本(冻结/更新)不同的InterSystems软件版本,InterSystems IRIS将确定对SQL优化器逻辑的更改是否会导致不同的查询计划。如果是,它将用`“1”`标记“SQL Statements Listing New Plan”列中的SQL语句。否则,它会用`“0”`标记SQL语句`New Plan`列。
可以通过调用`INFORMATION.SCHEMA.STATEMENTS`来检查此扫描的结果。以下示例返回所有冻结计划的SQL语句,指示冻结的计划是否与未冻结的计划不同。请注意,解冻语句可以是`Frozen=0`或`Frozen=3`:
```sql
SELECT Frozen,FrozenDifferent,Timestamp,Statement FROM INFORMATION_SCHEMA.STATEMENTS
WHERE Frozen=1 OR Frozen=2
```
## 冻结计划出错
如果语句的计划被冻结,并且计划使用的定义发生了某些更改,从而导致计划无效,则会发生错误。例如,如果从语句`PLAN`使用的类中删除了索引:
- 该声明的计划仍处于冻结状态。
- 在“SQL语句详细信息”页上,“编译设置”区域显示“计划错误”字段。例如,如果查询计划使用索引名`indxdob`,然后您修改了类定义以删除索引`indxdob`,则会显示如下消息: `Map 'indxdob' not defined in table 'Sample.Mytable', but it was specified in the frozen plan for the query`.
- 在SQL语句详细资料页上,查询计划区域显示由于冻结计划中的错误而无法确定计划。
如果在冻结计划处于错误状态时重新执行查询,则InterSystems IRIS不使用冻结计划。相反,系统会创建一个新的查询计划,该计划将在给定当前定义的情况下工作,并执行查询。此查询计划被分配了与前一个查询计划相同的缓存查询类名。
在计划解冻或修改定义以使计划返回有效状态之前,出错的计划将一直处于错误状态。
如果修改定义以使计划返回有效状态,请转到SQL语句详细资料页,然后按清除错误按钮以确定是否已更正错误。如果更正,计划错误字段将消失;否则将重新显示计划错误消息。如果已更正定义,则不必显式清除计划错误,SQL即可开始使用冻结计划。如果已更正定义,则清除错误按钮会使SQL语句详细资料页的冻结查询计划区域再次显示执行计划。
计划错误可能是 `“soft error.”`。当计划使用索引,但查询优化器当前无法选择该索引时,可能会出现这种情况,因为`SetMapSelecability()`已将其可选择性设置为`0`。这样做可能是为了[重建]索引。当InterSystems IRIS遇到具有冻结计划的语句的软错误时,查询处理器会尝试自动清除错误并使用冻结计划。如果该计划仍然出错,则该计划将再次标记为出错,并且查询执行将尽可能使用最佳计划。
# %NOFPLAN关键字
可以使用`%NOFPLAN`关键字覆盖冻结的计划。包含`%NOFPLAN`关键字的SQL语句将生成新的查询计划。冻结的计划将保留,但不会使用。这允许测试生成的计划行为,而不会丢失冻结的计划。
```sql
DECLARE CURSOR FOR SELECT %NOFPLAN ...
SELECT %NOFPLAN ....
INSERT [OR UPDATE] %NOFPLAN ...
DELETE %NOFPLAN ...
UPDATE %NOFPLAN
```
在`SELECT`语句中,`%NOFPLAN`关键字只能在查询中的第一个`SELECT`之后立即使用:它只能与`UNION`查询的第一个分支一起使用,不能在子查询中使用。`%NOFPLAN`关键字必须紧跟在`SELECT`关键字之后,位于`DISTINCT`或`TOP`等其他关键字之前。
# 导出和导入冻结计划
可以将SQL语句作为`XML`格式的文本文件导出或导入。这使可以将冻结的计划从一个位置移动到另一个位置。SQL语句导出和导入包括关联查询计划的编码版本和指示该计划是否冻结的标志。
文章
姚 鑫 · 三月 23, 2021
# 第十三章 使用动态SQL(一)
# 动态SQL简介
动态SQL是指在运行时准备并执行的SQL语句。在动态SQL中,准备和执行SQL命令是单独的操作。通过动态SQL,可以以类似于ODBC或JDBC应用程序的方式在InterSystems IRIS中进行编程(除了要在与数据库引擎相同的进程上下文中执行SQL语句)。动态SQL是从ObjectScript程序调用的。
动态SQL查询是在程序执行时准备的,而不是在编译时准备的。这意味着编译器无法在编译时检查错误,并且不能在Dynamic SQL中使用预处理器宏。这也意味着执行程序可以响应用户或其他输入而创建专门的Dynamic SQL查询。
动态SQL可用于执行SQL查询。它也可以用于发出其他SQL语句。本章中的示例执行SELECT查询。
动态SQL用于执行InterSystems IRIS SQL Shell,InterSystems IRIS管理门户网站“执行查询”界面,SQL代码导入方法以及“数据导入和导出实用程序”。
在Dynamic SQL(和使用它的应用程序)中,行的最大大小为`3,641,144`个字符。
## 动态SQL与嵌入式SQL
动态SQL与嵌入式SQL在以下方面有所不同:
- **动态SQL查询的初始执行效率比嵌入式SQL稍低,因为它不会生成查询的内联代码。但是,动态SQL和嵌入式SQL的重新执行比第一次执行查询要快得多,因为它们都支持缓存的查询。**
- 动态SQL可以通过两种方式接受输入到查询的文字值:使用`“?”`指定的输入参数。字符和输入主机变量(例如`:var`)。嵌入式SQL使用输入和输出主机变量(例如`:var`)。
- 使用结果集对象(即`Data`属性)的API检索动态SQL输出值。嵌入式SQL将主机变量(例如`:var`)与`SELECT`语句的`INTO`子句一起使用以输出值。
- **动态SQL设置`%SQLCODE`,`%Message`,`%ROWCOUNT`和`%ROWID`对象属性。嵌入式SQL设置相应的`SQLCODE`,`%msg`,`%ROWCOUNT`和`%ROWID`局部变量。动态SQL不会为`SELECT`查询设置`%ROWID`;嵌入式SQL为基于游标的`SELECT`查询设置`%ROWID`。**
- 动态SQL提供了一种简单的方法来查找查询元数据(例如列的数量和名称)。
- 动态SQL执行SQL特权检查;必须具有适当的权限才能访问或修改表,字段等。Embedded SQL不执行SQL特权检查。
- 动态SQL无法访问私有类方法。要访问现有的类方法,必须将该方法公开。这是一般的SQL限制。但是,嵌入式SQL克服了此限制,因为嵌入式SQL操作本身是同一类的方法。
动态SQL和嵌入式SQL使用相同的数据表示形式(默认情况下为逻辑模式,但是可以更改)和NULL处理。
# `%SQL.Statement`类
动态SQL的首选接口是`%SQL.Statement`类。要准备和执行动态SQL语句,请使用`%SQL.Statement`的实例。执行动态SQL语句的结果是一个SQL语句结果对象,该对象是`%SQL.StatementResult`类的实例。 SQL语句结果对象可以是单一值,结果集或上下文对象。在所有情况下,结果对象都支持标准接口。每个结果对象都会初始化`%SQLCODE`,`%Message`和其他结果对象属性;这些属性设置的值取决于发出的SQL语句。对于成功执行的`SELECT`语句,对象是结果集(特别是`%SQL.StatementResult`的实例),并且支持预期的结果集功能。
以下ObjectScript代码准备并执行动态SQL查询:
```java
/// d ##class(PHA.TEST.SQL).DynamicSQL()
ClassMethod DynamicSQL()
{
/* 简单的%SQL.Statement示例 */
SET myquery = "SELECT TOP 5 Name,DOB FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {
WRITE "%Prepare 失败"
DO $System.Status.DisplayError(qStatus)
QUIT
}
SET rset = tStatement.%Execute()
DO rset.%Display()
WRITE !,"End of data"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL()
Name DOB
yaoxin 54536
xiaoli
姚鑫 63189
姚鑫 63189
姚鑫 50066
5 Rows(s) Affected
End of data
```
本章中的示例使用与`%SQL.Statement`和`%SQL.StatementResult`类关联的方法。
# 创建一个对象实例
可以使用`%New()`类方法创建`%SQL.Statement`类的实例:
` SET tStatement = ##class(%SQL.Statement).%New()`
此时,结果集对象已准备好准备SQL语句。创建`%SQL.Statement`类的实例后,可以使用该实例发出多个动态SQL查询和/或`INSERT`,`UPDATE`或`DELETE`操作。
`%New()`按以下顺序接受三个可选的逗号分隔参数:
1. `%SelectMode`,它指定用于数据输入和数据显示的模式。
2. `%SchemaPath`,它指定用于为无限定的表名提供架构名称的搜索路径。
3. `%Dialect`,它指定Transact-SQL(TSQL)Sybase或MSSQL方言。默认值为IRIS(InterSystems SQL)。
还有一个`%ObjectSelectMode`属性,不能将其设置为`%New()`参数。 `%ObjectSelectMode`指定字段到其相关对象属性的数据类型绑定。
在下面的ObjectScript示例中,`%SelectMode`为2(显示模式),`%SchemaPath`将`“Sample”`指定为默认架构:
```java
SET tStatement = ##class(%SQL.Statement).%New(2,"Sample")
```
在下面的ObjectScript示例中,未指定`%SelectMode`(请注意占位符逗号),并且`%SchemaPath`指定包含三个架构名称的架构搜索路径:
```java
SET tStatement = ##class(%SQL.Statement).%New(,"MyTests,Sample,Cinema")
```
## %SelectMode属性
`%SelectMode`属性指定以下模式之一:`0 =Logical逻辑(默认)`,`1 = ODBC`,`2 =Display.显示`。这些模式指定如何输入和显示数据值。模式最常用于日期和时间值以及显示`%List`数据(包含编码列表的字符串)。数据以逻辑模式存储。
`SELECT`查询使用`%SelectMode`值确定用于显示数据的格式。
`INSERT`或`UPDATE`操作使用`%SelectMode`值来确定允许的数据输入格式。
`%SelectMode`用于数据显示。 SQL语句在内部以逻辑模式运行。例如,无论`%SelectMode`设置如何,`ORDER BY`子句均根据记录的逻辑值对记录进行排序。 SQL函数使用逻辑值,而不管`%SelectMode`设置如何。映射为SQLPROC的方法也可以在逻辑模式下运行。在SQL语句中称为函数的SQL例程需要以逻辑格式返回函数值。
- 对于`SELECT`查询,`%SelectMode`指定用于显示数据的格式。将`%SelectMode`设置为ODBC或Display也会影响用于指定比较谓词值的数据格式。某些谓词值必须以`%SelectMode`格式指定,而其他谓词值必须以逻辑格式指定,而与`%SelectMode`无关。
- `%SelectMode = 1(ODBC)`中的时间数据类型数据可以显示小数秒,这与实际的ODBC时间不同。 InterSystems IRIS Time数据类型支持小数秒。相应的ODBC TIME数据类型(TIME_STRUCT标准标头定义)不支持小数秒。 ODBC TIME数据类型将提供的时间值截断为整秒。 ADO DotNet和JDBC没有此限制。
- `%SelectMode = 0(逻辑)`中的`%List`数据类型数据不会显示内部存储值,因为`%List`数据是使用非打印字符编码的。而是,Dynamic SQL将`%List`数据值显示为`$LISTBUILD`语句,例如:`$lb("White","Green")`。 `%SelectMode = 1(ODBC)`中的`%List`数据类型数据显示用逗号分隔的列表元素;此元素分隔符指定为`CollectionOdbcDelimiter`参数。 `%SelectMode = 2`中的`%List`数据类型数据(显示)显示由`$ CHAR(10,13)`分隔的列表元素(换行,回车);此元素分隔符指定为CollectionDisplayDelimiter参数。
- 对于`INSERT`或`UPDATE`操作,`%SelectMode`指定将转换为逻辑存储格式的输入数据的格式。为了进行此数据转换,必须使用RUNTIME(默认)的选择模式编译SQL代码,以便在执行`INSERT`或`UPDATE`时使用`Display`或`ODBC %SelectMode`。有关日期和时间的允许输入值,请参考日期和时间数据类型。
可以将`%SelectMode`指定为`%New()`类方法的第一个参数,或直接对其进行设置,如以下两个示例所示:
```java
SET tStatement = ##class(%SQL.Statement).%New(2)
```
```java
SET tStatement = ##class(%SQL.Statement).%New()
SET tStatement.%SelectMode=2
```
下面的示例返回`%SelectMode`的当前值:
```java
/// d ##class(PHA.TEST.SQL).DynamicSQL1()
ClassMethod DynamicSQL1()
{
SET tStatement = ##class(%SQL.Statement).%New()
WRITE !,"默认选择模式=",tStatement.%SelectMode
SET tStatement.%SelectMode=2
WRITE !,"设置选择模式=",tStatement.%SelectMode
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL1()
默认选择模式=0
设置选择模式=2
```
可以使用`$SYSTEM.SQL.Util.GetOption("SelectMode")` 方法为当前进程确定`SelectMode`默认设置。当`n`可以为`0 =逻辑`,`1 = ODBC`或`2 = Display`时,可以使用`$SYSTEM.SQL.Util.SetOption("SelectMode",n)` 方法来更改当前进程的`SelectMode`默认设置。设置`%SelectMode`会覆盖当前对象实例的默认设置。它不会更改`SelectMode`进程的默认值。
## %SchemaPath属性
`%SchemaPath`属性指定用于为非限定的表名,视图名或存储过程名提供架构名的搜索路径。模式搜索路径用于数据管理操作,例如`SELECT`,`CALL`,`INSERT`和`TRUNCATE TABLE`;数据定义操作(例如`DROP TABLE`)将忽略它。
搜索路径被指定为带引号的字符串,其中包含模式名称或逗号分隔的一系列模式名称。 InterSystems IRIS以从左到右的顺序搜索列出的模式。 InterSystems IRIS会搜索每个指定的架构,直到找到第一个匹配的表,视图或存储过程名称。因为模式是按指定顺序搜索的,所以不会检测到歧义的表名。仅搜索当前名称空间中的架构名称。
模式搜索路径可以包含文字模式名称以及`CURRENT_PATH`,`CURRENT_SCHEMA`和`DEFAULT_SCHEMA`关键字。
- `CURRENT_PATH`指定当前模式搜索路径,如先前的`%SchemaPath`属性中所定义。这通常用于将架构添加到现有架构搜索路径的开头或结尾。
- 如果`%SQL.Statement`调用是从类方法中进行的,则`CURRENT_SCHEMA`指定当前模式容器的类名称。如果在类方法中定义了`#SQLCompile Path`宏指令,则`CURRENT_SCHEMA`是映射到当前类包的架构。否则,`CURRENT_SCHEMA`与`DEFAULT_SCHEMA`相同。
- `DEFAULT_SCHEMA`指定系统范围的默认架构。使用此关键字,可以在搜索其他列出的架构之前,在架构搜索路径中将系统范围的默认架构作为一个项目进行搜索。如果已经搜索了路径中指定的所有模式而没有匹配项,则在搜索模式搜索路径后始终会搜索系统范围内的默认模式。
`%SchemaPath`是InterSystems IRIS在架构中搜索匹配表名的第一位。如果未指定`%SchemaPath`,或者未列出包含匹配表名的架构,则InterSystems IRIS将使用系统范围的默认架构。
可以通过指定`%SchemaPath`属性或指定`%New()`类方法的第二个参数来指定模式搜索路径,如以下两个示例所示:
```java
SET path="MyTests,Sample,Cinema"
SET tStatement = ##class(%SQL.Statement).%New(,path)
```
```java
SET tStatement = ##class(%SQL.Statement).%New()
SET tStatement.%SchemaPath="MyTests,Sample,Cinema"
```
可以在使用它的`%Prepare()`方法之前的任何位置设置`%SchemaPath`。
下面的示例返回`%SchemaPath`的当前值:
```java
/// d ##class(PHA.TEST.SQL).DynamicSQL2()
ClassMethod DynamicSQL2()
{
SET tStatement = ##class(%SQL.Statement).%New()
WRITE !,"默认 path=",tStatement.%SchemaPath
SET tStatement.%SchemaPath="MyTests,Sample,Cinema"
WRITE !,"设置 path=",tStatement.%SchemaPath
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL2()
默认 path=
设置 path=MyTests,Sample,Cinema
```
可以使用`%ClassPath()`方法将`%SchemaPath`设置为为指定的类名定义的搜索路径:
```java
/// d ##class(PHA.TEST.SQL).DynamicSQL3()
ClassMethod DynamicSQL3()
{
SET tStatement = ##class(%SQL.Statement).%New()
SET tStatement.%SchemaPath=tStatement.%ClassPath("Sample.Person")
WRITE tStatement.%SchemaPath
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL3()
Sample
```
## %Dialect属性
`%Dialect`属性指定SQL语句方言。可以指定Sybase,MSSQL或IRIS(InterSystems SQL)。 Sybase或MSSQL设置导致使用指定的Transact-SQL方言处理SQL语句。
Sybase和MSSQL方言在这些方言中支持SQL语句的有限子集。它们支持`SELECT`,`INSERT`,`UPDATE`,`DELETE`和`EXECUTE`语句。他们支持`CREATE TABLE`语句用于永久表,但不支持临时表。支持创建视图。支持`CREATE TRIGGER`和`DROP TRIGGER`。但是,如果`CREATE TRIGGER`语句部分成功,但是在类编译时失败,则此实现不支持事务回滚。支持`CREATE PROCEDURE`和`CREATE FUNCTION`。
Sybase和MSSQL方言支持IF控制流语句。 IRIS(InterSystems SQL)方言不支持此命令。
默认值为InterSystems SQL,由空字符串(`“”`)表示,或指定为“ IRIS”
可以将`%Dialect`指定为`%New()`类方法的第三个参数,或者将其直接设置为属性,或者使用方法进行设置,如以下三个示例所示:
在`%New()`类方法中设置`%Dialect`:
```java
/// d ##class(PHA.TEST.SQL).DynamicSQL4()
ClassMethod DynamicSQL4()
{
SET tStatement = ##class(%SQL.Statement).%New(,,"Sybase")
WRITE "语言模式设置为=",tStatement.%Dialect
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL4()
语言模式设置为=Sybase
```
直接设置`%Dialect`属性:
```java
/// d ##class(PHA.TEST.SQL).DynamicSQL5()
ClassMethod DynamicSQL5()
{
SET tStatement = ##class(%SQL.Statement).%New()
SET defaultdialect=tStatement.%Dialect
WRITE "默认语言模式=",defaultdialect,!
SET tStatement.%Dialect="Sybase"
WRITE "语言模式设置为=",tStatement.%Dialect,!
SET tStatement.%Dialect="IRIS"
WRITE "语言模式重置为默认=",tStatement.%Dialect,!
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL5()
默认语言模式=
语言模式设置为=Sybase
语言模式重置为默认=iris
```
使用`%DialectSet()`实例方法设置`%Dialect`属性,该方法将返回错误状态:
```java
/// d ##class(PHA.TEST.SQL).DynamicSQL6()
ClassMethod DynamicSQL6()
{
SET tStatement = ##class(%SQL.Statement).%New()
SET tStatus = tStatement.%DialectSet("Sybase")
IF tStatus'=1 {
WRITE "%DialectSet 失败:"
DO $System.Status.DisplayError(tStatus) QUIT
}
WRITE "语言模式设置为=",tStatement.%Dialect
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL6()
语言模式设置为=Sybase
```
`%DialectSet()`方法返回`%Status`值:成功返回状态1。失败返回以0开头的对象表达式,后跟编码错误信息。因此,无法执行`tStatus = 0`测试是否失败;您可以执行`$$ISOK(tStatus)= 0`宏测试以检查失败
## %ObjectSelectMode属性
`%ObjectSelectMode`属性是一个布尔值。如果`%ObjectSelectMode = 0(默认)`,则`SELECT`列表中的所有列都将绑定到结果集中具有文字类型的属性。如果`%ObjectSelectMode = 1`,则`SELECT`列表中的列将绑定到具有关联属性定义中定义的类型的属性。
`%ObjectSelectMode`允许指定如何在从`SELECT`语句生成的结果集类中定义类型类为swizzleable类的列。如果`%ObjectSelectMode = 0`,则将在结果集中将与swizzleable列相对应的属性定义为与SQL表的RowID类型相对应的简单文字类型。如果`%ObjectSelectMode = 1`,则将使用列的声明类型定义属性。这意味着访问结果集属性将触发 swizzling。
无法将`%ObjectSelectMode`设置为`%New()`的参数。
下面的示例返回`%ObjectSelectMode`默认值,设置`%ObjectSelectMode`,然后返回新的`%ObjectSelectMode`值:
```java
/// d ##class(PHA.TEST.SQL).DynamicSQL7()
ClassMethod DynamicSQL7()
{
SET myquery = "SELECT TOP 5 %ID AS MyID,Name,Age FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
WRITE !,"默认 ObjectSelectMode=",tStatement.%ObjectSelectMode
SET tStatement.%ObjectSelectMode=1
WRITE !,"语言 ObjectSelectMode=",tStatement.%ObjectSelectMode
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL7()
默认 ObjectSelectMode=0
语言 ObjectSelectMode=1
```
当使用字段名称属性从结果集中返回值时,主要使用`%ObjectSelectMode = 1`。本章“从结果集中返回特定值”部分的字段名属性中的示例对此进行了进一步说明。
当`SELECT`列表中的字段链接到集合属性时,可以使用`%ObjectSelectMode = 1`。 `%ObjectSelectMode`将使集合swizzle。如果`%SelectMode = 1或2`,则系统在转换前将收集序列值转换为逻辑模式形式。生成的oref支持完整的收集接口。
文章
Qiao Peng · 一月 30, 2022
各种技术在交换数据的时候,就需要知道对方给的数据使用什么字符集和字符编码,否则很可能就解码错了。这里列举了医疗行业常见的数据交换技术方式和它们对字符集使用的声明方式。
2.1 文件
文件是字符型数据最常见的交换方式,文本编辑工具通常在保存时都会让用户选择保存成什么字符编码。对于不同的字符编码,文件是如何保存的呢?
通常会在文件头使用字节顺序标志(BOM,Byte Order Mark)来标记文件的编码。下表是常见的编码格式对应的BOM,注意ANSI并不需要BOM,我把它列在这里的目的是希望一目了然。
字节
编码格式
00 00 FE FF
UTF-32, big-endian
FF FE 00 00
UTF-32, little-endian
FE FF
UTF-16, big-endian
FF FE
UTF-16, little-endian
EF BB BF
UTF-8
空
ANSI
例如,汉字的“中”的各种编码如下:
用Windows的写字板软件将这个汉字分别保存成UTF-8、ANSI、Unicode编码,在UltraEdit打开其16进制模式查看,就可以看到如下的输出:
这里ANSI其实保存的是GBK码(使用GBK代码页936),Unicode其实保存的是UTF16编码。试图以ANSI保存文件时,对超出了GBK编码范围的汉字,Windows写字板软件会提示包含Unicode文字,以ANSI保存会丢失数据,如下图:
对于ANSI保存的包含汉字的文件,代码页信息并不在文件里。并不是所有的文本编辑器和文字处理代码都能正确解析这样的文件,因为它们并不知道代码页。这是可能造成文件中文乱码的一个原因。
另外,前面提到通过BOM可以确定文件编码方式,但并不是所有的文件都使用了BOM。因此特定的文本编辑器和文字处理代码对中文都可能产生显示乱码。
2.2 HTTP
对于HTTP消息内容,包括SOAP、RESTful,浏览器/客户端和服务器怎么知道字符编码呢?
HTTP头的Content-Type可以通过参数charset指定文字编码。例如:
Content-Type: text/html; charset = UTF-8
如果没有正确配置charset,就可能产生乱码。例如网页表单提交的中文数据,服务器没有正确解码从而产生乱码。
不同的Web服务器都可以设置默认的charset,例如Apache可以修改httpd.conf文件,通过配置AddDefaultCharset指定默认字符集编码。
2.3 XML
XML文件当然可以使用前面提到的文件编码方式设置,以HTTP传递的XML数据也可以使用HTTP的文字编码设置。同时,XML规范自己也定义了文字编码声明的方式,从而保证通过任何方式传递(例如TCP)的XML都可以被正确解析。
XML定义的编码方式是设置encoding属性,例如:
<?xml version="1.0" encoding="UTF-8"?>
注意,JSON并没有声明文字编码的设置。通常JSON数据都是通过HTTP传递的,因此使用HTTP的文字编码设置。
2.4 数据库连接
数据库连接也是造成文字乱码的一个重灾区。客户端从数据库服务器获取数据、向数据库服务器提交数据,怎么知道数据的文字编码呢?
和数据库相关的文字编码有2部分:数据库内码、数据库连接使用的字符编码。
数据库内码:
由于Unicode码历史并不悠久,数据库管理系统的历史远早于Unicode,直到最近这20年,数据库厂商才开始支持Unicode内码。当然,每个厂商的Unicode内码编码也不一样,例如InterSystems的Caché和IRIS是UTF-16格式,而Oracle是UTF-8和UTF-16。因为UTF-8处理效率低,大多数Unicode数据库都使用UTF-16。现在依然能看到不是Unicode内码的老版本数据库。
很多数据库相关的字符编码问题和数据库内码设置有关。例如国内不少的Oracle安装时没设置过字符编码,数据库内码NLS_CHARACTERSET默认为US7ASCII,客户端字符集NLS_LANG默认为AMERICAN_AMERICA.US7ASCII。这并不会造成中文无法保存,因为数据库只是将客户端的数据逐位保存下来,无论什么编码。但这样保存的中文数据只有Oracle自己的客户端能正常显示,其它的客户端可能就按ASCII处理从而造成乱码。因此正确设置数据库的内码很关键。
数据库连接使用的字符编码:
ODBC:
直到ODBC 3.5标准(1997年)之前绝大多数的ODBC连接的函数调用和字符串编码都是ANSI(单字节或双字节),所以中文生僻字大多都会处理异常。
ODBC 3.5 规定ODBC驱动管理器要能够透明地处理Unicode和ANSI之间的转换,从而让ANSI和Unicode的数据库客户端都可以正确地向数据库获取和提交数据。
当然,并不是所有的Unicode字符都能转为ANSI码,例如中文“”字,所以ANSI的数据库客户端依然会遇到生僻字乱码。
JDBC:
Java对字符串使用UTF-16编码,如果数据库内码也使用UTF-16,那么不会有乱码问题,例如InterSystems IRIS。但很多数据库并非使用UTF-16,这些数据库的JDBC驱动就需要支持UTF-16和数据库内码之间的转换。通常可以通过设置JDBC的连接字符串的特定属性来实现。
例如mysql的连接字符串中指定characterEncoding:
jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
其它连接方式:
另外,很多数据库还提供XDBC之外的连接方式,这些连接方式有自己的字符编码逻辑。
2.5 HL7 V2
在医疗行业,HL7 V2是目前最为广泛使用的消息交换标准。它可以通过文件、TCP、HTTP、SOAP 等多种通道交换。因此,HL7 V2消息标准中,也有设置消息字符编码的字段:MSH-18。正确设置该字段有助于不同的系统正确地处理HL7 V2消息里的字符数据。
例如下面的HL7消息在MSH段设置其字符编码为UTF-8:
MSH|^~\&||PHLS|||||ORU^R01|||2.5|||AL|||UTF-8|
PID||S2345|S2345^^^PHLS^MR|C9876^^^COR^XX~S45008787^^^MA^DL|张^三||19301019|M|||1 Memorial Drive^^剑桥市^MA^02142||||||||063070516
PV1||O|||||ISCGP001^建国^李|||||||EO|||||HSVN00008|||||||||||||||||||||||||20200912090700|20200912090700
ORC||00265-001|0606:H00550R||||^^^202009120910||202009120910|||ISCGP001^建国^李|PHLS||||||||PHLS||||||||LAB
OBR||00265-001|0606:H00550R|CBCD^血常规^L|||202009121049|||||||202009120937|Blood|ISCGP001^Moore^James||||||202009121227|||F
OBX||NM|WBC^WHITE BLOOD CELL COUNT||6.24|10(9)/L|4.0-10.6||||F|||202009121049
OBX||NM|RBC^RED BLOOD CELL COUNT||4.99|10x12/L|4.5-5.9||||F|||202009121049
OBX||NM|HGB^HEMOGLOBIN||13.6|g/dL|12.0-16.0||||F|||202009121049
OBX||NM|HCT^HEMATOCRIT||41.6|Percent|36.0-46.0||||F|||202009121049
2.6 其它
在上面提到的和字符集相关的乱码之外,有时我们会混淆一些其它的、并非真正乱码的情况。下面这些中文显示的“乱码”,并非乱码:
URL编码:
根据RFC 3986,如果URL的路径中有URL的保留字,就需要对URL路径中的保留字使用转义符% 进行转码,也叫做百分号编码。例如ASCII中下面的字符都需要转义:
!
#
$
&
'
(
)
*
+
,
/
:
;
=
?
@
[
]
%21
%23
%24
%26
%27
%28
%29
%2A
%2B
%2C
%2F
%3A
%3B
%3D
%3F
%40
%5B
%5D
对于非ASCII码字符,按其UTF-8编码字节顺序加%转义。例如“https://cn.community.intersystems.com/post/多语言字符集系列文章-第一篇-多语言字符集和相关标准简史”会被转义为:
https://cn.community.intersystems.com/post/%E5%A4%9A%E8%AF%AD%E8%A8%80%E5%AD%97%E7%AC%A6%E9%9B%86%E7%B3%BB%E5%88%97%E6%96%87%E7%AB%A0-%E7%AC%AC%E4%B8%80%E7%AF%87-%E5%A4%9A%E8%AF%AD%E8%A8%80%E5%AD%97%E7%AC%A6%E9%9B%86%E5%92%8C%E7%9B%B8%E5%85%B3%E6%A0%87%E5%87%86%E7%AE%80%E5%8F%B2
其中%E5%A4%9A就是“多”字的UTF-8编码E5A49A的3个字节加上%。
在InterSystems技术平台上,可以用$ZCVT("%E5%A4%9A","I","URI")将URL编码翻译为中文字符,或使用$ZCVT("多","O","URI")将汉字进行URL编码。
HTML实体编码(转义):
HTML实体编码(HTML Entity Encode)将字符编码为&开头;结尾的字符串,例如"<"编码为"<"。而中文字符就会被按其unicode编码进行转义,例如“广”字的unicode 10进制码为24191,所以它的HTML实体编码是"广"。在InterSystems技术平台上,可以用$ZCVT("广","I","HTML")将其翻译为中文字符。
Base64 编码:
当使用SOAP传递的数据是一个XML字符串或其它应被视为二进制类型的数据时,通常使用Base64对其进行编码,从而不会破坏XML结构解析。所以它也不是乱码。
文章
姚 鑫 · 八月 2, 2021
# 第五十三章 索引关键字 - PrimaryKey
指定此索引是否定义表的主键。
# 用法
要指定该表的主键由该索引所基于的属性构成,请使用以下语法:
```java
Index name On property_expression_list [ PrimaryKey ];
```
否则,省略此关键字或将单词`Not`放在关键字的前面。
# 详解
此关键字指定应通过SQL将此索引报告为此类(表)的主键。
`PrimaryKey`索引的行为也类似于唯一索引。
也就是说,对于在此索引中使用的属性(或属性组合),InterSystems IRIS强制唯一性。
在这个索引定义中,允许将`Unique`关键字指定为`true`,但这是多余的。
# 示例
```java
Index EmpIDX On EmployeeID [ PrimaryKey] ;
```
# 默认
如果忽略此关键字,则该表的主键不是由索引所基于的属性构成的。
# 第五十四章 索引关键字 - ShardKey
指定这个类的分片键。
# 用法
在分片类完全实现之前,InterSystems建议从`SQL`创建分片表,而不是从对象端。
但是,如果你查看一个通过创建一个分片表生成的类,你可能会看到如下代码:
```java
/// ShardKey分片表索引,由DDL CREATE table语句自动生成
Index ShardKey On DeptNum [ Abstract, CoshardWith = User.Department, ShardKey, SqlName = %ShardKey ];
```
在本例中,`DeptNum`属性是当前类的分片键。
# 第五十五章 索引关键字 - SqlName
指定索引的SQL别名。
# 用法
当通过`SQL`引用该索引时,要覆盖该索引的默认名称,使用以下语法:
```java
Index name On property_expression_list [ SqlName = sqlindexname];
```
其中`sqlindexname`是一个`SQL`标识符。
# 详解
当通过`SQL`引用该关键字时,可以为该索引定义一个替代名称。
# 默认
如果忽略此关键字,则索引的SQL名称为索引定义中给定的`indexname`。
# 第五十六章 索引关键字 - Type
指定索引的类型。
# 用法
要指定索引的类型,请使用以下语法:
```java
Index name On property_expression_list [ Type = indextype ];
```
其中`indextype`是下列类型之一:
- `bitmap` — 位图索引
- `bitslice` — 位片索引
- `index` —标准索引(默认)
- `key` — 废弃
# 详解
此关键字指定索引的类型,具体是将索引实现为位图索引还是标准(常规、非位图)索引。
位图索引不能标记为唯一`unique`。
# 默认
如果省略此关键字,则索引为标准索引。
文章
姚 鑫 · 八月 27, 2021
# 第153章 Storage关键字 - SqlRowIdProperty
指定`SQL RowId`属性。
# 大纲
```java
prop
```
# 值
该元素的值是一个`SQL`标识符。
# 描述
此元素仅由从早期InterSystems产品迁移的类使用。
# 默认值
``元素的默认值为空字符串。
# 第154章 Storage关键字 - SqlTableNumber
指定内部`SQL`表号。
# 大纲
```java
123
```
# 值
该元素的值是一个表号。
# 描述
此元素仅由从早期InterSystems产品迁移的类使用。
# 默认值
``元素的默认值为空字符串。
# 第155章 Storage关键字 - State
指定用于串行对象的数据定义。
# 大纲
```java
state
```
# 值
此元素的值是此存储定义中的数据定义的名称。
# 描述
对于串行(嵌入式)类,此关键字指示使用哪个数据定义来定义对象的序列化状态(序列化时对象属性的排列方式)。这也是默认数据定义,默认结构生成器将向其添加未存储的属性。
# 默认值
``元素的默认值为空字符串。
# 第156章 Storage关键字 - StreamLocation
指定流属性的默认存储位置。
# 大纲
```java
^Sample.PersonS
```
# 值
此元素的值是带有可选前导下标的全局名称。
# 描述
此元素允许指定用于在持久化类中存储任何流属性的默认全局设置。存储在此全局的根位置的值是一个计数器,每当存储此类的流值时,该计数器就会递增。
请注意,还可以单独指定每个流属性的存储。
# 默认值
如果未指定,则类编译器将生成``元素的值。通常,该值是`^MyApp.MyClassS`(其中`MyApp.MyClass`是类名),但是,它可能会根据许多因素而有所不同。
# 第157章 Storage关键字 - Type
用于提供持久性的存储类。
# 大纲
```java
%Storage.Persistent
```
# 值
该元素的值是一个类名。
# 描述
此元素指定为此类提供持久性的存储类。
`%Storage.Persistent`类是默认存储类,并提供默认存储结构。
`%Storage.SQL`类用于将类映射到旧数据结构。
对于串行(嵌入式)类,必须将其设置为`%Storage.Serial`(由新建类向导自动设置)。
# 默认值
``元素的默认值为`%Storage.Persistent`。
文章
姚 鑫 · 二月 16
# 第二十五章 S 开头的术语
# 以 S 开头的术语
### 模式演变 (schema evolution)
**对象(Objects)**
模式演变允许你在不丢失使用旧类定义存储的数据访问能力的情况下,为类添加新的类成员。
### 搜索用户 (search user)
**系统**
`IRIS` 连接到 `LDAP` 服务器的用户,其权限允许搜索 `LDAP` 数据库。其值在 `LDAP` 配置页面(系统管理 > 安全 > 系统安全 > `LDAP` 配置)的 `LDAP` 搜索用户 `DN` 或用于搜索的 `LDAP` 用户名字段中指定。(请注意,如果启用了 `Kerberos`,页面名称和菜单选项中会包含 `Kerberos`。)
### 次要卷 (secondary volume)
**系统**
卷集中的任何卷,除了主卷之外。在一个 `IRIS` 数据库中,可以有零到七个次要卷,总共八个卷。
### 安全顾问 (Security Advisor)
**系统**
管理门户内的诊断工具,提供有关 `IRIS` 实例设置与典型安全标准建议之间差异的反馈。
### 安全域 (security domain)
**系统**
一组逻辑上的机器分组,大致对应于 `Kerberos` 领域或 `Windows` 域。
### 选择性 (selectivity)
**InterSystems SQL**
属性的选择性指定整个值分布中特定值的大致频率。
### 顺序文件 (sequential file)
**通用**
数据按照输入顺序存储的文件。
### 服务器锁 (server lock)
**系统**
请参见 传入锁(Incoming Lock)。
### 服务器主进程 (server master)
**系统**
服务器软件的一个组件,监听通过 `TCP` 连接的客户端的连接尝试,并生成服务器进程以服务这些连接。每个服务器主进程都是一个 `IRIS` 进程。
### 服务器进程 (server process)
**系统**
服务器上的一个后台进程,用于服务单个客户端连接。服务器主进程为此目的创建服务器进程。
### 服务 (service)
**系统**
`InterSystems` 安全系统中的实体,通过现有路径(例如 `Telnet` 或 `JDBC`)调节对 `IRIS` 的访问。当连接到 `IRIS` 时,应用程序位于服务之上。
### 浅层保存 (shallow save)
**对象(Objects)**
浅层保存仅保存一个对象,而不保存其引用的对象。有关更多详细信息,请参见类参考内容中的 `%Persistent.%Save` 方法。