#Caché

0 关注者 · 975 帖子

  

InterSystems Caché 是一个多模型 DBMS 和应用服务器。

查看此处提供的更多详细信息

文档

文章 姚 鑫 · 四月 13, 2021 14m read

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

概述

索引是由持久类维护的结构,InterSystems IRIS®数据平台可以使用它来优化查询和其他操作。

可以在表中的字段值或类中的相应属性上定义索引。(还可以在多个字段/属性的组合值上定义索引。)。无论是使用SQL字段和表语法还是类属性语法定义相同的索引,都会创建相同的索引。当定义了某些类型的字段(属性)时,InterSystems IRIS会自动定义索引。可以在存储数据或可以可靠派生数据的任何字段上定义附加索引。InterSystems IRIS提供了几种类型的索引。可以为同一字段(属性)定义多个索引,为不同的目的提供不同类型的索引。

无论是使用SQL字段和表语法,还是使用类属性语法,只要对数据库执行数据插入、更新或删除操作,InterSystems IRIS就会填充和维护索引(默认情况下)。可以覆盖此默认值(通过使用%NOINDEX关键字)来快速更改数据,然后作为单独的操作生成或重新生成相应的索引。可以在用数据填充表之前定义索引。还可以为已经填充了数据的表定义索引,然后作为单独的操作填充(构建)索引。

InterSystems IRIS在准备和执行SQL查询时使用可用的索引。默认情况下,它选择使用哪些索引来优化查询性能。 可以根据需要覆盖此默认值,以防止对特定查询或所有查询使用一个或多个索引。

索引属性

每个索引都有一个唯一的名称。此名称用于数据库管理目的(报告、索引构建、删除索引等)。与其他SQL实体一样,索引同时具有SQL索引名和相应的索引属性名;这些名称在允许的字符、区分大小写和最大长度方面有所不同。如果使用SQL CREATE INDEX命令定义,系统将生成相应的索引属性名称。如果使用持久类定义进行定义,则SqlName关键字允许用户指定不同的SQL索引名(SQL映射名称)。 Management Portal SQL界面的Catalog Details显示每个索引的SQL索引名称(SQL映射名称)和相应的索引属性名称(索引名称)。

索引类型由两个索引类关键字TypeExtent定义。IRIS提供的索引类型包括:

  • 标准索引(Type = index)——一个持久数组,它将索引值与包含该值的行的 RowID相关联。 任何没有明确定义为位图索引、位片索引或区段索引的索引都是标准索引。
  • 位图索引(Type = Bitmap)——一种特殊的索引,使用一系列位字符串来表示与给定索引值对应的RowID值集; InterSystems IRIS包括许多位图索引的性能优化。
  • 位片索引(Type = Bitslice)——一种特殊的索引,能够非常快速地计算某些表达式,例如总和数和范围条件。 某些SQL查询自动使用位片索引。
  • 区段索引(Extent Indices)——一个区段中所有对象的索引。 有关更多信息,请参阅类定义参考中的区段索引关键字页。
  • 范围索引-范围中所有对象的索引。

表(类)的最大索引数为400。

存储类型和索引

这里描述的索引功能适用于存储在持久化类中的数据。

InterSystems SQL支持使用InterSystems IRIS默认存储结构存储的数据的索引功能:%Storage.Persistent(%Storage.Persistent映射类)。

InterSystems SQL还支持使用%Storage.SQL(%Storage.SQL映射的类)存储的数据的索引功能。可以使用函数索引类型为%Storage.SQL映射的类定义索引。索引的定义方式与使用默认存储的类中的索引相同,但有以下特殊注意事项:

  • 如果IdKey函数索引不是系统自动分配的,则该类必须定义IdKey函数索引。
  • 此功能索引必须定义为索引。

请注意,不应直接调用%Storage.Persistent%Storage.SQL类方法。相反,应该使用%Persistent类方法和本章中描述的操作调用索引功能。

索引全局名称

使用以下两种策略之一生成用于存储索引数据的下标全局:

  • %CLASSPARAMETER USEEXTENTSET=0使用全局命名策略创建由用户指定的名称、附加的字母代码和索引名称组成的“传统”全局名称。用户可以理解这些全局名称,但它们可能很长,并且效率低于散列的全局名称。
    • 如果USEEXTENTSET=0且未指定DEFAULTGLOBAL,则以下示例将描述生成的全局名称:Sample.MyTest持久类将定义名为^Sample.MyTestD的master map全局名^Sample.MyTestD位图范围索引全局名^Sample.MyTestI(“$MyTest”)(或^Sample.MyTestI(“DDLBEIndex”)),并且对于定义的索引NameIDX,它将定义名为^Sample.MyTestI(“DDLBEIndex”)的全局名。请注意,这些全局变量指定的是持久性类名(区分大小写),而不是SQL表名。
    • 如果USEEXTENTSET=0并指定了DEFAULTGLOBAL,则指定的全局名称将替换永久类名。这允许指定一个比持久类名称更短或更清晰的名称。例如,如果DEFAULTGLOBAL=“MyGlobal”,则全局变量的名称如下:^MyGlobalD^MyGlobalI(“NameIDX”)
  • %CLASSPARAMETER USEEXTENTSET=1使用创建哈希全局名称的全局命名策略。这包括对包名进行散列,对类名进行散列,然后追加一个点和一个标识索引的连续整数后缀。这些全局名称对用户来说不太容易理解,但往往更短、效率更高。

整数后缀仅作为索引名的关键字;与索引名和索引类型相关联的字段对整数编号没有影响。例如,^EW3K.CgZk.1Master Map^EW3K.CgZk.2是位图范围(Bitmap Extent),^EW3K.CgZk.3LastName字段的已定义标准索引NameIDX^EW3K.CgZk.4是已定义索引WorkIdIDX。如果删除NameIDX,则全局^EW3K.CgZk.3也会被删除,从而在整数序列中产生间隙。如果为LastName字段定义LNameIDX,则会创建全局^EW3K.CgZk.5;但是,如果稍后为FullName字段创建位图索引NameIDX,则全局索引将再次为^EW3K.CgZk.3

  • 如果USEEXTENTSET=1并且未指定DEFAULTGLOBAL,则包名和类名将被散列,如上所述。将追加连续的整数后缀。
  • 如果USEEXTENTSET=1并指定了DEFAULTGLOBAL,则使用DEFAULTGLOBAL名称,而不是散列的包名和类名。将追加连续的整数后缀。例如,如果DEFAULTGLOBAL="MyGlobal",则全局变量的名称如下:^MyGlobal.1^MyGlobal.3

如果使用CREATE TABLE命令定义表,则USEEXTENTSET默认为1。因此,默认情况下,CREATE TABLE创建散列全局名称。可以使用%CLASSPARAMETER关键字以及USEEXTENTSETDEFAULTGLOBAL参数更改此默认行为。可以使用$SYSTEM.SQL.Util.SetOption()方法在系统范围内更改此默认设置。

SET status=$SYSTEM.SQL.Util.SetOption("DDLUseExtentSet",0,.oldval).

如果定义投影到表的持久类,则USEEXTENTSET默认为0。因此,默认情况下,使用传统的全局名称。

DEFAULTGLOBAL(如果已定义)将作为默认值。如果定义了ExtentLocationDataLocationIndexLocation存储关键字,则使用这些值,而不是上述默认值。

可以向ZWRITE提供全局名称以显示索引数据。

Master Map

系统自动为每个表定义一个主图(Data/Master)。Master Map不是索引,它是使用其Map下标字段直接访问数据本身的Map。默认情况下,Master Map下标字段是系统定义的RowID字段。默认情况下,使用RowID字段进行的这种直接数据访问由SQL映射名称(SQL索引名称)IDKEY表示。

默认情况下,用户定义的主键不是IDKEY这是因为使用RowID整数查找 Master Map总是比使用主键值查找效率更高。 但是,如果指定主键为IDKEY,则主键索引被定义为表的主映射,SQL映射名称为主键SQL索引名。

对于单字段key/IDKEY,,主键索引是主映射,但主映射数据访问列仍然是RowID。这是因为在记录的唯一主键字段值和其RowID值之间存在一对一的匹配,而RowID被认为是更高效的查找。对于多字段主键/IDKEY,会为Master Map指定主键索引名称,并且Master Map Data Access列是主键字段。

可以通过Management Portal SQL Catalog Details(管理门户SQL目录详细信息)选项卡查看主图定义。除其他项目外,它还显示存储Master Map数据的全局名称。对于SQL和默认存储,此主映射全局默认为^Package.classnameD,并记录命名空间以防止歧义。对于自定义存储,未定义主地图数据存储全局名称;可以使用DATALOCATIONGLOBAL类参数指定数据存储全局名称。

对于SQL和默认存储,主映射数据存储在下标全局名为^package.classnameD^hashpackage.hashclass.1。请注意,全局名指定持久类名,而不是相应的SQL表名,并且全局名区分大小写。可以向ZWRITE提供全局名称以显示Master Map数据。

使用Master Map访问数据效率很低,尤其是对于大型表。因此,建议用户定义可用于访问WHERE条件、联接操作和其他操作中指定的数据字段的索引。

自动定义的索引

定义表时,系统会自动定义某些索引。在为表格定义并在添加或修改表数据时,自动生成以下索引。如果定义:

  • 不是IDKEY的主键,则系统会生成唯一类型的相应索引。主键索引的名称可以是用户指定的,也可以是从表名派生的。例如,如果定义一个未命名的主键,则相应的索引将命名为tablenamePKEY#,其中#是每个UNIQUEPRIMARY KEY约束的顺序整数。
  • 唯一的字段,Intersystems Iris为每个唯一字段生成索引,其中名称TableNameUnique#,其中是每个唯一和主键约束的顺序整数。
  • 唯一约束,系统为每个具有指定名称的唯一约束生成索引,为共同定义唯一值的字段编制索引。
  • shard key,系统在shard key字段上生成一个索引,命名为ShardKey

可以通过Management Portal SQL Catalog Details选项卡查看这些索引。CREATE INDEX命令可用于添加唯一字段约束;DROP INDEX命令可用于删除唯一字段约束。

默认情况下,系统在RowID字段上生成IDKEY索引。定义身份字段不会生成索引。但是,如果定义标识字段并将该字段作为主键,则InterSystems IRIS将在标识字段上定义IDKEY索引并将其作为主键索引。下面的示例显示了这一点:

CREATE TABLE Sample.MyStudents (
           FirstName VARCHAR(12),
           LastName VARCHAR(12),
           StudentID IDENTITY,
           CONSTRAINT StudentPK PRIMARY KEY (StudentID) )

同样,如果定义标识字段并为该字段提供唯一约束,则InterSystems IRIS将在标识字段上显式定义IdKey/Unique索引。下面的示例显示了这一点:

CREATE TABLE Sample.MyStudents (
           FirstName VARCHAR(12),
           LastName VARCHAR(12),
           StudentID IDENTITY,
           CONSTRAINT StudentU UNIQUE (StudentID) )

这些标识索引操作仅在没有明确定义的idkey索引时出现,并且表不包含任何数据。

位图范围索引

位图范围索引是表的行的位图索引,而不是针对表的任何指定字段。在位图范围索引中,每个位表示顺序ROWID整数值,并且每个位的值指定相应的行是否存在。

SQL使用此索引来提高Count(*)的性能,返回表中的记录数(行)。

一个表最多可以有一个位图区段索引。创建多个位图范围索引导致SQLCode -400错误。 其中 ERROR #5445: Multiple Extent indices defined:DDLBEIndex.

所有使用CREATE TABLE定义的表都会自动定义位图区段索引。 将此自动生成的索引分配索引名称(索引属性名称)DDLBEIndex和SQL MapName (SQL索引名称)%%DDLBEIndex。 定义为类的表可以有一个位图区索引,索引名和$ClassName的SQL MapName(其中ClassName是表的持久化类的名称)。

可以使用带有BITMAPEXTENT关键字的CREATE INDEX命令将位图区段索引添加到表中,或者重命名自动生成的位图区段索引。

可以通过管理门户SQL Catalog详细选项卡查看表的位图范围索引。虽然表只有一个位图范围索引,但是从另一个表中继承的表在其自身位图范围索引和它从其扩展的表中的位图范围索引中列出。例如,Sample.employee表扩展了Sample.person表;在目录详细信息映射 Sample.Employee列出$Employee$Person Bitmap范围索引。

在经历许多删除操作的表格中,位图范围索引的存储可以逐渐变得效率较低。可以通过选择表的“目录详细信息”选项卡,“映射”选项和选择重建索引来重建从管理门户中重建位图范围索引。

image

%SYS.Maint.Bitmap实用程序方法压缩位图范围索引,以及位图指数和bitslice索引。

在以下任何情况下,调用%BuildIndices()方法都会构建现有的位图范围索引:未指定%BuildIndices() pIndexList参数(构建所有定义的索引);pIndexList按名称指定位图范围索引;或pIndexList指定任何定义的位图索引。

定义索引

使用类定义定义索引

在Studio中,可以使用新建索引向导或通过编辑类定义的文本将索引定义添加到%Persistent类定义。索引在一个或多个索引属性表达式上定义,后跟一个或多个可选索引关键字(可选)。它采用以下形式:

INDEX index_name ON index_property_expression_list [index_keyword_list];
  • index_name是有效的标识符。
  • index_property_expression_list是一个或多个以逗号分隔的属性表达式的列表,它们作为索引的基础。
  • index_keyword_list是一个可选的索引关键字列表,用逗号分隔,用方括号括起来。 用于指定位图或位片索引的索引类型。 也用于指定唯一的、IdKeyPrimaryKey索引。 (根据定义,IdKeyPrimaryKey索引也是唯一索引。) 索引关键字的完整列表出现在类定义引用中。

index_property_expression_list参数由一个或多个索引属性表达式组成。 索引属性表达式包括:

  • 要建立索引的属性的名称。
  • 可选(元素)或(键)表达式,提供对集合子值进行索引的方法。 如果index属性不是一个集合,用户可以使用BuildValueArray()方法生成一个包含键和元素的数组。
  • 可选的排序规则表达式。 它包含一个排序规则名称,后面可选地跟着一个或多个以逗号分隔的排序规则参数列表。 不能为惟一索引、IdKey索引或PrimaryKey索引指定索引排序规则。 唯一索引或PrimaryKey索引从正在建立索引的属性(字段)中获取其排序规则。 IdKey索引总是精确(EXACT)的排序。

例如,下面的类定义定义了两个属性和一个基于它们的索引:

Class MyApp.Student Extends %Persistent [DdlAllowed]
{
 Property Name As %String;
 Property GPA As %Decimal;

 Index NameIDX On Name;
 Index GPAIDX On GPA;
}

更复杂的索引定义是:

 Index Index1 On (Property1 As SQLUPPER(77), Property2 AS EXACT);

可以建立索引的属性

唯一可以被索引的属性是:

  • 那些存储在数据库中的
  • 那些可以从存储的属性可靠地派生出来的

必须使用SQLComputed关键字定义可以可靠地派生(并且未存储)的属性; SQLComputeCode指定的代码必须是导出属性值的唯一方法,并且无法直接设置属性。

如果可以直接设置一个派生属性的值,比如是一个简单的情况下(non-collection)属性定义为瞬态和不也定义为计算,然后直接设置属性的值将覆盖SQLComputeCode中定义的计算和存储的值不能可靠地来自属性; 这种类型的派生属性称为不确定性。(计算的关键字实际上意味着没有分配实例内存。) 一般规则是,只有定义为calculateSQLComputed的派生属性才能被索引。 但是,派生集合有一个例外:派生的(SQLComputed)集合是暂时的(没有存储)集合,也没有定义为计算的集合(意味着没有实例内存)可以被索引。

注意:IdKey索引所使用的任何属性的值内都不能有连续的一对竖条(||),除非该属性是对持久类实例的有效引用。 这个限制是InterSystems SQL内部机制所要求的。 在IdKey属性中使用||会导致不可预知的行为。

多个属性的索引

可以在两个或多个属性(字段)的组合上定义索引。在类定义中,使用索引定义的ON子句指定属性列表,例如:

Class MyApp.Employee Extends %Persistent [DdlAllowed]
{
 Property Name As %String;
 Property Salary As %Integer;
 Property State As %String(MAXLEN=2);

 Index MainIDX On(State,Salary);
}

如果需要执行使用字段值组合的查询,例如:

SELECT Name,State,Salary
  FROM Employee
  ORDER BY State,Salary

索引排序

唯一索引、PrimaryKey索引或IdKey索引不能指定排序规则类型。 对于其他类型的索引,索引定义中指定的每个属性都可以有一个排序规则类型。 应用索引时,索引排序类型应与属性(字段)排序类型匹配。

  1. 如果索引定义包含为属性显式指定的排序规则,则索引使用该排序规则。
  2. 如果索引定义不包括为属性显式指定的排序规则,则索引使用属性定义中显式指定的排序规则。
  3. 如果属性定义不包括显式指定的排序规则,则索引使用属性数据类型的默认排序规则。

例如,Name属性被定义为字符串,因此在默认情况下具有SQLUPPER排序规则。 如果在Name上定义一个索引,默认情况下,它接受属性的排序规则,索引也将使用SQLUPPER定义。 属性排序和索引排序匹配。

但是,如果比较应用不同的排序规则,例如,WHERE %EXACT(Name)=%EXACT(:invar),则此用法中的属性排序规则类型不再与索引排序规则类型匹配。属性比较排序规则类型与索引排序规则类型之间的不匹配可能会导致不使用索引。因此,在这种情况下,可能希望为具有精确(EXACT)排序规则的Name属性定义索引。如果JOIN语句的ON子句指定了排序规则类型,例如,FROM Table1 LEFT JOIN Table2 ON %EXACT(Table1.Name) = %EXACT(Table2.Name),此处指定的属性排序类型与索引排序类型不匹配可能导致InterSystems IRIS不使用该索引。

以下规则控制索引和属性之间的排序规则匹配:

  • 匹配的排序规则类型总是最大限度地使用索引。
  • 排序规则类型不匹配,其中属性指定为精确的排序规则(如上所示),并且索引有一些其他的排序规则,允许使用索引,但是它的使用不如匹配排序类型有效。
  • 排序规则类型不匹配,其中属性排序规则不准确,属性排序规则不匹配索引排序规则,这将导致不使用索引。

要在索引定义中显式地为属性指定排序规则,语法如下:

Index IndexName On PropertyName As CollationName;
  • IndexName是索引的名称
  • PropertyName是被索引的属性
  • CollationName是用于索引的排序规则的类型

例如:

Index NameIDX On Name As Exact;

不同的属性可以有不同的排序规则类型。 例如,在下面的例子中,F1属性使用SQLUPPER排序,而F2使用EXACT排序:

Index Index1 On (F1 As SQLUPPER, F2 As EXACT);

注意:指定为UniquePrimaryKeyIdKey的索引不能指定索引排序规则。 索引从属性collations中获取其collation

0
0 319
文章 姚 鑫 · 四月 12, 2021 3m read

第一章 SQL性能优化简介

InterSystems SQL支持几个特性来优化InterSystems IRIS®数据平台的SQL性能。

表定义优化

SQL性能从根本上取决于良好的数据架构。 将数据划分为多个表并在这些表之间建立关系对于高效的SQL是必不可少的。

描述了以下优化表定义的操作。 这些操作要求定义表,但不要求用数据填充表:

  • 数据存储策略:可以选择使用%Storage.Persistent%Storage.SQL或自定义存储来存储数据。
  • 全局变量命名策略:可以使用USEEXTENTSET参数为数据和索引查找操作指定更短、更高效的散列全局名称。
  • 索引:可以为一个表字段或一组字段定义索引。可以定义几种不同类型的索引:标准索引、位图索引、位图索引和位图范围索引。SQL优化使用定义的索引而不是数据值本身来访问查询、更新或删除操作的特定记录。

表数据优化

根据对表中典型数据的分析,可以执行以下操作来优化表访问:

  • Tune Table:检查典型的表数据并生成ExtentSize(行数)、选择性(具有特定值的行的百分比)和BlockCount元数据。查询优化器使用此信息来确定最有效的查询执行计划。
  • 选择性和异常值选择性:确定某个字段具有特定值的行的百分比,以及某个值是否为异常值,该值明显比该字段的其他值更常见。

查询优化

在几乎所有情况下,用嵌入式SQL编写的查询的执行速度都比用动态SQL编写的查询快。还要注意,由于存在缓存查询,对于嵌入式SQL和动态SQL,重新执行查询的速度都比初始执行快得多。

可以执行以下操作来优化特定查询的执行。这些查询优化使用现有的表定义和表数据优化:

  • 运行时统计:用于衡量系统上查询执行的性能。
  • 显示计划显示查询的执行计划。
  • 缓存查询和文字替换:维护最近动态查询的缓存,允许重新执行查询,而不会重复准备查询的开销。
  • SQL语句和冻结计划允许保留查询执行计划,从而允许在不降低现有查询性能的情况下更改表。
  • 索引配置和使用:用于指定如何使用现有索引。
  • 索引优化提示:%ALLINDEX%IGNOREINDEX
  • 联接优化提示:%FIRSTTABLE%FULL%INORDER%STARTTABLE
  • 子查询优化提示:%NOFLATTEN%NOMERGE%NOREDUCE%NOSVSO
  • 并行查询执行:%Parallel
  • 联合优化: UNION %PARALLEL, UNION/OR

还可以通过使用数据分片来提高对大型数据库表的查询性能。

配置优化

默认情况下,内存和启动设置默认为自动配置,每个进程的最大内存默认为262144 kb。要优化在生产系统上运行的SQL,应该将默认值更改为手动配置,并增加每进程的最大内存设置。

分片

分片是跨多个系统对数据及其关联缓存进行分区。分片集群跨多个InterSystems IRIS实例(称为碎片数据服务器)水平(即按行)对大型数据库表进行分区,同时允许应用程序通过单个实例(称为碎片主数据服务器)透明地访问这些表。

必须将表定义为分片。分片表只能在分片环境中使用;非分片表可以在分片或非分片环境中使用。并不是所有的表都适合进行分片。分片环境中的最佳性能通常是通过组合使用分片表(通常非常大的表)和非分片表来实现的

快速命令

InterSystems SQL支持快速选择、快速插入和快速截断表。“快速”意味着这些SQL命令的标准调用是使用高效的内部代码执行的。这些快速操作“就是工作”;没有使用特殊语法,也没有提供优化选项。

通过ODBC或JDBC的SELECT查询支持快速选择。JDBC上的插入操作支持快速插入。对于不涉及参照完整性的截断表操作,支持快速截断表。

并不是所有的表都支持快速操作,也不是所有的命令语法都可以使用快速执行来执行。InterSystems SQL在可能的情况下执行快速执行;如果无法执行快速执行,InterSystems SQL将执行指定命令的标准执行。

0
0 185
文章 姚 鑫 · 三月 27, 2021 11m read

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

从结果集中返回特定的值

要从查询结果集中返回特定的值,必须一次一行遍历结果集。 要遍历结果集,请使用%Next()实例方法。 (对于单一值,结果对象中没有行,因此%Next()返回0,而不是错误。) 然后,可以使用%Print()方法显示整个当前行的结果,或者检索当前行的指定列的值。

%Next()方法获取查询结果中下一行的数据,并将该数据放入结果集对象的data属性中。 %Next()返回1,表示它位于查询结果中的某一行上。 %Next()返回0,表示它位于最后一行(结果集的末尾)之后。 每次调用%Next()返回1个增量%ROWCOUNT; 如果游标定位在最后一行之后(%Next()返回0),%ROWCOUNT表示结果集中的行数。

如果SELECT查询只返回聚合函数,每个%Next()设置%ROWCOUNT=1。 第一个%Next()返回1并设置%SQLCODE=0%ROWCOUNT=1,即使表中没有数据; 任何随后的%Next()返回0,并设置%SQLCODE=100%ROWCOUNT=1

从结果集中获取一行后,可以使用以下任何一种方式显示该行的数据:

  • rset.%Print()返回查询结果集中当前行的所有数据值。
  • rset.%GetRow()rset.getrows()以编码列表结构的元素形式从查询结果集中返回一行的数据值。
  • rset.name按查询结果集中的属性名称、字段名称、别名属性名称或别名字段名称返回数据值。
  • rset.%Get("fieldname")通过字段名或别名从查询结果集中或存储的查询返回一个数据值。
  • rset.%GetData(n)按列号从查询结果集中或存储的查询中返回一个数据值。

%Print()方法

%Print()实例方法从结果集中检索当前记录。默认情况下,%Print()在数据字段值之间插入空白空格分隔符。 %Print()不会在记录的第一个字段值之前或最后一个字段值之后插入空白; 它在记录的末尾发出一个行返回。 如果数据字段值已经包含空格,则将该字段值括在引号中,以将其与分隔符区分开来。 例如,如果%Print()返回城市名称,它将按如下方式返回它们: "New York" Boston Atlanta "Los Angeles" "Salt Lake City" Washington. 引用包含分隔符作为数据值一部分的字段值,即使从未使用过%Print()分隔符; 例如,如果结果集中只有一个字段。

可以选择指定%Print()参数,该参数提供在字段值之间放置的另一个定界符。指定其他定界符将覆盖包含空格的数据字符串的引用。此%Print()分隔符可以是一个或多个字符。它指定为带引号的字符串。通常,%Print()分隔符最好是在结果集数据中找不到的字符或字符串。但是,如果结果集中的字段值包含%Print()分隔符(或字符串),则该字段值将用引号引起来,以将其与分隔符区分开。

如果结果集中的字段值包含换行符,则该字段值将以引号引起来。

以下ObjectScript示例使用%Print()遍历查询结果集以显示每个结果集记录,并使用 "^|^" 定界符分隔值。请注意%Print()如何显示FavoriteColors字段中的数据,该字段是元素的编码列表:

/// d ##class(PHA.TEST.SQL).ROWCOUNTPrint()
ClassMethod ROWCOUNTPrint()
{
	SET q1="SELECT TOP 5 Name,DOB,Home_State,FavoriteColors "
	SET q2="FROM Sample.Person WHERE FavoriteColors IS NOT NULL"
	SET myquery = q1_q2
	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 "Row count ",rset.%ROWCOUNT,!
		DO rset.%Print("^|^")
	}
	WRITE !,"End of data"
	WRITE !,"Total row count=",rset.%ROWCOUNT
}

DHC-APP> d ##class(PHA.TEST.SQL).ROWCOUNTPrint()
Row count 1
yaoxin^|^54536^|^WI^|^$lb("Red","Orange","Yellow")
Row count 2
姚鑫^|^^|^^|^$lb("Red","Orange","Yellow","Green")
Row count 3
姚鑫^|^^|^^|^$lb("Red","Orange","Yellow","Green","Green")
Row count 4
Isaacs,Roberta Z.^|^^|^^|^$lb("Red","Orange","Yellow","Green","Yellow")
Row count 5
Chadwick,Zelda S.^|^50066^|^WI^|^$lb("White")
 
End of data
Total row count=5

下面的示例显示如何将包含定界符的字段值括在引号中。在此示例中,大写字母A用作字段定界符;因此,任何包含大写字母A的字段值(名称,街道地址或州缩写)都将以引号引起来。

/// d ##class(PHA.TEST.SQL).ROWCOUNTPrint2()
ClassMethod ROWCOUNTPrint2()
{
	SET myquery = "SELECT TOP 25 Name,Home_Street,Home_State,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() {
		DO rset.%Print("A")
	}
	WRITE !,"End of data"
	WRITE !,"Total row count=",rset.%ROWCOUNT
}
DHC-APP>d ##class(PHA.TEST.SQL).ROWCOUNTPrint2()
yaoxinA889 Clinton DriveAWIA30
xiaoliAAA
姚鑫AAA7
姚鑫AAA7
姚鑫AAA43
姚鑫AAA
姚鑫AAA
Isaacs,Roberta Z.AAA
Chadwick,Zelda S.A9889 Clinton DriveAWIA43
Fives,James D.A2091 Washington BlvdANDA88
Vonnegut,Jose P.A3660 Main PlaceAWIA47
Chadbourne,Barb B.A1174 Second StreetA"VA"A93
"Quigley,Barb A."A"6501 Ash Avenue"AKYA73

%GetRow()和%GetRows()方法

%GetRow()实例方法从结果集中检索当前行(记录),作为字段值元素的编码列表:

/// d ##class(PHA.TEST.SQL).ROWCOUNTPrint3()
ClassMethod ROWCOUNTPrint3()
{
	SET myquery = "SELECT TOP 17 %ID,Name,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()
	FOR  { 
		SET x=rset.%GetRow(.row,.status)
		IF x=1 {
			WRITE $LISTTOSTRING(row," | "),! 
		} ELSE {
			WRITE !,"End of data"
			WRITE !,"Total row count=",rset.%ROWCOUNT
			RETURN 
		}
	}
}

%GetRows()实例方法从结果集中检索指定大小的一组行(记录)。每行作为字段值元素的编码列表返回。

下面的示例返回结果集中的第1、6和11行。在此示例中,%GetRows()第一个参数(5)指定%GetRows()应该检索五行的连续组。如果成功检索到一组五行,%GetRows()将返回1。 .rows参数通过引用传递这五行的下标数组,因此,rows(1)返回每五组中的第一行:第1、6和11行。指定rows(2)将返回第2、7行和12。

/// d ##class(PHA.TEST.SQL).ROWCOUNTPrint4()
ClassMethod ROWCOUNTPrint4()
{
	SET myquery = "SELECT TOP 17 %ID,Name,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()
	FOR  { 
		SET x=rset.%GetRows(5,.rows,.status)
		IF x=1 {
			WRITE $LISTTOSTRING(rows(1)," | "),! 
		} ELSE {
			WRITE !,"End of data"
			WRITE !,"Total row count=",rset.%ROWCOUNT
			RETURN 
		}
	}
}

可以使用ZWRITE rows命令返回检索到的数组中的所有下标,而不是按下标检索单个行。请注意,上面的示例ZWRITE行不会返回结果集中的第16行和第17行,因为在检索到最后一组五行之后,这些行是余数。

rset.name属性

当InterSystems IRIS生成结果集时,它将创建一个结果集类,其中包含一个与该结果集中的每个字段名称和字段名称别名相对应的唯一属性。

可以使用rset.name属性按属性名称,字段名称,属性名称别名或字段名称别名返回数据值。

  • 属性名称:如果未定义字段别名,则将字段属性名称指定为rset.PropName。结果集字段属性名称取自表定义类中的相应属性名称。
  • 字段名称:如果没有定义字段别名,请将字段名称(或属性名称)指定为rset。“fieldname”。这是表定义中指定的SQLFIELDNAME。 Intersystems Iris使用此字段名称来查找相应的属性名称。在许多情况下,属性名称和字段名称(SQLFieldName)是相同的。
  • 别名属性名称:如果定义了字段别名,则将别名属性名称指定为rset.AliasProp。别名属性名称是根据SELECT语句中的列名称别名生成的。不能为具有已定义别名的字段指定字段属性名称。
  • 别名:如果定义了字段别名,则将此别名(或别名属性名称)指定为rset。“ alias”。这是SELECT语句中的列名别名。您不能为具有已定义别名的字段指定字段名称。
  • 集合,表达式或子查询:InterSystems IRIS为这些选择项分配一个字段名称Aggregate_nExpression_nSubquery_n(其中整数n对应于查询中指定的选择项列表的顺序)。可以使用字段名称(rset。“ SubQuery_7”不区分大小写),相应的属性名称(rset.Subquery7区分大小写)或用户定义的字段名称别名来检索这些select-item值。也可以只使用rset。%GetData(n)指定选择项的序列号。

指定属性名称时,必须使用正确的字母大小写;指定字段名称时,不需要正确的字母大小写。

使用属性名称对rset.name的调用具有以下后果:

  • 字母大小写:属性名称区分大小写。字段名称不区分大小写。 Dynamic SQL可以自动解决指定字段或别名与相应属性名称之间的字母大小写差异。但是,解决字母大小写需要时间。为了最大限度地提高性能,应该指定属性名称或别名的确切字母大小写。
  • 非字母数字字符:属性名称只能包含字母数字字符(起始的字符除外)。如果相应的SQL字段名称或字段名称别名包含非字母数字字符(例如Last_Name),则可以执行以下任一操作:
    • 指定用引号分隔的字段名称。例如,rset。“ Last_Name”)。分隔符的这种使用不需要启用分隔符。执行大写字母解析。
    • 指定相应的属性名称,以消除非字母数字字符。例如,rset.LastName(或rset。“ LastName”)。必须为属性名称指定正确的字母大小写。
    • 属性名称:通常,以字符开头的属性名称保留供系统使用。如果字段属性名称或别名以字符开头,并且该名称与系统定义的属性冲突,则返回系统定义的属性。例如,对于SELECT Notes AS%Message,调用rset。%Message将不返回Notes字段值。它返回为语句结果类定义的%Message属性。可以使用rset。%Get(“%Message”)返回字段值。
    • 列别名:如果指定了别名,则Dynamic SQL始终匹配该别名,而不匹配字段名称或字段属性名称。例如,对于SELECT Name AS Last_Name,只能使用rset.LastNamerset。“ Last_Name”来检索数据,而不能使用rset.Name
    • 重复名称:如果名称解析为相同的属性名称,则它们是重复的。重复名称可以是对表中同一字段的多个引用,对表中不同字段的别名引用或对不同表中字段的引用。例如,SELECT p.DOB,e.DOB指定两个重复的名称,即使这些名称引用了不同表中的字段。

如果SELECT语句包含相同字段名称或字段名称别名的多个实例,则rset.propnamerset。“fieldname”始终返回SELECT语句中指定的第一个。例如,对于SELECT C.NAME,P.NAME来自Sample.person as p,sample.company使用rset.name检索公司名称字段数据;选择C.Name,P.Name作为来自Sample.person的名称,As P,Sample.com本文使用RSET。“name”还检索公司名称字段数据。如果查询中存在重复的名称字段,则字段名称(名称)的最后一个字符由字符(或字符)替换为创建唯一属性名称。因此,查询中的重复名称字段名称具有相应的唯一属性名称,以NAM0(第一个重复)通过NAM9开始,并通过NAMZ继续大写字母NAMA

对于使用%Prepare()准备的用户指定的查询,可以单独使用属性名称。对于使用%PrepareClassQuery()准备的存储查询,必须使用%Get(“ fieldname”)方法。

下面的示例返回由属性名称指定的三个字段的值:两个属性值分别由属性名称和第三个属性值由别名属性名称。在这些情况下,指定的属性名称与字段名称或字段别名相同:

/// d ##class(PHA.TEST.SQL).PropSQL()
ClassMethod PropSQL()
{
	SET myquery = "SELECT TOP 5 Name,DOB AS bdate,FavoriteColors FROM Sample.Person"
	SET tStatement = ##class(%SQL.Statement).%New(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 "Row count ",rset.%ROWCOUNT,!
		WRITE rset.Name
		WRITE " prefers ",rset.FavoriteColors
		WRITE " birth date ",rset.bdate,!!
	}
	WRITE !,"End of data"
	WRITE !,"Total row count=",rset.%ROWCOUNT
}
DHC-APP>d ##class(PHA.TEST.SQL).PropSQL()
Row count 1
yaoxin prefers Red,Orange,Yellow birth date 1990-04-25
 
Row count 2
xiaoli prefers  birth date
 
Row count 3
姚鑫 prefers  birth date 2014-01-02
 
Row count 4
姚鑫 prefers  birth date 2014-01-02
 
Row count 5
姚鑫 prefers  birth date 1978-01-28
 
 
End of data
Total row count=5

在上面的示例中,返回的字段之一是FavoriteColors字段,其中包含%List数据。若要显示此数据,%New(1)类方法将%SelectMode属性参数设置为1(ODBC),从而导致该程序将%List数据显示为逗号分隔的字符串,并以ODBC格式显示出生日期:

下面的示例返回Home_State字段。因为属性名称不能包含下划线字符,所以本示例指定用引号(“ Home_State”)分隔的字段名称(SqlFieldName)。还可以指定不带引号的相应生成的属性名称(HomeState)。请注意,定界字段名称(“ Home_State”)不区分大小写,但是生成的属性名称(HomeState)是区分大小写的:

/// d ##class(PHA.TEST.SQL).PropSQL1()
ClassMethod PropSQL1()
{
	SET myquery = "SELECT TOP 5 Name,Home_State 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 "Row count ",rset.%ROWCOUNT,!
		WRITE rset.Name
		WRITE " lives in ",rset."Home_State",!
	}
	WRITE !,"End of data"
	WRITE !,"Total row count=",rset.%ROWCOUNT
}
DHC-APP>d ##class(PHA.TEST.SQL).PropSQL1()
Row count 1
yaoxin lives in WI
Row count 2
xiaoli lives in
Row count 3
姚鑫 lives in
Row count 4
姚鑫 lives in
Row count 5
姚鑫 lives in
 
End of data
Total row count=5
1
0 264
文章 姚 鑫 · 四月 9, 2021 4m read

第二十一章 导入和导出SQL数据

在InterSystems IRIS®Data Platform Management Portal中,有用于导入和导出数据的工具:

  • 从文本文件导入数据
  • 将数据导出到文本文件

这些工具使用动态SQL,这意味着查询是在运行时准备和执行的。可以导入或导出的行的最大大小为3,641,144个字符。

还可以使用%SQL.Import.Mgr类导入数据,使用%SQL.Export.Mgr类导出数据。

从文本文件导入数据

可以将数据从文本文件导入到合适的InterSystems IRIS类中。执行此操作时,系统将在表中为该类创建并保存新行。该类必须已经存在并且必须编译。要将数据导入到此类中,请执行以下操作:

  1. 从管理门户中选择系统资源管理器,然后选择SQL。使用页面顶部的切换选项选择一个命名空间;这将显示可用命名空间的列表。

  2. 在页面顶部,单击向导下拉列表,然后选择数据导入。

  3. 在向导的第一页上,从指定外部文件的位置开始。对于导入文件所在的位置,请单击要使用的服务器的名称。

  4. 然后输入文件的完整路径和文件名。

  5. 对于选择架构名称,单击要向其中导入数据的InterSystems IRIS包。

  6. 对于选择表名,单击将包含新创建的对象的类。

  7. 然后单击下一步。

  8. 在向导的第二页上,单击将包含导入数据的列。

  9. 然后单击下一步。

  10. 在向导的第三页上,描述外部文件的格式。

  • 有关用什么分隔符分隔您的列?,请单击与此文件中的分隔符对应的选项。
  • 单击第一行是否包含列标题?如果文件的第一行不包含数据,则选中此复选框。
  • 对于字符串引号,单击指示此文件用于开始和结束字符串数据的引号分隔符字符的选项。
  • 对于日期格式,请单击指示此文件中日期格式的选项。
  • 对于时间格式,请单击指示此文件中的时间格式的选项。
  • 对于时间戳格式,请单击指示此文件中的时间戳格式的选项。
  • 单击禁用验证?如果不希望向导在导入时验证数据,请选中此复选框。
  • 使用%SortBegin/%SortEnd?如果不希望向导在导入期间重新生成索引,请选中此复选框。如果选中延迟索引生成,向导将在将导入的数据插入到表中之前为该类调用%SortBegin方法。导入完成后,向导将调用%SortEnd方法。不执行任何验证(与使用%NOCHECK的INSERT相同)。这是因为当使用%SortBegin/%SortEnd时,在SQL INSERT期间不能检查索引的唯一性。如果选中延迟索引构建,则假定导入的数据有效,不会检查其有效性。
  • 或者,单击“预览数据”以查看向导将如何分析此文件中的数据。
  1. 单击“下一步”。
  2. 检查条目,然后单击Finish。向导将显示“数据导入结果”对话框。
  3. 单击关闭。或者单击给定的链接以查看后台任务页面。

在任何一种情况下,向导都会启动一个后台任务来完成工作。

将数据导出到文本文件

可以将给定类的数据导出到文本文件。为此:

  1. 从管理门户中选择系统资源管理器,然后选择SQL。使用页面顶部的切换选项选择一个命名空间;这将显示可用命名空间的列表。

  2. 在页面顶部,单击向导下拉列表,然后选择数据导出。

  3. 在向导的第一页上:

  • 输入要创建以保存导出数据的文件的完整路径和文件名。
  • 从下拉列表中,选择要从中导出数据的命名空间、方案名和表名。
  • 或者,从Charset下拉列表中选择一个字符集;默认值为Device Default。
  • 然后单击下一步。
  1. 在向导的第二页上,选择要导出的列。然后单击下一步。

  2. 在向导的第三页上,描述外部文件的格式。

  • 有关用什么分隔符分隔的列?,请单击与此文件中的分隔符对应的选项。
  • 单击导出列标题?如果要将列标题导出为文件的第一行,请选中此复选框。
  • 对于字符串引号,单击一个选项以指示如何开始和结束此文件中的字符串数据。
  • 对于日期格式,单击一个选项以指示要在此文件中使用的日期格式。
  • (可选)单击“预览数据”以查看结果的外观。然后单击下一步。
  1. 检查条目,然后单击Finish。该向导将显示“数据输出结果”对话框。

  2. 单击关闭。或单击给定的链接以查看后台任务页面。

在任何一种情况下,向导都启动后台任务来完成工作。

0
0 247
文章 姚 鑫 · 四月 7, 2021 12m read

第十九章 存储和使用流数据(BLOBs和CLOBs)

Intersystems SQL支持将流数据存储为Intersystems Iris ®DataPlatform数据库中的 BLOBs(二进制大对象)或 CLOBs(字符大对象)的功能。

流字段和SQL

Intersystems SQL支持两种流字段:

  • 字符流 Character streams,用于大量文本。
  • 二进制流 Binary streams,用于图像,音频或视频。

BLOBs and CLOBs

Intersystems SQL支持将BLOBs(二进制大对象)和CLOBs(字符大对象)存储为流对象的功能。 BLOBs用于存储二进制信息,例如图像,而CLOBs用于存储字符信息。 BLOBsCLOBs可以存储多达4千兆字节的数据(JDBC和ODBC规范所强加的限制)。

在各种方面,诸多方面的操作在通过ODBC或JDBC客户端访问时处理字符编码转换(例如Unicode到多字节):BLOB中的数据被视为二进制数据,从未转换为二进制数据另一个编码,而CLOB中的数据被视为字符数据并根据需要转换。

如果二进制流文件(BLOB)包含单个非打印字符$CHAR(0),则被认为是空二进制流。它相当于""空二进制流程值:它存在(不是null),但长度为0。

定义流数据字段

Intersystems SQL支持流字段的各种数据类型名称。这些Intersystems数据类型名称是与以下内容对应的同义词:

  • 字符流:数据类型LONGVARCHAR,映射到%stream.globalcharacter类和ODBC / JDBC数据类型-1
  • 字符流:数据类型LONGVARBINARY,映射到%Stream.GlobalBinary类和ODBC / JDBC数据类型-4

某些Intersystems流数据类型允许指定数据精度值。此值是no-op,对流数据的允许大小没有影响。提供它以允许用户记录预期的未来数据大小。

以下示例定义包含两个流字段的表:

CREATE TABLE Sample.MyTable (
    Name VARCHAR(50) NOT NULL,
    Notes LONGVARCHAR,
    Photo LONGVARBINARY)

分片表不能包含流数据类型字段。

流字段约束

Stream字段的定义符合以下字段数据约束:

流字段可以定义为 NOT NULL

流字段可以占用默认值,更新值或计算码值。

流字段不能定义为唯一,主键字段或idkey。试图这样做导致SQLCode -400致命错误,其中%MSG如下: ERROR #5414: Invalid index attribute: Sample.MyTable::MYTABLEUNIQUE2::Notes, Stream property is not allowed in a unique/primary key/idkey index > ERROR #5030: An error occurred while compiling class 'Sample.MyTable'.

无法使用指定的COLLATE 值定义流字段。试图这样做导致SQLCode -400致命错误,其中%MSG如下: ERROR #5480: Property parameter not declared: Sample.MyTable:Photo:COLLATION > ERROR #5030: An error occurred while compiling class 'Sample.MyTable'.

将数据插入流数据字段

将数据插入流字段有三种方法:

  • %Stream.Globalcharacter字段:可以直接插入字符流数据。例如,
INSERT INTO Sample.MyTable (Name,Notes)
    VALUES ('Fred','These are extensive notes about Fred')

image

Class Sample.MyTable Extends %Persistent [ ClassType = persistent, DdlAllowed, Final, Owner = {yx}, ProcedureBlock, SqlRowIdPrivate, SqlTableName = MyTable ]
{

Property Name As %Library.String(MAXLEN = 50) [ Required, SqlColumnNumber = 2 ];

Property Notes As %Stream.GlobalCharacter [ SqlColumnNumber = 3 ];

Property Photo As %Stream.GlobalBinary [ SqlColumnNumber = 4 ];

/// Bitmap Extent Index auto-generated by DDL CREATE TABLE statement.  Do not edit the SqlName of this index.
Index DDLBEIndex [ Extent, SqlName = "%%DDLBEIndex", Type = bitmap ];

Storage Default
{
<Data name="MyTableDefaultData">
<Value name="1">
<Value>Name</Value>
</Value>
<Value name="2">
<Value>Notes</Value>
</Value>
<Value name="3">
<Value>Photo</Value>
</Value>
</Data>
<DataLocation>^Sample.MyTableD</DataLocation>
<DefaultData>MyTableDefaultData</DefaultData>
<IdFunction>sequence</IdFunction>
<IdLocation>^Sample.MyTableD</IdLocation>
<IndexLocation>^Sample.MyTableI</IndexLocation>
<StreamLocation>^Sample.MyTableS</StreamLocation>
<Type>%Library.CacheStorage</Type>
}

}
  • %stream.globalcharacter%stream.globalbinary字段:可以使用oref插入流数据。可以使用Write()方法将字符串附加到字符流,或者写入的方法,以将具有行终结器的字符串附加到字符流。默认情况下,行终结器是$CHAR(13,10)(回车返回/线路);可以通过设置LineTerminator 属性来更改行终结器。在以下示例中,示例的第一部分创建由两个字符串和其终端组组成的字符流,然后使用嵌入的SQL将其插入流字段。示例的第二部分返回字符流长度,并显示显示终结器的字符流数据:

/// d ##class(PHA.TEST.SQL).StreamField()
ClassMethod StreamField()
{
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 "插入成功",!
	}
DisplayTheCharacterStream
	KILL ^CacheStream
	WRITE gcoref.%Save(),!
	ZWRITE ^CacheStream
}
DHC-APP>d ##class(PHA.TEST.SQL).StreamField()
插入成功
1
^CacheStream=1
^CacheStream(1)=1
^CacheStream(1,0)=25
^CacheStream(1,1)="First Line"_$c(13,10)_"Second Line"_$c(13,10)

image

  • %stream.globalcharacter%stream.globalbinary字段:可以通过从文件读取它来插入流数据。例如,
/// d ##class(PHA.TEST.SQL).StreamField1()
ClassMethod StreamField1()
{
	SET myf="E:\temp\game.jpg"
	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 "SQLCODE ERROR:"_SQLCODE_" "_%msg  QUIT
	} ELSE {
		WRITE "插入成功",!
	}
	CLOSE myf
}
DHC-APP>d ##class(PHA.TEST.SQL).StreamField1()
 
  WRITE "插入成功",!
  ^
<WRITE>zStreamField1+11^PHA.TEST.SQL.1
DHC-APP 2d0>g
 
  WRITE "插入成功",!
               ^
<WRITE>zStreamField1+11^PHA.TEST.SQL.1
DHC-APP 2d0>g
 
DHC-APP>

image

作为默认值或计算值插入的字符串数据以适合于流字段的格式存储。

查询流字段数据

选择流字段的查询选择项返回流对象的完全形成的OID(对象ID)值,如下例所示:

SELECT Name,Photo,Notes 
FROM Sample.MyTable WHERE Photo IS NOT NULL

image

OID是一个 %List 格式化数据地址,如以下内容:$lb("1","%Stream.GlobalCharacter","^EW3K.Cn9X.S")

  • OID的第一个元素是一个连续的正整数(从1开始),它被分配给每个插入到表中的流数据值。 例如,如果第1行插入流字段PhotoNotes的值,则将它们赋值为1和2。 如果第2行插入了一个Notes值,则将该值赋给3。 如果用PhotoNotes的值插入第3行,则将它们赋值为4和5。 分配顺序是表定义中列出字段的顺序,而不是INSERT命令中指定字段的顺序。 默认情况下,使用单个整数序列,它对应于流位置全局计数器。 然而,一个表可能有多个流计数器,如下所述。

  • 更新操作不会改变初始整数值。 DELETE操作可以在整型序列中创建空白,但不会改变这些整型值。 使用DELETE删除所有记录不会重置此整数计数器。 如果所有表流字段都使用默认的StreamLocation值,则使用TRUNCATE TABLE删除所有记录将重置此整数计数器。 不能使用TRUNCATE表为嵌入式对象(%SerialObject)类重置流整数计数器。

  • OID的第二个元素是流数据类型,可以是%Stream.GlobalCharacter%Stream.GlobalBinary

  • OID的第三个元素是一个全局变量。 默认情况下,它的名称是从与表对应的包名和持久类名生成的。 一个“S”(用于流)被追加。

    • 如果表是使用SQL CREATE TABLE命令创建的,这些包和持久化类名称将被散列为每个4个字符(例如,^EW3K.Cn9X.S)。 这个全局变量包含流数据插入计数器最近分配的值。 如果没有插入流字段数据,或者使用TRUNCATE TABLE删除所有表数据,那么这个全局变量是未定义的。
    • 如果表是作为一个持久化类创建的,那么这些包和持久化类名不会被散列(例如^Sample.MyTableS)。 默认情况下,这是StreamLocation存储关键字<StreamLocation>^Sample.MyTableS</StreamLocation> 价值。

默认流位置是全局位置,如^Sample.MyTableS。此全局变量用于计算插入到没有自定义位置的所有流属性(字段)的次数。例如,如果Sample.MyTable中的所有流属性都使用默认流位置,则在Sample.MyTable的流属性中插入了10个流数据值时,^Sample.MyTableS全局变量包含值10。此全局变量包含最近分配的流数据插入计数器的值。如果没有插入流字段数据,或者使用截断表删除了所有表数据,则此全局变量未定义。

定义流字段属性时,可以定义自定义位置,如下所示:Property Note2 As %Stream.GlobalCharacter (LOCATION="^MyCustomGlobalS");。在这种情况下,^MyCustomGlobalS全局用作指定此位置的流属性(或多个属性)的流数据插入计数器;未指定位置的流属性使用默认流位置全局(^Sample.MyTableS)作为流数据插入计数器。每个全局计数与该位置相关联的流属性的插入。如果没有插入流场数据,则位置GLOBAL是未定义的。如果一个或多个流属性定义了位置,则截断表不重置流计数器。

这些流位置全局变量的下标包含每个流字段的数据。例如,^EW3K.Cn9X.S(3)表示第三个插入的流数据项。^EW3K.Cn9X.S(3,0)是数据的长度。^EW3K.Cn9X.S(3,1)是实际的流数据值。

注意:流字段的OIDRowIDReference字段返回的OID不同。%OID函数返回RowID或引用字段的OID%OID不能与流字段一起使用。试图将流字段用作%OID的参数会导致SQLCODE-37错误。

在查询的WHERE子句或HAVING子句中使用流字段受到严格限制。不能将相等条件或其他关系运算符(=, !=, <, >)或包含运算符(])或跟随运算符([)与流字段一起使用。尝试将这些运算符与流字段一起使用会导致SQLCODE-313错误。

Result Set Display

  • 从程序执行的动态SQL以$lb("6","%Stream.GlobalCharacter","^EW3K.Cn9X.S").格式返回OID
  • SQL Shell作为动态SQL执行,并以$lb("6","%Stream.GlobalCharacter","^EW3K.Cn9X.S")格式返回OID
  • 嵌入式SQL返回相同的OID,但以编码%LIST的形式返回。可以使用$LISTTOSTRING函数将OID显示为元素以逗号分隔的字符串:6,%Stream.GlobalBinary,^EW3K.Cn9X.S

从管理门户SQL执行界面运行查询时,不返回OID。取而代之的是:

  • 字符流字段返回字符流数据的前100个字符。如果字符流数据超过100个字符,则用省略号(...)表示。在第100个字符之后。这等效于SUBSTRING(cstream field,1,100)
  • 二进制流字段返回字符串<binary>

在表数据的管理门户SQL界面打开表显示中显示相同的值。

要从管理门户SQL执行界面显示OID值,请将空字符串连接到流值,如下所示:SELECT Name, ''||Photo, ''||Notes FROM Sample.MyTable

image

DISTINCT, GROUP BY, and ORDER BY

每个流数据字段的OID值是唯一的,即使数据本身包含重复。 这些SELECT子句操作的是流的OID值,而不是数据值。 因此,当应用到查询中的流字段时:

  • 不同的子句对重复的流数据值没有影响。 DISTINCT子句将流字段为NULL的记录数减少为一个NULL记录。
  • GROUP BY子句对重复的流数据值没有影响。 GROUP BY子句将流字段为空的记录数量减少为一个空记录。
  • ORDER BY子句根据数据流的OID值来排序数据,而不是数据值。 ORDER BY子句列出流字段为空的记录,然后列出带有流字段数据值的记录。

谓词条件和流

IS [NOT] NULL谓词可以应用于流字段的数据值,示例如下:

SELECT Name,Notes 
FROM Sample.MyTable WHERE Notes IS NOT NULL

BETWEEN, EXISTS, IN, %INLIST, LIKE, %MATCHES, and %PATTERN 谓词可以应用于流对象的OID值,示例如下:

SELECT Name,Notes 
FROM Sample.MyTable WHERE Notes %MATCHES '*1[0-9]*GlobalChar*'

尝试在流字段上使用任何其他谓词条件会导致SQLCODE -313错误。

聚合函数和流

COUNT聚合函数接受一个流字段,并对该字段中包含非空值的行进行计数,示例如下:

SELECT COUNT(Photo) AS PicRows,COUNT(Notes) AS NoteRows
FROM Sample.MyTable

image

但是,流字段不支持COUNT(DISTINCT)。

对于流字段不支持其他聚合函数。 尝试将流字段与任何其他聚合函数一起使用会导致SQLCODE -37错误。

标量函数和流

除了%OBJECTCHARACTER_LENGTH(或CHAR_LENGTH或DATALENGTH)、SUBSTRINGCONVERTXMLCONCATXMLELEMENTXMLFOREST%INTERNAL函数外,InterSystems SQL不能对流字段应用任何函数。 尝试使用流字段作为任何其他SQL函数的参数会导致SQLCODE -37错误。

尝试使用流字段作为任何其他SQL函数的参数会导致SQLCODE -37错误。

  • %OBJECT函数打开一个流对象(接受一个OID)并返回oref(对象引用),示例如下:
SELECT Name,Notes,%OBJECT(Notes) AS NotesOref
FROM Sample.MyTable WHERE Notes IS NOT NULL

image

  • CHARACTER_LENGTHCHAR_LENGTHDATALENGTH函数接受流字段并返回实际的数据长度,如下面的示例所示:
SELECT Name,DATALENGTH(Notes) AS NotesNumChars,DATALENGTH(Photo) AS PhotoNumChars
FROM Sample.MyTable 

image

SUBSTRING函数接受一个流字段,并返回流字段的实际数据值的指定子字符串,如下面的示例所示:

SELECT Name,SUBSTRING(Notes,1,10) AS Notes1st10Chars
FROM Sample.MyTable WHERE Notes IS NOT NULL

image

当从管理门户SQL Execute接口发出时,子字符串函数返回流字段数据最多100个字符的子字符串。 如果流数据的指定子字符串大于100个字符,则在第100个字符后用省略号()表示。

  • CONVERT函数可用于将流数据类型转换为VARCHAR,示例如下:
SELECT Name,CONVERT(VARCHAR(100),Notes) AS NotesTextAsStr
FROM Sample.MyTable WHERE Notes IS NOT NULL

image

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使用BLOBCLOB值;相反,使用SQL来查找BlobClob的流标识符,然后创建%AbstractStream对象的实例以访问数据。

使用来自ODBC的流字段

ODBC规范不提供对BLOBCLOB字段的任何识别或特殊处理。 InterSystems SQL将ODBC中的CLOB字段表示为具有LONGVARCHAR(-1)类型。 BLOB字段表示为类型为LONGVARBINARY(-4)。 对于流数据类型的ODBC/JDBC数据类型映射,请参考InterSystems SQL reference中的数据类型引用页中的数据类型整数代码。

ODBC驱动程序/服务器使用一种特殊协议来访问BLOBCLOB字段。 通常,必须在ODBC应用程序中编写特殊的代码来使用CLOBBLOB字段; 标准的报告工具通常不支持它们。

使用来自JDBC的流字段

在Java程序中,可以使用标准的JDBC BLOBCLOB接口从BLOBCLOB检索或设置数据。 例如:

    Statement st = conn.createStatement();
    ResultSet rs = st.executeQuery("SELECT MyCLOB,MyBLOB FROM MyTable");
    rs.next();      // fetch the 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());

    // ...

注意:当使用BLOBCLOB结束时,必须显式调用free()方法来关闭Java中的对象,并向服务器发送消息以释放流资源(对象和锁)。 仅仅让Java对象超出范围并不会发送清理服务器资源的消息。

0
0 203
文章 姚 鑫 · 四月 6, 2021 13m read

第十八章 定义和使用存储过程

本章介绍如何在IntersystemsIRIS®数据平台上定义和使用Intersystems SQL中的存储过程。它讨论了以下内容:

  • 存储过程类型的概述
  • 如何定义存储过程
  • 如何使用存储过程如
  • 何列出存储过程及其参数。

概述

SQL例程是可执行的代码单元,可以由SQL查询处理器调用。 SQL例程有两种类型:功能和存储过程。从支持FunctionName()语法的任何SQL语句中调用函数。存储过程只能由CALL语句调用。函数接受某些输入定向参数并返回单个结果值。存储过程接受某些输入,输入输出和输出参数。存储过程可以是用户定义的函数,返回单个值。 CALL语句也可以调用函数。

与大多数关系数据库系统一样,Intersystems Iris允许创建SQL存储过程。存储过程(SP)提供存储在数据库中的可调用可调用的程序,并且可以在SQL上下文中调用(例如,通过使用呼叫语句或通过ODBC或JDBC)。

与关系数据库不同,Intersystems Iris使可以将存储过程定义为类的方法。实际上,存储过程只不过是SQL可用的类方法。在存储过程中,可以使用基于对象的全系列Intersystems的功能。

  • 可以通过查询数据库将存储过程定义为返回单个结果集数据集的查询。
  • 可以将存储过程定义为可以用作用户定义函数的函数过程,返回单个值。
  • 可以将存储过程定义为可以修改数据库数据并返回单个值或一个或多个结果集的方法。

可以确定使用 $SYSTEM.SQL.Schema.ProcedureExists()方法是否已存在该过程。此方法还返回过程类型:“函数function”“查询query”

定义存储过程

与Intersystems SQL的大多数方面一样,有两种方法可以定义存储过程:使用DDL和使用类。这些在以下部分中描述。

使用DDL定义存储过程

Intersystems SQL支持以下命令来创建查询:

  • CREATE PROCEDURE可以创建始终作为存储过程投影的查询。 查询可以返回单个结果集。
  • CREATE QUERY创建一个查询,该查询可以选择性地投影为存储过程。 查询可以返回单个结果集。

InterSystems SQL支持以下命令来创建方法或函数:

  • CREATE PROCEDURE可以创建始终作为存储过程投影的方法。 方法可以返回单个值,也可以返回一个或多个结果集。
  • CREATE METHOD可以创建一个方法,该方法可以选择投影为存储过程。 方法可以返回单个值,也可以返回一个或多个结果集。
  • CREATE FUNCTION可以创建一个函数过程,该函数过程可以选择投影为存储过程。 函数可以返回单个值。

这些命令中指定的可执行代码块可以用InterSystems SQL或ObjectScript编写。 可以在ObjectScript代码块中包含嵌入式SQL。

SQL到类名转换

使用DDL创建存储过程时,指定的名称将转换为类名。 如果类不存在,系统将创建它。

  • 如果名称是不限定的,并且没有提供FOR子句:使用系统范围的默认模式名作为包名,后跟一个点,后跟一个生成的类名,由字符串 ‘func’, ‘meth’, ‘proc’, or ‘query’组成,后跟去掉标点字符的SQL名。 例如,未限定的过程名Store_Name会产生如下类名User.procStoreName: 这个过程类包含方法StoreName()
  • 如果名称是限定的,并且没有提供FOR子句:模式名被转换为包名,后跟一个点,后跟字符串‘func’, ‘meth’, ‘proc’, or ‘query’ ,后跟去掉标点字符的SQL名。 如果需要,将指定的包名转换为有效的包名。

如果名称是限定的,并且提供了FOR子句:在FOR子句中指定的限定类名将覆盖在函数、方法、过程或查询名称中指定的模式名。

  • SQL存储过程名称遵循标识符命名约定。 InterSystems IRIS从SQL名称中去除标点字符,从而为过程类及其类方法生成唯一的类实体名称。

下面的规则管理模式名到有效包名的转换:

  • 如果架构名称包含下划线,则此字符将转换为点,表示子包。例如,合格的名称myprocs.myname创建包myprocs。限定名称my_procs.myname创建了包含子包procs的包。

以下示例显示了标点符号在类名和SQL调用中的不同之处。它定义了一个包含包含两个点的类名的方法。从SQL中调用时,示例将第一个点替换为下划线字符:

Class Sample.ProcTest Extends %RegisteredObject 
 {  
    ClassMethod myfunc(dummy As %String) As %String [ SqlProc ] 
    { 
        /* method code */
        Quit "abc" 
    }
 }  
SELECT Sample.ProcTest_myfunc(Name)
FROM Sample.Person

使用类定义方法存储过程

类方法可以公开为存储过程。 这些是不返回数据的操作的理想选择,例如计算值并将其存储在数据库中的存储过程。 几乎所有类都可以将方法公开为存储过程; 例外是生成器类,比如数据类型类([ClassType = datatype])。 生成器类没有运行时上下文。 只有在其他实体(如属性)的运行时中使用数据类型上下文才有效。

要定义方法存储过程,只需定义一个类方法并设置其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(作为%qHandle:%SQLProcContext)使用%sqlcontext对象自动生成的。

%sqlcontextSQLCODE错误状态、SQL行数、错误消息等属性组成,使用相应的SQL变量设置,如下所示:

  SET %sqlcontext.%SQLCode=SQLCODE
  SET %sqlcontext.%ROWCOUNT=%ROWCOUNT
  SET %sqlcontext.%Message=%msg

不需要对这些值做任何事情,但是它们的值将由客户机解释。 在每次执行之前都会重置%sqlcontext对象。

该方法不应该返回任何值。

一个类的用户定义方法的最大数目是2000个。

例如,假设有一个CalcAvgScore()方法:

ClassMethod CalcAvgScore(firstname As %String,lastname As %String) [sqlproc]
{
  New SQLCODE,%ROWID
  &sql(UPDATE students SET avgscore = 
    (SELECT AVG(sc.score) 
     FROM scores sc, students st
     WHERE sc.student_id=st.student_id 
       AND st.lastname=:lastname
       AND st.firstname=:firstname)
     WHERE students.lastname=:lastname
       AND students.firstname=:firstname)

  IF ($GET(%sqlcontext)'= "") {
    SET %sqlcontext.%SQLCODE = SQLCODE
    SET %sqlcontext.%ROWCOUNT = %ROWCOUNT
  }
  QUIT
}

使用类定义查询存储过程

许多从数据库返回数据的存储过程可以通过标准查询接口实现。 只要可以用嵌入式SQL编写过程,这种方法就可以很好地工作。 注意,在以下示例中,使用了嵌入式SQL host变量为WHERE子句提供一个值:

Class MyApp.Person Extends %Persistent [DdlAllowed]
{

    /// This procedure result set is the persons in a specified Home_State, ordered by Name
    Query ListPersons(state As %String = "") As %SQLQuery [ SqlProc ]
    {
        SELECT ID,Name,Home_State
        FROM Sample.Person
        WHERE Home_State = :state
        ORDER BY Name
    }
}

要将查询公开为存储过程,可以将Studio Inspector条目中的SQLProc字段的值更改为True,或者在查询定义中添加以下“[SQLProc]”字符串:

Query QueryName() As %SQLQuery( ... query definition ... ) 
    [ SqlProc ]

编译这个类之后,ListPersons查询将作为存储过程MyApp.Person_ListPersons投影到SQL中。 可以使用查询的SqlName关键字更改SQL用于该过程的名称。

MyApp。 从SQL调用Person_ListPersons,它将自动返回由查询的SQL语句定义的结果集。

下面是一个使用结果集的存储过程的示例:

Class apc.OpiLLS.SpCollectResults1 [ Abstract ]
{

/// This SP returns a number of rows (pNumRecs) from WebService.LLSResults, and updates a property for each record
Query MyQuery(pNumRecs As %Integer) As %Query(ROWSPEC = "Name:%String,DOB:%Date") [ SqlProc ]
{
}

/// You put initial code here in the Execute method
ClassMethod MyQueryExecute(ByRef qHandle As %Binary, pNumRecs As %Integer) As %Status
{
    SET mysql="SELECT TOP ? Name,DOB FROM Sample.Person"       
    SET rset=##class(%SQL.Statement).%ExecDirect(,mysql,pNumRecs)
            IF rset.%SQLCODE'=0 {QUIT rset.%SQLCODE}
    SET qHandle=rset
    QUIT $$$OK
}

/// This code is called by the SQL framework for each row, until no more rows are returned
ClassMethod MyQueryFetch(ByRef qHandle As %Binary, ByRef Row As %List, 
                         ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = NewQuery1Execute ]
{
     SET rset=qHandle
     SET tSC=$$$OK 
      
     FOR {
        ///Get next row, quit if end of result set
        IF 'rset.%Next() {
                SET Row = "", AtEnd = 1
                SET tSC=$$$OK
                QUIT
                }
        SET name=rset.Name
        SET dob=rset.DOB
        SET Row = $LISTBUILD(name,dob)
        QUIT
        }         
        QUIT tSC
}

ClassMethod MyQueryClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = NewQuery1Execute ]
{
        KILL qHandle   //probably not necesary as killed by the SQL Call framework
        QUIT $$$OK
}

}

如果可以将查询编写为一个简单的SQL语句并通过查询向导创建它,那么就不需要了解实现查询的底层方法。

在后台,对于每个查询,类编译器都会根据存储过程的名称生成方法,包括:

  • stored-procedure-nameExecute()
  • stored-procedure-nameFetch()
  • stored-procedure-nameFetchRows()
  • stored-procedure-nameGetInfo()
  • stored-procedure-nameClose()

如果查询类型为%SQLQuery,则类编译器会自动将一些嵌入式SQL插入到生成的方法中。 Execute()为SQL声明并打开存储的游标。 Fetch()被反复调用,直到它返回一个空行(SET row ="")。 还可以选择让Fetch()返回一个AtEnd=1布尔标志,以表明当前获取构成最后一行,下一个获取预期返回空行。 然而,应该总是使用空行(row ="")作为测试,以确定结果集何时结束; 当设置AtEnd=1时,应该始终设置Row=""

FetchRows()在逻辑上等同于反复调用Fetch()。 调用GetInfo()返回存储过程签名的详细信息。 Close()关闭游标。

当从客户机调用存储过程时,会自动调用所有这些方法,但理论上可以从运行在服务器上的ObjectScript直接调用这些方法。

要将对象从Execute()传递给Fetch(),或从Fetch()传递给下一次调用Fetch(),可以将查询处理程序设置为希望传递的对象的对象引用(oref)。 要传递多个对象,可以将qHandle设置为一个数组:

  SET qHandle(1)=oref1,qHandle(2)=oref2

可以基于自定义编写的代码(而不是SQL语句)创建结果集存储过程。

对一个类的用户定义查询Query的最大数目是200。

自定义Query

对于复杂的查询或不适合查询模型的存储过程,通常需要通过替换查询的部分或全部方法来自定义查询。 你可以使用 %Library.Query

如果选择类型%query (%Library.Query)而不是%SQLQuery (%Library.SQLQuery),则通常更容易实现查询。 这生成了相同的5个方法,但是现在FetchRows()只是重复调用Fetch() (%SQLQuery进行了一些优化,导致了其他行为)。 GetInfo()只是从签名中获取信息,因此代码不太可能需要更改。 这将问题简化为为其他三个类中的每一个创建类方法。 请注意,在编译类时,编译器会检测到这些方法的存在,而不会覆盖它们。

这些方法需要特定的签名:它们都接受类型为%BinaryQhandle(查询处理程序)。 这是一个指向保存查询的性质和状态的结构的指针。 它通过引用传递给Execute()Fetch(),通过值传递给Close():

ClassMethod SP1Close(qHandle As %Binary) As %Status
{
   // ... 
}

ClassMethod SP1Execute(ByRef qHandle As %Binary,
    p1 As %String) As %Status
{
   // ...
}

ClassMethod SP1Fetch(ByRef qHandle As %Binary, 
    ByRef Row As %List, ByRef AtEnd As %Integer=0) As %Status
{
   // ...
}

Query SP1(p1 As %String) 
   As %Query(CONTAINID=0,ROWSPEC="lastname:%String") [sqlproc ]
{
}

代码通常包括SQL游标的声明和使用。 从类型为%SQLQuery的查询中生成的游标自动具有诸如Q14这样的名称。 必须确保查询具有不同的名称。

在尝试使用游标之前,类编译器必须找到游标声明。 因此,DECLARE语句(通常在Execute中)必须与CloseFetch语句在同一个MAC例程中,并且必须出现在它们中的任何一个之前。 直接编辑源代码,CloseFetch定义中都使用方法关键字PLACEAFTER,以确保实现这一点。

错误消息引用内部游标名,它通常有一个额外的数字。 因此,游标Q140的错误消息可能指向Q14

使用存储过程

使用存储过程有两种不同的方式:

  • 可以使用SQL CALL语句调用存储过程;
  • 可以像使用SQL查询中的内置函数一样使用存储函数(即返回单个值的基于方法的存储过程)。

注意:当执行一个以SQL函数为参数的存储过程时,请使用CALL调用存储过程,示例如下:

CALL sp.MyProc(CURRENT_DATE)

SELECT查询不支持执行带有SQL函数参数的存储过程。 SELECT支持执行带有SQL函数参数的存储函数。

xDBC不支持使用SELECTCALL来执行带有SQL函数参数的存储过程。

存储方法

存储函数是返回单个值的基于方法的存储过程。 例如,下面的类定义了一个存储函数Square,它返回给定值的平方:

Class MyApp.Utils Extends %Persistent [DdlAllowed]
{
    ClassMethod Square(val As %Integer) As %Integer [SqlProc]
    {
        Quit val * val
    }
}

存储的函数只是指定了SqlProc关键字的类方法。

注意:对于存储的函数,ReturnResultsets关键字必须不指定(默认)或以关键字not作为开头。

可以在SQL查询中使用存储函数,就像使用内置SQL函数一样。 函数的名称是存储函数(在本例中为“Square”)的SQL名称,该名称由定义该函数的模式(包)名称限定(在本例中为“MyApp”)。

下面的查询使用了Square函数:

SELECT Cost, MyApp.Utils_Square(Cost) As SquareCost FROM Products

如果在同一个包(模式)中定义了多个存储函数,则必须确保它们具有惟一的SQL名称。

下面的示例定义了一个名为Sample的表。 具有两个定义的数据字段(属性)和两个定义的存储函数TimePlusDTime的工资:

Class Sample.Wages Extends %Persistent [ DdlAllowed ]
{  
  Property Name As %String(MAXLEN = 50) [ Required ];
  Property Salary As %Integer;
  ClassMethod TimePlus(val As %Integer) As %Integer [ SqlProc ]
  {
   QUIT val * 1.5
  }
  ClassMethod DTime(val As %Integer) As %Integer [ SqlProc ]
  {
   QUIT val * 2
  }
} 

下面的查询使用这些存储过程返回同一个表Sample.Wages中每个员工的Salarytime- halfdouble time工资率:

SELECT Name,Salary,
       Sample.Wages_TimePlus(Salary) AS Overtime,
       Sample.Wages_DTime(Salary) AS DoubleTime FROM Sample.Wages

下面的查询使用这些存储过程返回不同表Sample.Employee中每个员工的Salarytime- halfdouble time工资率:

SELECT Name,Salary,
       Sample.Wages_TimePlus(Salary) AS Overtime,
       Sample.Wages_DTime(Salary) AS DoubleTime FROM Sample.Employee

权限

要执行一个过程,用户必须具有该过程的execute权限。 使用GRANT命令或$SYSTEM.SQL.Security.GrantPrivilege()方法将指定过程的执行权限分配给指定用户。

通过调用$SYSTEM.SQL.Security.CheckPrivilege()方法,可以确定指定的用户是否具有指定过程的执行权限。

要列出用户具有EXECUTE权限的所有过程,请转到管理门户。 从系统管理中选择Security,然后选择Users或Roles。 为所需的用户或角色选择Edit,然后选择SQL Procedures选项卡。 从下拉列表中选择所需的名称空间。

List 存储过程

INFORMATION.SCHEMA.ROUTINES persistent类显示关于当前命名空间中所有例程和过程的信息。

当在嵌入式SQL中指定时,INFORMATION.SCHEMA。 例程需要#include %occInclude宏预处理指令。 动态SQL不需要这个指令。

下面的例子返回例程名称、方法或查询名称、例程类型(过程或函数)、例程主体(SQL=class query with SQL, EXTERNAL=not a class query with SQL)、返回数据类型,以及当前命名空间中模式“Sample”中所有例程的例程定义:

SELECT ROUTINE_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 AS PrecisionScale,
ROUTINE_DEFINITION 
FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='SQLUser'

image

INFORMATION.SCHEMA.PARAMETERS persistent类显示关于当前命名空间中所有例程和过程的输入和输出参数的信息。

下面的示例返回例程名称、参数名称(不管是输入参数还是输出参数)以及当前命名空间中模式“Sample”中的所有例程的参数数据类型信息:

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='SQLUser'

image

使用管理门户SQL界面中的Catalog Details选项卡,可以为单个过程显示大部分相同的信息。 过程的目录详细信息包括过程类型(查询或函数)、类名称、方法或查询名称、描述以及输入和输出参数的数量。 目录详细信息存储过程信息显示还提供了运行存储过程的选项。

0
0 756
文章 姚 鑫 · 四月 5, 2021 6m read

第十七章 使用触发器

本章介绍如何在Intersystems SQL中定义触发器。触发器是响应某些SQL事件执行的代码行。本章包括以下主题:

定义触发器

有几种方法可以为特定表定义触发器:

  • 在将投影到SQL表的持久性类定义中包含触发定义。例如,MyApp.person类的此定义包括Loggevent触发器的定义,在每个成功的数据插入到MyApp.person表之后,将在每个成功的数据插入后调用:
Class MyApp.Person Extends %Persistent [DdlAllowed]
{
    // ... Class Property Definitions

    Trigger LogEvent [ Event = INSERT, Time = AFTER ]
    {
        // Trigger code to log an event 
    }
 }
  • 使用SQL创建触发命令创建触发器。这在相应的持久性类中生成触发对象定义。 SQL触发器名称按照标识符命名约定进行操作。 IntersystemsIris®数据平台使用SQL触发名称生成相应的触发类实体名称。

必须拥有%create_trigger管理级别权限来创建触发器。必须具有删除触发器的%drop_trigger管理级别权限。

类的最大用户定义触发器数为200。

注意:Intersystems Iris不支持收集投影的表上的触发。用户无法定义这样的触发器,并且作为子表的集合的投影不认为涉及该基本集合的触发。 Intersystems Iris不支持修改Security.RolesSecurity.Users表的触发器。

触发器的类型

触发器由以下内容定义:

  • 导致它执行的事件类型。触发器可以是单个事件触发器或多事件触发。定义单个事件触发器以在指定表上发生插入,更新或删除事件时执行。定义多事件触发器以执行当在指定的表中发生多个指定的事件中的任何一个时执行。可以使用类定义或创建触发命令定义插入/更新,更新/删除或插入/更新/删除多事件触发器。事件类型在Class定义中指定了所需的事件触发器关键字。
  • 触发器执行的时间:在事件发生之前或之后。 这是由可选的Time trigger关键字在类定义中指定的。 默认为Before
  • 可以将多个触发器与同一事件和时间相关联;在这种情况下,可以使用order trigger关键字来控制触发多个触发器的顺序。先触发顺序较低的触发器。 如果多个触发器具有相同的Order值,则不指定它们的触发顺序。
  • 可选的Foreach trigger关键字提供了额外的粒度。 该关键字控制触发器是每一行触发一次(Foreach = row),还是每一行或对象访问触发一次(Foreach = row/object),还是每语句触发一次(Foreach = statement)。 没有Foreach trigger关键字定义的触发器每一行触发一次。 如果触发器是用Foreach = row/object定义的,那么触发器也会在对象访问期间的特定点被调用,如本章后面所述。 可以使用INFORMATION.SCHEMA.TRIGGERSACTIONORIENTATION属性列出每个触发器的Foreach

下面是可用的触发器及其等价的回调方法:

  • BEFORE INSERT (等价于 %OnBeforeSave())

  • AFTER INSERT (等价于 %OnAfterSave())

  • BEFORE UPDATE (等价于 %OnBeforeSave())

  • AFTER UPDATE (等价于 %OnAfterSave())

  • BEFORE UPDATE OF specified column(s)

  • AFTER UPDATE OF specified column(s)

  • BEFORE DELETE (等价于 %OnDelete())

  • AFTER DELETE (等价于 %OnAfterDelete())

注意:当触发器执行时,它不能直接修改正在处理的表中的属性值。 这是因为InterSystems IRIS在字段(属性)值验证代码之后执行触发代码。 例如,触发器不能将LastModified字段设置为正在处理的行中的当前时间戳。 但是,触发器代码可以对表中的字段值发出更新。 更新执行自己的字段值验证。

AFTER Triggers

INSERTUPDATEDELETE事件发生后执行AFTER触发器:

  • 如果SQLCODE=0(事件成功完成),InterSystems IRIS将执行AFTER触发器。
  • 如果SQLCODE是负数(事件失败),系统间IRIS就不会执行AFTER触发器。
  • 如果SQLCODE=100(没有发现要插入、更新或删除的行),则系统间IRIS执行AFTER触发器。

递归触发器

触发器执行可以是递归的。 例如,如果表T1有一个对表T2执行插入操作的触发器,表T2也有一个对表T1执行插入操作的触发器。 当表T1有一个调用例程/过程的触发器,并且该例程/过程执行对T1的插入操作时,也可以发生递归。 触发器递归的处理取决于触发器的类型:

  • 行和行/对象触发器:InterSystems IRIS不阻止行触发器和行/对象触发器递归地执行。 处理触发器递归是程序员的责任。 如果触发代码不处理递归执行,则可能发生runtime <FRAMESTACK>错误。
  • 语句触发器:InterSystems IRIS阻止AFTER语句触发器递归执行。 如果InterSystems IRIS检测到该触发器在执行堆栈中已经被调用,它将不会发出AFTER触发器。 没有错误发出; 触发器不会被第二次执行。

InterSystems IRIS不会阻止BEFORE语句触发器递归地执行。 在触发递归之前处理是程序员的责任。 如果BEFORE触发器代码不处理递归执行,可能会发生runtime <FRAMESTACK>错误。

Trigger Code

每个触发器包含执行触发操作的一行或多行代码。 每当与触发器关联的事件发生时,SQL引擎就会调用这段代码。 如果触发器是使用CREATE触发器定义的,则可以用ObjectScript或SQL编写此操作代码。 (InterSystems IRIS将SQL编写的代码转换为类定义中的ObjectScript。) 如果触发器是使用Studio定义的,那么这个操作代码必须用ObjectScript编写。

因为触发器的代码不是作为过程生成的,所以触发器中的所有局部变量都是公共变量。 这意味着触发器中的所有变量都应该用一个新语句显式声明; 这可以防止它们与调用触发器的代码中的变量发生冲突。

%ok, %msg, and %oper 系统变量

  • %ok:仅在触发器代码中使用的变量。 如果触发代码成功,它设置%ok=1。 如果触发代码失败,它设置%ok=0。 如果在触发器执行期间发出SQLCODE错误,InterSystems IRIS将设置%ok=0。 当%ok=0时,触发器代码中止,触发器操作和调用触发器的操作被回滚。 如果插入或更新触发器代码失败,并且表中定义了一个外键约束,InterSystems IRIS将释放外键表中相应行上的锁。

触发代码可以显式设置%ok=0。 这会创建一个运行时错误,中止触发器的执行并回滚操作。 通常,在设置%ok=0之前,触发器代码显式地将%msg变量设置为用户指定的字符串,用于描述这个用户定义的触发器代码错误。

%ok变量是一个必须显式更新的公共变量。 在完成非触发代码SELECTINSERTUPDATEDELETE语句后,%ok的值与之前的值没有变化。 %ok仅在执行触发器代码时定义。

%msg:触发代码可以显式地将%msg变量设置为描述运行时错误原因的字符串。 设置变量%msg

%oper:仅在触发器代码中使用的变量。 触发器代码可以引用变量%oper,该变量包含触发触发器的事件(插入、更新或删除)的名称。

{fieldname}语法

在触发器代码中,可以使用特殊的{fieldname}语法引用字段值(对于属于触发器关联的表的字段)。 例如,下面是MyAppLogEvent触发器的定义。 Person类包含一个对ID字段的引用,如{ID}:

Class MyApp.Person Extends %Persistent [DdlAllowed]
{
    // ... Definitions of other class members

    /// This trigger updates the LogTable after every insert
    Trigger LogEvent [ Event = INSERT, Time = AFTER ]
    {
        // get row id of inserted row
        NEW id,SQLCODE,%msg,%ok,%oper
        SET id = {ID}

        // INSERT value into Log table
        &sql(INSERT INTO LogTable 
            (TableName, IDValue) 
            VALUES ('MyApp.Person', :id))
        IF SQLCODE<0 {SET baderr="SQLCODE ERROR:"_SQLCODE_" "_%msg
                      SET %ok=0
                    RETURN baderr }

      }
   // ... Definitions of other class members

}

这个{fieldname}语法支持统一字段。 它不支持%SerialObject集合属性。 例如,如果表引用了嵌入的串行对象类Address(其中包含属性City),那么触发器语法{Address_City}就是对字段的有效引用。 触发器语法{Address}是对集合属性的引用,不能使用。

触发器代码中的宏

触发器代码可以包含一个引用字段名的宏定义(使用{fieldname}语法)。 但是,如果你的触发代码包含一个#Include预处理器指令,用于一个引用字段名的宏(使用{fieldname}语法),那么这个字段名就不能被访问。 这是因为InterSystems IRIS在代码被传递给宏预处理器之前,翻译触发器代码中的{fieldname}引用。 如果一个{fieldname}引用在#Include文件中,它不会在触发器代码中“看到”,因此不会被转换。

这种情况的解决方法是定义一个带参数的宏,然后将{fieldname}传递给触发器中的宏。 例如,#Include文件可以包含如下一行:

#Define dtThrowTrigger(%val) SET x=$GET(%val,"?")

然后在触发器中调用提供{fieldname}语法作为参数的宏:

  $$$dtThrowTrigger({%%ID})   

{name*O}{name*N}{name*C}触发代码语法

在更新触发器代码中有三种语法快捷方式可用。

可以使用下面的语法引用旧的(预更新的)值:

{fieldname*O}

其中fieldname是字段的名称,星号后面的字符是字母“O”(表示旧)。 对于插入触发器,{fieldname*O}总是空字符串("")

你可以使用下面的语法来引用新的(更新后的)值:

{fieldname*N}

其中fieldname是字段的名称,星号后面的字符是字母“N”(表示新字段)。 {fieldname*N}语法只能用于引用要存储的值; 它不能用来更改值。 不能在触发器代码中设置{fieldname*N}。 在插入或更新时计算字段的值应该通过其他方法实现,比如SqlComputeOnChange

可以使用以下语法测试字段值是否被更改(更新):

{fieldname*C}

其中,fieldname是字段的名称,星号后面的字符是字母“C”(表示已更改)。 {fieldname*C}的计算结果是1,如果字段已经被修改,0,如果它没有被修改。 对于插入触发器,InterSystems IRIS将{fieldname*C}设置为1。

对于具有流属性的类,如果SQL语句(INSERTUPDATE)没有插入/更新流属性本身,则对流属性{stream *N}{stream *O}的SQL触发器引用将返回流的OID。 然而,如果SQL语句确实插入/更新了stream属性,{stream *O}仍然是OID,但{stream *N}的值被设置为以下之一:

  • 在触发器之前,将流字段的值以传递给更新或插入的任何格式返回。 这可以是输入到stream属性中的文字数据值,也可以是临时stream对象的OREF或OID。
  • AFTER trigger将流的Id作为{stream *N}的值返回。 这是InterSystems IRIS的Id值,存储在流字段名为global^classnameD中。 该值根据流属性的CLASSNAME类型参数使用适当的Id格式。

如果一个流属性使用InterSystems IRIS对象更新,{stream *N}的值总是一个OID

注意:对于由串行对象的数组集合创建的子表触发器,触发器逻辑与对象访问/保存一起工作,但与SQL访问(插入或更新)不工作。

附加触发器代码语法

在ObjectScript中编写的触发器代码可以包含伪域引用变量{%%CLASSNAME}{%%CLASSNAMEQ}{%%OPERATION}{%%TABLENAME}{%%ID}。 这些伪字段在类编译时被转换成特定的值。

可以从触发器代码、SQL计算代码和SQL映射定义中使用类方法,因为类方法不依赖于拥有开放对象。 必须使用##class(classname). methodname()语法从触发器代码中调用方法。 你不能使用..Methodname()语法,因为这个语法需要一个当前打开的对象。

可以将当前行字段的值作为类方法的参数传递,但是类方法本身不能使用字段语法。

Pulling Triggers

如果调用对应于该表的DML命令,则“拉出”(执行)已定义的触发器。

对于DML命令成功插入、更新或删除的每一行,都会拉取一行或行/对象触发器。

对于每个成功执行的INSERTUPDATEDELETE语句,都会拉出一次语句触发器,而不管该语句是否实际更改了表数据中的任何行。

  • INSERT语句拉动相应的插入触发器。 插入可以通过指定%NOTRIGGER关键字来阻止触发相应的触发器。 指定%NOJOURN关键字的插入不会记录该插入或相应的插入触发器。 这意味着插入事件或触发事件都不可能回滚。

快速插入不能用于具有插入触发器的表。

  • UPDATE语句拉动相应的更新触发器。 更新可以通过指定%NOTRIGGER关键字来阻止触发相应的触发器。 指定%NOJOURN关键字的更新不会记录该更新或相应的更新触发器。 这意味着更新事件或触发事件都不可能回滚。

  • 根据执行的DDL操作的类型,INSERTUPDATE语句拉动相应的INSERT触发器或UPDATE触发器。 要防止触发任何类型的触发器,请指定%NOTRIGGER关键字。

  • DELETE语句拉动相应的DELETE触发器。 DELETE可以通过指定%NOTRIGGER关键字来阻止触发相应的触发器。 指定%NOJOURN关键字的删除不会记录删除或相应的删除触发器。 这意味着删除事件或触发事件都不可能回滚。

  • TRUNCATE TABLE语句不会触发删除触发器。

默认情况下,DDL语句和相应的触发操作被记录在日志中。 %NOJOURN关键字阻止DDL命令和触发动作的日志记录。

触发器和对象访问

如果触发器是用Foreach = row/object定义的,那么触发器也会在对象访问期间的特定点被调用,这取决于触发器定义的EventTime关键字,如下所示:

EventTime此时也调用Trigger
INSERTBEFORE在新对象的%Save()之前
INSERTAFTER在新对象的%Save()
UPDATEBEFORE在已存在对象的%Save()之前
UPDATEAFTER在已存在对象的%Save()
DELETEBEFORE在现有对象的%DeleteId()之前
DELETEAFTER在现有对象的%DeleteId()

因此,也没有必要为了保持SQL和对象行为同步而实现回调方法,

在对象访问期间没有拔出触发器

默认情况下,SQL对象使用%Storage.Persistent存储。 InterSystems IRIS也支持 %Storage.SQL storage。 SQL存储。

在使用 %Storage.SQL storage的类中保存或删除对象时。 SQL存储,所有语句(Foreach = statement)、行(Foreach = row)和行/对象(Foreach = row/object)触发器被拉出。 没有定义Foreach trigger关键字的触发器是行触发器。 提取所有触发器是默认行为。

但是,在使用%Storage.SQL storage保存或删除类中的对象时。 SQL,可以指定只有定义为Foreach = row/object的触发器应该被拉出。 定义为Foreach = statementForeach = row的触发器不会被拉取。 这是通过指定类参数OBJECTSPULLTRIGGERS = 0来实现的。 默认值是OBJECTSPULLTRIGGERS = 1

此参数仅应用于使用%Storage.SQL定义的类。

触发器与事务

触发器在事务中执行触发器码。它设置事务级别,然后执行触发器代码。成功完成触发器代码后,触发器提交事务。

注意:使用事务的触发器的结果是,如果触发器调用提交事务的代码,则触发器的完成失败,因为事务级别已经递减为0.调用生产的业务服务时可能发生这种情况。

使用INSERT语句级别对象触发器后,如果触发器集%OK = 0,则使用SQLCODE -131错误失败行的插入失败。如下所示,可能会发生交易回滚:

  • 如果auto_commit = on,则插入的事务将被回滚。
  • 如果auto_commit =off,则应用于回滚或提交输入的事务。
  • 如果使用no_auto_commit模式,则不启动事务,因此插入件不能回滚。

Auto_Commit模式是使用 SET TRANSACTION %COMMITMODE optionSetOption()方法建立的,如下所示 SET status=$SYSTEM.SQL.Util.SetOption("AutoCommit",intval,.oldval). 可用方法INTVAL值为0(无),1(隐式)和2(显式)。

触发器可以在触发器中的%MSG变量中设置错误消息。此消息将返回给呼叫者,给出触发器失败的信息。

列出触发器

在管理门户SQL接口目录详细信息中列出了为指定表定义的触发器。这列出了每个触发器的基本信息。

Information.schema.triggers类列出了当前命名空间中的定义触发器。对于每个触发信息.Schema.triggers列出了各种属性,包括触发器的名称,关联的架构和表名称,EventManipulation属性(插入,更新,删除,插入/更新,ActionTiming属性(之前,之后),创建的属性(触发创建时间戳)和ActionStatement属性,它是生成的SQL触发器代码。

创建的属性从上次修改课程定义时派生触发创建时间戳。因此,随后使用此类(例如,定义其他触发器)可能导致创建属性值的意外更新。

可以从SQL查询中访问此信息.Schema.triggers信息,如下例所示:

SELECT TABLE_NAME,TRIGGER_NAME,CREATED,EVENT_MANIPULATION,ACTION_TIMING,ACTION_ORIENTATION,ACTION_STATEMENT 
FROM INFORMATION_SCHEMA.TRIGGERS WHERE TABLE_SCHEMA='SQLUser'

image

0
0 371
文章 姚 鑫 · 四月 4, 2021 3m read

第十六章 导入SQL Code

本章介绍如何将SQL代码从文本文件导入Intersystems SQL。导入SQL代码时,IntersystemsIris®数据平台使用动态SQL准备并执行每行SQL。如果遇到无法解析的代码行,则SQL导入跳过该行代码并继续准备和执行后续行,直到它到达文件的末尾。所有SQL代码导入操作导入到当前名称空间。

SQL导入主要用于导入数据定义语言(DDL)命令(例如Create Table),并使用InsertUpdateDelete命令填充表。 SQL导入确实准备并执行SELECT查询,但不创建结果集。

SQL导入可用于导入Intersystems SQL代码。它也可以用于代码迁移,从其他供应商导入SQL代码(FDBMSInformixInterbaseMSSQLServerMySQLOracleSybase)。来自其他供应商的代码被转换为Intersystems SQL代码并执行。 SQL导入无法将所有SQL命令导入Intersystems SQL。它导入与SQL标准的Intersystems Iris实现兼容的那些命令和条款。不兼容的功能通常被解析,但忽略了。

SQL导入可以成功准备一个SQL查询 - 在适当的情况下创建相应的缓存查询 - 但它不会执行查询。

通过从%system.sql.schema类中调用相应的方法,执行SQL代码导入。导入SQL代码时,这些方法可以创建其他两个文件:errors.log文件,它记录解析SQL命令中的错误,以及一个不支持的。其中包含该方法无法识别为SQL命令的文字文本。

导入Intersystems SQL.

可以使用以下%System.sql.schema方法从文本文件中导入Intersystems SQL代码:

  • ImportDDL()是一个通用的SQL导入方法。此方法运行作为背景(非交互式)进程。要导入Intersystems SQL,请将“IRIS”指定为第一个参数。
  • run()是一个Intersystems SQL导入方法。该方法从终端交互方式运行。它会提示指定导入文本文件的位置,创建Errors.log文件和Unsupported.log文件以及其他信息。

注意:此导入和执行SQL DDL代码不应与管理门户SQL接口的Import语句操作混淆。该操作以XML格式导入SQL语句。

以下示例导入Intersystems Iris SQL代码文件Pathname MySQLCode.txt,在当前命名空间中执行该文件中列出的SQL命令:

DO $SYSTEM.SQL.Schema.ImportDDL("IRIS",$USERNAME,"c:\temp\mysqlcode.txt",,1)

默认情况下,ImportDDL()创建错误日志文件。此示例在与SQL代码文件中创建名为mysqlcode_errors.log的文件。第五个参数是一个布尔值,指定是否创建列出不受支持的SQL命令的文件。默认值为0.在此示例中,第五个参数设置为1,在与SQL代码文件相同的目录中创建名为mysqlcode_unsupported.log的文件。即使没有写入它们时,也会创建这些日志文件。

从终端执行ImportDDL()时,它首先列出输入文件,错误日志文件和不受支持的日志文件。然后,对于每个SQL命令,它显示一个列表,例如以下内容:

SQL statement to process (number 1):
     CREATE TABLE Sample.MyStudents (StudentName VARCHAR(32),
     StudentDOB DATE)
      Preparing SQL statement...
      Executing SQL statement...
  DONE

如果在任何SQL命令中发生错误,则终端显示错误,如以下示例所示:

SQL statement to process (number 3):
       INSERT INTO Sample.MyStudents (StudentName,StudentDOB) SELECT Name,
       DOB FROM Sample.Person WHERE Age <= '21'
    Preparing SQL statement...
ERROR #5540: SQLCODE: -30 Message:  Table 'SAMPLE.PERSON' not found
       Pausing 5 seconds - read error message!  (Type Q to Quit)

如果在5秒内没有退出,则导入DDL()继续执行下一个SQL命令。错误记录在错误日志文件中,具有时间戳,用户名和命名空间名称。

导入文件格式

SQL文本文件必须是未格式化的文件,例如.txt文件。每个SQL命令必须在自己的行中开始。 SQL命令可能会被丢进到多行,允许缩进。默认情况下,每个SQL命令必须在其自己的行上进行Go语句。

以下是有效的Intersystems SQL Import文件文本的示例:

  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

image

DHC-APP>DO $SYSTEM.SQL.DDLImport("CACHE","glenn","e:\temp\yxtest.txt",,1)
 
 
Importing SQL Statements from file: e:\temp\yxtest.txt
 
 
Recording any errors to principal device and log file: e:\temp\yxtest_Errors.log
 
 
Recording unsupported statements to file: e:\temp\yxtest_Unsupported.log
 
 
  SQL statement to process (number 1):
     CREATE TABLE Sample.MyStudents (StudentName VARCHAR(32),
     StudentDOB DATE)
      Preparing SQL statement...
      Executing SQL statement...
  DONE
 
  SQL statement to process (number 2):
       INSERT INTO Sample.MyStudents (StudentName,StudentDOB) SELECT Name,
       DOB FROM Sample.Person WHERE Age <= '21'
      Preparing SQL statement...
      Executing SQL statement...
  DONE
 
  SQL statement to process (number 3):
     INSERT INTO Sample.MyStudents (StudentName,StudentDOB)
               VALUES ('Jones,Mary',60123)
      Preparing SQL statement...
      Executing SQL statement...
  DONE
 
  SQL statement to process (number 4):
     INSERT OR UPDATE INTO Sample.MyStudents (StudentName,StudentDOB) VALUES ('
     Smith-Jones,Mary',60123)
      Preparing SQL statement...
      Executing SQL statement...
  DONE
 
  SQL statement to process (number 5):
     DELETE FROM Sample.MyStudents WHERE StudentName %STARTSWITH 'A'
      Preparing SQL statement...
      Executing SQL statement...
  DONE
 
  SQL statement to process (number 6):
     SELECT TOP 5 * FROM Sample.MyStudents
      Preparing SQL statement...
      Executing SQL statement...
  DONE
 
Elapsed time: 5.750462 seconds

通过设置ImportDDL(“IRIS”)DEOS第七参数,此方法可以接受(但不需要)指定的语句末尾分隔符,通常是分号(;),在每个SQL命令的末尾。默认值不支持终止终止分隔符。始终支持SQL命令后行的“Go”语句,但如果deos指定语句结束分隔符,则不需要。

支持的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

可以导入以其他供应商使用的SQL格式的SQL代码。来自其他供应商的代码被转换为Intersystems SQL代码并执行。提供以下方法:

  • ImportDDL()是一个通用的SQL导入方法。此方法运行作为背景(非交互式)进程。

要以特定格式导入SQL,将该格式的名称指定为第一个参数:FDBMS,Informix,Interbase,MSSQLSERVER(或MSSQL),MySQL,Oracle或Sybase

以下示例导入SQL代码文件SQLCode.txt,在当前命名空间中执行该文件中列出的SQL命令:

  DO $SYSTEM.SQL.Schema.ImportDDL("MSSQL",$USERNAME,$lb("C:\temp\somesql.sql","UTF8"),,1)

请注意,如果第一个参数是MSSQLSybaseInformixMySQL,则第三个参数可以是SQL代码文件路径名或具有第一个元素的两个元素%list,SQL代码文件路径名和第二个元素是i / o用于使用的翻译表。

  • %system.sql.schema中提供单个交互式方法,用于导入以下类型的SQL:loadfdbms()loadInformix()loadInterbase()loadmssqlserver()loadoracle()loadybase()。这些方法与终端交互式运行。它会提示指定导入文本文件的位置,创建Errors.log文件和Unsupported.log文件以及其他信息。
  • ImportDDLDIR()允许从目录中的多个文件导入SQL代码。此方法运行作为背景(非交互式)进程。它支持InformixMSSQLServerSybase。要导入的所有文件必须具有.sql扩展后缀。
  • ImportDir()允许从目录中的多个文件导入SQL代码。提供比ImportDDIR()更多的选项。此方法运行作为背景(非交互式)进程。它支持MSSQLServerSybase。可以指定允许文件扩展后缀的列表。
0
0 231
文章 姚 鑫 · 四月 3, 2021 12m read

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

过滤模式内容

Management Portal SQL界面的左侧允许查看模式(或匹配筛选器模式的多个模式)的内容

  1. 通过单击SQL interface页面顶部的Switch选项,指定希望使用的名称空间。 这将显示可用名称空间的列表,可以从中进行选择。
  2. 应用筛选器或从模式下拉列表中选择模式。

可以使用Filter字段通过输入搜索模式来筛选列表。 可以在一个模式或多个模式中筛选模式,或筛选表/视图/过程名(项)。 搜索模式由模式名、点(.)和项目名组成——每个名称由文字和通配符的某种组合组成。字面值不区分大小写。 通配符是:

  • 星号(*)表示0个或多个任意类型的字符。
  • 下划线(_)表示任意类型的单个字符。
  • 撇号(')倒装前缀,意为“不”(除了)。
  • 反斜杠(\)转义字符:\_表示字面上的下划线字符。

例如,S*返回所有以S S*开头的模式。 Person返回所有以S. *开头的模式中的所有Person项。 Person*返回所有模式中以Person开头的所有项。 可以使用逗号分隔的搜索模式列表来选择满足所列模式(或逻辑)中的任何一种的所有项。 例如,* .Person * *Employee*选择所有模式中的所有Person和Employee项。

若要应用筛选器搜索模式,请单击refresh按钮或按Tab键。

过滤器搜索模式将一直有效,直到显式地更改它。 过滤器字段右侧的“x”按钮清除搜索模式。

  1. 从schema下拉列表中选择一个模式将覆盖并重置之前的任何筛选器搜索模式,选择单个模式。 指定筛选器搜索模式将覆盖之前的任何模式。
  2. 可选地,使用下拉“应用到”列表来指定要列出的项目类别:表、视图、过程、缓存查询,或以上所有。 默认为All。 在“应用到”下拉列表中指定的任何类别都受到筛选器或模式的限制。 在“应用到”中没有指定的类别继续在名称空间中列出该类别类型的所有项。
  3. 可选地,单击System复选框以包含系统项目(名称以%开头的项目)。 默认情况下不包含系统项。
  4. 展开类别的列表,列出指定架构或指定筛选器搜索模式的项。 展开列表时,不包含项的任何类别都不会展开。
  5. 单击展开列表中的项,在SQL界面的右侧显示其目录详细信息。

如果所选项目是表或过程,则Catalog Details类名信息提供到相应类参考文档的链接。

请注意,筛选器设置是用户自定义的,并保留以供该用户将来使用。

Browse选项卡

Browse选项卡提供了一种方便的方式,可以快速查看名称空间中的所有模式,或者名称空间中经过过滤的模式子集。 可以选择Show All Schemas或Show Schemas with Filter,这将应用在管理门户SQL界面左侧指定的过滤器。 通过单击模式名称标题,可以按字母升序或降序列出模式。

每个列出的模式都提供指向其关联表、视图、过程和查询(缓存的查询)列表的链接。 如果模式没有该类型的项,则在该模式列表列中显示一个连字符(而不是命名链接)。 这使能够快速获得关于模式内容的信息。

单击“表”、“视图”、“过程”或“查询”链接将显示有关这些项的基本信息的表。 通过单击表标题,可以按该列的值升序或降序对列表进行排序。 过程表总是包括区段过程,而不管管理门户SQL界面左侧的过程设置如何。

可以使用Catalog Details选项卡获得关于单个表、视图、过程和缓存查询的更多信息。 从Browse选项卡中选择表或视图不会激活该表的Open Table链接。

目录详情

管理门户提供每个表,视图,过程和缓存查询的目录详细信息。管理门户SQL界面的过滤架构内容(左侧)组件允许您选择单个项目以显示其目录详细信息。

目录表的详细信息

每个表提供以下目录详细信息选项:

  • 表信息:表类型:表类型:无论是表,全局临时或系统表(仅在选择系统复选框时显示系统表),所有者名称,最后编译的时间戳,外部和读取的布尔值,类名称,范围大小,子表的名称和/或父表(如果相关)和一个或多个引用字段到其他表(如果相关),无论是使用%storage.persistent默认存储类,无论是支持位图指标, ROWID字段名称,ROWID基于(如果相关)的字段列表,以及表是否被分析。如果有一个显式分片键,它会显示分片键字段。

类名是在Intersystems类参考文档中的相应条目的链接。类名是通过删除标点字符,如标识符和类实体名称中所述从表名派生的唯一包。

只有当当前表中的某个字段对另一个表有一个或多个引用时,引用才会出现在表信息中。 这些对其他表的引用作为指向所引用表的表信息的链接列出。

Sharded:如果表是一个分片主表,那么表信息将显示分片本地类和表的名称,并链接到InterSystems类参考文档中相应的条目。 如果该表是一个碎片本地表,表信息将显示碎片主类和表的名称,并链接到InterSystems类参考文档中相应的条目。 只有选中“System”复选框时,才会显示“Shard-local”表。

该选项还为打开表时要加载的行数提供了一个可修改的值。 这将设置打开表中显示的最大行数。 可用范围从1到10,000; 默认值为100。 管理门户将一个超出可用范围的值修正为一个有效值:0修正为100; 一个小数四舍五入到下一个更大的整数; 大于10,000的数字更正为10,000。

  • 字段:表中字段的列表,显示字段名,数据类型,列#,必需的,惟一的,排序,隐藏,MaxLen, MaxVal, MinVal,流,容器,xDBC类型,引用,版本列,选择性,离群值选择性,离群值和平均字段大小。
  • 映射/索引:为表定义的索引列表,显示:索引名、SQL映射名、列、类型、块计数、映射继承和全局。

索引名称是索引属性名称,然后遵循属性命名约定;从SQL索引名称生成时,将删除SQL索引名称中的标点符号(例如下划线)。 SQL映射名称是索引的SQL名称。生成的SQL映射名称与约束名称相同,并遵循相同的命名约定(下面描述)。列指定为索引指定的字段或逗号分隔的字段列表;它可以指定index collation类型和full schinea.table.field参考,如下例所示:$$sqlupper({sample.people.name})。类型可以是以下之一:位图范围,数据/主,索引(标准索引),位图或bitslice索引以及唯一的约束。块计数包含计数和该计数的确定:由Class Author(定义)明确地设置,由可调组织(测量)计算,或由类编译器(估计)估计。如果映射继承?是的,map是从超类继承的。全局是包含索引数据的下标全局的名称。索引全局的命名约定在索引全局名称中描述。您可以向ZWRITE提供此全局名称以显示索引数据。

此选项还为每个索引提供重建索引的链接。

  • 触发:为表显示的触发器列表显示:触发名称,时间事件,订单,代码。
  • 约束:表格的字段列表,显示:约束名称,约束类型和约束数据(括号中列出的字段名称)。约束包括主键,外键和唯一约束。主键是定义,唯一;它仅列出一次。此选项列出约束名称的约束;使用显示组件字段的逗号分隔列表的约束数据列出了一次涉及多个字段的约束。约束类型可以是唯一的主键,隐式主键,外键或隐式外键。

还可以通过调用Information_schema.constraint_column_usage来列出约束。此列表按字段名称约束。以下示例返回字段的名称和所有唯一,主键,外键和Check Constraints的约束的名称:

SELECT Column_Name,Constraint_Name FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_SCHEMA='Sample' AND TABLE_NAME='Person'

image

如果该表定义为%PublicraWID,并且没有定义显式主键,则RowID字段列出了具有约束名称RowidField_As_PKeyContriced主键的约束类型。

对于显式约束,约束名称是如下生成的:

  • 字段定义中指定的约束:例如,fullname varchar(48)唯一或fullname varchar(48)主键。字段的约束名称值是具有语法tableName_ctype#的生成值,其中ctype是唯一的,pkeyfkey是在表定义中指定的顺序分配给未命名约束的顺序整数。例如,如果FullName具有MyTest表中的第二个未命名的唯一约束(不包括ID字段),则FullName的生成约束名称将是mytest_unique2;如果fullnameMyTest表中指定的主键和第3个未命名约束(不包括ID字段),则FullName的生成约束名称将是MyTest_pKey3
  • 约束关键字命名约束子句:例如,约束UFULLNAME唯一(名字,LastName)或约束Pkname主键(FullName)),约束名称是指定的唯一约束名称。例如,MyTest表中的名字和LastName每个都将每个约束名称UfullName; fullname将具有约束名称pkname
  • 未命名约束子句:例如,唯一(名字,姓氏)或主键(FullName)。约束名称值是具有语法tableNamectype#的生成值,其中ctype是唯一的,pkeyfkey##是在表定义中指定的顺序分配给未命名约束的顺序整数。例如,如果FirstNameLastName具有MyTest表中的第2个未命名的唯一约束(不包括ID字段),则FirstNameLastName的生成约束名称将是MyTestunique2;如果FullNameMyTest表中指定的主要键和第3个未命名的约束(不包括ID字段),则FullName的生成约束名称将是MyTestPKEY3。 (注意混合大写/小写,没有下划线。)

如果一个字段涉及多个唯一约束,则为每个约束名称单独列出。

  • 缓存查询:表的缓存查询列表显示:例程名称,查询文本,创建时间,源,查询类型。
  • 表的SQL语句:为此表生成的SQL语句列表。与命名空间的SQL语句相同的信息。

目录的视图详细信息

Management Portal SQL接口还提供视图,过程和缓存查询的目录详细信息:

为每个视图提供以下目录详细信息选项:

  • 查看信息:所有者名称,最后编译的时间戳。使用“编辑视图”链接并保存更改时,此时间戳更新。

定义为只读,视图是可更新的布尔值:如果仅读取的视图定义,则它们分别设置为1和0。否则,如果查看视图是从单个表定义的,它们被设置为0和1;如果视图由已加入的表定义,则它们设置为0和0。可以使用编辑视图链接更改此选项。

类名是唯一的包。通过删除标点字符,如标识符和类实体名称中所述,从视图名称派生的名称。

如果查看定义包含“使用”选项“子句,则仅列出选项。它可以是本地的或级联。您可以使用编辑视图链接更改此选项。

类类型是视图。它提供了编辑视图链接以编辑视图定义。

查看文本是用于定义视图的SELECT语句。可以使用编辑视图链接更改视图定义。

字段列表包括字段名称,数据类型,maxlen参数,maxval参数,minval参数,blob(%stream.globalcharacter%stream.globalbinary字段),长度,精度和比例。

  • 查看的SQL语句:为此视图生成的SQL语句列表。与命名空间的SQL语句相同的信息。

存储过程的目录详细信息

为每个过程提供以下目录详细信息:

  • 存储过程信息:

类名是一个唯一的包。通过将类型标识符( ‘func’, ‘meth’, ‘proc’, or ‘query’)预定到类名(例如,SQL函数MyProc变为FuncMyProc)并删除标点符号字符,如标识符和类实体名称中所述。类文档是Intersystems类参考中相应条目的链接。过程类型(例如,函数)。方法或查询名称生成的类方法或类查询的名称;此名称在标识符和类实体名称中描述。运行过程链接提供交互方式的选项。

  • 存储过程SQL语句:为此存储过程生成的SQL语句列表。与命名空间的SQL语句相同的信息。

缓存查询的目录详细信息

缓存查询提供查询的全文,一个选项来显示查询执行计划,以及交互式执行缓存查询的选项。

向导

  • 数据导入向导 - 运行向导将数据从文本文件导入Intersystems Iris类。
  • 数据导出向导 - 运行向导将数据从Intersystems Iris类导出到文本文件中。
  • 数据迁移向导 - 运行向导以从外部源迁移数据,并创建一个Intersystems Iris类定义来存储它。
  • 链接表向导 - 运行向导,以链接到外部源中的表或视图,就像它是本机Intersystems Iris数据一样。
  • 链接过程向导 - 运行向导,以链接到外部源中的过程。

操作

  • 创建视图 - 显示一个页面以创建视图。使用此选项的说明提供了本书的“定义和使用视图”章节。
  • 打印目录 - 允许打印有关表定义的完整信息。单击打印目录显示打印预览。通过单击此打印预览上的指数,触发器和/或约束,可以从目录打印输出中包含或排除此信息。
  • Purege缓存查询 - 提供三种用于清除缓存查询的选项:清除当前命名空间的所有缓存查询,清除指定表的所有缓存查询,或者仅清除所选缓存的查询。
  • 调谐表信息 - 对选定的表运行调谐表工具。这计算了每个表列对当前数据的选择性。选择性值1表示定义为唯一(因此具有所有唯一数据值)的列。选择性值为1.0000%表示未定义所有当前数据值是唯一值的唯一列。 1.0000%的百分比值更大,指示当前数据中该列的重复值的相对数量。通过使用这些选择性值,可以确定要定义的索引以及如何使用这些索引来优化性能。
  • 调整架构中的所有表 - 运行调谐表工具,针对所属于当前命名空间中指定架构的所有表。
  • 重建表索引 - 重建指定表的所有索引。
  • 删除此项目 - 删除(删除)指定的表定义,查看定义,过程或缓存查询。必须具有适当的权限来执行此操作。除非表类定义包括[DDLOWALLED],否则否则不能在通过定义持久性类创建的表上使用删除。否则,操作失败了,使用SQLCode -300错误,其中包含类“Schema.TableName”%MSG DDL。如果相应的持久性类具有子类(派生类),则不能在表格上使用删除;使用%msg'schema.tableName'具有派生类SQLCode -300错误失败,因此无法通过DDL删除。

如果一个类被定义为链接表,则下降操作也会将链接表放在本地系统上,即使链接的表类未被定义为ddlowed。下降不会删除实际表此链接引用服务器上的引用。

  • 导出所有语句 - 将所有SQL语句导出在当前命名空间中。 SQL语句以XML格式导出。可以选择导出到文件,或导出到浏览器显示页面。
  • 导入语句 - 将SQL语句从XML文件导入当前命名空间。

打开表

如果在管理门户SQL接口的左侧选择表或视图,则会显示该表或视图的目录详细信息。页面顶部的打开表链接也变为活动状态。打开表显示表中的实际数据(或通过视图访问)。数据以显示格式显示。

默认情况下,将显示前100行数据;通过在“目录详细信息”选项卡信息中将表打开时,通过设置要加载的行数来修改此默认值。如果表格中的行数多于此行到加载值,则在数据显示的底部显示越多的数据...指示器。如果表格中的行较少,则要加载值的行数,则在数据显示的底部显示完整的指示符。

一列数据类型%Stream.globalcharacter将实际数据(最多100个字符)显示为字符串。超出前100个字符的附加数据由省略号(...)表示。

一列数据类型%Stream.Globalbinary显示为<二进制>。

工具

System Explorer,SQL,Tools下拉列表提供对以下工具的访问。这些是系统资源管理器,工具,SQL性能工具的相同工具:

  • SQL运行时统计信息:用户界面生成指定查询的SQL运行时统计信息。
  • 索引分析仪:用于收集指定架构的各种类型索引分析的用户界面。
  • 替代表演计划:用户界面生成指定查询的备用显示计划。
  • 生成报告以将SQL查询性能报告提交给Intersystems WRC(全球响应中心客户支持)。要使用此报告工具,必须先从WRC获取WRC跟踪号码。
  • 导入报告以通过文件名导入现有WRC报告。仅用于Intersystems使用。

image

0
0 114
文章 姚 鑫 · 四月 2, 2021 13m read

第十五章 使用管理门户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文本框不仅允许编写SELECTCALL查询,还允许编写大多数SQL语句,包括DDL语句(如CREATE TABLE)和DML语句(如INSERTUPDATEDELETE)。

可以在“执行查询”文本框中指定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字段在结果表单元格中右对齐。 ROWIDNUMERIC和所有其他字段都是左对齐的。

当使用动态SQL代码,SQL Shell或嵌入式SQL代码执行相同的查询时,不会发生这些结果显示功能。

如果指定的查询返回多个结果集,则执行查询将这些结果集显示为命名选项卡:Result #1, Result #2等。

查询执行指标

如果成功,则执行查询显示性能信息和缓存查询例程的名称。如果显示数据以显示,则显示在性能信息下方。执行信息包括行计数,性能,缓存查询,显示缓存的查询名称,最后更新指定查询的最后一次执行的时间戳。

  • Row count:对于CREATE TABLE这样的DDL语句,如果操作成功,则显示Row count: 0。 对于INSERTUPDATEDELETE等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。
0
0 202
文章 姚 鑫 · 四月 1, 2021 7m read

第十四章 使用SQL Shell界面(三)

SQL元数据、查询计划和性能指标

显示元数据

SQL Shell支持MMetadata命令以显示有关当前查询的元数据信息。

对于每个结果集项目,此命令列出以下元数据:列名称(SQL字段名称),键入(ODBC数据类型整数代码),PRE(精度或最大长度),比例(最大分数数字),NULL(BOOLEAN:1 = NULL允许,0 =不允许空值),标签(标题标签,请参阅列别名),表(SQL表名称),架构(架构名称),CTYPE(客户端数据类型,请参阅%SQL.statementColumn ClientType属性)。

SHOW STATEMENT

可以执行查询,然后发出show语句或显示st以显示准备好的SQL语句。默认情况下,必须执行查询。可以避免通过设置executemode =延迟执行查询,从而发出查询,然后发出show语句sql shell命令。

显示声明信息包含实现类(缓存查询名称),参数(一个以逗号分隔的实际参数值,如上面条款和WHERE子句文字值),和语句文本(文字文本的SQL命令,包括字母大小写和参数值)。

EXPLAIN and Show Plan

有两种方式显示SQL查询的查询计划; 如果需要,两者都可以显示备用的查询计划。

  • EXPLAIN:前言用解释命令选择SELECT查询。例如:
SQL]USER>>EXPLAIN SELECT Name FROM Sample.MyTable WHERE Name='Fred Rogers'
  • SHOW PLAN:发出查询,然后发出show plan shell命令。例如:
SQL]USER>>SELECT Name FROM Sample.MyTable WHERE Name='Fred Rogers'
SQL]USER>>SHOW PLAN

EXPLAIN SQL命令显示有关指定选择查询的查询计划信息而不执行查询。EXPLAIN Alt允许显示备用查询计划。EXPLAIN Stat返回性能统计信息以及查询计划。EXPLAIN只能用于返回选择查询的查询计划;它不会返回用于执行查询操作的InsertUpdateDELETE语句等其他命令的查询计划。

Show Plan SQL shell命令允许显示SQL Shell成功发布的上次查询的查询计划信息。显示计划可用于执行查询操作的任何SQL命令,包括选择,插入,更新和删除。默认情况下,必须执行查询。可以避免通过设置executemode=deferred,执行查询,发出查询,然后发出以下SQL shell命令之一:

  • SHOW PLAN、SHOW PL(或简单的SHOW)显示关于当前查询的查询计划信息。 查询计划可用于调试和优化查询的性能。 它指定查询的执行方式,包括索引的使用和查询的成本值。 可以返回查询计划的语句有:SELECTDECLAREnon-cursor UPDATE or DELETEINSERT…SELECT。 该命令有一个V (VERBOSE)选项。
  • 显示PLANALT显示当前查询的备用显示计划。 该命令有一个V (VERBOSE)选项。

可以使用$SYSTEM.SQL.Explain()方法从ObjectScript生成查询计划。

SQL Shell Performance

成功执行一个SQL语句后,SQL Shell会显示四个语句准备值(times(s)/globals/cmds/disk)和四个语句执行值(times(s)/globals/cmds/disk):

  • 语句准备时间是指准备动态语句所花费的时间。 这包括生成和编译语句所花费的时间。 它包括在语句缓存中查找语句所花费的时间。 因此,如果执行了一条语句,然后按编号或名称回收,回收语句的准备时间接近于零。 如果一条语句已经准备好并执行,然后通过发出GO命令重新执行,那么重新执行时的准备时间为零。
  • 经过的执行时间是从调用%execute()%Display()返回所经过的时间。 它不包括输入参数值的等待时间。

语句globals是全局引用的计数,cmds是执行的SQL命令的计数,disk是磁盘延迟时间,单位是毫秒。 SQL Shell为准备操作和执行操作保留单独的计数。

这些性能值只在“DISPLAYMODE”设置为“currentdevice”“MESSAGES”设置为“ON”时显示。 这些是SQL Shell的默认设置。

Transact-SQL支持

默认情况下,SQL Shell执行InterSystems SQL代码。 但是,SQL Shell可以用来执行Sybase或MSSQL代码。

Setting DIALECT

默认情况下,SQL Shell将代码解析为InterSystems SQL。 可以使用SET DIALECT来配置SQL Shell以执行SybaseMSSQL代码。 若要更改当前方言,请将“方言”设置为SybaseMSSQL或IRIS。 默认值是Dialect=IRIS。 这些设置的方言选项不区分大小写。

下面是一个从SQL Shell中执行MSSQL程序的例子:

DHC-APP>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
 
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
DHC-APP>>SET DIALECT MSSQL
 
dialect = MSSQL
DHC-APP>>SELECT TOP 5 name + '-' + ssn FROM Sample.Person
1.      SELECT TOP 5 name + '-' + ssn FROM Sample.Person
 
Expression_1
yaoxin-111-11-1117
xiaoli-111-11-1111
姚鑫-111-11-1112
姚鑫-111-11-1113
姚鑫-111-11-1114
 
5 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.1989s/46546/257369/114ms
          execute time(s)/globals/lines/disk: 0.0032s/24/676/3ms
---------------------------------------------------------------------------

SybaseMSSQL方言支持这些方言中有限的SQL语句子集。 它们支持SELECTINSERTUPDATEDELETE语句。 它们对永久表支持CREATE TABLE语句,但对临时表不支持。 支持创建视图。 支持创建触发器和删除触发器。 但是,如果CREATE TRIGGER语句部分成功,但在类编译时失败,则此实现不支持事务回滚。 支持CREATE PROCEDURECREATE FUNCTION

Setting COMMANDPREFIX

可以使用SET COMMANDPREFIX指定必须追加到后续SQL Shell命令的前缀(通常是单个字符)。 在SQL Shell提示符发出的SQL语句中不使用此前缀。 这个前缀的目的是防止SQL Shell命令和SQL代码语句之间的歧义。 例如,SET是一个SQL Shell命令; SET也是SybaseMSSQL中的SQL代码语句。

默认情况下,没有命令前缀。 要建立命令前缀,设置COMMANDPREFIX=prefix,指定的前缀不带引号。 要恢复为没有命令前缀,设置COMMANDPREFIX=""。 以下示例显示了命令前缀/(斜杠字符)被设置、使用和恢复的示例:

DHC-APP>>SET COMMANDPREFIX=/
 
commandprefix = /
DHC-APP>>/SET LOG=ON
 
log = xsql19388.log
DHC-APP>>  << entering multiline statement mode >>
        1>>SELECT TOP 3 Name,Age
        2>>FROM Sample.Person
        3>>/GO
2.      SELECT TOP 3 Name,Age
        FROM Sample.Person
 
Name    Age
yaoxin  30
xiaoli
姚鑫    7
 
3 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0595s/46282/256257/9ms
          execute time(s)/globals/lines/disk: 0.0003s/3/714/0ms
---------------------------------------------------------------------------
DHC-APP>>/SET COMMANDPREFIX
 
commandprefix = /
DHC-APP>>/SET COMMANDPREFIX=""
 
commandprefix = ""
DHC-APP>>SET COMMANDPREFIX
 
commandprefix =

当设置了命令前缀时,除了?#GO之外的所有SQL Shell命令都需要该命令前缀; 可以使用或不使用命令前缀发出这三个SQL Shell命令。

当发出SETSET COMMANDPREFIX命令时,SQL Shell将显示当前命令前缀,作为SQL Shell初始化的一部分,并且在? 命令选项显示。

运行命令

SQL Shell RUN命令执行SQL脚本文件。 在发出运行命令之前必须设置方言,以指定IRIS (InterSystems SQL)、Sybase (Sybase TSQL)或MSSQL (Microsoft SQL); 默认的方言是IRIS。 可以调用RUN scriptname,也可以只调用RUN,然后提示输入脚本文件名。

RUN加载脚本文件,然后准备并执行文件中包含的每个语句。 脚本文件中的语句必须分隔,通常用GO行或分号(;)分隔。 RUN命令提示指定分隔符。

SQL脚本文件结果显示在当前设备上,也可以显示在日志文件中。 还可以生成一个包含准备失败语句的文件。

RUN命令返回指定这些选项的提示符,示例如下:

[SQL]USER>>SET DIALECT=Sybase

dialect = Sybase
[SQL]USER>>RUN
 
Enter the name of the SQL script file to run: SybaseTest
 
Enter the file name that will contain a log of statements, results and errors (.log): SyTest.log
     SyTest.log
 
Many script files contain statements not supported by IRIS SQL.
Would you like to log the statements not supported to a file so they
can be dealt with manually, if applicable?   Y=> y
Enter the file name in which to record non-supported statements (_Unsupported.log): SyTest_Unsupported.log
 
Please enter the end-of-statement delimiter (Default is 'GO'):  GO=>
 
Pause how many seconds after error?   5 => 3
 
Sybase Conversion Utility (v3)
Reading source from file:
Statements, results and messages will be logged to: SyTest.log
.
.
.

TSQL例子

下面的SQL Shell示例创建了一个Sybase过程AvgAge。 它使用Sybase EXEC命令执行这个过程。 然后,它将方言更改为InterSystems IRIS,并使用InterSystems SQL CALL命令执行相同的过程。

DHC-APP>>SET DIALECT Sybase
 
dialect = Sybase
DHC-APP>>  << entering multiline statement mode >>
        1>>CREATE PROCEDURE AvgAge
        2>>AS SELECT AVG(Age) FROM Sample.Person
        3>>GO
3.      CREATE PROCEDURE AvgAge
        AS SELECT AVG(Age) FROM Sample.Person
 
 
statement prepare time(s)/globals/lines/disk: 0.0173s/8129/22308/4ms
          execute time(s)/globals/lines/disk: 0.0436s/3844/23853/34ms
---------------------------------------------------------------------------
DHC-APP>>EXEC AvgAge
4.      EXEC AvgAge
 
 
 
Dumping result #1
Aggregate_1
50.68137254901960784
 
1 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0086s/8096/21623/0ms
          execute time(s)/globals/lines/disk: 0.1131s/90637/458136/19ms
---------------------------------------------------------------------------
DHC-APP>>SET DIALECT=IRIS
 
Dialect 'iris' is not supported.
0
0 274
文章 姚 鑫 · 三月 31, 2021 15m read

第十四章 使用SQL Shell界面(二)

存储和调用SQL语句

通过数据回调

SQL Shell自动将在终端会话期间发出的每个成功的SQL语句存储在本地缓存中,并为其分配一个顺序号。这些数字用于在当前Terminal过程中重新调用以前的SQL语句。 SQL Shell仅将数字分配给成功的SQL语句。如果在准备SQL语句期间发生错误,则不会分配任何编号。这些数字分配不是特定于名称空间的。以下是可用的数字调用命令:

  • :可以使用#列出所有先前缓存的SQL语句及其分配的编号。
  • #n:可以通过在SQL Shell提示符下指定#n来调用并执行先前的SQL语句,其中n是SQL Shell分配给该语句的整数。
  • #0:可以通过在SQL Shell提示符下指定#0来调用并执行最近准备的SQL语句。 #0调用最近准备的SQL语句,而不必调用最近执行的SQL语句。因此,调用和执行SQL语句对#0调用哪个SQL语句没有影响。

通过数字调用SQL语句不会为该语句分配新的数字。 SQL Shell在终端会话的持续时间内顺序分配数字;退出并重新进入SQL Shell或更改名称空间不会影响数字分配或先前分配的数字的有效性。

要删除所有号码分配,请使用#CLEAR并在显示的提示符下确认此操作。这将删除所有先前的号码分配,并从1重新开始号码分配。

通过名字回调

可以选择为SQL语句分配名称,然后按名称重新调用该语句。这些名称用于重新调用从任何当前用户的Terminal进程发出的SQL语句。通过名称保存和调用SQL语句有两种方法:

  • 使用SAVEGLOBAL保存到全局;使用OPEN从全局调用。
  • 使用SAVE保存到文件;使用LOAD从文件中调用。

保存到全局变量

要将全局名称分配给最新的SQL语句,请使用sql shell命令saveglobal名称,该名称可以缩写为SG名称。然后,可以使用SQL Shell命令打开名称来调用全局的SQL语句。如果Executemode是立即的,则SQL shell都会调用并执行该语句。如果延迟了executemode,则将准备该语句,但在指定GO命令之前,不会执行该语句。

每次使用打开名称以全局名称调用SQL语句时,SQL shell会为语句分配新号码。旧的和新数字都对调用数字仍然有效。

名称可以包含除空白字符之外的任何可打印字符。名称中的字母区分大小写。名称可以是任何长度。名称特定于当前命名空间。可以多次使用不同名称保存相同的SQL语句;所有已保存的名称都保持有效。如果使用已分配的名称保存SQL语句,则SQL Shell会提示是否希望覆盖现有名称,将其重新分配给新的SQL语句。

为当前命名空间分配全局名称。可以使用SQL Shell L(或列表)命令列出当前命名空间的所有分配的全局名称。分配后,所有当前用户的终端进程都可以使用名称。在创建它结束的终端进程后,分配的名称仍然存在。如果没有名称分配,则列表返回“保存”消息的“无语句”。

要删除全局名称分配,请使用清除名称。要删除当前命名空间的所有全局名称分配,请在显示的PROMP下使用清除并确认此操作

保存到文件

要将文件名分配给最新的SQL语句,请使用SQL Shell命令保存名称。然后,可以使用SQL Shell命令加载名称来调用SQL语句。如果Executemode是立即的,则SQL shell都会调用并执行该语句。每次使用Load Name按文件名调用SQL语句时,SQL Shell会将新号码分配给语句。旧的和新数字都对召回数字仍然有效。

名称可以包含除空白字符之外的任何可打印字符。名称中的字母区分大小写。名称可以是任何长度。名称特定于当前命名空间。可以多次使用不同名称保存相同的SQL语句;所有已保存的名称都保持有效。如果尝试使用已分配的名称保存SQL语句,则SQL Shell会提示是否希望覆盖现有名称,将其重新分配给新的SQL语句。

为当前命名空间分配名称。分配后,所有当前用户的终端进程都可以使用名称。在创建它结束的终端进程后,分配的名称仍然存在。

清除缓存查询Query

SQL shell提供了清除(缩写p)命令,以清除当前命名空间中的所有缓存查询。此命令清除名称空间中的所有缓存查询,而不仅仅是使用SQL Shell生成的查询。

$SYSTEM.SQL.Purge()方法和管理门户操作下拉列表选项为提供了更具体的选项,仅清除所选择的缓存查询或清除命名空间中的所有缓存查询。

配置SQL shell

  • 可以使用Management Portal配置SQL Shell默认值。
  • 可以使用SQL Shell参数配置单个SQL shell。更改SQL Shell参数覆盖SQL shell的当前调用的系统范围默认值;它不会更改系统范围的SQL shell默认值。

以下是可用的SQL Shell配置选项,相应的shell参数和默认设置:

管理门户shell配置Shell 参数默认
Select ModeselectmodeLogical
SQL Dialect (TSQL)dialect (TSQL)IRIS
Schema Search Pathpathnone
Result Column AlignmentcolalignDelimiter
Command Prefix (TSQL)commandprefix (TSQL)none
Result Output Display ModedisplaymodeCurrent Device
Display Pathdisplaypathnone
Display Filedisplayfilenone
Display File Translate Tabledisplaytranslatetablenone
Echo ModeechoOn
Execute Modeexecutemode
Messages ModemessagesOn
IF condition to allow execution1
logOff

标记为(TSQL)的参数主要用于从SQL Shell执行SybaseMSSQLTransact-SQL代码。

配置SQL Shell系统范围默认值

转到管理门户,选择系统管理,配置,SQL和对象设置,SQL。选择SQL Shell选项卡。查看并设置SQL Shell系统范围的当前默认设置。

如果更改一个或多个配置设置,则在管理门户路径之后立即由屏幕的左上角的星号(*)表示。例如,系统>配置> SQL *。按SAVE按钮接受更改。激活更改,星号消失。

为SQL shell配置参数

SQL Shell配置参数特定于当前终端进程上的当前SQL Shell调用。设置跨名称空间应用。但是,如果退出SQL Shell,则所有SQL Shell参数都会重置为系统宽的默认值。 Intersystems Iris提供系统默认值;您可以使用Set Save建立当前进程的不同默认值,如下所述。

SQL shell set命令(没有参数)显示当前shell配置参数,如以下示例所示。在此示例中,该组显示系统默认值,这些值是调用SQL Shell时建立的值:

[SQL]USER>>SET
 
commandprefix = ""
dialect = IRIS
displayfile =
displaymode = currentdevice
displaypath =
displaytranslatetable =
echo = on
executemode = immediate
log = off
messages = on
path = SQLUser
selectmode = logical
[SQL]USER>>

要显示单个配置参数的当前设置,请指定set param。例如,SET SelectMode返回当前选择介绍设置。

可以使用SQL Shell Set命令设置shell配置参数。 SQL Shell调用的持续时间持续一个设定值;每次调用SQL shell时,参数都会重置为默认值。设置可以使用以下任一语法表单:

SET param value
SET param = value

参数和值都不区分大小写。允许空间,但不需要,之前和之后。

SQL Shell Set Save命令将当前shell配置参数设置保存为用户默认值。这些默认值应用于当前进程的所有后续SQL Shell调用。它们也被应用于SQL Shell默认值,以在该用户调用的终端过程中的任何后续调用的SQL Shell。它们仍然有效,直到特别重置。使用Set保存不会影响当前正在运行的SQL Shell调用。

SQL Shell Set Clear命令清除(重置为系统默认值)当前进程的当前shell配置参数设置。 Intersystems IRIS将此重置应用于当前进程的后续SQL Shell调用,或者当前用户调用的任何新终端进程。设置清除不会影响当前运行的SQL Shell调用。

既不设定保存也没有设置清除更改系统范围的SQL Shell Shell默认设置,使用管理门户进行配置和显示。

Setting COLALIGN

可以使用Set Colalign来指定用于显示查询ResultSet数据和列标题的空格格式。可用选项包括:

  • 分隔符:ResultSet标题/数据列将基于标准分隔符(标签)对齐。这是默认值。
  • 标题:ResultSet标题/数据列将基于列标题的长度和标准分隔符(标签)对齐。
  • 数据:ResultSet标题/数据列将基于列数据属性的精度/长度和标准分隔符(标签)对齐。

设置displaymode和displaytranslatetable

可以使用Set DisplayMode指定用于显示查询数据的格式,如以下示例所示:

DHC-APP>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
 
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
DHC-APP>>SET DISPLAYMODE XML
 
displaymode = xml
DHC-APP>>

DisplayMode默认值是CurrentDevice,其在TXT格式中显示终端上的查询数据。可以指定set displaymode = cur恢复CurrentDevice默认值。

其他可用选项有TXTHTMLPDFXMLCSV。 格式的选择决定了文件类型。 InterSystems IRIS创建这种类型的文件,将查询数据写入该文件,并在可能的情况下启动适当的程序来显示该查询数据文件。 对于除TXT之外的所有选项,将创建第二个文件来记录结果集消息。 默认情况下,SQL Shell在InterSystems IRIS mgr\Temp\目录中创建这些文件,并分配一个随机生成的带有适当文件类型后缀的文件名。 生成的消息文件名与数据文件名相同,除了附加的字符串“Messages”。 对于HTMLPDFXML选项,消息文件具有与查询数据文件相同的文件类型后缀。 对于CSV选项,消息文件具有TXT文件类型后缀。

以下是DisplayMode = TXT时创建的文件的示例:

C:\InterSystems\IRIS\mgr\Temp\sGm7qLdVZn5VbA.txt
C:\InterSystems\IRIS\mgr\Temp\sGm7qLdVZn5VbAMessages.txt

每次运行查询时,SQL shell都会创建一个具有随机生成的文件名的新文件。

如果显示屏是txtcsv,则可以选择在执行格式转换时指定要应用的翻译表的名称可以指定SET DISPLAYTRANSLATESET DISPLAYTRANSLATERATE。转换表名称值区分大小写。

如果DisplayMode被设置为除CurrentDevice以外的值,则任何查询结果集包含控制字符的数据会导致生成的警告消息。通常,控制字符仅在逻辑模式下出现在查询结果集数据中。例如,列表结构中的数据包含在逻辑模式下显示的控制字符。因此,建议将DisplayMode设置为CurrentDevice以外的值时,还将SelectMode设置为显示或ODBC。

设置displayfile和displaypath

如果DisplayMode设置为“CurrentDevice以外的值”,则可以使用DisplayFileDisplayPath参数指定目标文件位置:

  • DISPLAYFILE:设置为一个没有后缀的简单文件名; 例如:SET DISPLAYFILE = myfile。 也可以将该参数设置为部分限定路径,系统间的IRIS将该路径追加到DISPLAYPATH值或默认目录中,根据需要创建子目录; 例如:SET DISPLAYFILE = mydir\myfile。 如果设置了DISPLAYPATH,系统将在指定的目录中创建一个以该文件名命名的文件; 如果没有设置DISPLAYPATH,系统将在InterSystems IRIS mgr\Temp\目录下创建一个以该文件名命名的文件。
  • DISPLAYPATH:根据操作系统平台的不同,设置为以斜杠(“/”)或反斜杠(“\”)结尾的现有的全限定目录路径结构。 如果设置了DISPLAYFILE,系统将在此目录下创建一个名为DISPLAYFILE的文件; 如果没有设置DISPLAYFILE,系统将在该目录下创建一个随机生成的文件名文件。 如果目录“DISPLAYPATH”不存在,InterSystems IRIS将忽略“DISPLAYPATH”“DISPLAYFILE”的设置,使用默认目录和随机生成的默认文件名。

必要时,系统自动在DISPLAYPATH值的末尾添加斜杠(或反斜杠)和/或从DISPLAYFILE值的开始删除斜杠(或反斜杠),以创建有效的完全限定目录路径。

设置DISPLAYMODEDISPLAYFILEDISPLAYPATH:

DHC-APP>>SET DISPLAYMODE XML
 
displaymode = xml
DHC-APP>>SET DISPLAYFILE = myfile
 
displayfile = myfile
DHC-APP>>SET DISPLAYPATH = C:\temp\mydir\
 
displaypath = C:\temp\mydir\
DHC-APP>>

执行查询时,SQL shell将生成以下文件。第一个包含查询数据。第二个包含Query执行产生的任何消息:

C:\temp\mydir\myfile.xml
C:\temp\mydir\myfileMessages.xml

如果既不指定DISPLAYFILE也不指定DISPLAYPATH,系统将在Mgr\Temp\目录下为InterSystems IRIS安装(例如,C:\InterSystems\IRIS\Mgr\Temp\)创建一个随机生成的文件名。

如果显示屏未设置为CurrentDevice,则每次使用displayfile集运行查询时,命名文件中的任何现有数据都会被新查询数据替换为新的查询数据。每次使用displayfile未设置查询时,SQL shell都会使用随机生成的文件名和新的相应邮件文件创建一个新文件。

如果displaymode设置为currentDevice,则DisplayFileDisplayPath参数无效。

设置executemode

SQL Shell支持立即和延迟的SQL语句执行。立即执行准备并在按Enter键时执行指定的SQL语句。延迟执行准备在输入Enter时,但在指定转到SQL提示符之前,不会执行它。

可用选项已立即设置ExecuteMode(默认值),设置ExecuteMode延迟和设置ExecuteMode以显示当前模式设置。以下示例设置执行模式:

DHC-APP>>SET EXECUTEMODE DEFERRED
 
Executemode = deferred

延迟执行允许准备多个SQL查询,然后按名称或编号调用它们以进行执行。要执行准备好的SQL语句,请调用所需的语句(来自适当的命名空间),然后指定Go

以下示例显示了在延迟模式下准备三个查询。前两个保存并分配了调用名称;第三个未分配一个名称,但可以通过数字来调用:

DHC-APP>>SELECT TOP 5 Name,Home_State FROM Sample.Person
1.      SELECT TOP 5 Name,Home_State FROM Sample.Person
 
---------------------------------------------------------------------------
DHC-APP>>SAVE 5sample
 
...statement saved as: 5sample
DHC-APP>>SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State
2.      SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State
 
---------------------------------------------------------------------------
DHC-APP>>SAVE 5ordered
 
...statement saved as: 5ordered
DHC-APP>>SELECT Name,Home_State FROM Sample.Person ORDER BY Home_State
3.      SELECT Name,Home_State FROM Sample.Person ORDER BY Home_State
 
---------------------------------------------------------------------------

以下示例显示了延迟模式执行前一个示例中定义的两个查询的执行。请注意,此示例通过名称调用一个查询(在调用SQL Shell提供新号码时,并按编号调用一个查询:

DHC-APP>>go
 
 
C:\InterSystems\Cache\mgr\Temp\ffQlXfFdbGnOxA.xml
Messages.xml
statement prepare time(s)/globals/lines/disk: 0.0526s/45464/263430/5ms
          execute time(s)/globals/lines/disk: 0.2948s/153553/1042318/75ms
---------------------------------------------------------------------------

Setting ECHO

可以使用Set Echo来指定是否将查询结果恢复到SQL Shell。如果指定SET Echo = OFF,则准备查询,定义缓存查询,并执行查询。终端没有查询结果。这在以下示例中显示:

DHC-APP>>set echo=off
 
echo = off
DHC-APP>>SELECT Name,Age FROM Sample.Person
4.      SELECT Name,Age FROM Sample.Person
 
--------------------------------------------------------------------------

如果指定SET Echo = ON(默认值),则将查询结果显示给终端。这在以下示例中显示:

DHC-APP>>set echo=on
 
echo = on
DHC-APP>>SELECT Name,Age FROM Sample.Person
5.      SELECT Name,Age FROM Sample.Person
 
---------------------------------------------------------------------------
DHC-APP>>go
 
 
C:\InterSystems\Cache\mgr\Temp\LVZpPfjfxXXJBg.xml
Messages.xml
statement prepare time(s)/globals/lines/disk: 0.0001s/5/187/0ms
          execute time(s)/globals/lines/disk: 0.1613s/152365/1040157/0ms
---------------------------------------------------------------------------

SET Echo如果displaymode = currentDevice(默认值)仅有意义。

SET ECHOSET MESSAGES指定终端显示的内容; 它们不会影响查询的准备或执行。 如果SET MESSAGES=OFF和SET ECHO=OFF,则查询准备好了,一个缓存的查询被创建,查询执行创建一个查询结果集,但是没有返回给终端。

Setting MESSAGES

可以使用SET MESSAGES来指定是否显示查询错误消息(如果不成功)或查询执行信息(如果成功):

  • 如果查询执行不成功:如果指定SET MESSAGES=OFF,则终端不会显示任何信息。 如果指定SET MESSAGES=ON(默认值),则返回查询错误提示,如下所示:error #5540: SQLCODE: -30 message: Table 'SAMPLE。 值得注意的没有找到。
  • 如果查询执行成功:如果指定SET MESSAGES=OFF,则只显示查询结果和受影响的n行。 如果指定SET MESSAGES=ON(默认值),则查询结果和受影响的n行(s行)后面紧跟着语句准备度量、语句执行度量和生成的缓存查询的名称。

准备和执行指标以运行时间(以毫秒为单位)、全局引用总数、执行的命令总数和磁盘读取延迟(以毫秒为单位)来衡量。

设置DISPLAYMODE不会改变SET MESSAGES=ON时显示的信息。 一些DISPLAYMODE选项同时创建一个查询结果集文件和一个消息文件。 该消息文件包含结果集消息,而不是set messages =ON时显示到终端的查询准备和执行消息。

设置消息并设置echo指定终端上显示的内容;它们不会影响查询的准备或执行。如果SET MESSAGENT = OFFSET ECHO = OFF,则准备成功的查询,创建缓存的查询,查询执行创建查询结果集,但没有返回到终端。

Setting LOG

可以使用Set日志指定是否将SQL Shell活动记录到文件。可用选项包括:

  • SET LOG OFF: 默认值。 Intersystems IRIS不会为当前SQL Shell记录活动。
  • SET LOG ON: Intersystems Iris将SQL Shell活动记录到默认日志文件。
  • SET LOG pathname:Intersystems Iris将SQL Shell活动记录到Pathname指定的文件。

SET LOG ON在IRIS\mgr\namespace中创建一个日志文件,其中namespace是进程当前命名空间的名称。 这个默认日志文件名为xsqlnnnn。 其中nnnn是当前进程的进程ID (pid)号。

日志文件可以挂起并恢复。 创建日志文件后,SET log OFF会挂起对该日志文件的写入。 设置LOG ON恢复写入默认日志文件。 日志重新启动:日志恢复时,将日期时间写入日志文件。 设置LOG ON总是激活默认日志文件。 因此,如果暂停写入指定的路径名日志文件,则在恢复时必须指定SET log pathname

激活日志文件创建终端上显示的SQL Shell活动的副本;它不会重定向SQL Shell终端输出。 SQL Shell Log为失败的SQL执行和SQL代码记录SQL错误,并为成功的SQL执行而导致的行计数。 SQL Shell日志不会记录结果集数据。

如果日志已处于活动状态,则指定“设置”登录无效。如果日志已处于活动状态,则指定设置日志路径名暂停当前日志并激活路径名指定的日志。、

Setting PATH

可以使用SET路径架构来设置Schema Search Path,SQL用于提供不合格表名的正确架构名称。架构可以是单个架构名称,或者逗号分隔的架构名称列表,如下例所示:

DHC-APP>>SET PATH cinema,sample,user
 
path = cinema,sample,user

没有任何参数的设置路径删除了当前架构搜索路径,恢复系统范围的默认模式名称。

如果未指定SET路径架构,或者在指定的模式中找不到表,则SQL Shell使用系统范围的默认模式名称。

Setting SELECTMODE

可以使用SetSeliteMode指定用于显示查询数据的模式。

DHC-APP>>SET SELECTMODE DISPLAY
 
selectmode = display

可用选项显示,逻辑和ODBC。逻辑是默认值。要确定当前模式,请指定SETELESMODE,无需值:

DHC-APP>>SET SELECTMODE logical
 
selectmode = logical

%List数据使用非打印字符进行编码。因此,当SelectMode =逻辑时,SQL shell将%List数据值显示为$listbuild语句,例如以下$lb("White","Green")。时间数据类型数据支持分数秒。因此,当SelectMode = ODBC时,SQL Shell显示分数秒,这与ODBC标准不对应。实际的ODBC时间数据类型截断分数秒。

还可以使用SET SELECTMODE指定输入数据是否将从显示格式转换为逻辑存储格式。 要进行此数据转换,必须使用select运行时模式编译SQL代码。 在执行时,SET SELECTMODE必须设置为LOGICAL(默认值)。

0
0 163
文章 姚 鑫 · 三月 30, 2021 9m read

第十四章 使用SQL Shell界面(一)

执行SQL的其他方式

可以使用$SYSTEM.SQL.Execute() 方法从Terminal命令行执行一行SQL代码,而无需调用SQL Shell。以下示例显示如何在终端提示下使用此方法:

DHC-APP>SET result=$SYSTEM.SQL.Execute("SELECT TOP 5 name,dob,ssn FROM Sample.Person")
 
DHC-APP>DO result.%Display()
Name    DOB     SSN
yaoxin  54536   111-11-1117
xiaoli          111-11-1111
姚鑫    63189   111-11-1112
姚鑫    63189   111-11-1113
姚鑫    50066   111-11-1114
 
5 Rows(s) Affected

如果SQL语句包含错误,则Execute()方法成功完成;否则,该方法无效。 %Display()方法返回错误信息,例如:

USER>DO result.%Display()

[SQLCODE: <-29>:<Field not found in the applicable tables>]
[%msg: < Field 'GAME' not found in the applicable tables^ SELECT TOP ? game ,>]
0 Rows Affected
USER>

Execute()方法还提供了可选的SelectModeDialectObjectSelectMode参数。

InterSystems IRIS支持许多其他编写和执行SQL代码的方法

这些包括:

  • 嵌入式SQL:嵌入ObjectScript代码中的SQL代码。
  • 动态SQL:使用%SQL。 从ObjectScript代码中执行SQL语句的语句类方法。
  • 管理门户SQL接口:使用Execute Query接口从InterSystems IRIS管理门户执行动态SQL。

调用SQL Shell

可以使用$SYSTEM.SQL.Shell()方法在终端提示符中调用SQL Shell,如下所示:

DO $SYSTEM.SQL.Shell()

或者,可以使用%SQL作为一个实例调用SQL Shell。 Shell类,如下所示:

  DO ##class(%SQL.Shell).%Go("IRIS")

  SET sqlsh=##class(%SQL.Shell).%New()
  DO sqlsh.%Go("IRIS")

无论如何调用,SQL Shell都会返回SQL Shell提示符,如下所示:

[SQL]termprompt>>

其中[SQL]是指在SQL Shell中,termprompt是配置的终端提示符,>>是指SQL命令行。 默认情况下,SQL Shell提示符如下所示:[SQL]nsp>>,其中“nsp”是当前命名空间的名称。

DHC-APP>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
 
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
DHC-APP>>  << entering multiline statement mode >>
        1>>
        1>>SELECT TOP 5 name,dob,ssn FROM Sample.Person
        2>>
        2>>q
DHC-APP>>q

在这个提示符下,可以使用以下任一Shell模式:

  • 单行模式:在提示符下键入一行SQL代码。 结束SQL语句,按“Enter”。 默认情况下,这将准备并执行SQL代码(这称为立即执行模式)。 对于查询,结果集显示在终端屏幕上。 对于其他SQL语句,将在终端屏幕上显示SQLCODE和行数值。
  • 多行模式:在提示符下按Enter。这使进入多行模式。可以键入多行SQL代码,每个新行提示均指示行号。 (空行不会增加行号。)要结束多行SQL语句,请键入GO并按Enter。默认情况下,这既准备并执行SQL代码。对于查询,结果集显示在终端屏幕上。对于其他SQL语句,SQLCODE和行计数值显示在终端屏幕上。

多行模式提供以下命令,可以在多行提示符下键入以下命令,然后按Enter:LLIST列出到目前为止输入的所有SQL代码。 CCLEAR删除到目前为止输入的所有SQL代码。 C nCLEAR n(其中n是行号整数)以删除特定的SQL代码行。 GGO准备和执行SQL代码,然后返回单行模式。 QQUIT删除到目前为止输入的所有SQL代码并返回单行模式。这些命令不区分大小写。发出命令不会增加下一个多行提示的行号。打在多行提示符处列出了这些多行命令。

为了准备一条SQL语句,SQL Shell首先验证该语句,包括确认指定的表存在于当前名称空间中,并且指定的字段存在于表中。如果不是,它将显示适当的SQLCODE。

如果该语句有效,并且具有适当的特权,则SQL Shell会回显SQL语句,并为其分配一个序号。无论您是否更改名称空间和/或退出并重新进入SQL Shell,这些数字在终端会话期间都将按顺序分配。这些分配的语句编号允许重新调用以前的SQL语句,如下所述。

也可以使用DO Shell^%apiSQL.在终端提示下调用SQL Shell。

要列出所有可用的SQL Shell命令,请输入。在SQL提示下。

要终止SQL Shell会话并返回到Terminal提示符,请在SQL提示符下输入QQUIT命令或E命令。 SQL Shell命令不区分大小写。

以下是使用默认参数设置的示例SQL Shell会话:

DHC-APP>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
 
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
DHC-APP>>SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State
1.      SELECT TOP 5 Name,Home_State FROM Sample.Person ORDER BY Home_State
 
Name    Home_State
xiaoli
姚鑫
姚鑫
姚鑫
姚鑫
 
5 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0523s/45502/270281/2ms
          execute time(s)/globals/lines/disk: 0.0004s/225/2915/0ms
---------------------------------------------------------------------------
DHC-APP>>q

以下是使用默认参数设置的多行SQL Shell会话:

DHC-APP>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
 
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
DHC-APP>>  << entering multiline statement mode >>
        1>>SELECT TOP 5
        2>>Name,Home_State
        3>>FROM Sample.Person
        4>>ORDER BY Home_State
        5>>GO
2.      SELECT TOP 5
        Name,Home_State
        FROM Sample.Person
        ORDER BY Home_State
 
Name    Home_State
xiaoli
姚鑫
姚鑫
姚鑫
姚鑫
 
5 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0002s/18/1168/0ms
          execute time(s)/globals/lines/disk: 0.0003s/225/2886/0ms
---------------------------------------------------------------------------

GO命令

SQL Shell GO命令执行最新的SQL语句。在单行模式下,GO重新执行最近执行的SQL语句。在多行模式下,GO命令用于执行多行SQL语句并退出多行模式。单行模式下的后续GO将重新执行先前的多行SQL语句。

输入参数

SQL Shell支持使用“?”输入参数的使用SQL语句中的字符。每次执行SQL语句时,系统都会提示指定这些输入参数的值。必须以与“?”相同的顺序指定这些值字符出现在SQL语句中:第一个提示为第一个“?”提供一个值,第二个提示为第二个“?”提供一个值,依此类推。

输入参数的数量没有限制。可以使用输入参数将值提供给TOP子句,WHERE子句,并将表达式提供给SELECT列表。不能使用输入参数将列名提供给SELECT列表。

可以将主机变量指定为输入参数值。在输入参数提示下,指定一个以冒号(:)开头的值。该值可以是公共变量,ObjectScript特殊变量,数字文字或表达式。然后,SQL Shell会提示“这是文字(Y / N)吗?”。在此提示下指定N(否)(或仅按Enter)意味着将输入值解析为主机变量。例如,:myval将被解析为局部变量myval的值; :^ myval将被解析为全局变量^myval的值; :$HOROLOG将被解析为$HOROLOG特殊变量的值; :3将被解析为数字3; :10-3将被解析为数字7。在此提示符下指定Y(是)表示将输入值(包括冒号)作为文字提供给输入参数。

执行ObjectScript命令

在SQL Shell中,可能希望发出一个ObjectScript命令。例如,通过使用SET $NAMESPACE命令将InterSystems IRIS命名空间更改为包含要引用的SQL表或存储过程的命名空间。可以使用SQL Shell!命令或OBJ命令以发出由一个或多个ObjectScript命令组成的ObjectScript命令行。 (OBJ是OBJECTSCRIPT的缩写。)!,OBJ和OBJECTSCRIPT命令是同义词。以下示例显示了这些命令的用法:

%SYS>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
[SQL]%SYS>>! SET oldns=$NAMESPACE SET $NAMESPACE="USER" WRITE "changed the namespace"
changed the namespace
[SQL]USER>>OBJ SET $NAMESPACE=oldns WRITE "reverted to old namespace"
reverted to old namespace
[SQL]%SYS>>

OBJ命令之后的其余命令行被视为ObjectScript代码。 !之间不需要空格。和ObjectScript命令行。可以在SQL Shell单行模式或SQL Shell多行模式下指定OBJ命令。以下示例在USER名称空间中定义的表上执行SELECT查询:

%SYS>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
[SQL]%SYS>>  << entering multiline statement mode >>
        1>>OBJ SET $NAMESPACE="USER"

        1>>SELECT TOP 5 Name,Home_State
        2>>FROM Sample.Person
        3>>GO
   /* SQL query results */
[SQL]USER>>

请注意,OBJ语句不会增加SQL行数。

在SQL Shell多行模式下,在返回行时将执行OBJ命令,但是直到指定GO才发出SQL语句。因此,以下示例在功能上与先前的示例相同:

%SYS>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
[SQL]%SYS>>  << entering multiline statement mode >>
        1>>SELECT TOP 5 Name,Home_State
        2>>FROM Sample.Person
        3>>OBJ SET $NAMESPACE="USER" WRITE "changed namespace"
changed namespace
        3>>GO
   /* SQL query results */
[SQL]USER>>

以下示例使用OBJ命令定义主机变量:

USER>DO $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
[SQL]USER>>  << entering multiline statement mode >>
        1>>SELECT TOP :n Name,Home_State
        2>>FROM Sample.Person
        3>>OBJ SET n=5

        3>>GO

浏览命名空间

SQL Shell支持BROWSE命令,该命令显示在当前名称空间中定义或可从其访问的架构,表和视图。该显示包括多个级别的提示。要返回上一个提示级别,请在提示时按Return键。名称区分大小写。

  1. 在SQL Shell提示符下键入BROWSE,以列出当前名称空间中的架构。
  2. 在“架构:”提示下,按名称或编号选择一个架构。这将列出架构中的表和视图。
  3. 在“表/视图:”提示下,按名称或编号选择一个表(T)或视图(V)。这将显示表信息,然后显示选项列表。
  4. 在“选项:”提示下,按编号选择一个选项。可以使用此选项列出为表定义的字段或映射。

指定选项1(按名称表示的字段)或选项2(按数字表示的字段)以显示“Field:”提示。指定选项3(地图)以显示“Map:”提示。

  1. “Field:”提示下,按数字或名称选择一个字段,或指定*以列出所有字段。这列出了详细的字段信息。

“Map:”提示下,按数字或名称选择地图,或指定*列出所有Map。这列出了详细的Map信息。

CALL 命令

可以使用SQL Shell发出SQL CALL语句来调用SQL存储过程,如以下示例所示:

DHC-APP>>CALL Sample.PersonSets('G','NY')
3.      CALL Sample.PersonSets('G','NY')
 
 
 
Dumping result #1
Name    DOB     Spouse
Gallant,Thelma Q.       45767   94
Gibbs,Mark S.   37331   13
Goldman,Will H. 59069   10
Gomez,Mo Q.     55626   55
Gore,Alfred M.  42991   13
Gore,Fred X.    32391   6
Grabscheid,Jocelyn B.   59676   79
 
7 Rows(s) Affected
 
Dumping result #2
Name    Age     Home_City       Home_State
Chadbourne,Danielle G.  34      Pueblo  NY
Eastman,Clint G.        4       Miami   NY
Pape,Linda M.   71      Vail    NY
Peterson,Janice N.      49      Islip   NY
Schaefer,Jocelyn V.     93      Zanesville      NY
 
5 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0043s/2153/15795/0ms
          execute time(s)/globals/lines/disk: 0.0015s/315/7829/0ms
---------------------------------------------------------------------------

如果指定的存储过程在当前名称空间中不存在,则SQL Shell会发出SQLCODE -428错误。

如果指定的输入参数多于存储过程中定义的参数,则SQL Shell会发出SQLCODE -370错误。可以使用文字(“字符串”),主机变量(:var)和输入参数()的任意组合为存储过程指定参数值。

可以在CALL语句中使用主机变量,如以下示例所示:

[SQL]USER>>OBJ SET a="G",b="NY"
[SQL]USER>>CALL Sample.PersonSets(:a,:b)
  • 可以在CALL语句中使用输入参数(“?”字符),如以下示例所示:
[SQL]USER>>CALL Sample.PersonSets(?,?)

当执行CALL语句时,SQL Shell会提示为每个输入参数提供一个值。

执行SQL脚本文件

SQL Shell RUN命令执行一个SQL脚本文件。脚本文件的类型由DIALECT设置确定。 DIALECT的默认值为IRIS(InterSystems SQL)。

0
0 882
公告 Nicky Zhu · 三月 30, 2021

2021年3月23日 – 提醒:镜像Dejournaling的潜在数据完整性问

InterSystems 已经纠正了一个缺陷,该缺陷在极少数情况下会导致非主镜像成员的数据不一致问题。此缺陷影响到InterSystems产品的所有发布版本。

如果发生该缺陷,则会在镜像系统的正常运行中悄然发生。该缺陷的结果是,镜像成员未能删除数据库日志的子集,然后导致镜像成员之间的数据不一致。这对故障转移和异步成员都有影响。虽然这个缺陷遇到的可能性非常小,但InterSystems建议所有使用镜像的客户都要获得修正,并使用DataCheck验证镜像数据库的一致性。有关可能触发该缺陷的具体情况以及将遇到该缺陷的风险降至最低的步骤的更多详细信息,请联系全球响应中心(WRC)。

该缺陷的更正被确定为 SML2898、HYY2434 和 HYY2435,并将包含在所有未来的产品版本中,包括 Caché 和 Ensemble 2018.1.5 以及 InterSystems IRIS 和 InterSystems IRIS for Health 2019.1.2 和 2020.1.1。这些更正也可通过InterSystems全球响应中心(WRC)的Ad hoc分发版本获得。

如果您对这个提醒有任何疑问,请联系 Worldwide Response Center

0
0 171
公告 Nicky Zhu · 三月 30, 2021

现已推出三套新的维护版本:

  • Caché  2018.1.5, Ensemble 2018.1.5, and HSAP 2018.1.5
  • InterSystems IRIS 2019.1.2, IRIS for Health 2019.1.2, and HealthShare Health Connect 2019.1.2
  • InterSystems IRIS 2020.1.1, IRIS for Health 2020.1.1, and HealthShare Health Connect 2020.1.1

安装包和容器版本可从以下网站下载  WRC Software Distribution site

这些都是维护版本,在广泛的领域内有许多更新。  有关这些版本中的修正信息,请参考该版本的文档,其中包括发行说明和升级检查表、发行变更列表,以及类参考资料和全套指南、参考资料、教程和文章。所有的文档都可以通过以下方式获得 docs.intersystems.com 。

在这些版本中也加入了新的平台支持。  特别是,Ubuntu 20.04 LTS的支持已经添加到所有版本中,IBM AIX 7.1和7.2对System p-64的支持已经添加到2019.1.2中(并且已经在2020.1中),而对Linux的ARM64支持已经添加到2020.1.1中。  详情请参见各版本的支持平台文档。

0
0 353
文章 姚 鑫 · 三月 29, 2021 16m read

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

SQL元数据

动态SQL提供以下类型的元数据:

  • 在“准备”之后,描述查询类型的元数据。
  • 在“准备”之后,描述查询中选择项的元数据(“列”和“扩展列信息”)。
  • 在准备之后,描述查询参数的元数据:参数,:var参数和常量。 (语句参数,形式参数和对象)
  • 执行之后,描述查询结果集的元数据。在执行Prepare操作(%Prepare()%PrepareClassQuery()%ExecDirect())之后,可以使用%SQL.StatementMetadata属性值。
  • 可以直接为最新的%Prepare()返回%SQL.Statement元数据属性。
  • 可以返回包含%SQL.StatementMetadata属性的oref的%SQL.Statement%Metadata属性。这使可以返回多个准备操作的元数据。

SELECTCALL语句返回所有这些元数据。 INSERTUPDATEDELETE返回语句类型元数据和形式参数。

语句类型元数据

使用%SQL.Statement类进行Prepare之后,可以使用%SQL.StatementMetadata statementType属性来确定准备哪种类型的SQL语句,如以下示例所示。本示例使用%SQL.Statement%Metadata属性来保存和比较两个Prepare操作的元数据:

/// d ##class(PHA.TEST.SQL).MetaData()
ClassMethod MetaData()
{
	SET tStatement = ##class(%SQL.Statement).%New()
	SET myquery1 = "SELECT TOP ? Name,Age,AVG(Age),CURRENT_DATE FROM Sample.Person"
	SET myquery2 = "CALL Sample.SP_Sample_By_Name(?)"
	SET qStatus = tStatement.%Prepare(myquery1)
	IF qStatus'=1 {
		WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	SET meta1 = tStatement.%Metadata
	SET qStatus = tStatement.%Prepare(myquery2)
	IF qStatus'=1 {
		WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	SET meta2 = tStatement.%Metadata
	WRITE "语句类型query 1: ",meta1.statementType,!
	WRITE "语句类型query 2: ",meta2.statementType,!
	WRITE "End of metadata"
}

DHC-APP>d ##class(PHA.TEST.SQL).MetaData()
语句类型query 1: 1
语句类型query 2: 45
End of metadata

statementType属性的“类引用”条目列出了语句类型整数代码。最常见的代码是1(SELECT查询)和45(CALL到存储的查询)。

可以使用%GetImplementationDetails()实例方法返回相同的信息,如成功准备的结果中所述。

执行查询后,可以从结果集中返回语句类型名称(例如SELECT)。

选择项目Select-item元数据

使用%SQL.Statement类准备SELECTCALL语句之后,可以通过显示所有元数据或指定各个元数据项来返回有关查询中指定的每个选择项列的元数据。此列元数据包括ODBC数据类型信息,以及客户端类型和InterSystems Objects属性的起源以及类类型信息。

以下示例返回最近准备的查询中指定的列数:

/// d ##class(PHA.TEST.SQL).MetaData1()
ClassMethod MetaData1()
{
	SET myquery = "SELECT %ID AS id,Name,DOB,Age,AVG(Age),CURRENT_DATE,Home_State 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}
	WRITE "Number of columns=",tStatement.%Metadata.columnCount,!
	WRITE "End of metadata"
}
DHC-APP>d ##class(PHA.TEST.SQL).MetaData1()
Number of columns=7
End of metadata

以下示例返回列名称(或列别名),ODBC数据类型,最大数据长度(精度),以及每个SELECT项目字段的比例:

/// d ##class(PHA.TEST.SQL).MetaData2()
ClassMethod MetaData2()
{
	SET $NAMESPACE="SAMPLES"
	SET myquery=2
	SET myquery(1)="SELECT Name AS VendorName,LastPayDate,MinPayment,NetDays,"
	SET myquery(2)="AVG(MinPayment),$HOROLOG,%TABLENAME FROM Sample.Vendor"
	SET rset = ##class(%SQL.Statement).%New()
	SET qStatus = rset.%Prepare(.myquery)
	IF qStatus'=1 {
		WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	SET x=rset.%Metadata.columns.Count()
	SET x=1
	WHILE rset.%Metadata.columns.GetAt(x) {
		SET column=rset.%Metadata.columns.GetAt(x)
		WRITE !,x," ",column.colName," 是数据类型 ",column.ODBCType
		WRITE " 大小为 ",column.precision," 规模 = ",column.scale
		SET x=x+1 
	}
	WRITE !,"End of metadata"
}

DHC-APP>d ##class(PHA.TEST.SQL).MetaData2()
 
1 VendorName 是数据类型 12 大小为 50 规模 = 0
2 LastPayDate 是数据类型 9 大小为 10 规模 = 0
3 MinPayment 是数据类型 8 大小为 6 规模 = 0
4 NetDays 是数据类型 4 大小为 3 规模 = 0
5 Aggregate_5 是数据类型 8 大小为 20 规模 = 0
6 Expression_6 是数据类型 12 大小为 255 规模 = 0
7 Literal_7 是数据类型 12 大小为 13 规模 = 0
End of metadata

下面的示例使用%SQL.StatementMetadata%Display()实例方法显示所有列元数据:

/// d ##class(PHA.TEST.SQL).MetaData3()
ClassMethod MetaData3()
{
	SET tStatement = ##class(%SQL.Statement).%New()
	SET qStatus = tStatement.%Prepare("SELECT %ID AS id,Name,DOB,Age,AVG(Age),CURRENT_DATE,Home_State FROM Sample.Person")
	IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
	DO tStatement.%Metadata.%Display()
	WRITE !,"End of metadata"
}
DHC-APP>d ##class(PHA.TEST.SQL).MetaData3()
 
 
Columns (SQLRESULTCOL, property 'columns'):
 
Column Name  Type Prec Scale Null Label        Table        Schema       CType
-----------  ---- ---- ----- ---- ------------ ------------ ------------ -----
id              4   10     0    0 id           Person       Sample          5
Name           12   50     0    0 Name         Person       Sample         10
DOB             9   10     0    1 DOB          Person       Sample          2
Age             4   10     0    1 Age          Person       Sample          5
Aggregate_5     2   20     8    1 Aggregate_5                              14
Expression_6    9   11     0    2 Expression_6                              2
Home_State     12    2     0    1 Home_State   Person       Sample         10
 
 
Extended Column Info (SQLRESULTCOL)
     Flags: 1:AutoIncrement,2:CaseSensitive,3:Currency,4:ReadOnly,5:RowVersion,
            6:Unique,7:Aliased,8:Expression,9:Hidden,10:Identity,11:KeyColumn,
            12:RowId
 
Column Name  Linked Prop           Type Class            Flags
------------ --------------------- --------------------- -----------------------
id                                 Sample.Person         Y,N,N,Y,N,Y,Y,N,N,Y,Y,Y
Name         Sample.Person.Name    %Library.String       N,N,N,N,N,N,N,N,N,N,N,N
DOB          Sample.Person.DOB     %Library.Date         N,N,N,N,N,N,N,N,N,N,N,N
Age          Sample.Person.Age     %Library.Integer      N,N,N,N,N,N,N,N,N,N,N,N
Aggregate_5                        %Library.Numeric      N,N,N,Y,N,N,Y,N,N,N,N,N
Expression_6                       %Library.Date         N,N,N,Y,N,N,Y,Y,N,N,N,N
Home_State   Sample.Address.State
                                   %Library.String       N,N,N,N,N,N,N,N,N,N,N,N
 
 
Statement Parameters (property 'parameters'):
 
Nbr. Type precision scale nullable colName      columntype
---- ---- --------- ----- -------- ------------ ----------
 
 
Formal Parameters (property 'formalParameters'):
 
Nbr. Type precision scale nullable colName      columntype
---- ---- --------- ----- -------- ------------ ----------
 
 
Objects:
 
Col Column Name Extent            ExportCall
--- ----------- ----------------- -----------------------------
  1 id          Sample.Person     ##class(Sample.Person).%SQLQuickLoad

这将返回所选字段的两个表列表。第一列元数据表列出了列定义信息:

显示标题%SQL.StatementColumn属性描述
Column NamecolName列的SQL名称。如果为该列提供了别名,则会在此处列出该列的别名,而不是字段名称。名称和别名将被截断为12个字符。对于表达式,聚合,文字,主机变量或子查询,列出了分配的“ Expression_n”“ Aggregate_n”“ Literal_n”“ HostVar_n”“ Subquery_n”标签(nSELECT项序列号)。如果为表达式,聚合,文字,主机变量或子查询分配了别名,则在此处列出该别名。
TypeODBCTypeODBC数据类型的整数代码。请注意,这些ODBC数据类型代码与CType数据类型代码不同。
Precprecision精度或最大长度(以字符为单位)。日期,时间,PosixTime和TimeStamp数据类型中描述了TIME数据类型的精度和小数位元数据。
Scalescale小数位数的最大数目。对于整数或非数值返回0。日期,时间,PosixTime和TimeStamp数据类型中描述了TIME数据类型的精度和小数位元数据。
NullisNullable一个整数值,指示是否将列定义为Non-NULL(0),或者是否允许NULL(1)。 RowID返回0。如果SELECT项是可能导致NULL的聚合或子查询,或者如果它指定NULL文字,则该项设置为1。如果SELECT项是表达式或主机变量,则设置此项到2(无法确定)。
Labellabel列名或列别名(与列名相同)。
TabletableNameSQL表名称。即使为表指定了别名,也始终在此处列出实际的表名。如果SELECT项是表达式或聚合,则不会列出任何表名。如果SELECT项是子查询,则列出子查询表名称。
SchemaschemaName表的架构名称。如果未指定架构名称,则返回系统范围的默认架构。如果SELECT项是表达式或聚合,则不会列出任何模式名称。如果SELECT项是子查询,则不会列出任何架构名称。
CTypeclientType客户端数据类型的整数代码。

第二列元数据表列出了扩展列信息。扩展列信息表列出了具有十二个布尔标志(SQLRESULTCOL)的每一列,这些标志被指定为Y(是)或N(否):

显示标题%SQL.StatementColumn属性描述
1: AutoIncrementisAutoIncrementTRowID和IDENTITY字段返回Y。
2: CaseSensitiveisCaseSensitive具有%EXACT归类的字符串数据类型字段返回Y。引用%SerialObject嵌入式对象的属性返回Y。
3: CurrencyisCurrency使用%Library.Currency数据类型定义的字段,例如MONEY数据类型。
4: ReadOnlyisReadOnly表达式,聚合,文字,HostVar或子查询返回Y。RowID,IDENTITY和RowVersion字段返回Y。
5: RowVersionisRowVersionRowVersion字段返回Y。
6: UniqueisUnique定义为具有唯一值约束的字段。 RowID和IDENTITY字段返回Y。
7: AliasedisAliased系统为非字段选择项提供别名。因此,无论用户是否通过指定列别名替换了系统别名,表达式,聚合,文字,HostVar或子查询都将返回Y。此标志不受用户指定的列别名的影响。
8: ExpressionisExpression表达式返回Y。
9: HiddenisHidden如果使用%PUBLICROWIDSqlRowIdPrivate = 0(默认值)定义表,则RowID字段返回N。否则,RowID字段返回Y。引用%SerialObject嵌入式对象的属性返回Y。
10: IdentityisIdentity定义为IDENTITY字段的字段返回Y。如果未隐藏RowID,则RowID字段返回Y。
11: KeyColumnisKeyColumn定义为主键字段或外键约束目标的字段。 RowID字段返回Y。
12: RowIDisRowIdROWID和Identity字段返回Y.

扩展列信息元数据表列出了每个选定字段的列名称(SQL名称或列别名),链接属性(链接的持久性类属性)和类型类(数据类型类)。请注意,链接属性列出了持久性类名(不是SQL表名)和属性名(不是列别名)。

  • 对于普通表字段(SELECT Name FROM Sample.Person): Linked Prop=Sample.Person.Name, Type Class=%Library.String.
  • 对于表格的RowID (SELECT %ID FROM Sample.Person): Linked Prop= [none], Type Class=Sample.Person.
  • 对于表达式,聚合,文字,HostVar或子查询 (SELECT COUNT(Name) FROM Sample.Person): Linked Prop= [none], Type Class=%Library.BigInt.
  • 供参考%Serial Object嵌入式对象属性 (SELECT Home_State FROM Sample.Person). Linked Prop=Sample.Address.State, Type Class=%Library.String.
  • 对于引用%SerialObject嵌入式对象的字段(SELECT Home FROM Sample.Person). Linked Prop=Sample.Person.Home, Type Class=Sample.Address.

在此示例中,Sample.Person中的Home_State字段引用%SerialObjectSample.AddressState属性。

下面的示例返回带有一个形式参数(也就是语句参数)的被调用存储过程的元数据:

/// d ##class(PHA.TEST.SQL).MetaData4()
ClassMethod MetaData4()
{
	SET $NAMESPACE="SAMPLES"
	SET mysql = "CALL Sample.SP_Sample_By_Name(?)"
	SET tStatement = ##class(%SQL.Statement).%New()
	SET qStatus = tStatement.%Prepare(.mysql)
	IF qStatus'=1 {
		WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	DO tStatement.%Metadata.%Display()
	WRITE !,"End of metadata"
}

它不仅返回列(字段)信息,还返回语句参数,形式参数和对象的值。

以下示例返回具有三个形式参数的的元数据。这三个参数之一用问号()指定,使其成为语句参数:

/// d ##class(PHA.TEST.SQL).MetaData5()
ClassMethod MetaData5()
{
	SET $NAMESPACE="SAMPLES"
	SET mycall = "CALL personsets(?,'MA')"
	SET tStatement = ##class(%SQL.Statement).%New(0,"sample")
	SET qStatus = tStatement.%Prepare(mycall)
	IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
	DO tStatement.%Metadata.%Display()
	WRITE !,"End of metadata"
}

DHC-APP>d ##class(PHA.TEST.SQL).MetaData5()
 
 
Columns (SQLRESULTCOL, property 'columns'):
 
Column Name  Type Prec Scale Null Label        Table        Schema       CType
-----------  ---- ---- ----- ---- ------------ ------------ ------------ -----
 
 
Extended Column Info (SQLRESULTCOL)
     Flags: 1:AutoIncrement,2:CaseSensitive,3:Currency,4:ReadOnly,5:RowVersion,
            6:Unique,7:Aliased,8:Expression,9:Hidden,10:Identity,11:KeyColumn,
            12:RowId
 
Column Name  Linked Prop           Type Class            Flags
------------ --------------------- --------------------- -----------------------
 
 
Statement Parameters (property 'parameters'):
 
Nbr. Type precision scale nullable colName      columntype
---- ---- --------- ----- -------- ------------ ----------
   1   12     50        0     2    name            1
 
 
Formal Parameters (property 'formalParameters'):
 
Nbr. Type precision scale nullable colName      columntype
---- ---- --------- ----- -------- ------------ ----------
   1    4      4        0     2    _isc_sp_ret_val 5
   2   12     50        0     2    name            1
   3   12     50        0     2    state           1
 
 
Objects:
 
Col Column Name Extent            ExportCall
--- ----------- ----------------- -----------------------------
 
End of metadata

请注意,此元数据不返回任何列信息,但是“语句参数”,“形式参数”列表包含列名称和数据类型。

Query参数元数据

使用%SQL.Statement类进行Prepare之后,您可以返回有关查询参数的元数据:输入参数(指定为问号()),输入主机变量(指定为:varname)和常量(文字值)。可以返回以下元数据:

  • 参数:parameterCount属性
  • ODBC数据类型为参数:%SQL.StatementMetadata%Display()实例方法“语句参数”列表。
  • ?,v(:var)和c(常量)参数的列表:%GetImplementationDetails()实例方法,如成功准备的结果中所述。
  • ?,v(:var)和c(常量)参数的ODBC数据类型:formalParameters属性。 %SQL.StatementMetadata%Display()实例方法“形式参数”列表。
  • 查询文本,其中显示以下参数:%GetImplementationDetails()实例方法,如成功准备结果中所述。

语句元数据%Display()方法列出了“语句参数”和“形式参数”。对于每个参数,它列出了顺序参数号,ODBC数据类型,精度,小数位数,该参数是否可为空(2表示始终提供一个值)及其对应的属性名称(colName)和列类型。

请注意,某些ODBC数据类型以负整数形式返回。

下面的示例按顺序返回每个查询参数(:var和常量)的ODBC数据类型。请注意,TOP参数以数据类型12(VARCHAR)而不是数据类型4(INTEGER)返回,因为可以指定TOP ALL

/// d ##class(PHA.TEST.SQL).MetaData6()
ClassMethod MetaData6()
{
	SET myquery = 4
	SET myquery(1) = "SELECT TOP ? Name,DOB,Age+10 "
	SET myquery(2) = "FROM Sample.Person"
	SET myquery(3) = "WHERE %ID BETWEEN :startid :endid AND DOB=?"
	SET myquery(4) = "ORDER BY $PIECE(Name,',',?)"
	SET tStatement = ##class(%SQL.Statement).%New()
	SET qStatus = tStatement.%Prepare(.myquery)
	IF qStatus'=1 {
		WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	SET prepmeta = tStatement.%Metadata
	WRITE "Number of ? parameters=",prepmeta.parameterCount,!
	SET formalobj = prepmeta.formalParameters
	SET i=1
	WHILE formalobj.GetAt(i) {
		SET prop=formalobj.GetAt(i)
		WRITE prop.colName," type= ",prop.ODBCType,!
		SET i=i+1
	}
	WRITE "End of metadata"
}

执行Execute之后,无法从查询结果集元数据中获取参数元数据。在结果集中,所有参数均已解析。因此parameterCount = 0formalParameters不包含任何数据。

Query结果集元数据

使用%SQL.Statement类执行Execute之后,可以通过调用返回结果集元数据:

  • %SQL.StatementResult类的属性。
  • %SQL.StatementResult%GetMetadata()方法,访问%SQL.StatementMetadata类属性。

%SQL.StatementResult属性

执行查询操作后,%SQL.StatementResult返回:

  • %StatementType属性返回与最近执行的SQL语句相对应的整数代码。以下是这些整数代码的部分列表:1 = SELECT; 2 = INSERT; 3 = UPDATE; 4 = DELETE or TRUNCATE TABLE; 9 = CREATE TABLE; 15 = CREATE INDEX; 45 = CALL.
  • %StatementTypeName计算的属性基于%StatementType返回最近执行的SQL语句的命令名称。此名称以大写字母返回。请注意,TRUNCATE TABLE操作将作为DELETE返回。即使执行了更新操作,INSERT OR UPDATE也将作为INSERT返回。
  • %ResultColumnCount属性返回结果集行中的列数。

下面的示例显示这些属性:

/// d ##class(PHA.TEST.SQL).MetaData7()
ClassMethod MetaData7()
{
	SET myquery = "SELECT TOP ? Name,DOB,Age FROM Sample.Person WHERE Age > ?"
	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(10,55)
	IF rset.%SQLCODE=0 {
		WRITE "Statement type=",rset.%StatementType,!
		WRITE "Statement name=",rset.%StatementTypeName,!
		WRITE "Column count=",rset.%ResultColumnCount,!
		WRITE "End of metadata" 
	} ELSE { 
		WRITE !,"SQLCODE=",rset.%SQLCODE," ",rset.%Message 
	}
}

%SQL.StatementResult %GetMetadata()

执行之后,可以使用%SQL.StatementResult %GetMetadata()方法访问%SQL.StatementMetadata类属性。这些是在Prepare之后由%SQL.Statement%Metadata属性访问的相同属性。

以下示例显示了属性:

/// d ##class(PHA.TEST.SQL).MetaData8()
ClassMethod MetaData8()
{
	SET myquery=2
	SET myquery(1)="SELECT Name AS VendorName,LastPayDate,MinPayment,NetDays,"
	SET myquery(2)="AVG(MinPayment),$HOROLOG,%TABLENAME FROM Sample.Vendor"
	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()
	IF rset.%SQLCODE=0 {
	SET rsmeta=rset.%GetMetadata()
	SET x=rsmeta.columns.Count()
	SET x=1
	WHILE rsmeta.columns.GetAt(x) {
		SET column=rsmeta.columns.GetAt(x)
		WRITE !,x," ",column.colName," is data type ",column.ODBCType
		WRITE " with a size of ",column.precision," and scale = ",column.scale
		SET x=x+1 }
	} ELSE { 
		WRITE !,"SQLCODE=",rset.%SQLCODE," ",rset.%Message 
	}
	WRITE !,"End of metadata"
}

请注意,结果集元数据不提供参数元数据。这是因为Execute操作会解析所有参数。因此,在结果集中,parameterCount = 0,而formalParameters不包含任何数据。

审核动态SQL

InterSystems IRIS支持动态SQL语句的可选审核。启用%System /%SQL / DynamicStatement系统审核事件时,将执行动态SQL审核。默认情况下,未启用此系统审核事件。

如果启用%System /%SQL / DynamicStatement,则系统将自动审核在系统范围内执行的每个%SQL.Statement动态语句。审核将信息记录在审核数据库中。

要查看审核数据库,请依次转到管理门户,系统管理,选择安全性,审核,然后查看审核数据库。可以将“事件名称”过滤器设置为DynamicStatement,以将View Audit Database限制为Dynamic SQL语句。审核数据库列出了时间(本地时间戳),用户,PID(进程ID)和事件的描述。说明指定动态SQL语句的类型。例如,SQL SELECT语句(%SQL.Statement)或SQL CREATE VIEW语句(%SQL.Statement)。

通过选择事件的详细信息链接,可以列出其他信息,包括事件数据。事件数据包括执行的SQL语句和该语句的任何参数的值。例如:

SELECT TOP ? Name , Age FROM Sample . MyTest WHERE Name %STARTSWITH ?
/*#OPTIONS {"DynamicSQLTypeList":",1"} */ 
Parameter values:
%CallArgs(1)=5 
%CallArgs(2)="Fred"

事件数据的总长度(包括语句和参数)为3,632,952个字符。如果该语句和参数长于3632952,则事件数据将被截断。

InterSystems IRIS还支持ODBC和JDBC语句的审核(事件名称= XDBCStatement),以及嵌入式SQL语句的审核(事件名称= EmbeddedStatement)。

0
0 224
文章 Qiao Peng · 三月 29, 2021 4m read

 

前面介绍了通过mirroring或shadow,使用journal日志过滤器的方式,在不改动数据模型的情况下实现InterSystems IRIS/Caché上的CDC能力。
但如果你可以修改InterSystems IRIS/Caché上的数据模型,也可以考虑使用DSTIME特性实现变更数据捕获。

DSTIME特性

DSTIME特性是InterSystems IRIS/Caché的嵌入式实时BI工具DeepSee用于跟踪数据变更的。
InterSystems IRIS和2011版之后的Caché,都支持DSTIME特性。它会自动记录数据库中SQL表记录或持久化对象的变更,并将变更记录写入持久化的多维数组^OBJ.DSTIME中。

DSTIME特性是针对于SQL表/持久化类的,因此需要为每个需要记录变化的表/持久化类开启。
当开启了DSTIME的表/持久化类,有记录插入、更新、删除时,InterSystems IRIS/Caché引擎会自动在^OBJ.DSTIME中记录这些操作。其格式为:
^OBJ.DSTIME(类名,DSTIME,对象ID) = 执行的操作代码

DSTIME:
当SQL表/持久化类的参数DSINTERVAL未被设置时,DSTIME=0;
当SQL表/持久化类的参数DSINTERVAL被设置时,DSTIME=1840/12/31午夜12点到记录发生时的秒数。

执行的操作代码

0
0 556
文章 Qiao Peng · 三月 29, 2021 8m read

 

一些熟悉SQL的用户希望用SQL表的方式获取InterSystems IRIS/Caché的变更数据。知道了Global和SQL表的对应关系,就可以知道是哪一张SQL表数据变化了,甚至通过SQL查询获取变更的数据。
下面介绍如何实现这种方式,和注意事项。

获取Global和SQL表的对应关系

通常InterSystems IRIS/Caché的持久化的对象模型(类)和SQL表之间有一一对应的关系;而持久化的对象模型和Global之间也有一一对应关系。建立Global和SQL表的对应关系,通常可以使用以下的SQL查询特定SQL schema下所有表对应的Global:

SELECT CC.SqlQualifiedNameQ SQLTable, CS.parent Class, CS.DataLocation 
FROM %Dictionary.CompiledStorage CS, %Dictionary.CompiledClass CC
WHERE CS.parent = CC.ID 
AND CC.SqlSchemaName= <schemaname>

其中<schemaname>是SQL的Schema名称; 返回字段SQLTable是SQL表名、Class是对象类名、DataLocation是保存数据的Global名称。

多种建模方式Global和SQL表的对应关系的影响

0
1 1008
文章 Qiao Peng · 三月 29, 2021 2m read

Caché Shadow

上次介绍了使用InterSystems IRIS/Caché的Reporting类型的异步镜像成员获取数据变更。但早期的Caché并不支持镜像,例如Caché 2008,它只支持Shadow。

在这些早期版本上是否也可以实利用Journal实现类似CDC的功能呢?答案是肯定的,就是利用Shadow服务器的Dejournaling filter routine (Dejournaling过滤器routine)。

Dejournaling filter routine

InterSystems Caché的 Dejournaling过滤器routine是一个用户自定义的routine,用于在Shadow服务器上过滤Journal记录。当配置了Dejournaling 过滤器后,Shadow服务器在redo journal前会自动调用Dejournaling过滤器routine。该routine传入参数以下:

  1. pid:记录进程ID;
  2. dir:源数据库目录;
  3. glo:Global节点(包括下标);
  4. type: 操作类型,为S (Set)、K (Kill)、s (BITSET)、k (ZKILL);
  5. addr:Journal记录在Journal文件中的偏移量;
  6. time:记录的时间戳。
0
0 585
文章 姚 鑫 · 三月 28, 2021 7m read

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

%ObjectSelectMode = 1 Swizzling字段名称属性

下面的示例使用%ObjectSelectMode = 1进行准备,当使用字段名称属性返回值时,其类型类别为可Swizzle类型的字段(持久性类,序列类或流类)将自动发生Swizzle。转换字段值的结果是相应的对象参考(oref)。使用%Get()%GetData()方法访问字段时,InterSystems IRIS不会执行此筛选操作。在此示例中,rset.Home处于Swizzle状态,而引用同一字段的rset.%GetData(2)处于not swizzled状态:

/// 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
}

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字段:

/// 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
}

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 FAIL>错误。如果引用的属性已从磁盘中意外删除或被另一个进程锁定,则会发生这种情况。要确定SWIZZLE失败的原因,请在<SWIZZLE FAIL>错误之后立即在%objlasterror中查找并解码此%Status值。

默认情况下,未配置<SWIZZLE FAIL>。可以通过设置SET ^%SYS("ThrowSwizzleError")=1或使用InterSystems IRIS管理门户来全局设置此行为。在“系统管理”中,选择“配置”,然后选择“ SQL和对象设置”,然后选择“对象”。在此屏幕上,可以设置<SWIZZLE FAIL>选项。

%Get("fieldname")方法

可以使用%Get(“ fieldname”)实例方法按字段名称或字段名称别名返回数据值。 Dynamic SQL根据需要解析字母大小写。如果指定的字段名称或字段名称别名不存在,系统将生成<PROPERTY DOES NOT EXIST>错误。

下面的示例从查询结果集中返回Home_State字段和Last_Name别名的值。

/// 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
}
DHC-APP>d ##class(PHA.TEST.SQL).PropSQL4()
WI : yaoxin
 : xiaoli
 : 姚鑫
 : 姚鑫
 : 姚鑫
 
End of data
Total row count=5

必须使用%Get("fieldname")实例方法从使用%PrepareClassQuery()准备的现有查询中按字段属性名称检索单个数据项。如果字段属性名称不存在,则系统会生成<PROPERTY DOES NOT EXIST>错误。

下面的示例从内置查询中按字段属性名称返回Nsp(命名空间)字段值。因为此查询是现有的存储查询,所以此字段检索需要使用%Get("fieldname") 方法。请注意,由于“Nsp”是属性名称,因此区分大小写:

/// 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
}
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相反,后者返回查询中指定的重复名称的第一个实例。在下面的示例中显示:

/// 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”)返回最后一个重复项。

/// 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
}
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()方法分别返回多个结果集:

/// 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"
}
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
0
0 191
文章 姚 鑫 · 三月 26, 2021 6m read

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

返回完整结果集

使用%Execute()%ExecDirect()执行语句将返回一个实现%SQL.StatementResult接口的对象。该对象可以是单一值,结果集或从CALL语句返回的上下文对象。

%Display()方法

可以通过调用%SQL.StatementResult类的%Display()实例方法来显示整个结果集(结果对象的内容),如以下示例所示:

  DO rset.%Display()

请注意,%Display()方法不会返回%Status值。

显示查询结果集时,%Display()通过显示行数来结束:“受影响的5行”。 (这是%Display()遍历结果集之后的%ROWCOUNT值。)请注意,%Display()不会在此行计数语句之后发出行返回。

%Display()有两个可选参数:

  • 分隔符:在数据列和数据标题之间插入的字符串。它出现在结果集列之间,紧靠标题或数据值之前。默认为无定界符。如果省略,请在“列对齐”标志之前指定一个占位符逗号。
  • 列对齐:整数标志,指定如何计算数据列和数据标题之间的空格。可用的选项有:
    • 0:结果集标题/数据列将根据标准定界符(选项卡)对齐。这是默认值。
    • 1:结果集标题/数据列将根据列标题和标准定界符(标签)的长度对齐。
    • 2:结果集标题/数据列将根据列数据属性的精度/长度和标准定界符(选项卡)进行对齐。

%DisplayFormatted()方法

可以通过调用%SQL.StatementResult类的%DisplayFormatted()实例方法,而不是调用%Display(),将结果集内容重新格式化并重定向到生成的文件。

可以通过指定字符串选项%DisplayFormatted(“HTML”)或相应的整数代码%DisplayFormatted(1)来指定结果集格式。可以使用以下格式:XML(整数代码0),HTML(整数代码1),PDF(整数代码2),TXT(整数代码99)或CSV(整数代码100)。 (请注意,CSV格式未实现为真正的逗号分隔值输出;相反,它使用制表符来分隔列。)TXT格式(整数代码99)以行数结尾(例如,“受影响的5行”) ”);其他格式不包括行数。 InterSystems IRIS生成指定类型的文件,并附加适当的文件扩展名。

可以指定或省略结果集文件名:

  • 如果指定一个目标文件(例如,%DisplayFormatted(99,"myresults")),则在当前命名空间的子目录的mgr目录中生成具有该名称和相应后缀(文件扩展名)的文件。 例如,C:\InterSystems\IRIS\mgr\user\myresults.txt. 如果具有该后缀的指定文件已经存在,则InterSystems IRIS将用新数据覆盖它。
  • 如果没有指定目标文件(例如,%DisplayFormatted(99),则在Temp子目录的mgr目录中生成一个具有随机生成的名称和适当后缀(文件扩展名)的文件。 例如,C:\InterSystems\IRIS\mgr\Temp\w4FR2gM7tX2Fjs.txt. 每次运行一个查询时,都会生成一个新的目标文件。

这些例子显示了Windows文件名; InterSystems IRIS支持其他操作系统上的等效位置。

如果无法打开指定的文件,则此操作将在30秒后超时并显示一条错误消息;否则,该操作将超时。当用户没有对指定目录(文件夹)的WRITE权限时,通常会发生这种情况。

如果无法以指定的格式呈现数据,则将创建目标文件,但不会将结果集数据写入其中。而是将适当的消息写入目标文件。例如,流字段OID包含与XML和HTML特殊格式字符冲突的字符。可以通过在流字段上使用XMLELEMENT函数来解决此XML和HTML流字段问题。例如SELECT Name,XMLELEMENT(“ Para”,Notes)

可以选择提供%DisplayFormatted()在执行指定格式转换时将使用的转换表的名称。

如果一个结果集序列中有多个结果集,则每个结果集的内容都将写入其自己的文件中。

可选的第三个%DisplayFormatted()参数指定消息存储在单独的结果集中。成功完成后,将返回类似以下的消息:

Message
21 row(s) affected.

下面的Windows示例在C:\InterSystems\IRIS\mgr\user\中创建了两个PDF(整数代码2)结果集文件。 它为消息创建一个mess结果集,然后使用%Display()将消息显示到终端:

/// d ##class(PHA.TEST.SQL).CreatePDF()
ClassMethod CreatePDF()
{

	SET myquery=2
	SET myquery(1)="SELECT Name,Age FROM Sample.Person"
	SET myquery(2)="WHERE Age > ? AND Age < ? ORDER BY Age"
	SET rset = ##class(%SQL.Statement).%ExecDirect(,.myquery,12,20)
	IF rset.%SQLCODE'=0 {
		WRITE !,"1st ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message  QUIT
	}
	DO rset.%DisplayFormatted(2,"Teenagers",.mess)
	DO mess.%Display()
	WRITE !,"End of teen data",!!
	SET rset2 = ##class(%SQL.Statement).%ExecDirect(,.myquery,19,30)
	IF rset2.%SQLCODE'=0 {
		WRITE !,"2nd ExecDirect SQLCODE=",rset2.%SQLCODE,!,rset2.%Message  QUIT
	}
	DO rset2.%DisplayFormatted(2,"Twenties",.mess)
	DO mess.%Display()
	WRITE !,"End of twenties data"
}
DHC-APP>d ##class(PHA.TEST.SQL).CreatePDF()
Message
9 row(s) affected.
 
End of teen data
 
Message
20 row(s) affected.
 
End of twenties data
/// d ##class(PHA.TEST.SQL).CreatePDF1()
ClassMethod CreatePDF1()
{
	  ZNSPACE "SAMPLES"
  SET myquery=2
  SET myquery(1)="SELECT Name,Age FROM Sample.Person"
  SET myquery(2)="WHERE Age > ? AND Age < ? ORDER BY Age"
  SET rset = ##class(%SQL.Statement).%ExecDirect(,.myquery,12,20)
  DO rset.%DisplayFormatted(2,"Teenagers")
  WRITE !,"End of teen data",!!
  SET rset2 = ##class(%SQL.Statement).%ExecDirect(,.myquery,19,30)
  DO rset2.%DisplayFormatted(2,"Twenties")
  WRITE !,"End of twenties data"
}
DHC-APP>d ##class(PHA.TEST.SQL).CreatePDF1()
 
End of teen data
 
 
End of twenties data

对结果集进行分页

可以使用一个视图ID (%VID)来分页结果集。下面的例子从结果集中返回页面,每个页面包含5行:

/// d ##class(PHA.TEST.SQL).Paginating()
ClassMethod Paginating()
{
	SET q1="SELECT %VID AS RSRow,* FROM "
	SET q2="(SELECT Name,Home_State FROM Sample.Person WHERE Home_State %STARTSWITH 'M') "
	SET q3="WHERE %VID BETWEEN ? AND ?"
	SET myquery = q1_q2_q3
	SET tStatement = ##class(%SQL.Statement).%New()
	SET qStatus=tStatement.%Prepare(myquery)
	IF qStatus'=1 {
		WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	FOR i=1:5:25 {
		WRITE !!,"Next Page",!
		SET rset=tStatement.%Execute(i,i+4)
		DO rset.%Display()
	}
}
DHC-APP>d ##class(PHA.TEST.SQL).Paginating()
 
 
Next Page
RSRow   Name    Home_State
1       O'Rielly,Chris H.       MS
2       Orwell,John V.  MT
3       Zevon,Heloisa O.        MI
4       Kratzmann,Emily Z.      MO
5       King,Dmitry G.  MO
 
5 Rows(s) Affected
 
Next Page
RSRow   Name    Home_State
6       Hanson,George C.        MD
7       Martinez,Emilio G.      MO
8       Cheng,Charlotte Y.      MI
9       Emerson,Edgar T.        MO
10      Nelson,Neil E.  MT
 
5 Rows(s) Affected
 
Next Page
RSRow   Name    Home_State
11      Larson,Nataliya Z.      MD
12      Lennon,Chelsea T.       MD
13      Ingleman,Kristen U.     MT
14      Zucherro,Olga H.        MN
15      Ng,Lola H.      MD
 
5 Rows(s) Affected
 
Next Page
RSRow   Name    Home_State
16      Frost,Xavier D. MO
17      Adams,Diane F.  MD
18      Isaacs,Chad N.  MN
19      Van De Griek,Phil S.    MS
20      Schaefer,Usha G.        MO
 
5 Rows(s) Affected
 
Next Page
RSRow   Name    Home_State
21      Wells,Sophia U. MS
22      Vivaldi,Michelle N.     MD
23      Anderson,Valery N.      MD
24      Frost,Heloisa K.        MI
25      Gallant,Thelma Q.       MA
 
5 Rows(s) Affected
0
0 278
文章 姚 鑫 · 三月 25, 2021 11m read

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

执行SQL语句

有两种使用%SQL.Statement类执行SQL语句的方法:

  • %Execute(),它执行以前使用%Prepare()%PrepareClassQuery()准备的SQL语句。
  • %ExecDirect(),它同时准备和执行一条SQL语句。

也可以通过使用$SYSTEM.SQL.Execute()方法执行SQL语句而无需创建对象实例。此方法既准备又执行SQL语句。它创建一个缓存的查询。下面的终端示例显示Execute()方法:

USER>SET topnum=5
USER>SET rset=$SYSTEM.SQL.Execute("SELECT TOP :topnum Name,Age FROM Sample.Person")
USER>DO rset.%Display()

%Execute()

准备查询后,可以通过调用%SQL.Statement类的%Execute()实例方法来执行查询。对于非SELECT语句,%Execute()调用所需的操作(例如执行INSERT)。对于SELECT查询,%Execute()生成一个结果集,用于后续遍历和数据检索。例如:

  SET rset = tStatement.%Execute()

%Execute()方法为所有SQL语句设置%SQL.StatementResult类属性%SQLCODE%Message%Execute()设置其他%SQL.StatementResult属性,如下所示:

  • INSERTUPDATEINSERTUPDATEDELETETRUNCATE TABLE语句将%ROWCOUNT设置为受操作影响的行数。 TRUNCATE TABLE无法确定删除的实际行数,因此将%ROWCOUNT设置为-1。

INSERTUPDATEINSERT OR UPDATEDELETE%ROWID设置为最后一条插入,更新或删除的记录的RowID值。如果该操作未插入,更新或删除任何记录,则%ROWID是未定义的,或保持设置为其先前值。 TRUNCATE TABLE没有设置%ROWID

  • SELECT语句在创建结果集时会将%ROWCOUNT属性设置为0。当程序遍历结果集的内容(例如,使用%Next()方法)时,%ROWCOUNT会增加。 %Next()返回1表示它位于一行上,返回0表示它位于最后一行之后(在结果集的末尾)。如果光标位于最后一行之后,则%ROWCOUNT的值指示结果集中包含的行数。

如果SELECT查询仅返回聚合函数,则每个%Next()都将设置%ROWCOUNT = 1。即使表中没有数据,第一个%Next()始终设置%SQLCODE = 0。任何后续的%Next()都会设置%SQLCODE = 100并设置%ROWCOUNT = 1

SELECT还设置%CurrentResult%ResultColumnCountSELECT未设置%ROWID

可以使用ZWRITE返回所有%SQL.StatementResult类属性的值。

具有输入参数的%Execute()

%Execute()方法可以采用一个或多个与准备的SQL语句中的输入参数(以“?”表示)相对应的参数。 %Execute()参数对应于“?”的顺序字符出现在SQL语句中:第一个参数用于第一个“?”,第二个参数用于第二个“?”,依此类推。多个%Execute()参数以逗号分隔。可以通过指定占位符逗号来省略参数值。 %Execute()参数的数量必须与“?”相对应输入参数。如果%Execute()参数少于或大于相应的“?”输入参数,执行失败,并且%SQLCODE属性设置为SQLCODE -400错误。

可以使用输入参数为SELECT列表和其他查询子句(包括TOP子句和WHERE子句)提供文字值或表达式。不能使用输入参数为SELECT列表或其他查询子句提供列名或列名别名。

当指定为显式%Execute()参数时,最大输入参数数为255。使用可变长度数组%Execute(vals ...)指定时,最大输入参数数为380。

在执行Prepare之后,可以使用Prepare参数元数据来返回的计数和所需的数据类型。输入参数。可以使用%GetImplementationDetails()方法返回的列表。在准备好的查询中输入参数,并在查询文本中使用输入参数显示在上下文中。

以下ObjectScript示例使用两个输入参数执行查询。它在%Execute()方法中指定输入参数值(21和26)。

/// d ##class(PHA.TEST.SQL).PrepareClassQuery7()
ClassMethod PrepareClassQuery7()
{
	SET tStatement = ##class(%SQL.Statement).%New(1)
	SET tStatement.%SchemaPath = "MyTests,Sample,Cinema"
	SET myquery=2
	SET myquery(1)="SELECT Name,DOB,Age FROM Person"
	SET myquery(2)="WHERE Age > ? AND Age < ? ORDER BY Age"
	SET qStatus = tStatement.%Prepare(.myquery)
	IF qStatus'=1 {
		WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	SET rset = tStatement.%Execute(21,26)
	WRITE !,"Execute OK: SQLCODE=",rset.%SQLCODE,!!
	DO rset.%Display()
	WRITE !,"End of data: SQLCODE=",rset.%SQLCODE
}
DHC-APP>d ##class(PHA.TEST.SQL).PrepareClassQuery7()
 
Execute OK: SQLCODE=0
 
Name    DOB     Age
Van De Griek,Dick U.    1998-12-21      22
Peterson,Kirsten R.     1997-12-13      23
Van De Griek,Phil S.    1996-09-26      24
Wijnschenk,Lydia G.     1997-01-17      24
Xiang,Kirsten U.        1996-08-06      24
Schaefer,Usha G.        1995-09-16      25
Peterson,Sophia A.      1995-12-05      25
Petersburg,Bill O.      1995-10-23      25
 
8 Rows(s) Affected
End of data: SQLCODE=100

下面的ObjectScript示例执行相同的查询。 %Execute()方法形式参数列表使用可变长度数组(dynd ...)指定不确定数量的输入参数值;在这种情况下,为dynd数组的下标。 dynd变量设置为2以指示两个下标值。

/// d ##class(PHA.TEST.SQL).PrepareClassQuery8()
ClassMethod PrepareClassQuery8()
{
	SET tStatement = ##class(%SQL.Statement).%New(1)
	SET tStatement.%SchemaPath = "MyTests,Sample,Cinema"
	SET myquery=2
	SET myquery(1)="SELECT Name,DOB,Age FROM Person"
	SET myquery(2)="WHERE Age > ? AND Age < ? ORDER BY Age"
	SET dynd=2,dynd(1)=21,dynd(2)=26
	SET qStatus = tStatement.%Prepare(.myquery)
	IF qStatus'=1 {
		WRITE "%Prepare failed:" 
		DO $System.Status.DisplayError(qStatus) 
		QUIT
	}
	SET rset = tStatement.%Execute(dynd...)
	WRITE !,"Execute OK: SQLCODE=",rset.%SQLCODE,!!
	DO rset.%Display()
	WRITE !,"End of data: SQLCODE=",rset.%SQLCODE
}
DHC-APP>d ##class(PHA.TEST.SQL).PrepareClassQuery8()
 
Execute OK: SQLCODE=0
 
Name    DOB     Age
Van De Griek,Dick U.    1998-12-21      22
Peterson,Kirsten R.     1997-12-13      23
Van De Griek,Phil S.    1996-09-26      24
Wijnschenk,Lydia G.     1997-01-17      24
Xiang,Kirsten U.        1996-08-06      24
Schaefer,Usha G.        1995-09-16      25
Peterson,Sophia A.      1995-12-05      25
Petersburg,Bill O.      1995-10-23      25
 
8 Rows(s) Affected
End of data: SQLCODE=100

可以对准备好的结果集执行多个%Execute()操作。这使可以多次运行查询,并提供不同的输入参数值。不必在%Execute()操作之间关闭结果集,如以下示例所示:

/// d ##class(PHA.TEST.SQL).PrepareClassQuery9()
ClassMethod PrepareClassQuery9()
{
	SET myquery="SELECT Name,SSN,Age FROM Sample.Person WHERE Name %STARTSWITH ?"
	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("A")
	DO rset.%Display()
	WRITE !,"End of A data",!!
	SET rset = tStatement.%Execute("B")
	DO rset.%Display()
	WRITE !,"End of B data"
}

DHC-APP>d ##class(PHA.TEST.SQL).PrepareClassQuery9()
Name    SSN     Age
Alton,Martin S. 624-25-8488     47
Ahmed,Elmo X.   950-40-6135     77
Anderson,Mario L.       604-10-9256     77
Adams,Diane F.  640-77-5933     9
Anderson,Valery N.      882-50-4971     27
Alton,Phil T.   785-37-8519     68
Adams,Susan E.  947-66-8684     52
 
7 Rows(s) Affected
End of A data
 
Name    SSN     Age
Bukowski,Mario V.       683-32-4214     85
Bachman,Susan O.        102-59-3932     88
Bush,Jules K.   547-97-7915     13
Basile,Filomena X.      888-66-1725     86
Browne,Robert X.        308-58-1444     82
Burroughs,Barbara H.    627-56-2213     86
Beatty,Molly Z. 794-64-5615     54
 
7 Rows(s) Affected
End of B data

使用TRY / CATCH处理%Execute错误

可以在TRY块结构内执行Dynamic SQL,将运行时错误传递给关联的CATCH块异常处理程序。对于%Execute()错误,可以使用%Exception.SQL类创建一个异常实例,然后将其扔到CATCH异常处理程序中。

下面的示例在发生%Execute()错误时创建一个SQL异常实例。在这种情况下,错误是数量之间的基数不匹配。输入参数(1)和%Execute()参数的数量(3)。它将%SQLCODE%Message属性值(作为CodeData)抛出到CATCH异常处理程序中。异常处理程序使用%IsA()实例方法测试异常类型,然后显示%Execute()错误:

/// d ##class(PHA.TEST.SQL).SQLTRY()
ClassMethod SQLTRY()
{
	TRY {
		SET myquery = "SELECT TOP ? Name,DOB 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(7,9,4)
		IF rset.%SQLCODE=0 { 
			WRITE !,"Executed query",! 
		} ELSE { 
			SET badSQL=##class(%Exception.SQL).%New(,rset.%SQLCODE,,rset.%Message)
			THROW badSQL 
		}
		DO rset.%Display()
		WRITE !,"End of data"
		RETURN
	}
	CATCH exp { 
		WRITE "In the CATCH block",!
		IF 1=exp.%IsA("%Exception.SQL") {
		WRITE "SQLCODE: ",exp.Code,!
		WRITE "Message: ",exp.Data,! }
		ELSE { WRITE "Not an SQL exception",! }
		RETURN
	}
}
DHC-APP>d ##class(PHA.TEST.SQL).SQLTRY()
In the CATCH block
SQLCODE: -400
Message: Dynamic SQL Execute, more parameter values passed than are specified in the dynamic statement

%ExecDirect()

%SQL.Statement类提供%ExecDirect()类方法,该方法可以在单个操作中准备和执行查询。它可以准备指定的查询(如%Prepare())或现有的类查询(如%PrepareClassQuery())。

%ExecDirect()准备并执行指定的查询:

/// d ##class(PHA.TEST.SQL).ExecDirect()
ClassMethod ExecDirect()
{
	SET myquery=2
	SET myquery(1)="SELECT Name,Age FROM Sample.Person"
	SET myquery(2)="WHERE Age > 21 AND Age < 30 ORDER BY Age"
	SET rset = ##class(%SQL.Statement).%ExecDirect(,.myquery)
	IF rset.%SQLCODE=0 { 
		WRITE !,"ExecDirect OK",!! 
	} ELSE { 
		WRITE !,"ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message  QUIT
	}
	DO rset.%Display()
	WRITE !,"End of data: SQLCODE=",rset.%SQLCODE
}

DHC-APP> d ##class(PHA.TEST.SQL).ExecDirect()
 
ExecDirect OK
 
Name    Age
Van De Griek,Dick U.    22
Peterson,Kirsten R.     23
Van De Griek,Phil S.    24
Wijnschenk,Lydia G.     24
Xiang,Kirsten U.        24
Schaefer,Usha G.        25
Peterson,Sophia A.      25
Petersburg,Bill O.      25
Ng,Josephine Z. 26
Munt,Valery W.  26
Ingleman,Martin T.      26
Eno,Diane U.    26
Pascal,Kim P.   27
Ipsen,Jane A.   27
Anderson,Valery N.      27
Gomez,Mo Q.     27
Xerxes,Angelo P.        28
Young,Barbara N.        29
 
18 Rows(s) Affected
End of data: SQLCODE=100

%ExecDirect()准备并执行现有的类查询:

/// d ##class(PHA.TEST.SQL).ExecDirect1()
ClassMethod ExecDirect1()
{
	SET mycallq = "?=CALL Sample.PersonSets('A','NH')" 
	SET rset = ##class(%SQL.Statement).%ExecDirect(,mycallq)
	IF rset.%SQLCODE=0 { 
		WRITE !,"ExecDirect OK",!! 
	} ELSE { 
		WRITE !,"ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message  QUIT
	}
	DO rset.%Display()
	WRITE !,"End of data: SQLCODE=",rset.%SQLCODE
}

可以将输入参数值指定为%ExecDirect()类方法的第三个参数和后续参数,如以下示例所示:

/// d ##class(PHA.TEST.SQL).ExecDirect2()
ClassMethod ExecDirect2()
{
	SET myquery=2
	SET myquery(1)="SELECT Name,Age FROM Sample.Person"
	SET myquery(2)="WHERE Age > ? AND Age < ? ORDER BY Age"
	SET rset = ##class(%SQL.Statement).%ExecDirect(,.myquery,12,20)
	IF rset.%SQLCODE'=0 {
		WRITE !,"1st ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message  QUIT
	}
	DO rset.%Display()
	WRITE !,"End of teen data",!!
	SET rset2 = ##class(%SQL.Statement).%ExecDirect(,.myquery,19,30)
	IF rset2.%SQLCODE'=0 {
		WRITE !,"2nd ExecDirect SQLCODE=",rset2.%SQLCODE,!,rset2.%Message  QUIT
	}
	DO rset2.%Display()
	WRITE !,"End of twenties data"
}
DHC-APP> d ##class(PHA.TEST.SQL).ExecDirect2()
Name    Age
Bush,Jules K.   13
...
Eastman,Howard K.       18
 
9 Rows(s) Affected
End of teen data
 
Name    Age
Ingrahm,Susan N.        20
...
Young,Barbara N.        29
 
20 Rows(s) Affected
End of twenties data

%ExecDirect()输入参数对应于“?”的顺序字符出现在SQL语句中:第三个参数用于第一个“?”,第四个参数用于第二个“?”,依此类推。可以通过指定占位符逗号来省略参数值。如果%ExecDirect()输入参数少于相应的“?”输入参数,则使用默认值(如果存在)。

在下面的示例中,第一个%ExecDirect()指定所有三个“?”输入参数,第二个%ExecDirect()仅指定第二个输入参数,并省略第一个和第三个。它使用第三个输入参数的默认Sample.PersonSets()('MA')

/// d ##class(PHA.TEST.SQL).ExecDirect3()
ClassMethod ExecDirect3()
{
	SET mycall = "?=CALL Sample.PersonSets(?,?)"
	SET rset = ##class(%SQL.Statement).%ExecDirect(,mycall,"","A","NH")
	IF rset.%SQLCODE'=0 {WRITE !,"1st ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message  QUIT}
	DO rset.%Display()
	WRITE !,"End of A people data",!!
	SET rset2 = ##class(%SQL.Statement).%ExecDirect(,mycall,,"B")
	IF rset2.%SQLCODE'=0 {WRITE !,"2nd ExecDirect SQLCODE=",rset2.%SQLCODE,!,rset2.%Message  QUIT}
	DO rset2.%Display()
	WRITE !,"End of B people data"
}
DHC-APP>d ##class(PHA.TEST.SQL).ExecDirect3()
 
 
Output Values:
 
 0. 1
 
Dumping result #1
Name    DOB     Spouse
...
 
1 Rows(s) Affected
End of B people data

%ExecDirect()可以调用%SQL.Statement%Display()实例方法或%GetImplementationDetails()实例方法以返回当前准备好的语句的详细信息。因为%ExecDirect()可以准备并执行指定的查询或现有的类查询,所以可以使用%GetImplementationDetails()pStatementType参数来确定准备哪种查询:

/// d ##class(PHA.TEST.SQL).ExecDirect4()
ClassMethod ExecDirect4()
{
	SET mycall = "?=CALL Sample.PersonSets('A',?)"
	SET rset = ##class(%SQL.Statement).%ExecDirect(tStatement,mycall,,"NH")
	IF rset.%SQLCODE'=0 {
		WRITE !,"ExecDirect SQLCODE=",rset.%SQLCODE,!,rset.%Message  QUIT
	}
	SET bool = tStatement.%GetImplementationDetails(.pclassname,.ptext,.pargs,.pStatementType)
	IF bool=1 {
		IF pStatementType=1 {WRITE "Type= specified query",!
	} ELSEIF pStatementType=45 {
		WRITE "Type= existing class query",!
	}
	WRITE "Implementation class= ",pclassname,!
	WRITE "Statement text= ",ptext,!
	WRITE "Arguments= ",$LISTTOSTRING(pargs),!!  }
	ELSE {WRITE "%GetImplementationDetails() failed"}
	DO rset.%Display()
	WRITE !,"End of data"
}
0
0 188
文章 姚 鑫 · 三月 23, 2021 12m read

第十三章 使用动态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查询:

/// 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"
}
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查询和/或INSERTUPDATEDELETE操作。

%New()按以下顺序接受三个可选的逗号分隔参数:

  1. %SelectMode,它指定用于数据输入和数据显示的模式。
  2. %SchemaPath,它指定用于为无限定的表名提供架构名称的搜索路径。
  3. %Dialect,它指定Transact-SQL(TSQL)Sybase或MSSQL方言。默认值为IRIS(InterSystems SQL)。

还有一个%ObjectSelectMode属性,不能将其设置为%New()参数。 %ObjectSelectMode指定字段到其相关对象属性的数据类型绑定。

在下面的ObjectScript示例中,%SelectMode为2(显示模式),%SchemaPath“Sample”指定为默认架构:

  SET tStatement = ##class(%SQL.Statement).%New(2,"Sample")

在下面的ObjectScript示例中,未指定%SelectMode(请注意占位符逗号),并且%SchemaPath指定包含三个架构名称的架构搜索路径:

  SET tStatement = ##class(%SQL.Statement).%New(,"MyTests,Sample,Cinema")

%SelectMode属性

%SelectMode属性指定以下模式之一:0 =Logical逻辑(默认)1 = ODBC2 =Display.显示。这些模式指定如何输入和显示数据值。模式最常用于日期和时间值以及显示%List数据(包含编码列表的字符串)。数据以逻辑模式存储。

SELECT查询使用%SelectMode值确定用于显示数据的格式。

INSERTUPDATE操作使用%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参数。
  • 对于INSERTUPDATE操作,%SelectMode指定将转换为逻辑存储格式的输入数据的格式。为了进行此数据转换,必须使用RUNTIME(默认)的选择模式编译SQL代码,以便在执行INSERTUPDATE时使用DisplayODBC %SelectMode。有关日期和时间的允许输入值,请参考日期和时间数据类型。

可以将%SelectMode指定为%New()类方法的第一个参数,或直接对其进行设置,如以下两个示例所示:

  SET tStatement = ##class(%SQL.Statement).%New(2)
  SET tStatement = ##class(%SQL.Statement).%New()
  SET tStatement.%SelectMode=2

下面的示例返回%SelectMode的当前值:


/// d ##class(PHA.TEST.SQL).DynamicSQL1()
ClassMethod DynamicSQL1()
{
	SET tStatement = ##class(%SQL.Statement).%New()
	WRITE !,"默认选择模式=",tStatement.%SelectMode
	SET tStatement.%SelectMode=2
	WRITE !,"设置选择模式=",tStatement.%SelectMode
}
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL1()
 
默认选择模式=0
设置选择模式=2

可以使用$SYSTEM.SQL.Util.GetOption("SelectMode") 方法为当前进程确定SelectMode默认设置。当n可以为0 =逻辑1 = ODBC2 = Display时,可以使用$SYSTEM.SQL.Util.SetOption("SelectMode",n) 方法来更改当前进程的SelectMode默认设置。设置%SelectMode会覆盖当前对象实例的默认设置。它不会更改SelectMode进程的默认值。

%SchemaPath属性

%SchemaPath属性指定用于为非限定的表名,视图名或存储过程名提供架构名的搜索路径。模式搜索路径用于数据管理操作,例如SELECTCALLINSERTTRUNCATE TABLE;数据定义操作(例如DROP TABLE)将忽略它。

搜索路径被指定为带引号的字符串,其中包含模式名称或逗号分隔的一系列模式名称。 InterSystems IRIS以从左到右的顺序搜索列出的模式。 InterSystems IRIS会搜索每个指定的架构,直到找到第一个匹配的表,视图或存储过程名称。因为模式是按指定顺序搜索的,所以不会检测到歧义的表名。仅搜索当前名称空间中的架构名称。

模式搜索路径可以包含文字模式名称以及CURRENT_PATHCURRENT_SCHEMADEFAULT_SCHEMA关键字。

  • CURRENT_PATH指定当前模式搜索路径,如先前的%SchemaPath属性中所定义。这通常用于将架构添加到现有架构搜索路径的开头或结尾。
  • 如果%SQL.Statement调用是从类方法中进行的,则CURRENT_SCHEMA指定当前模式容器的类名称。如果在类方法中定义了#SQLCompile Path宏指令,则CURRENT_SCHEMA是映射到当前类包的架构。否则,CURRENT_SCHEMADEFAULT_SCHEMA相同。
  • DEFAULT_SCHEMA指定系统范围的默认架构。使用此关键字,可以在搜索其他列出的架构之前,在架构搜索路径中将系统范围的默认架构作为一个项目进行搜索。如果已经搜索了路径中指定的所有模式而没有匹配项,则在搜索模式搜索路径后始终会搜索系统范围内的默认模式。

%SchemaPath是InterSystems IRIS在架构中搜索匹配表名的第一位。如果未指定%SchemaPath,或者未列出包含匹配表名的架构,则InterSystems IRIS将使用系统范围的默认架构。

可以通过指定%SchemaPath属性或指定%New()类方法的第二个参数来指定模式搜索路径,如以下两个示例所示:

  SET path="MyTests,Sample,Cinema"
  SET tStatement = ##class(%SQL.Statement).%New(,path)
  SET tStatement = ##class(%SQL.Statement).%New()
  SET tStatement.%SchemaPath="MyTests,Sample,Cinema"

可以在使用它的%Prepare()方法之前的任何位置设置%SchemaPath

下面的示例返回%SchemaPath的当前值:

/// 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
}

DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL2()
 
默认 path=
设置 path=MyTests,Sample,Cinema

可以使用%ClassPath()方法将%SchemaPath设置为为指定的类名定义的搜索路径:

/// d ##class(PHA.TEST.SQL).DynamicSQL3()
ClassMethod DynamicSQL3()
{
	SET tStatement = ##class(%SQL.Statement).%New()
	SET tStatement.%SchemaPath=tStatement.%ClassPath("Sample.Person")
	WRITE tStatement.%SchemaPath
}
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语句的有限子集。它们支持SELECTINSERTUPDATEDELETEEXECUTE语句。他们支持CREATE TABLE语句用于永久表,但不支持临时表。支持创建视图。支持CREATE TRIGGERDROP TRIGGER。但是,如果CREATE TRIGGER语句部分成功,但是在类编译时失败,则此实现不支持事务回滚。支持CREATE PROCEDURECREATE FUNCTION

Sybase和MSSQL方言支持IF控制流语句。 IRIS(InterSystems SQL)方言不支持此命令。

默认值为InterSystems SQL,由空字符串(“”)表示,或指定为“ IRIS”

可以将%Dialect指定为%New()类方法的第三个参数,或者将其直接设置为属性,或者使用方法进行设置,如以下三个示例所示:

%New()类方法中设置%Dialect

/// d ##class(PHA.TEST.SQL).DynamicSQL4()
ClassMethod DynamicSQL4()
{
	SET tStatement = ##class(%SQL.Statement).%New(,,"Sybase")
	WRITE "语言模式设置为=",tStatement.%Dialect
}
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL4()
语言模式设置为=Sybase

直接设置%Dialect属性:

/// 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,!
}
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL5()
默认语言模式=
语言模式设置为=Sybase
语言模式重置为默认=iris

使用%DialectSet()实例方法设置%Dialect属性,该方法将返回错误状态:

/// 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
}
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值:

/// 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
}
DHC-APP>d ##class(PHA.TEST.SQL).DynamicSQL7()
 
默认 ObjectSelectMode=0
语言 ObjectSelectMode=1

当使用字段名称属性从结果集中返回值时,主要使用%ObjectSelectMode = 1。本章“从结果集中返回特定值”部分的字段名属性中的示例对此进行了进一步说明。

SELECT列表中的字段链接到集合属性时,可以使用%ObjectSelectMode = 1%ObjectSelectMode将使集合swizzle。如果%SelectMode = 1或2,则系统在转换前将收集序列值转换为逻辑模式形式。生成的oref支持完整的收集接口。

0
0 596
文章 姚 鑫 · 三月 22, 2021 1m read

第十二章 使用嵌入式SQL(六)

持久类方法中的嵌入式SQL

下面的示例显示了一个持久类,其中包含一个类方法和一个实例方法,两者都包含嵌入式SQL:

Class Sample.MyClass Extends %Persistent [DdlAllowed]
 { 
 ClassMethod NameInitial(Myval As %String) As %String [SqlProc]
  {
     &sql(SELECT Name INTO :n FROM Sample.Stuff WHERE Name %STARTSWITH :Myval)
        IF SQLCODE<0 {WRITE "SQLCODE error ",SQLCODE  RETURN %msg}
        ELSEIF SQLCODE=100 {WRITE "Query returns no results"  RETURN}
   WRITE "Hello "  RETURN n
  }
 Method CountRows() As %Integer
  {
   &sql(SELECT COUNT(*) INTO :count FROM Sample.Stuff)
        IF SQLCODE<0 {WRITE "SQLCODE error ",SQLCODE  RETURN %msg}
        ELSEIF SQLCODE=100 {WRITE "Query returns no results"  RETURN}
   WRITE "Number of rows is "  RETURN count
  }
 }

类方法的调用如下:

 WRITE ##class(Sample.MyClass).NameInitial("G")

实例方法的调用如下:

  SET x=##class(Sample.MyClass).%New()
  WRITE x.CountRows()

要成功编译这些方法,不必存在诸如表和字段之类的SQL实体。由于检查SQL实体是否存在是在运行时执行的,因此嵌入式SQL方法应包含SQLCODE测试逻辑。

以测试嵌入式SQL中指定的SQL实体是否存在,而无需执行代码。验证嵌入式SQL代码中对此进行了描述。

验证嵌入式SQL代码

可以通过两种方式验证嵌入式SQL代码而无需执行代码:

  • 使用/compileembedded = 1限定符编译包含嵌入式SQL代码的例程。
  • 使用$SYSTEM.OBJ.GenerateEmbedded()方法编译多个嵌入式SQL例程。
  • 使用管理门户网站SQL界面的“显示计划”选项测试嵌入式SQL代码。

使用/compileembedded限定符进行编译

可以通过使用$SYSTEM.OBJ类的编译类方法并在qspec参数中指定/ compileembedded = 1限定符来验证嵌入式SQL代码。

  • $SYSTEM.OBJ.Compile()编译指定的类以及该类中的所有例程。
  • $SYSTEM.OBJ.CompileList()编译指定类的列表以及这些类中的所有例程。
  • $SYSTEM.OBJ.CompilePackage()编译指定包(架构)中的所有类/例程。
  • $SYSTEM.OBJ.CompileAll()编译当前名称空间中的所有类/例程。
  • $SYSTEM.OBJ.CompileAllNamespaces()编译所有命名空间中的所有类/例程。

要显示qspec限定词(包括/compileembedded)的列表,请调用:

  DO $SYSTEM.OBJ.ShowQualifiers()

使用Show Plan进行测试

可以使用Management Portal SQL界面来验证嵌入式SQL代码,而无需执行该代码。此操作既可以验证SQL语法,也可以检查指定的SQL实体是否存在。

从Management Portal System Explorer选项中,选择SQL选项以显示Execute Query code区域。

  1. 输入嵌入式SQL查询。例如,SELECT Name INTO:n from Sample.MyTest或DECLARE MyCursor CURSOR FOR SELECT Name,Age INTO:n,:a FROM Sample.MyTest,age> 21,仅供只读。
  2. 按下显示计划按钮以检查代码。如果代码有效,则“显示计划”将显示一个查询计划。如果代码无效,则“显示计划”将显示SQLCODE错误值和消息。

image

请注意,如果缺少INTO子句,Show Plan验证将不会发出错误,因为可以在FETCH语句中指定INTO子句。如果INTO子句包含错误或位于错误的位置,则Show Plan将发出适当的错误。

不能使用“执行”按钮执行嵌入式SQL代码。

审核嵌入式SQL

InterSystems IRIS支持对嵌入式SQL语句的可选审核。满足以下两个要求时,将执行嵌入式SQL审核:

  1. %System /%SQL / EmbeddedStatement系统审核事件在系统范围内启用。默认情况下,未启用此系统审核事件。要启用,请依次转到管理门户,系统管理,安全性,审核,然后配置系统事件。
  2. 包含嵌入式SQL语句的例程必须包含#SQLCompile Audit宏预处理程序指令。如果此伪指令设置为ON,则在执行时将审核编译例程中跟在其后的任何嵌入式SQL语句。

审核将信息记录在审核数据库中。要查看审核数据库,请依次转到管理门户,系统管理,选择安全性,审核,然后查看审核数据库。可以将“事件名称”过滤器设置为Embedded Statement,以将“查看审核数据库”限制为“嵌入式SQL”语句。审核数据库列出了时间(本地时间戳记),用户,PID(进程ID)和描述,它们指定了嵌入式SQL语句的类型。例如,SQL SELECT语句。

image

通过选择事件的详细信息链接,可以列出其他信息,包括事件数据。事件数据包括执行的SQL语句和该语句的任何输入参数的值。例如:

SELECT TOP :n Name,ColorPreference INTO :name,:color FROM Sample.Stuff WHERE Name %STARTSWITH :letter 
Parameter values: 
n=5 
letter="F"

InterSystems IRIS还支持对动态SQL语句(事件名称= DynamicStatement)以及ODBC和JDBC语句(事件名称= XDBCStatement)进行审核。

0
0 192
文章 姚 鑫 · 三月 21, 2021 9m read

第十二章 使用嵌入式SQL(五)

嵌入式SQL变量

以下局部变量在嵌入式SQL中具有特殊用途。这些局部变量名称区分大小写。在过程启动时,这些变量是不确定的。它们由嵌入式SQL操作设置。也可以使用SET命令直接设置它们,或使用NEW命令将其重置为未定义。像任何局部变量一样,值将在过程持续期间或直到设置为另一个值或使用NEW进行定义之前一直存在。例如,某些成功的嵌入式SQL操作未设置%ROWID。执行这些操作后,%ROWID是未定义的或保持设置为其先前值。

  • %msg
  • %ROWCOUNT
  • %ROWID
  • SQLCODE

这些局部变量不是由Dynamic SQL设置的。 (请注意,SQL Shell和Management Portal SQL接口执行Dynamic SQL。)相反,Dynamic SQL设置相应的对象属性。

在嵌入式SQL中使用以下ObjectScript特殊变量。这些特殊的变量名称不区分大小写。在过程启动时,这些变量将初始化为一个值。它们由嵌入式SQL操作设置。不能使用SET或NEW命令直接设置它们。

  • $TLEVEL
  • $USERNAME

作为已定义的InterSystems IRIS嵌入式SQL接口的一部分,InterSystems IRIS可以在嵌入式SQL处理期间设置任何这些变量。

如果嵌入式SQL在类方法中(procedureBlock = ON),则系统会自动将所有这些变量放在PublicList中,并自动将SQLCODE%ROWID%ROWCOUNT%msg以及SQL语句。可以通过引用方法来传递这些变量;通过引用传递的变量将不会在类方法过程块中自动更新。

如果嵌入式SQL在例程中,则程序员有责任在调用嵌入式SQL之前新建%msg%ROWCOUNT%ROWIDSQLCODE变量。更新这些变量可防止干扰这些变量的先前设置。为避免<FRAMESTACK>错误,不应在迭代周期内执行此NEW操作。

%msg

包含系统提供的错误消息字符串的变量。如果InterSystems SQL将SQLCODE设置为负整数(表示错误),则仅设置%msg。如果SQLCODE设置为0100,则%msg变量与其先前值保持不变。

此行为不同于相应的Dynamic SQL %Message属性,当没有当前错误时,该属性将设置为空字符串。

在某些情况下,特定的SQLCODE错误代码可能与一个以上的%msg字符串相关联,描述了生成SQLCODE的不同条件。 %msg还可以接受用户定义的消息字符串。当触发器代码显式设置%ok = 0来中止触发器时,这最常用于从触发器发出用户定义的消息。

当执行SQL代码时,将使用有效的NLS语言生成错误消息字符串。可以在不同的NLS语言环境中编译SQL代码。该消息将根据运行时NLS环境生成。请参见$ SYS.NLS.Locale.Language

%ROWCOUNT

一个整数计数器,指示受特定语句影响的行数。

  • INSERTUPDATEINSERT OR UPDATEDELETE%ROWCOUNT设置为受影响的行数。带有显式值的INSERT命令只能影响一行,因此将%ROWCOUNT设置为01INSERT查询结果,UPDATEDELETE可以影响多行,因此可以将%ROWCOUNT设置为0或正数。整数。
  • 无论删除多少行还是删除任何行,TRUNCATE TABLE始终将%ROWCOUNT设置为–1。因此,要确定实际删除的行数,请在TRUNCATE TABLE之前对表执行COUNT(*),或者使用DELETE而不是TRUNCATE TABLE删除表中的所有行。
  • 没有声明游标的SELECT只能作用于一行,因此执行简单的SELECT总是会将%ROWCOUNT设置为1(与检索到的选择标准匹配的单行)或0(没有与选择标准匹配的行)。
  • DECLARE游标名CURSOR FOR SELECT不会初始化%ROWCOUNTSELECT之后,%ROWCOUNT不变,而OPEN游标名之后,%ROWCOUNT不变。第一个成功的FETCH设置%ROWCOUNT。如果没有行符合查询选择条件,则FETCH设置%ROWCOUNT = 0;否则,设置%ROWCOUNT = 0。如果FETCH检索与查询选择条件匹配的行,则它将设置%ROWCOUNT = 1。随后的每个获取行的FETCH都将递增%ROWCOUNTCLOSE时或FETCH发出SQLCODE 100(无数据或无更多数据)时,%ROWCOUNT包含已检索的总行数。

SELECT行为与相应的Dynamic SQL%ROWCOUNT属性不同,该属性在查询执行完成时设置为0,并且仅在程序迭代查询返回的结果集时才递增。

如果SELECT查询仅返回聚合函数,则每个FETCH都将设置%ROWCOUNT = 1。即使表中没有数据,第一个FETCH始终以SQLCODE = 0来完成;任何后续的FETCH均以SQLCODE = 100完成,并设置%ROWCOUNT = 1

以下嵌入式SQL示例声明一个游标,并使用FETCH来获取表中的每一行。到达数据结尾(SQLCODE = 100)时,%ROWCOUNT包含已检索的行数:

/// d ##class(PHA.TEST.SQL).ROWCOUNT()
ClassMethod ROWCOUNT()
{
	SET name="LastName,FirstName",state="##"
	&sql(DECLARE EmpCursor CURSOR FOR 
		SELECT Name, Home_State
		INTO :name,:state FROM Sample.Person
		WHERE Home_State %STARTSWITH 'M')
	WRITE !,"BEFORE: Name=",name," State=",state
	&sql(OPEN EmpCursor)
	QUIT:(SQLCODE'=0)
	FOR { 
		&sql(FETCH EmpCursor)
		QUIT:SQLCODE  
		WRITE !,"Row fetch count: ",%ROWCOUNT
		WRITE " Name=",name," State=",state
	}
	WRITE !,"最终提取SQLCODE: ",SQLCODE
	&sql(CLOSE EmpCursor)
	WRITE !,"AFTER: Name=",name," State=",state
	WRITE !,"提取的总行数: ",%ROWCOUNT
}
DHC-APP>d ##class(PHA.TEST.SQL).ROWCOUNT()
 
BEFORE: Name=LastName,FirstName State=##
Row fetch count: 1 Name=O'Rielly,Chris H. State=MS
Row fetch count: 2 Name=Orwell,John V. State=MT
Row fetch count: 3 Name=Zevon,Heloisa O. State=MI
...
Row fetch count: 37 Name=Joyce,Elmo R. State=MO
Row fetch count: 38 Name=Jafari,Christine Z. State=MI
最终提取SQLCODE: 100
AFTER: Name=Jafari,Christine Z. State=OH
提取的总行数: 38

以下嵌入式SQL示例执行UPDATE并设置受更改影响的行数:

/// d ##class(PHA.TEST.SQL).ROWCOUNT1()
ClassMethod ROWCOUNT1()
{
	&sql(UPDATE Sample.Employee 
		SET Salary = (Salary * 1.1)
		WHERE Salary < 50000)
	IF SQLCODE<0 {
		WRITE "SQLCODE error ",SQLCODE," ",%msg  QUIT
		}
	WRITE "Employees: ", %ROWCOUNT,!
}
DHC-APP>d ##class(PHA.TEST.SQL).ROWCOUNT1()
Employees: 48

请记住,所有嵌入式SQL语句(在给定进程内)都会修改%ROWCOUNT变量。如需要%ROWCOUNT提供的值,请确保在执行其他Embedded SQL语句之前获取其值。根据嵌入式SQL的调用方式,可能必须在输入嵌入式SQL之前新建%ROWCOUNT变量。

另请注意,显式回滚事务不会影响%ROWCOUNT的值。例如,以下内容将报告已进行了更改,即使它们已经滚动了。

/// d ##class(PHA.TEST.SQL).ROWCOUNT2()
ClassMethod ROWCOUNT2()
{
	TSTART // 开始事务
	NEW SQLCODE,%ROWCOUNT,%ROWID
	&sql(UPDATE Sample.Employee 
		SET Salary = (Salary * 1.1)
		WHERE Salary < 50000)
	IF SQLCODE<0 {
		WRITE "SQLCODE error ",SQLCODE," ",%msg  QUIT
	}
	TROLLBACK // 强制回滚;不会修改%rowcount
	Write "Employees: ", %ROWCOUNT,!
}

DHC-APP>d ##class(PHA.TEST.SQL).ROWCOUNT2()
Employees: 37

隐式事务(例如,如果UPDATE未通过约束检查)由%ROWCOUNT反映。

%ROWID

初始化进程时,未定义%ROWID。当发出NEW %ROWID命令时,%ROWID将重置为未定义。 %ROWID由下面描述的嵌入式SQL操作设置。如果该操作不成功或成功完成,但未获取或修改任何行,则%ROWID值与其先前值保持不变:未定义,或由先前的嵌入式SQL操作设置为某个值。因此,在每个嵌入式SQL操作之前,请务必新建%ROWID

%ROWID设置为受以下操作影响的最后一行的RowID

  • INSERTUPDATEINSERT OR UPDATEDELETE:单行操作后,%ROWID变量包含系统分配的RowID(对象ID)值,该值分配给插入,更新或删除的记录。经过多行操作之后,%ROWID变量包含系统分配的最后一条插入,更新或删除的记录的RowID(对象ID)的值。如果未插入,更新或删除任何记录,则%ROWID变量值将保持不变。 TRUNCATE TABLE没有设置%ROWID
  • 基于游标的SELECT:DECLARE游标名称CURSOROPEN游标名称语句未初始化%ROWID%ROWID值与其先前值保持不变。第一个成功的FETCH设置%ROWID。随后的每个获取行的FETCH都会将%ROWID重置为当前RowID值。如果FETCH检索一行可更新游标,则会设置%ROWID。可更新游标是其中顶部FROM子句仅包含一个元素(单个表名或可更新视图名)的游标。如果游标不可更新,则%ROWID保持不变。如果没有行符合查询选择条件,则FETCH不会更改先前的%ROWID值(如果有)。 CLOSE时或FETCH发出SQLCODE 100(无数据或无更多数据)时,%ROWID包含检索到的最后一行的RowID

具有DISTINCT关键字或GROUP BY子句的基于游标的SELECT不会设置%ROWID%ROWID值与其先前的值(如果有)保持不变。

如果基于游标的SELECT仅返回聚合函数值,则不会设置%ROWID。如果它同时返回字段值和聚合函数值,则将每个FETCH%ROWID值设置为查询返回的最后一行的RowID

  • 没有声明游标的SELECT不会设置%ROWID。完成简单的SELECT语句后,%ROWID值将保持不变。

在Dynamic SQL中,相应的%ROWID属性返回插入,更新或删除的最后一条记录的RowID值。执行SELECT查询时,Dynamic SQL不会返回%ROWID属性值。

可以使用以下方法调用从ObjectScript中检索当前的%ROWID

DHC-APP>  WRITE $SYSTEM.SQL.GetROWID()
213

在执行INSERTUPDATEDELETETRUNCATE TABLE或基于游标的SELECT操作之后,LAST_IDENTITY SQL函数将为最近修改的记录返回IDENTITY字段的值。如果表没有IDENTITY字段,则此函数返回最近修改记录的RowID

SQLCODE

运行嵌入式SQL查询后,必须在处理输出主机变量之前检查SQLCODE

如果SQLCODE = 0,则查询成功完成并返回数据。输出主机变量包含字段值。 如果SQLCODE = 100,则查询成功完成,但是输出主机变量值可能不同。任何一个:

  • 查询返回一个或多个数据行(SQLCODE = 0),然后到达数据的末尾(SQLCODE = 100),在这种情况下,输出主机变量设置为返回的最后一行的字段值。 %ROWCOUNT> 0
  • 查询未返回任何数据,在这种情况下,输出主机变量未定义。 %ROWCOUNT = 0

如果查询仅返回聚合函数,则即使表中没有数据,第一个FETCH也会始终以SQLCODE = 0%ROWCOUNT = 1来完成。第二个FETCHSQLCODE = 100%ROWCOUNT = 1结束。如果表中没有数据或没有数据与查询条件匹配,查询将根据需要将输出主机变量设置为0或空字符串。

如果SQLCODE为负数,则查询失败,并显示错误条件。

根据嵌入式SQL的调用方式,可能必须在输入嵌入式SQL之前新建SQLCODE变量。在触发代码中,将SQLCODE设置为非零值会自动将%ok = 0设置为中止并回滚触发操作。

在动态SQL中,相应的%SQLCODE属性返回SQL错误代码值。

$TLEVEL

事务级计数器。 InterSystems SQL将$TLEVEL初始化为0。 如果没有当前事务,$TLEVEL为0。

  • 初始START TRANSACTION$LEVEL设置为1。其他START TRANSACTION语句对$TLEVEL无效。
  • 每个SAVEPOINT语句将$TLEVEL加1。
  • ROLLBACK TO SAVEPOINT点名语句减少$TLEVEL。递减量取决于指定的保存点。
  • COMMIT$LEVEL重置为0。
  • ROLLBACK$LEVEL重置为0。

还可以使用%INTRANSACTION语句来确定事务是否在进行中。

$TLEVEL也由ObjectScript事务命令设置。

$USERNAME

SQL用户名与InterSystems IRIS用户名相同,存储在ObjectScript $USERNAME特殊变量中。用户名可以用作系统范围的默认架构,也可以用作架构搜索路径中的元素。

0
0 177
文章 姚 鑫 · 三月 20, 2021 5m read

第十二章 使用嵌入式SQL(四)

SQL游标

游标是指向数据的指针,该数据允许嵌入式SQL程序对所指向的记录执行操作。通过使用游标,Embedded SQL可以遍历结果集。嵌入式SQL可以使用游标执行查询,该查询从多个记录返回数据。嵌入式SQL还可以使用游标更新或删除多个记录。

必须首先对SQL游标进行DECLARE,并为其命名。在DECLARE语句中,提供了SELECT语句,该语句标识游标将指向的记录。然后,将此游标名称提供给OPEN游标语句。然后,反复发出FETCH游标语句以遍历SELECT结果集。然后,发出CLOSE游标语句。

  • 基于游标的查询使用DECLARE游标名称CURSOR FOR SELECT来选择记录,并(可选)将select列值返回到输出主机变量中。 FETCH语句遍历结果集,使用这些变量返回选定的列值。
  • 基于游标的DELETEUPDATE使用DECLARE游标名CURSOR FOR SELECT选择操作的记录。没有指定输出主机变量。 FETCH语句遍历结果集。 DELETEUPDATE语句包含WHERE CURRENT OF子句,以标识当前光标位置,以便对所选记录执行操作。

请注意,游标不能跨越方法。因此,必须在同一类方法中声明,打开,获取和关闭游标。在生成类和方法的所有代码(例如从.CSP文件生成的类)中考虑这一点很重要。

下面的示例使用游标执行查询并将结果显示给主体设备:

/// d ##class(PHA.TEST.SQL).CURSOR()
ClassMethod CURSOR()
{
	&sql(DECLARE C5 CURSOR FOR
		SELECT %ID,Name
		INTO :id, :name
		FROM Sample.Person
		WHERE Name %STARTSWITH 'A'
		ORDER BY Name
	)

	&sql(OPEN C5)
	QUIT:(SQLCODE'=0)
	&sql(FETCH C5)

	While (SQLCODE = 0) {
		Write id, ":  ", name,!        
		&sql(FETCH C5)
	}

	&sql(CLOSE C5)
}
DHC-APP>d ##class(PHA.TEST.SQL).CURSOR()
95:  Adams,Diane F.
183:  Adams,Susan E.
71:  Ahmed,Elmo X.
28:  Alton,Martin S.
175:  Alton,Phil T.
86:  Anderson,Mario L.
131:  Anderson,Valery N.

此示例执行以下操作:

  1. 声明一个游标C1,该游标返回一组按Name排序的Person行。
  2. 打开游标。
  3. 游标上调用FETCH直到到达数据末尾。每次调用FETCH之后,如果有更多数据要提取,则SQLCODE变量将设置为0。每次调用FETCH后,返回的值都将复制到DECLARE语句的INTO子句指定的主机变量中。
  4. 关闭光标。

DECLARE游标声明

DECLARE语句同时指定了游标名称和定义游标的SQL SELECT语句。 DECLARE语句必须在例程中出现在使用游标的任何语句之前。

游标名称区分大小写。

游标名称在类或例程中必须唯一。因此,递归调用的例程不能包含游标声明。在这种情况下,最好使用动态SQL。

下面的示例声明一个名为MyCursor的游标:

 &sql(DECLARE MyCursor CURSOR FOR
    SELECT Name, DOB
    FROM Sample.Person
    WHERE Home_State = :state
    ORDER BY Name
    )

DECLARE语句可以包括一个可选的INTO子句,该子句指定在遍历游标时将接收数据的本地主机变量的名称。例如,我们可以在前面的示例中添加一个INTO子句:

 &sql(DECLARE MyCursor CURSOR FOR
    SELECT Name, DOB
    INTO :name, :dob
    FROM Sample.Person
    WHERE Home_State = :state
    ORDER BY Name
    )

INTO子句可以包含逗号分隔的主机变量列表,单个主机变量数组或两者的组合。如果指定为以逗号分隔的列表,则INTO子句宿主变量的数量必须与游标的SELECT列表中的列数完全匹配,否则在编译该语句时会收到“基数不匹配”错误。

如果DECLARE语句不包含INTO子句,则INTO子句必须出现在FETCH语句中。通过在DECLARE语句而不是FETCH语句中指定INTO子句,可能会导致性能的小幅提高。

因为DECLARE是声明,而不是执行的语句,所以它不会设置或终止SQLCODE变量。

如果已经声明了指定的游标,则编译将失败,并显示SQLCODE -52错误,游标名称已声明。

执行DECLARE语句不会编译SELECT语句。 SELECT语句在第一次执行OPEN语句时被编译。嵌入式SQL不在常规编译时进行编译,而是在SQL执行时(运行时)进行编译。

OPEN游标声明

OPEN语句为后续执行准备了一个游标:

 &sql(OPEN MyCursor)

执行OPEN语句将编译在DECLARE语句中找到的Embedded SQL代码,创建优化的查询计划,并生成缓存的查询。执行OPEN(在SQL运行时)时,会发出涉及缺少资源(例如未定义的表或字段)的错误。

成功调用OPEN后,SQLCODE变量将设置为0。

必须先调用OPEN才能从游标中获取数据。

FETCH游标声明

FETCH语句获取游标下一行的数据(由游标查询定义):

 &sql(FETCH MyCursor)

必须先对游标进行DECLARE并打开,然后才能在其上调用FETCH

FETCH语句可以包含INTO子句,该子句指定在游标游标时将接收数据的本地主机变量的名称。例如,我们可以在前面的示例中添加一个INTO子句:

 &sql(FETCH MyCursor INTO :a, :b)

INTO子句可以包含逗号分隔的主机变量列表,单个主机变量数组或两者的组合。如果指定为以逗号分隔的列表,则INTO子句宿主变量的数量必须与游标的SELECT列表中的列数完全匹配,否则在编译该语句时,将收到SQLCODE -76“基数不匹配”错误。

通常,INTO子句是在DECLARE语句中指定的,而不是在FETCH语句中指定的。如果DECLARE语句中的SELECT查询和FETCH语句都包含INTO子句,则仅设置由DECLARE语句指定的主机变量。如果仅FETCH语句包含INTO子句,则将设置由FETCH语句指定的主机变量。

如果FETCH检索数据,则将SQLCODE变量设置为0;否则,将SQLCODE变量设置为0。如果没有数据(或没有更多数据)到FETCH,则将SQLCODE设置为100(没有更多数据)。主机变量值仅应在SQLCODE = 0时使用。

根据查询,第一次调用FETCH可能会执行其他任务(例如对临时数据结构中的值进行排序)。

CLOSE游标声明

CLOSE语句终止游标的执行:

 &sql(CLOSE MyCursor)

CLOSE语句清除查询执行所使用的任何临时存储。无法调用CLOSE的程序将遇到资源泄漏(例如,不需要的IRIS TEMP临时数据库增加)。

成功调用CLOSE后,SQLCODE变量将设置为0。因此,在关闭游标之前,应检查最终的FETCH是否将SQLCODE设置为0100

0
0 158
文章 姚 鑫 · 三月 19, 2021 11m read

第十二章 使用嵌入式SQL(三)

主机变量

主机变量是将文字值传入或传出嵌入式SQL的局部变量。 最常见的是,主机变量用于将本地变量的值作为输入值传递给Embedded SQL,或者将SQL查询结果值作为输出主机变量传递给Embedded SQL查询。

主机变量不能用于指定SQL标识符,例如架构名称,表名称,字段名称或游标名称。主机变量不能用于指定SQL关键字。

  • 输出主机变量仅在嵌入式SQL中使用。它们在INTO子句中指定,INTO子句是仅嵌入式SQL支持的SQL查询子句。
  • 输入主机变量可以在嵌入式SQL或动态SQL中使用。在动态SQL中,还可以使用“?”向SQL语句输入文字。输入参数。这 ”?”语法不能在Embedded SQL中使用。

在嵌入式SQL中,可以在可以使用文字值的任何位置使用输入主机变量。使用SELECT或FETCH语句的INTO子句指定输出主机变量。

注意:当SQL NULL输出到ObjectScript时,它由一个ObjectScript空字符串(“”)表示,该字符串的长度为零。

要将变量或属性引用用作宿主变量,请在其前面加上一个冒号(:)。 嵌入式InterSystems SQL中的主机变量可以是以下之一:

  • 一个或多个ObjectScript局部变量,例如:myvar,指定为以逗号分隔的列表。局部变量可以完全形成并且可以包含下标。像所有局部变量一样,它区分大小写,并且可以包含Unicode字母字符。
  • 单个ObjectScript局部变量数组,例如:myvars()。局部变量数组只能从单个表(而不是联接表或视图)中接收字段值。
  • 对象引用,例如:oref.Prop,其中Prop是属性名称,带有或不带有前导字符。这可以是简单属性或多维数组属性,例如:oref.Prop(1)。它可以是一个实例变量,例如:i%Prop或:i %% Data。属性名称可以定界。例如:Person."Home City".即使停用了对分隔标识符的支持,也可以使用分隔属性名称。多维属性可以包括:i%Prop()和:m%Prop()主机变量引用。对象引用主机变量可以包含任意数量的点语法级别;例如,例如,:Person.Address.City

oref.Prop用作过程块方法内的宿主变量时,系统会自动将oref变量(而不是整个oref.Prop引用)添加到PublicList并对其进行更新。

主机变量中的双引号指定文字字符串,而不是带分隔符的标识符。例如,:request.GetValueAt("PID:SetIDPID") or :request.GetValueAt("PID:PatientName(1).FamilyName").

主机变量应在ObjectScript过程的PublicList变量列表中列出,并使用NEW命令重新初始化。您可以配置InterSystems IRIS以便在注释文本中列出Embedded SQL中使用的所有主机变量。使用InterSystems SQL的注释部分对此进行了描述。

主机变量值具有以下行为:

  • 输入主机变量永远不会被SQL语句代码修改。即使嵌入式SQL运行后,它们仍保留其原始值。但是,输入主机变量值在提供给SQL语句代码之前会被“轻度格式化”:有效数字值将去除前导和尾随零,单个前导加号和尾随小数点。时间戳记值将除去尾随空格,以小数秒为单位的尾随零和(如果没有小数秒的话)尾随的小数点。
  • SQLCODE = 0时,即返回有效行时,将设置INTO子句中指定的输出主机变量。如果执行SELECT语句或FETCH语句导致SQLCODE = 100(没有数据与查询匹配),则INTO子句中指定的输出主机变量将设置为null(“”)。如果在执行SELECT语句或FETCH语句之前未定义INTO变量,导致SQLCODE = 100,则该变量将保持未定义状态。主机变量值仅应在SQLCODE = 0时使用。在DECLARE ... SELECT ... INTO语句中,请勿在两个FETCH调用之间修改INTO子句中的输出主机变量,因为这可能会导致不可预测的查询结果。

在处理输出主机变量之前,必须检查SQLCODE值。仅当SQLCODE = 0时才应使用输出主机变量值。

当在INTO子句中使用逗号分隔的主机变量列表时,必须指定与选择项数量相同的主机变量数量(字段,集合函数,标量函数,算术表达式,文字)。宿主变量太多或太少都会在编译时导致SQLCODE -76基数错误。

在嵌入式SQL中使用SELECT *时,这通常是一个问题。例如,SELECT * FROM Sample.Person仅对以逗号分隔的15个主机变量列表有效(非隐藏列的确切数目,具体取决于表定义,该数目可能包含也可能不包含系统生成的RowID) (ID)列)。

因为列数可以更改,所以用单个宿主变量的INTO子句列表指定SELECT *通常不是一个好主意。使用SELECT *时,通常最好使用主机变量下标数组,例如:

/// d ##class(PHA.TEST.SQL).EmbedSQL9()
ClassMethod EmbedSQL9()
{
	NEW SQLCODE
	&sql(SELECT %ID,* INTO :tflds() FROM Sample.Person )
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	FOR i=0:1:25 { 
		IF $DATA(tflds(i)) {
			WRITE "field ",i," = ",tflds(i),! 
		}
	}
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL9()
field 1 = 1
field 2 = 30
field 3 = 54536
field 4 = ReOrangYellow
field 6 = yaoxin
field 8 = 111-11-1117
field 9 = 13
field 11 = St Louis
field 12 = WI
field 13 = 889 Clinton Drive
field 14 = 78672
field 15 = Ukiah
field 16 = AL
field 17 = 9619 Ash Avenue
field 18 = 56589

本示例使用%ID返回RowID作为字段号1,无论RowID是否隐藏。 注意,在此示例中,字段编号下标可能不是连续序列;有些字段可能被隐藏并被跳过。包含NULL的字段以空字符串值列出。 ** 退出嵌入式SQL后立即检查SQLCODE值是一种良好的编程习惯。仅当SQLCODE = 0时才应使用输出主机变量值。**

主机变量示例

在下面的ObjectScript示例中,Embedded SQL语句使用输出主机变量将名称和归属状态地址从SQL查询返回到ObjectScript:

/// d ##class(PHA.TEST.SQL).EmbedSQL10()
ClassMethod EmbedSQL10()
{
	&sql(SELECT Name,Home_State
		INTO :CName,:CAddr
		FROM Sample.Person)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	
	WRITE !,"Name is: ",CName
	WRITE !,"State is: ",CAddr
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL10()
 
Name is: yaoxin
State is: WI

嵌入式SQL使用INTO子句指定主机变量:CName:CAddr,以在局部变量CName中返回所选客户的姓名,并在局部变量CAddr中返回主目录状态。

下面的示例使用带下标的局部变量执行相同的操作:

/// d ##class(PHA.TEST.SQL).EmbedSQL11()
ClassMethod EmbedSQL11()
{
	&sql(SELECT Name,Home_State
	INTO :CInfo(1),:CInfo(2)
	FROM Sample.Person)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}	
	WRITE !,"Name is: ",CInfo(1)
	WRITE !,"State is: ",CInfo(2)
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL11()
 
Name is: yaoxin
State is: WI

这些主机变量是带有用户提供的下标(:CInfo(1))的简单局部变量。但是,如果省略下标(:CInfo()),则InterSystems IRIS使用SqlColumnNumber填充主机变量下标数组,如下所述。

在下面的ObjectScript示例中,嵌入式SQL语句同时使用输入主机变量(在WHERE子句中)和输出主机变量(在INTO子句中):

/// d ##class(PHA.TEST.SQL).EmbedSQL12()
ClassMethod EmbedSQL12()
{
	SET minval = 10000
	SET maxval = 50000
	&sql(SELECT Name,Salary INTO :outname, :outsalary
	FROM Sample.Employee
	WHERE Salary > :minval AND Salary < :maxval)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	WRITE !,"Name is: ",outname
	WRITE !,"Salary is: ",outsalary
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL12()
 
Name is: Chadwick,Phyllis L.
Salary is: 16377

以下示例在输入主机变量上执行“light normalization”。请注意,InterSystems IRIS将输入变量值视为字符串,并且不对其进行规范化,但是Embedded SQL将此数字规范化为65,以在WHERE子句中执行相等比较:

/// d ##class(PHA.TEST.SQL).EmbedSQL13()
ClassMethod EmbedSQL13()
{
	SET x="+065.000"
		&sql(SELECT Name,Age
		INTO :a,:b
		FROM Sample.Person
		WHERE Age=:x)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	WRITE !,"Input value is: ",x
	WRITE !,"Name value is: ",a
	WRITE !,"Age value is: ",b
}

DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL13()
 
Input value is: +065.000
Name value is: Houseman,Martin D.
Age value is: 65

在下面的ObjectScript示例中,嵌入式SQL语句使用对象属性作为宿主变量:

   &sql(SELECT Name, Title INTO :obj.Name, :obj.Title
        FROM MyApp.Employee
        WHERE %ID = :id )

在这种情况下,obj必须是对具有可变(即可以修改)属性NameTitle的对象的有效引用。请注意,如果查询包含INTO语句并且没有返回任何数据(即SQLCODE100),则执行查询可能会导致修改主机变量的值。

用列号下标的主机变量

如果FROM子句包含一个表,则可以为从该表中选择的字段指定带下标的主机变量;否则,可以为该表指定一个下标主机变量。例如,本地数组:myvar()。 InterSystems IRIS使用每个字段的SqlColumnNumber作为数字下标填充本地数组。请注意,SqlColumnNumber是表定义中的列号,而不是选择列表序列。 (不能将带下标的宿主变量用于视图的字段。)

主机变量数组必须是省略了最低级别下标的局部数组。因此,:myvar(), :myvar(5,), and :myvar(5,2,)都是有效的主机变量下标数组。

  • 主机变量下标数组可以用于INSERTUPDATEINSERT OR UPDATE语句VALUES子句中的输入。当在INSERTUPDATE语句中使用时,主机变量数组使您可以定义在运行时而不是在编译时更新哪些列。
  • 主机变量下标数组可以用于SELECTDECLARE语句INTO子句中的输出。在下面的示例中显示了SELECT中的下标数组用法。

在下面的示例中,SELECT使用指定字段的值填充Cdata数组。 Cdata()的元素对应于表列定义,而不是SELECT元素。因此,在Sample.Person中,“名称”字段是第6列,“年龄”字段是第2列,“出生日期”DOB)字段是第3列:

/// d ##class(PHA.TEST.SQL).EmbedSQL14()
ClassMethod EmbedSQL14()
{
	&sql(SELECT Name, Age, DOB
		INTO :Cdata()
		FROM Sample.Person)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	WRITE !,"Name is: ",Cdata(6)
	WRITE !,"Age is: ",Cdata(2)
	WRITE !,"DOB is: ",$ZDATE(Cdata(3),1)
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL14()
 
Name is: yaoxin
Age is: 30
DOB is: 04/25/90

以下示例使用带下标的数组主机变量返回行的所有字段值:

/// d ##class(PHA.TEST.SQL).EmbedSQL15()
ClassMethod EmbedSQL15()
{
   	&sql(SELECT * INTO :Allfields()
		FROM Sample.Person)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	SET x=1
	WHILE x '="" {
		WRITE !,x," field is ",Allfields(x)
		SET x=$ORDER(Allfields(x))
	}
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL15()
 
1 field is 1
2 field is 30
3 field is 54536
4 field is ReOrangYellow
6 field is yaoxin
8 field is 111-11-1117
9 field is 13
11 field is St Louis
12 field is WI
13 field is 889 Clinton Drive
14 field is 78672
15 field is Ukiah
16 field is AL
17 field is 9619 Ash Avenue
18 field is 56589

请注意,此WHILE循环使用$ORDER而不是简单的x = x + 1进行递增。这是因为在许多表(例如Sample.Person)中,可能存在隐藏的列。这些导致列号序列不连续。

如果SELECT列表包含不是该表中的字段的项,例如表达式或箭头语法字段,则INTO子句还必须包含逗号分隔的非数组主机变量。下面的示例组合了一个带下标的数组主机变量,以返回与定义的表列对应的值,而主机变量组合为返回与定义的表列不对应的值:

/// d ##class(PHA.TEST.SQL).EmbedSQL16()
ClassMethod EmbedSQL16()
{
   	&sql(SELECT Name, Home_City, {fn NOW}, Age, ($HOROLOG-DOB)/365.25, Home_State
		INTO :Allfields(), :timestmp('now'), :exactage
		FROM Sample.Person)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	SET x = $ORDER(Allfields(""))
	WHILE x '="" {
		WRITE !,x," field is ",Allfields(x)
		SET x=$ORDER(Allfields(x)) 
	}
	WRITE !,"date & time now is ",timestmp("now")
	WRITE !,"exact age is ",exactage
}
DHC-APP> d ##class(PHA.TEST.SQL).EmbedSQL16()
 
1 field is 1
2 field is 30
3 field is 54536
4 field is ReOrangYellow
6 field is yaoxin
8 field is 111-11-1117
9 field is 13
11 field is St Louis
12 field is WI
13 field is 889 Clinton Drive
14 field is 78672
15 field is Ukiah
16 field is AL
17 field is 9619 Ash Avenue
18 field is 56589
date & time now is 2021-03-13 16:00:40
exact age is 30.88295687885010267

请注意,非数组主机变量必须在数量和顺序上与非列SELECT项匹配。

将主机变量用作下标数组受以下限制:

  • 只有在FROM子句的单个表中选择字段时,才可以使用带下标的列表。这是因为从多个表中选择字段时,SqlColumnNumber值可能会发生冲突。
  • 下标列表只能在选择表字段时使用。它不能用于表达式或聚合字段。这是因为这些选择列表项没有SqlColumnNumber值。

NULL和未定义的主机变量

如果指定未定义的输入主机变量,则嵌入式SQL将其值视为NULL

/// d ##class(PHA.TEST.SQL).EmbedSQL17()
ClassMethod EmbedSQL17()
{
	NEW x
	&sql(SELECT Home_State,:x
		INTO :a,:b
		FROM Sample.Person)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	WRITE !,"Home_State的长度为: ",$LENGTH(a)
	WRITE !,"x的长度是: ",$LENGTH(b)
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL17()
 
Home_State的长度为: 2
x的长度是: 0

SQL NULL等效于ObjectScript“”字符串(长度为零的字符串)。

如果将NULL输出到主机变量,则Embedded SQL会将其值视为ObjectScript“”字符串(零长度字符串)。例如,Sample.Person中的某些记录具有NULL Spouse字段。执行此查询后:

/// d ##class(PHA.TEST.SQL).EmbedSQL18()
ClassMethod EmbedSQL18()
{
	&sql(SELECT Name,Spouse
		INTO :name, :spouse
		FROM Sample.Person
		WHERE Spouse IS NULL)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	WRITE !,"Name: ",name," of length ",$LENGTH(name)," defined: ",$DATA(name)
	WRITE !,"Spouse: ",spouse," of length ",$LENGTH(spouse)," defined: ",$DATA(spouse)
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL18()
 
Name: xiaoli of length 6 defined: 1
Spouse:  of length 0 defined: 1

宿主变量spouse将设置为“”(长度为零的字符串)以指示NULL值。因此,不能使用ObjectScript $DATA函数来确定SQL字段是否为NULL。当传递带有NULL值的SQL字段的输出主机变量时,$DATA返回true(定义了变量)。

在极少数情况下,表字段包含SQL零长度字符串(''),例如,如果应用程序将字段显式设置为SQL ''字符串,则主机变量将包含特殊标记值$CHAR(0 )(长度为1的字符串,仅包含一个ASCII 0字符),它是SQL零长度字符串的ObjectScript表示形式。强烈建议不要使用SQL零长度字符串。

下面的示例比较SQL NULL和SQL零长度字符串输出的主机变量:

/// d ##class(PHA.TEST.SQL).EmbedSQL19()
ClassMethod EmbedSQL19()
{
	&sql(SELECT '',Spouse
		INTO :zls, :spouse
		FROM Sample.Person
		WHERE Spouse IS NULL)
	IF SQLCODE<0 {
		WRITE "SQLCODE错误 ",SQLCODE," ",%msg  QUIT
	} ELSEIF SQLCODE=100 {
		WRITE "查询没有结果"  QUIT
	}
	WRITE "In ObjectScript"
	WRITE !,"ZLS is of length ",$LENGTH(zls)," defined: ",$DATA(zls)
	/* Length=1, Defined=1 */
	WRITE !,"NULL is of length ",$LENGTH(spouse)," defined: ",$DATA(spouse)
	/* Length=0, Defined=1 */
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL19()
In ObjectScript
ZLS is of length 1 defined: 1
NULL is of length 0 defined: 1

请注意,此主机变量NULL行为仅在基于服务器的查询(嵌入式SQL和动态SQL)中为true。在ODBC和JDBC中,使用ODBC或JDBC接口显式指定NULL值。

主机变量的有效性

  • 嵌入式SQL永远不会修改输入主机变量。
  • 仅当SQLCODE = 0时,输出主机变量才在Embedded SQL之后可靠地有效。

例如,以下OutVal的用法不可靠:

/// d ##class(PHA.TEST.SQL).EmbedSQL20()
ClassMethod EmbedSQL20()
{
InvalidExample
	SET InVal = "1234"
	SET OutVal = "None"
	&sql(SELECT Name
		INTO :OutVal
		FROM Sample.Person
		WHERE %ID=:InVal)
	IF OutVal="None" {          
		WRITE !,"没有数据返回"
		WRITE !,"SQLCODE=",SQLCODE 
	} ELSE {
		WRITE !,"Name is: ",OutVal 
	}
}

DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL20()
 
没有数据返回
SQLCODE=100

调用嵌入式SQL之前设置的OutVal的值在从嵌入式SQL返回之后不应该被IF命令引用。

相反,应该使用SQLCODE变量编写如下示例:

/// d ##class(PHA.TEST.SQL).EmbedSQL21()
ClassMethod EmbedSQL21()
{
ValidExample
	SET InVal = "1234"
	&sql(SELECT Name
		INTO :OutVal
		FROM Sample.Person
		WHERE %ID=:InVal)
	IF SQLCODE'=0 { 
		SET OutVal="None" 
		IF OutVal="None" {
		WRITE !,"没有数据返回"
		WRITE !,"SQLCODE=",SQLCODE } 
	} ELSE {
		WRITE !,"Name is: ",OutVal 
	}
}
DHC-APP>d ##class(PHA.TEST.SQL).EmbedSQL21()
 
没有数据返回
SQLCODE=100

嵌入式SQL将SQLCODE变量设置为0,以指示成功地检索输出行。 SQLCODE值为100表示没有找到与SELECT条件匹配的行。 SQLCODE负数表示SQL错误条件。

主机变量和程序块

如果嵌入式SQL在过程块内,则所有输入和输出主机变量必须是公共的。可以通过在过程块开始处的PUBLIC部分中声明它们,或用一个初始字符命名它们(自动使它们公开)来完成它们。但是请注意,用户定义的主机变量是自动公开的,但不是自动更新的。用户有责任根据需要对这些变量执行NEW。如嵌入式SQL变量中所述,某些SQL变量(例如%ROWCOUNT%ROWID%msg)既自动公开又自动更新。必须将SQLCODE声明为public

在以下过程块示例中,主机变量zipcitystate以及SQLCODE变量被声明为PUBLIC。 SQL系统变量%ROWCOUNT%ROWID%msg已经公开,因为它们的名称以字符开头。然后,过程代码对SQLCODE,其他SQL系统变量和状态局部变量执行NEW

/// d ##class(PHA.TEST.SQL).EmbedSQL22()
ClassMethod EmbedSQL22()
{
UpdateTest(zip,city)
	[SQLCODE,zip,city,state] PUBLIC {
	NEW SQLCODE,%ROWCOUNT,%ROWID,%msg,state
	SET state="MA"
	&sql(UPDATE Sample.Person
		SET Home_City = :city, Home_State = :state
		WHERE Home_Zip = :zip)
	IF SQLCODE<0 {
		WRITE "SQLCODE error ",SQLCODE," ",%msg  QUIT
	}
		QUIT %ROWCOUNT
	}
}
0
0 148