0 关注者 · 478 帖子

SQL 是在关系数据库中存储、操作和检索数据的标准语言。

文章 姚 鑫 · 五月 2, 2021 5m read

第一章 简介global

InterSystems IRIS®的核心功能之一是其多维存储引擎。此功能允许应用程序以紧凑、高效的多维稀疏数组存储数据。这些数组称为全局数组。

本章介绍:

  • 什么是全局变量(globals ),以及可以对其执行的操作。
  • 全局变量的逻辑和物理结构,包括在分布式数据库体系结构中使用全局变量。
  • 如何使用全局变量在应用程序中存储和检索数据。
  • 如何使用全局变量。

特点

全局变量提供了一种在持久的多维数组中存储数据的易于使用的方法。

例如,可以使用名为^Settings的全局变量将值“Red”与键“Color”相关联:

SET ^Settings("Color")="Red"

可以利用全局变量的多维特性来定义更复杂的结构:

 SET ^Settings("Auto1","Properties","Color") = "Red"
 SET ^Settings("Auto1","Properties","Model") = "SUV"
 SET ^Settings("Auto2","Owner") = "Mo"
 SET ^Settings("Auto2","Properties","Color") = "Green"

全局变量具有以下功能:

  • 简单易用-全局变量和其他编程语言变量一样易于使用。
  • 多维-可以使用任意数量的下标指定全局内节点的地址。 例如,在 ^Settings("Auto2","Properties","Color")中,下标Color是全局设置中的第三级节点。 下标可以是整数、数字或字符串值,并且不需要是连续的。
  • 稀疏-用于寻址全局节点的下标高度压缩,不需要连续的值。
  • 高效-全局变量上的操作(插入、更新、删除、遍历和检索)都经过高度优化,可实现最高性能和并发性。还有用于特殊操作(如批量插入数据)的其他命令。有一组特殊的全局变量是为临时数据结构设计的(例如,用于对记录进行排序)。
  • 可靠-InterSystems IRIS数据库提供了许多机制来确保存储在全局数据库中的数据的可靠性,包括逻辑级和物理级日志记录。执行数据库备份操作时,将备份存储在全局数据库中的数据。
  • 分布式IRIS提供了多种方法来控制存储在全局数据库中的数据的物理位置。可以定义用于存储全局的物理数据库,或将全局的部分分布到多个数据库中。使用InterSystems IRIS的分布式数据库功能,可以在数据库和应用程序服务器系统网络中共享全局数据。此外,通过镜像技术,存储在一个系统上的全局数据库中的数据可以自动复制到另一个系统上。
  • 并发-全局支持多个进程之间的并发访问。在单个节点(数组元素)中设置和检索值始终是原子的:不需要锁定即可保证可靠的并发访问。此外,InterSystems IRIS支持一组强大的锁定操作,可用于为涉及多个节点的更复杂情况提供并发性。使用对象或SQL访问时,会自动处理此并发。
  • 事务性-InterSystems IRIS提供定义事务边界的命令;可以启动、提交或回滚事务。在回滚的情况下,事务内对全局变量所做的所有修改都将被撤消;数据库的内容将恢复到事务前的状态。通过将各种InterSystems IRIS锁定操作与事务结合使用,可以使用全局变量执行传统的ACID事务。(ACID事务提供原子性、一致性、隔离性和持久性。)。使用对象或SQL访问时,事务会自动处理。

注意:本文档中描述的全局变量不应与另一种类型的InterSystems IRIS数组变量混淆:进程私有全局变量。进程私有全局变量不是持久的;它们仅在创建它们程序期间持续。进程私有全局变量也不是并发的;它们只能由创建它们的进程访问。进程专用全局可以通过其多字符名称前缀:^||^|"^"|轻松地与全局区分开来。

例如

一个简单的例子就可以展示全局变量的易用性和性能。下面的程序示例创建一个10,000个节点的数组(如果存在,则首先将其删除)并将其存储在数据库中。可以尝试这样做,以了解全局变量的性能:

/// w ##class(PHA.TEST.Global).GlobalSimple()
ClassMethod GlobalSimple()
{
	Set start = $ZH  // get current time

	Kill ^Test.Global
	For i = 1:1:10000 {
	Set ^Test.Global(i) = i
	}

	Set elap = $ZH - start  // get elapsed time
	Write "Time (seconds): ",elap
	
	q ""
}
DHC-APP> w ##class(PHA.TEST.Global).GlobalSimple()
Time (seconds): .00307

我们还可以看到迭代和读取数组中的值需要多长时间(确保首先运行上面的示例来构建数组):

-读取持久数组-

/// w ##class(PHA.TEST.Global).ReadGlobalSimple()
ClassMethod ReadGlobalSimple()
{
	Set start = $ZH  // get current time
	Set total = 0
	Set count = 0

	// get key and value for first node
	Set i = $Order(^Test.Global(""),1,data)

	While (i '= "") {
		Set count = count + 1
		Set total = total + data

		// get key and value for next node
		Set i = $Order(^Test.Global(i),1,data)
	}

	Set elap = $ZH - start  // get elapsed time

	Write "Nodes:          ",count,!
	Write "Total:          ",total,!
	Write "Time (seconds): ",elap,!
	
	q ""
}
DHC-APP>w ##class(PHA.TEST.Global).ReadGlobalSimple()
Nodes:          10000
Total:          50005000
Time (seconds): .001879

在应用程序中使用

在InterSystems IRIS应用程序中,全局变量有多种使用方式,包括:

  • 作为对象和SQL引擎共享的底层存储机制。
  • 作为用于为对象和SQL数据提供各种索引(包括位图索引)的机制。
  • 作为用于执行不适合进程存储器的某些操作的工作空间。例如,当没有预先存在的索引可用于排序数据时,SQL引擎使用临时全局变量对数据进行排序。
  • 用于在对象或SQL访问方面难以表达或效率低下的持久性对象或SQL表上执行专用操作。例如,可以定义一个方法(或存储过程或Web方法)来对表中保存的数据执行专门的分析。通过使用方法,这样的操作是完全封装的;调用者只需调用该方法。
  • 实现特定于应用程序的自定义存储结构。许多应用程序需要存储难以用关系表示的数据。使用全局变量,可以定义自定义结构,并通过对象方法将其提供给外部客户端。
  • 用于InterSystems IRIS系统使用的各种特殊用途的数据结构,例如配置数据、类定义、错误消息和可执行代码。

全局变量不受关系模型的限制。 它们提供了开发针对特定应用程序优化的定制结构的自由。 对于许多应用程序来说,明智地使用全局变量可能是提供性能的秘密武器,而这种性能是关系应用程序开发人员梦寐以求的。

无论应用程序是否直接使用全局变量,了解它们的操作都是有用的。 理解全局及其功能将帮助设计更高效的应用程序,并为确定应用程序的最佳部署配置提供帮助。

0
0 274
文章 姚 鑫 · 四月 30, 2021 12m read

第八章 解释SQL查询计划(二)

SQL语句的详细信息

有两种方式显示SQL语句的详细信息:

  • 在SQL Statements选项卡中,通过单击左侧列中的Table/View/Procedure Name链接选择一个SQL Statement。 这将在单独的选项卡中显示SQL语句详细信息。 该界面允许打开多个选项卡进行比较。 它还提供了一个Query Test按钮,用于显示SQL Runtime Statistics页面。
  • 从表的Catalog Details选项卡(或SQL Statements选项卡)中,通过单击右边列中的Statement Text链接选择一个SQL语句。 这将在弹出窗口中显示SQL语句详细信息。

可以使用“SQL语句详细信息”显示来查看查询计划,并冻结或解冻查询计划。

“SQL语句详细信息”提供冻结或解冻查询计划的按钮。 它还提供了一个Clear SQL Statistics按钮来清除性能统计,一个Export按钮来将一个或多个SQL语句导出到一个文件,以及一个RefreshClose页面按钮。

SQL语句详细信息显示包含以下部分。 每个部分都可以通过选择部分标题旁边的箭头图标展开或折叠:

  • 语句详细信息,其中包括性能统计
  • 编译设置
  • 语句在以下例程中定义
  • 语句使用如下关系
  • 语句文本和查询计划(在其他地方描述)

声明的细节部分

  • 语句散列Statement hash:语句定义的内部散列表示形式,用作SQL语句索引的键(仅供内部使用)。 有时,看起来相同的SQL语句可能具有不同的语句散列项。 需要生成不同SQL语句的代码的设置/选项的任何差异都会导致不同的语句散列。 这可能发生在支持不同内部优化的不同客户端版本或不同平台上。
  • 时间戳Timestamp:最初,创建计划时的时间戳。 这个时间戳会在冻结/解冻之后更新,以记录计划解冻的时间,而不是重新编译计划的时间。 可能必须单击Refresh Page按钮来显示解冻时间戳。 将Plan Timestamp与包含该语句的例程/类的datetime值进行比较,可以知道,如果再次编译该例程/类,它是否使用了相同的查询计划。
  • 版本Version:创建计划的InterSystems IRIS版本。 如果“计划”状态是“冻结/升级”,则这是InterSystems IRIS的早期版本。 解冻查询计划时,“计划”状态变为“解冻”,“版本”变为当前的InterSystems IRIS版本。
  • 计划状态Plan state:冻结/显式、冻结/升级、解冻、解冻/并行。 Frozen/Explicit意味着该语句的计划已被显式用户操作冻结,无论生成此SQL语句的代码发生了什么变化,该冻结的计划都将是将要使用的查询计划。 冻结/升级意味着该语句的计划已被InterSystems IRIS版本升级自动冻结。 解冻意味着该计划目前处于解冻状态,可能被冻结。 Unfrozen/Parallel表示该计划被解冻,并使用%Parallel处理,因此不能被冻结。 NULL(空白)计划状态意味着没有关联的查询计划。
  • 自然查询Natural query:一个布尔标志,指示该查询是否是“自然查询”。 如果勾选此项,则该查询是自然查询,不会记录查询性能统计信息。 如果不检查,性能统计可能会被记录; 其他因素决定了统计数据是否真正被记录下来。 自然查询被定义为嵌入式SQL查询,它非常简单,记录统计数据的开销会影响查询性能。 将统计信息保存在自然查询上没有任何好处,因为查询已经非常简单了。 一个很好的自然查询示例是SELECT Name INTO:n FROM Table WHERE %ID=? 这个查询的WHERE子句是一个相等条件。 此查询不涉及任何循环或任何索引引用。 动态SQL查询(缓存查询)不会被标记为自然查询; 缓存查询的统计数据可能被记录,也可能不被记录。
  • 冻结计划不同Frozen plan different:冻结计划时,会显示该字段,显示冻结的计划与未冻结的计划是否不同。 冻结计划时,语句文本和查询计划将并排显示冻结的计划和未冻结的计划,以便进行比较。

本节还包括五个查询性能统计字段,将在下一节中进行描述。

性能统计数据

执行查询会将性能统计数据添加到相应的SQL语句。 此信息可用于确定哪些查询执行得最慢,哪些查询执行得最多。 通过使用这些信息,您可以确定哪些查询将通过优化提供显著的好处。

除了SQL语句名称、计划状态、位置和文本之外,还为缓存查询提供了以下附加信息:

  • 计数Count:运行此查询次数的整数计数。 如果对该查询产生不同的查询计划(例如向表中添加索引),则将重置该计数。
  • 平均计数Average count:每天运行此查询的平均次数。
  • 总时间Total time:运行此查询所花费的时间(以秒为单位)。
  • 平均时间Average time:运行此查询所花费的平均时间(以秒为单位)。 如果查询是缓存的查询,则查询的第一次执行所花费的时间很可能比从查询缓存中执行优化后的查询所花费的时间要多得多。
  • 标准差Standard deviation:总时间和平均时间的标准差。 只运行一次的查询的标准偏差为0。 运行多次的查询通常比只运行几次的查询具有更低的标准偏差。
  • 第一次看到的日期Date first seen:查询第一次运行(执行)的日期。 这可能与Last Compile Time不同,后者是准备查询的时间。

UpdateSQLStats任务会定期更新已完成的查询执行的查询性能统计数据。 这将最小化维护这些统计信息所涉及的开销。 因此,当前运行的查询不会出现在查询性能统计中。 最近完成的查询(大约在最近一个小时内)可能不会立即出现在查询性能统计中。

可以使用Clear SQL Statistics按钮清除这6个字段的值。

InterSystems IRIS不单独记录%PARALLEL子查询的性能统计数据。 %PARALLEL子查询统计信息与外部查询的统计信息相加。 由并行运行的实现生成的查询没有单独跟踪其性能统计信息。

InterSystems IRIS不记录“自然”查询的性能统计数据。 如果系统收集了统计信息,则会降低查询性能,而自然查询已经是最优的,因此没有进行优化的可能。

可以在“SQL语句”选项卡显示中查看多个SQL语句的查询性能统计信息。 您可以按任何列对SQL Statements选项卡列表进行排序。 这使得很容易确定,例如,哪个查询具有最大的平均时间。

还可以通过查询INFORMATION.SCHEMA.STATEMENTS类属性来访问这些查询性能统计数据,如查询SQL语句中所述。

编译设置部分

  • 选择模式Select mode:编译语句时使用的SelectMode。 对于DML命令,可以使用#SQLCompile Select; 默认为Logical。 如果#SQLCompile Select=Runtime,调用$SYSTEM.SQL.Util.SetOption()方法的SelectMode选项可以改变查询结果集的显示,但不会改变SelectMode值,它仍然是Runtime。
  • 默认模式Default schema(s):编译语句时设置的默认模式名。 这通常是在发出命令时生效的默认模式,尽管SQL可能使用模式搜索路径(如果提供的话)而不是默认模式名来解析非限定名称的模式。 但是,如果该语句是嵌入式SQL中使用一个或多个#Import宏指令的DML命令,则#Import指令指定的模式将在这里列出。
  • 模式路径Schema path:编译语句时定义的模式路径。 如果指定,这是模式搜索路径。 如果没有指定架构搜索路径,则此设置为空。 但是,对于在#Import宏指令中指定搜索路径的DML Embedded SQL命令,#Import搜索路径显示在默认模式设置中,并且该模式路径设置为空白。
  • 计划错误Plan Error:该字段仅在使用冻结计划时发生错误时出现。 例如,如果一个查询计划使用一个索引,则该查询计划被冻结,然后该索引从表中删除,就会出现如下的计划错误:Map 'NameIDX' not defined in table 'Sample.Person', but it was specified in the frozen plan for the query. 删除或添加索引将导致重新编译表,从而更改“最后编译时间”值。 一旦导致错误的条件得到纠正,Clear Error按钮可用于清除Plan Error字段——例如,通过重新创建缺失的索引。 在错误条件被纠正后使用“清除错误”按钮会导致“计划错误”字段和“清除错误”按钮消失。

例程和关系部分

语句在以下例程部分中定义:

  • 例程Routine:与缓存查询关联的类名(对于动态SQL DML),或者例程名(对于嵌入式SQL DML)。
  • 类型:类方法或MAC例程(对于嵌入式SQL DML)。
  • 上次编译时间Last Compile Time:例程的上次编译时间或准备时间。如果SQL语句解冻,重新编译MAC例程会同时更新此时间戳和Plan时间戳。如果SQL语句已冻结,则重新编译MAC例程仅更新此时间戳;在您解冻计划之前,Plan时间戳不会更改;然后Plan时间戳将显示计划解冻的时间。

语句使用以下关系部分列出了一个或多个用于创建查询计划的定义表。对于使用查询从另一个表提取值的INSERT,或者使用FROM子句引用另一个表的UPDATEDELETE,这两个表都在此处列出。每个表都列出了下列值:

  • 表或视图名称Table or View Name:表或视图的限定名称。
  • 类型Type:表或视图。
  • 上次编译时间Last Compile Time:表(持久化类)上次编译的时间。
  • Classname:与表关联的类名。

本节包括用于重新编译类的编译类选项。如果重新编译解冻计划,则所有三个时间字段都会更新。如果重新编译冻结的计划,则会更新两个上次编译时间字段,但不会更新计划时间戳。解冻计划并单击刷新页面按钮后,计划时间戳将更新为计划解冻的时间。

查询SQL语句

可以使用SQLTableStatements()存储查询返回指定表的SQL语句。下面的示例显示了这一点:

/// w ##class(PHA.TEST.SQL).SQLTableStatements()
ClassMethod SQLTableStatements()
{
	SET mycall = "CALL %Library.SQLTableStatements('Sample','Person')"
	SET tStatement = ##class(%SQL.Statement).%New()
	SET qStatus=tStatement.%Prepare(mycall)
	IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
	SET rset=tStatement.%Execute()
	IF rset.%SQLCODE '= 0 {WRITE "SQL error=",rset.%SQLCODE QUIT}
	DO rset.%Display()
}
DHC-APP>w ##class(PHA.TEST.SQL).SQLTableStatements()
 
 
Dumping result #1
SCHEMA  RELATION_NAME   PLAN_STATE      LOCATION        STATEMENT
SAMPLE  PERSON  0       %sqlcq.DHCdAPP.cls228.1 DECLARE C CURSOR FOR SELECT * INTO :%col(1) , :%col(2) , :%col(3) , :%col(4) , :%col(5) , :%col(6) , :%col(7) , :%col(8) , :%col(9) , :%col(10) , :%col(11) , :%col(12) , :%col(13) , :%col(14) , :%col(15) FROM SAMPLE . PERSON
SAMPLE  PERSON  0       Sample.Person.1 SELECT AGE , DOB , FAVORITECOLORS , HOME , NAME , OFFICE , SSN , SPOUSE , X__CLASSNAME , HOME_CITY , HOME_STATE , HOME_STREET , HOME_ZIP , OFFICE_CITY , OFFICE_STATE , OFFICE_STREET , OFFICE_ZIP INTO :%e ( ) FROM %IGNOREINDEX * SAMPLE . PERSON WHERE ID = :%rowid
...
CURSOR FOR SELECT P . NAME , P . AGE , E . NAME , E . AGE FROM %ALLINDEX SAMPLE . PERSON AS P LEFT OUTER JOIN SAMPLE . EMPLOYEE AS E ON P . NAME = E . NAME WHERE P . AGE > 21 AND %NOINDEX E . AGE < 65
SAMPLE  PERSON  0       PHA.TEST.SQL.1  SELECT NAME , SPOUSE INTO :name , :spouse FROM SAMPLE . PERSON WHERE SPOUSE IS NULL
SAMPLE  PERSON  0       PHA.TEST.ObjectScript.1 SELECT NAME , DOB , HOME INTO :n , :d , :h FROM SAMPLE . PERSON
 
70 Rows(s) Affected

可以使用INFORMATION_SCHEMA包表来查询SQL语句列表。InterSystems IRIS支持以下类:

  • INFORMATION_SCHEMA.STATEMENTS:包含当前名称空间中的当前用户可以访问的SQL语句索引项。
  • INFORMATION_SCHEMA.STATEMENT_LOCATIONS:包含调用SQL语句的每个例程位置:持久类名或缓存查询名。
  • INFORMATION_SCHEMA.STATEMENT_RELATIONS:包含SQL语句使用的每个表或视图条目。

以下是使用这些类的一些示例查询:

下面的示例返回命名空间中的所有SQL语句,列出哈希值(唯一标识规范化SQL语句的计算ID)、冻结状态标志(值0到3)、准备语句和保存计划时的本地时间戳以及语句文本本身:

SELECT Hash,Frozen,Timestamp,Statement FROM INFORMATION_SCHEMA.STATEMENTS

image

以下示例返回所有冻结计划的SQL语句,指示冻结的计划是否与未冻结的计划不同。请注意,解冻语句可以是Frozen=0Frozen=3。不能冻结的单行INSERT等语句在冻结列中显示NULL:

SELECT Frozen,FrozenDifferent,Timestamp,Statement FROM INFORMATION_SCHEMA.STATEMENTS
WHERE Frozen=1 OR Frozen=2

以下示例返回给定SQL表的所有SQL语句和语句所在的例程。(请注意,指定表名(SAMPLE.PERSON)时必须使用与SQL语句文本中相同的字母大小写:全部大写字母):

SELECT Statement,Frozen,STATEMENT_LOCATIONS->Location AS Routine,STATEMENT_LOCATIONS->Type AS RoutineType
       FROM INFORMATION_SCHEMA.STATEMENTS 
       WHERE STATEMENT_RELATIONS->Relation='SAMPLE.PERSON'

image

以下示例返回当前命名空间中具有冻结计划的所有SQL语句:

SELECT Statement,Frozen,Frozen_Different,STATEMENT_LOCATIONS->Location AS Routine,STATEMENT_LOCATIONS->Type AS RoutineType
       FROM INFORMATION_SCHEMA.STATEMENTS 
       WHERE Frozen=1 OR Frozen=2

以下示例返回当前命名空间中包含COUNT(*)聚合函数的所有SQL语句。(请注意,指定语句文本(COUNT(*))时必须使用与SQL语句文本相同的空格):

       SELECT Statement,Frozen,STATEMENT_LOCATIONS->Location AS Routine,STATEMENT_LOCATIONS->Type AS RoutineType
       FROM INFORMATION_SCHEMA.STATEMENTS 
       WHERE Statement [ ' COUNT ( * )

image

导出和导入SQL语句

可以将SQL语句作为XML格式的文本文件导出或导入。这使可以将冻结的计划从一个位置移动到另一个位置。SQL语句导出和导入包括关联的查询计划。

可以导出单个SQL语句,也可以导出命名空间中的所有SQL语句。

可以导入先前导出的包含一个或多个SQL语句的XML文件。

注意:将SQL语句作为XML导入不应与从文本文件导入和执行SQL DDL代码混淆。

导出SQL语句

导出单个SQL语句:

  • 使用SQL语句详细资料页导出按钮。在管理门户系统资源管理器SQL界面中,选择SQL语句选项卡,然后单击语句以打开SQL语句详细信息页。选择导出按钮。这将打开一个对话框,允许选择将文件导出到服务器(数据文件)或浏览器。

    • 服务器(默认):输入导出XML文件的完整路径名。第一次导出时,此文件的默认名称为statementexport.xml。当然,可以指定不同的路径和文件名。成功导出SQL语句文件后,上次使用的文件名将成为默认值。

    默认情况下,未选中在后台运行导出复选框。

    • Browser:将文件statementexport.xml导出到用户默认浏览器中的新页面。可以为浏览器导出文件指定其他名称,或指定其他软件显示选项。
  • 使用$SYSTEM.SQL.Statement.ExportFrozenPlans()方法。

导出命名空间中的所有SQL语句:

  • 使用管理门户中的导出所有对帐单操作。从管理门户系统资源管理器SQL界面中,选择操作下拉列表。从该列表中选择Export all Statements。这将打开一个对话框,允许您将命名空间中的所有SQL语句导出到服务器(数据文件)或浏览器。
    • 服务器(默认):输入导出XML文件的完整路径名。第一次导出时,此文件的默认名称为statementexport.xml。当然,可以指定不同的路径和文件名。成功导出SQL语句文件后,上次使用的文件名将成为默认值。

默认情况下,在后台运行导出复选框处于选中状态。这是导出所有SQL语句时的建议设置。选中在后台运行导出时,系统会为提供一个查看后台列表页面的链接,可以在该页面中查看后台作业状态。

  • Browser:将文件statementexport.xml导出到用户默认浏览器中的新页面。可以为浏览器导出文件指定其他名称,或指定其他软件显示选项。

使用$SYSTEM.SQL.Statement.ExportAllFrozenPlans()方法。

导入SQL语句

从先前导出的文件导入一条或多条SQL语句:

  • 使用管理门户中的导入对帐单操作。从管理门户系统资源管理器SQL界面中,选择操作下拉列表。从该列表中选择Import Statements。这将打开一个对话框,允许指定导入XML文件的完整路径名。

默认情况下,在后台运行导入复选框处于选中状态。这是导入SQL语句文件时的推荐设置。选中在后台运行导入时,系统会为您提供一个查看后台列表页面的链接,可以在该页面中查看后台作业状态。

使用$SYSTEM.SQL.Statement.ImportFrozenPlans()方法。

查看和清除后台任务

在管理门户系统操作选项中,选择后台任务,查看导出和导入后台任务的日志。可以使用清除日志按钮清除此日志。

0
0 182
文章 姚 鑫 · 四月 29, 2021 10m read

第九章 冻结计划

大多数SQL语句都有一个关联的查询计划。查询计划是在准备SQL语句时创建的。默认情况下,添加索引和重新编译类等操作会清除此查询计划。下次调用查询时,将重新准备查询并创建新的查询计划。冻结计划使可以跨编译保留(冻结)现有查询计划。查询执行使用冻结的计划,而不是执行新的优化并生成新的查询计划。

对系统软件的更改也可能导致不同的查询计划。通常,这些升级会带来更好的查询性能,但软件升级可能会降低特定查询的性能。冻结计划使可以保留(冻结)查询计划,以便查询性能不会因系统软件升级而改变(降级或提高)。

如何使用冷冻计划

使用冻结计划有两种策略-乐观策略和悲观策略:

  • 乐观:如果假设更改系统软件或类定义会提高性能,请使用此策略。运行查询并冻结计划。导出(备份)冻结的计划。解冻该计划。更改软件。重新运行查询。这会产生一个新的计划。比较这两个查询的性能。如果新计划没有提高性能,可以从备份文件中导入先前冻结的计划。
  • 悲观:如果假设系统软件或类定义的更改可能不会提高特定查询的性能,请使用此策略。运行查询并冻结计划。更改软件。使用%NOFPLAN关键字重新运行查询(这会导致冻结的计划被忽略)。比较这两个查询的性能。如果忽略冻结的计划没有提高性能,请保持冻结该计划并从查询中删除%NOFPLAN

软件版本升级自动冻结计划

将InterSystems IRIS®Data Platform升级到新的主要版本时,现有的查询计划将自动冻结。这可确保重大软件升级永远不会降低现有查询的性能。升级软件版本后,对性能关键型查询执行以下步骤:

  1. 执行计划状态为冻结/升级的查询,并监控性能。这是在软件升级之前创建的优化查询计划。
  2. %NOFPLAN关键字添加到查询中,然后执行并监视性能。这将使用软件升级提供的SQL优化器优化查询计划。它不会解冻现有的查询计划。
  3. 比较性能指标。
  • 如果%NOFPLAN性能更好,则软件升级改进了查询计划。解冻查询计划。删除%NOFPLAN关键字。
  • 如果%NOFPLAN性能较差,则软件升级会使查询计划降级。保持查询计划冻结状态,将查询计划从冻结/升级升级为冻结/显式。删除%NOFPLAN关键字。
  1. 测试性能关键型查询后,可以解冻所有剩余的冻结/升级计划。

当在比最初创建计划时使用的InterSystems软件版本更新的InterSystems软件版本下准备/编译查询时,会发生这种自动冻结。例如,考虑一条在系统软件版本xxxx.1下准备/编译的SQL语句。随后升级到版本xxxx.2,再次准备/编译SQL语句。系统将检测到这是SQL语句在新版本上的第一次准备/编译,并自动将计划状态标记为冻结/升级,并将现有计划用于新的准备/编译。这确保使用的查询计划不会比以前版本的查询计划差。

只有主要版本的InterSystems系统软件升级才会自动冻结现有查询计划。维护发布版本升级不会冻结现有查询计划。例如,主要版本升级(如从2018.1升级到2019.1)将执行此操作。维护版本升级(如2018.1.0到2018.1.1)不执行此操作。

在管理门户SQL界面中,SQL语句计划状态列将这些自动冻结的计划指示为冻结/升级,计划版本指示原始计划的系统间软件版本。

可以使用INFORMATION.SCHEMA.STATEMENTSFrozen=2属性列出当前命名空间中的所有冻结/升级计划。

可以使用以下$SYSTEM.SQL.Statement方法冻结单个查询计划或多个查询计划:FreezeStatement()用于单个计划;FreezeRelation()用于关系的所有计划;FreezeSchema()用于架构的所有计划;FreezeAll()用于当前命名空间中的所有计划。有相应的解冻方法。

  • 冻结方法可以提升(“冻结”)标记为冻结/升级到冻结/显式的查询计划。通常,可以使用此方法有选择地将适当的冻结/升级计划升级为冻结/显式,然后解冻所有剩余的冻结/升级计划。
  • 解冻方法可以解冻指定范围内的冻结/升级查询计划:命名空间、架构、关系(表)或单个查询。

冻结计划界面

冻结计划界面有两种,用途不同:

  • Management Portal SQL语句界面,用于冻结(或解冻)单个查询的计划。
  • $SYSTEM.SQL.Statement冻结和解冻方法,用于冻结或解冻命名空间、架构、表或单个查询的所有计划。

在Management Portal SQL界面中,选择Execute Query选项卡。编写查询,然后单击显示计划按钮以显示当前查询执行计划。如果计划被冻结,则查询计划部分的第一行是“冻结计划”。

在管理门户SQL界面中,选择SQL语句选项卡。这将显示SQL语句列表。此列表的计划状态列指定解冻、解冻/并行、冻结/显式或冻结/升级。(如果语句没有关联的查询计划,则计划状态列为空。)

可以使用INFORMATION.SCHEMA.STATEMENTS Frozen属性值列出当前命名空间中所有SQL语句的计划状态:UNFRECTED(0)Frozen/EXPLICIT(1)Frozen/Upgrade(2)UNFORMATED/PARALLEL(3)

要冻结或解冻计划,请在SQL语句文本列中选择SQL语句。这将显示“SQL语句详细信息”框。在此框的底部显示对帐单文本和查询计划。如果计划未冻结,则这些横断面的背景颜色为绿色,如果计划已冻结,则背景颜色为蓝色。在其正上方的对帐单操作下,可以根据需要选择冻结计划或解冻计划按钮。然后选择关闭。

  • 冻结计划按钮:单击此按钮将冻结此语句的查询优化计划。冻结计划并编译该SQL语句时,SQL编译将使用冻结的计划信息并跳过查询优化阶段。
  • 解冻计划按钮:点击该按钮将删除该语句冻结的计划,该语句的新编译将进入查询优化阶段,以确定要使用的最佳计划。

还可以使用$SYSTEM.SQL.Statement冻结和解冻方法冻结或解冻一个或多个计划。通过指定适当的方法,可以指定冻结或解冻操作的范围:单个计划的FreezeStatement();关系的所有计划的FreezeRelation();架构的所有计划的FreezeSchema();当前命名空间中的所有计划的FreezeAll()。有相应的解冻方法。

权限

用户只能查看他们具有EXECUTE权限的那些SQL语句。这既适用于Management Portal SQL语句列表,也适用于INFORMATION.SCHEMA.STATEMENTS类查询。

管理门户SQL语句访问要求对%Development资源具有“USE”权限。任何可以在管理门户中看到SQL语句的用户都可以冻结或解冻该语句。

对于SQL语句的目录访问,如果您具有执行该语句的权限或对%Development资源具有“Use”权限,则可以看到这些语句。

对于$SYSTEM.SQL.Statement冻结或解冻方法调用,必须对%Developer资源拥有“U”权限。

冻结计划不同

如果计划被冻结,可以确定解冻该计划是否会导致不同的计划,而无需实际解冻该计划。此信息可以帮助您确定哪些SQL语句值得使用%NOFPLAN进行测试,以确定解冻计划是否会带来更好的性能。

可以使用INFORMATION.SCHEMA.STATEMENTSFrozenDifferent属性列出当前命名空间中此类型的所有冻结计划。

冻结的计划可能会因以下任一操作而与当前计划不同:

  • 重新编译该表或该表引用的表
  • 使用SetMapSelecability()激活或停用索引
  • 在表上运行TuneTable
  • 升级InterSystems软件版本

重新编译会自动清除现有的缓存查询。对于其他操作,必须手动清除现有缓存查询才能使新查询计划生效。

这些操作可能会也可能不会产生不同的查询计划。有两种方法可以确定它们是否这样做:

  • 手工检查个别冻结计划
  • 每天自动扫描所有冻结计划

如果计划尚未由这两个操作中的任何一个检查,或者计划未冻结,则列出新计划的SQL语句列为空。解冻选中的冻结计划会将新建计划列重置为空。

手动冻结计划检查

在冻结计划的SQL语句详细资料页的顶部有一个检查冻结按钮。按此按钮将显示解冻不同计划复选框。如果选中此框,则解冻计划将导致不同的查询计划。

对冻结计划执行此检查冻结测试后:

  • 如果选中解冻计划不同框,则列出新计划的SQL语句列包含“1”。这表明解冻计划将导致不同的计划。
  • 如果未选中解冻计划不同框,则列出新计划的SQL语句列将包含“0”。这表明解冻计划不会产生不同的计划。
    • 已冻结的缓存查询的New Plan为“0”;清除缓存查询,然后解冻该计划会导致SQL语句消失。
    • 已冻结的Natural查询在New Plan列中为空。

执行此测试后,检查冻结按钮消失。如果要重新测试冻结的计划,请选择刷新页面按钮。这将重新显示检查冻结按钮。

日冻结计划自动检查

InterSystems SQL每晚2:00自动扫描SQL语句清单中的所有冻结语句。这次扫描最多持续一个小时。如果扫描未在一小时内完成,系统会记下它停止的位置,并从该点继续进行下一次每日扫描。可以使用管理门户监视此每日扫描或强制其立即扫描:选择系统操作、任务管理器、任务计划,然后选择扫描冻结计划任务。

此扫描检查所有冻结的计划:

  • 如果冻结的计划具有与当前版本相同的InterSystems软件版本,InterSystems IRIS®Data Platform将计算两个计划的引用表和时间戳的散列,以创建可能已更改的内部计划列表。对于这个子集,它然后执行两个计划的逐个字符串比较,以确定哪些计划实际上不同。如果两个计划之间有任何不同(无论有多小),它都会在列出New Plan列的SQL语句中用“1”标记SQL语句。这表明解冻计划将导致不同的查询计划。
  • 如果冻结的计划具有与当前版本相同的InterSystems IRIS版本,并且两个计划的逐字符串比较完全匹配,则它会将列出新计划的SQL语句列中的SQL语句标记为“0”。这表明解冻计划不会导致不同的查询计划。
  • 如果冻结的计划具有与当前版本(冻结/更新)不同的InterSystems软件版本,InterSystems IRIS将确定对SQL优化器逻辑的更改是否会导致不同的查询计划。如果是,它将用“1”标记“SQL Statements Listing New Plan”列中的SQL语句。否则,它会用“0”标记SQL语句New Plan列。

可以通过调用INFORMATION.SCHEMA.STATEMENTS来检查此扫描的结果。以下示例返回所有冻结计划的SQL语句,指示冻结的计划是否与未冻结的计划不同。请注意,解冻语句可以是Frozen=0Frozen=3

SELECT Frozen,FrozenDifferent,Timestamp,Statement FROM INFORMATION_SCHEMA.STATEMENTS 
WHERE Frozen=1 OR Frozen=2

冻结计划出错

如果语句的计划被冻结,并且计划使用的定义发生了某些更改,从而导致计划无效,则会发生错误。例如,如果从语句PLAN使用的类中删除了索引:

  • 该声明的计划仍处于冻结状态。
  • 在“SQL语句详细信息”页上,“编译设置”区域显示“计划错误”字段。例如,如果查询计划使用索引名indxdob,然后您修改了类定义以删除索引indxdob,则会显示如下消息: Map 'indxdob' not defined in table 'Sample.Mytable', but it was specified in the frozen plan for the query.
  • 在SQL语句详细资料页上,查询计划区域显示由于冻结计划中的错误而无法确定计划。

如果在冻结计划处于错误状态时重新执行查询,则InterSystems IRIS不使用冻结计划。相反,系统会创建一个新的查询计划,该计划将在给定当前定义的情况下工作,并执行查询。此查询计划被分配了与前一个查询计划相同的缓存查询类名。

在计划解冻或修改定义以使计划返回有效状态之前,出错的计划将一直处于错误状态。

如果修改定义以使计划返回有效状态,请转到SQL语句详细资料页,然后按清除错误按钮以确定是否已更正错误。如果更正,计划错误字段将消失;否则将重新显示计划错误消息。如果已更正定义,则不必显式清除计划错误,SQL即可开始使用冻结计划。如果已更正定义,则清除错误按钮会使SQL语句详细资料页的冻结查询计划区域再次显示执行计划。

计划错误可能是 “soft error.”。当计划使用索引,但查询优化器当前无法选择该索引时,可能会出现这种情况,因为SetMapSelecability()已将其可选择性设置为0。这样做可能是为了[重建]索引。当InterSystems IRIS遇到具有冻结计划的语句的软错误时,查询处理器会尝试自动清除错误并使用冻结计划。如果该计划仍然出错,则该计划将再次标记为出错,并且查询执行将尽可能使用最佳计划。

%NOFPLAN关键字

可以使用%NOFPLAN关键字覆盖冻结的计划。包含%NOFPLAN关键字的SQL语句将生成新的查询计划。冻结的计划将保留,但不会使用。这允许测试生成的计划行为,而不会丢失冻结的计划。

DECLARE <cursor name> CURSOR FOR SELECT %NOFPLAN ...  
SELECT %NOFPLAN ....  
INSERT [OR UPDATE] %NOFPLAN ...  
DELETE %NOFPLAN ...  
UPDATE %NOFPLAN 

SELECT语句中,%NOFPLAN关键字只能在查询中的第一个SELECT之后立即使用:它只能与UNION查询的第一个分支一起使用,不能在子查询中使用。%NOFPLAN关键字必须紧跟在SELECT关键字之后,位于DISTINCTTOP等其他关键字之前。

导出和导入冻结计划

可以将SQL语句作为XML格式的文本文件导出或导入。这使可以将冻结的计划从一个位置移动到另一个位置。SQL语句导出和导入包括关联查询计划的编码版本和指示该计划是否冻结的标志。

0
0 144
文章 姚 鑫 · 四月 28, 2021 11m read

第八章 解释SQL查询计划(一)

SQL语句

这个SQL语句列表为每个表提供了SQL查询和其他操作的记录,包括插入、更新和删除。 这些SQL语句链接到一个查询计划,该链接提供冻结该查询计划的选项。

系统为每个SQL DML操作创建一条SQL语句。 这提供了一个按表、视图或过程名称列出的SQL操作列表。 如果更改表定义,可以使用此SQL Statements列表来确定每个SQL操作的查询计划是否会受到此DDL更改的影响,以及/或是否需要修改某个SQL操作。 然后,可以:

  • 确定每个SQL操作使用哪个查询计划。 可以决定使用反映对表定义所做更改的修改后的查询计划。 或者可以冻结当前查询计划,保留在更改表定义之前生成的查询计划。
  • 根据对表定义所做的更改,确定是否对对该表执行SQL操作的例程进行代码更改。

注意:SQL语句是一个SQL例程列表,它们可能会受到表定义更改的影响。 它不应该用作表定义或表数据更改的历史记录。

创建SQL语句操作

下面的SQL操作会创建相应的SQL语句:

数据管理(DML)操作包括对表的查询、插入、更新和删除操作。 每个数据管理(DML)操作(动态SQL和嵌入式SQL)在执行时都会创建一个SQL语句。

  • 动态SQL SELECT命令在准备查询时创建SQL语句。 此外,在管理门户缓存查询列表中创建了一个条目。
  • 嵌入式SQL基于指针的SELECT命令在OPEN命令调用声明的查询时创建SQL语句。管理门户缓存查询列表中不会创建单独的条目。

如果查询引用多个表,则在名称空间的SQL语句中创建一条SQL语句,该语句列出表/视图/过程名列中的所有被引用表,并且对于每个单独的被引用表,该表的SQL语句列表都包含该查询的条目。

SQL语句是在第一次准备查询时创建的。如果多个客户端发出相同的查询,则只记录第一次准备。例如,如果JDBC发出一个查询,然后ODBC发出一个相同的查询,那么SQL语句索引将只有关于第一个JDBC客户端的信息,而不是关于ODBC客户端的信息。

大多数SQL语句都有关联的查询计划。 创建该查询计划时,将解冻该查询计划; 可以随后将该查询计划指定为冻结计划。 带有查询计划的SQL语句包括涉及SELECT操作的DML命令。 下面的“计划状态”部分列出了没有查询计划的SQL语句。

注意:SQL语句只列出SQL操作的最新版本。 除非冻结SQL语句,否则InterSystems IRIS®数据平台将用下一个版本替换它。 因此,在例程中重写和调用SQL代码将导致旧的SQL代码从SQL语句中消失。

其他SQL语句操作

下面的SQL命令执行更复杂的SQL语句操作:

  • CREATE TRIGGER: 在定义触发器的表中,无论是在定义触发器还是在提取触发器时,都不会创建SQL语句。 但是,如果触发器对另一个表执行DML操作,那么定义触发器将在被触发器代码修改过的表中创建一个SQL语句。 Location指定在其中定义触发器的表。 在定义触发器时定义SQL语句; 删除触发器将删除SQL语句。 触发触发器不会创建SQL语句。
  • CREATE VIEW 不创建SQL语句,因为没有编译任何内容。 它也不会更改源表的SQL语句的Plan Timestamp。 然而,为视图编译DML命令会为该视图创建一个SQL语句。

List SQL语句

本节介绍使用Management Portal界面列出SQL语句的详细信息。 也可以使用^rINDEXSQL全局返回SQL语句的索引列表。 注意,这个SQL语句List可能包含过时的(不再有效的)List

从Management Portal SQL界面可以列出如下SQL语句:

  • SQL语句选项卡:此选项卡列出名称空间中的所有SQL语句,先按模式排序,然后按每个模式中的表名/视图名排序。此列表仅包括当前用户拥有权限的那些表/视图。如果SQL语句引用多个表,则表/视图/过程名列将按字母顺序列出所有被引用的表。
  • 通过单击列标题,可以按表/视图/过程名、计划状态、位置、SQL语句文本或列表中的任何其他列对SQL语句列表进行排序。这些可排序列使能够快速查找,例如,所有冻结计划(计划状态)、所有缓存查询(位置)或最慢的查询(平均时间)。
  • 可以使用此选项卡提供的Filter选项将列出的SQL语句缩小到指定的子集。 指定的筛选器字符串筛选SQL语句列表中的所有数据,最有用的是模式或模式。 表名、例程位置或SQL语句文本中找到的子字符串。 过滤字符串不区分大小写,但必须紧跟语句文本标点空格(name , age, not name,age)。 如果查询引用了多个表,如果它选择了表/视图/过程名称列中的任何引用表,则Filter包括SQL语句。 过滤选项是用户自定义的。
  • 最大行选项默认为1,000。 最大值为10,000。 最小值为10。 要列出超过10,000条SQL语句,请使用INFORMATION_SCHEMA.STATEMENTS。 页面大小和最大行选项是用户自定义的。
  • Catalog Details选项卡:选择一个表并显示其Catalog详细信息。 此选项卡提供了一个表的SQL语句按钮,用于显示与该表关联的SQL语句。 注意,如果一个SQL语句引用了多个表,那么它将在表的SQL语句列表中列出每个被引用的表,但只有当前选择的表在表名列中列出。

通过单击列标题,可以根据列表的任何列对表的SQL语句列表进行排序。

可以使用SQLTableStatements()目录查询或INFORMATION_SCHEMA。 语句,列出根据各种条件选择的SQL语句,如下面的查询SQL语句中所述。

列表列

SQL语句选项卡列出名称空间中的所有SQL语句。目录详细信息选项卡表的SQL语句按钮列出了所选表的SQL语句。这两个列表都包含以下列标题:

  • #:列表行的顺序编号。这些数字与特定的SQL语句没有关联。
  • 表/视图/过程名:限定的SQL表(或视图或过程)名:schema.name。如果SQL语句查询引用了多个表或视图,则所有这些表或视图都会在此处列出。
  • 计划状态:请参阅下面的计划状态。
  • 新计划:见“冻结计划”一章中不同的新计划。
  • 自然查询:请参阅下面的语句详细信息部分。
  • 计数:请参阅下面的性能统计数据。
  • 平均计数:请参阅下面的性能统计数据。
  • 总时间:请参阅下面的性能统计数据。
  • 平均时间:请参阅下面的性能统计数据。
  • 标准开发人员:请参阅下面的性能统计数据。
  • Location(S):编译查询的位置,例程名称(对于嵌入式SQL)或缓存查询名称(对于动态SQL)。如果包名为%sqlcq,则SQL语句为缓存查询。
  • SQL语句文本:规范化格式的SQL语句文本(截断为128个字符),可能与以下SQL语句文本中指定的命令文本不同。

计划状态

计划状态列出以下内容之一:

  • 解冻Unfrozen:未冻结,可冻结。
  • 解冻/平行Unfrozen/Parallel::未冻结,不能冻结。
  • 冻结/显式Frozen/Explicit:由用户动作冻结,可以解冻。
  • Frozen/Upgrade:被InterSystems IRIS版本升级冻结,可以解冻。
  • blank:没有关联的查询计划:
    • INSERT... VALUES() 命令创建的SQL语句没有关联的查询计划,因此无法解冻或冻结(计划状态列为空)。尽管此SQL命令不会生成查询计划,但它在SQL语句中的列表仍然很有用,因为它允许快速定位针对该表的所有SQL操作。例如,如果向表中添加一列,则可能需要找出该表的所有SQL插入的位置,以便可以更新这些命令以包括此新列。
    • 基于游标的UPDATEDELETE命令没有关联的查询计划,因此不能解冻或冻结(“计划状态”列为空)。对已声明的游标执行OPEN命令会生成一条带有关联查询计划的SQL语句。使用该游标的嵌入式SQL语句(FETCH cursor, UPDATE...WHERE CURRENT OF cursor, DELETE...WHERE CURRENT OF cursor, and CLOSE cursor)不生成单独的SQL语句。即使基于游标的UPDATEDELETE不会产生查询计划,但SQL语句中列出的查询计划仍然很有用,因为它允许快速定位针对该表的所有SQL操作。

SQL语句文本

SQL语句文本通常不同于SQL命令,因为SQL语句生成规范化了字母和空格。 其他差异如下:

如果从Management Portal接口或SQL Shell接口发出查询,所得到的SQL语句与在SELECT语句前面加上DECLARE QRS CURSOR FOR(其中“QRS”可以是各种生成的游标名称)的查询不同。 这允许语句文本与Dynamic SQL缓存的查询相匹配。

如果SQL命令指定了一个非限定的表或视图名,那么生成的SQL语句将使用模式搜索路径(如果提供了DML)或默认模式名来提供模式。

SQL语句文本在1024个字符之后被截断。 要查看完整的SQL语句文本,请显示SQL语句详细信息。

一个SQL命令可能会产生多个SQL语句。 例如,如果一个查询引用一个视图,SQL Statements将显示两个语句文本,一个列在视图名称下,另一个列在基础表名称下。 冻结任意一条语句都会导致两个语句的Plan State为Frozen。

当通过xDBC准备SQL语句时,如果需要这些选项来生成语句索引散列,则SQL语句生成会向语句文本添加SQL Comment Options (# Options)。 如下面的例子所示:

DECLARE C CURSOR FOR SELECT * INTO :%col(1) , :%col(2) , :%col(3) , :%col(4) , :%col(5) FROM SAMPLE . COMPANY /*#OPTIONS {"xDBCIsoLevel":0} */ 

陈旧的SQL语句

删除与SQL语句关联的例程或类时,不会自动删除SQL语句列表。这种类型的SQL语句列表称为陈旧。由于访问此历史信息以及与SQL语句相关联的性能统计信息通常很有用,因此这些过时的条目将保留在管理门户SQL语句列表中。

可以使用Clean Stale(清除陈旧)按钮删除这些陈旧条目。清除陈旧删除关联例程或类(表)不再存在或不再包含SQL语句查询的所有非冻结SQL语句。清除陈旧不会删除冻结的SQL语句。

可以使用$SYSTEM.SQL.Statement.Clean()方法执行相同的清除陈旧操作。

如果删除与SQL语句关联的表(持久化类),则会修改表/视图/过程名称列,如下例所示:SAMPLE.MYTESTTABLE - Deleted??; ;已删除表的名称将转换为全部大写字母,并标记为“DELETED??”。或者,如果SQL语句引用了多个表:SAMPLE.MYTESTTABLE - Deleted?? Sample.Person.

  • 对于动态SQL查询,删除表时Location列为空,因为与该表关联的所有缓存查询都已自动清除。CLEAN STALE删除SQL语句。
  • 对于嵌入式SQL查询,Location列包含用于执行查询的例程的名称。当更改例程使其不再执行原始查询时,位置列为空。CLEAN STALE删除SQL语句。删除查询使用的表时,该表被标记“Deleted??”;Clean Stale不会删除SQL语句。

注:系统任务在所有名称空间中每小时自动运行一次,以清除任何可能过时或具有过时例程引用的SQL语句的索引。执行此操作是为了维护系统性能。此内部清理不会反映在管理门户SQL语句列表中。可以使用管理门户监视此每小时一次的清理或强制其立即执行。要查看此任务上次完成和下次调度的时间,请依次选择系统操作、任务管理器、任务调度,然后查看清理SQL语句索引任务。可以单击任务名称查看任务详细信息。在Task Details(任务详细信息)显示中,可以使用Run(运行)按钮强制立即执行任务。请注意,这些操作不会更改SQL语句清单;必须使用Clean Stale来更新SQL语句清单。

数据管理(DML)SQL语句

创建SQL语句的数据管理语言(DML)命令包括:INSERTUPDATEINSERTUPDATEDELETETRUNCATE TABLESELECTOPEN CURSOR(用于声明的基于游标的SELECT)。可以使用动态SQL或嵌入式SQL来调用DML命令。可以为表或视图调用DML命令,InterSystems IRIS将创建相应的SQL语句。

注意:系统在准备动态SQL或打开嵌入式SQL游标时(而不是在执行DML命令时)创建SQL语句。SQL语句时间戳记录此SQL代码调用的时间,而不是查询执行的时间(或是否)。因此,SQL语句可能表示从未实际执行的表数据更改。

准备动态SQL DML命令将创建相应的SQL语句。与此SQL语句关联的位置是缓存查询。动态SQL是在从管理门户SQL界面、SQL Shell界面执行SQL或从.txt文件导入时准备的。清除未冻结的缓存查询会将相应的SQL语句标记为清除陈旧删除。清除冻结的缓存查询会删除相应SQL语句的位置值。解冻SQL语句会将其标记为Clean Stale删除。

执行非游标嵌入式SQL数据管理语言(DML)命令将创建相应的SQL语句。每个嵌入式SQL DML命令都会创建相应的SQL语句。如果一个例程包含多个嵌入式SQL命令,则每个嵌入式SQL命令都会创建一个单独的SQL语句。(某些嵌入式SQL命令会创建多条SQL语句。)。SQL语句清单的Location列指定包含嵌入式SQL的例程。通过这种方式,SQL语句维护每个嵌入式SQL DML命令的记录。

打开基于游标的嵌入式SQL数据管理语言(DML)例程将创建带有查询计划的SQL语句。 关联的嵌入式SQL语句(FETCH游标、CLOSE游标)不会生成单独的SQL语句。 在FETCH游标之后,一个关联的UPDATE table WHERE CURRENT OF cursor 或DELETE FROM table WHERE CURRENT OF cursor会生成一个单独的SQL语句,但不会生成单独的Query Plan

插入文字值的INSERT命令将创建一个“计划状态”列为空的SQL语句。 由于该命令不会创建查询计划,因此无法冻结SQL语句。

select命令

调用查询将创建相应的SQL语句。 它可以是一个简单的SELECT操作,也可以是一个基于指针的SELECT/FETCH操作。 可以对表或视图发出查询。

  • 包含JOIN的查询为每个表创建相同的SQL语句。 Location是清单中存储的每个表的相同查询。 如SQL语句详细信息例程和关系部分所述,该语句使用以下关系列出所有表。
  • 包含选择项子查询的查询为每个表创建相同的SQL语句。 Location是清单中存储的每个表的相同查询。 如SQL语句详细信息例程和关系部分所述,该语句使用以下关系列出所有表。
  • 引用外部(链接)表的查询不能被冻结。
  • 一个包含FROM子句%PARALLEL关键字的查询可以创建多个SQL语句。 你可以通过调用来显示这些生成的SQL语句:

image

这将显示包含原始查询的语句哈希的Statement列和包含生成的查询版本的语句哈希的ParentHash列。

%PARALLEL查询的SQL语句的计划状态为“未冻结/并行”,不能被冻结。

  • 不包含FROM子句(因此不引用任何表)的查询仍然创建SQL语句。 例如:SELECT $LENGTH('this string')创建一个SQL语句,表列值%TSQL_sys.snf
0
0 154
文章 姚 鑫 · 四月 27, 2021 5m read

第七章 解释SQL查询计划

本章介绍由ShowPlan生成的InterSystems SQL查询访问计划中使用的语言和术语。

存储在映射中的表

SQL表存储为一组映射。 每个表都有一个包含表中所有数据的主映射; 表还可以有其他的映射,如索引映射和位图。 每个映射可以被描绘成一个多维全局,其中一些字段的数据在一个或多个下标中,其余字段存储在节点值中。 下标控制要访问的数据。

  • 对于主映射,RowIDIDKEY字段通常用作映射下标。
  • 对于索引映射,通常将其他字段用作前导下标,将RowID/IDKEY字段用作附加的较低级别的下标。
  • 对于位图,可以将位图层视为附加的RowID下标级别。但是,位图只能用于为正整数的RowID

发展计划

编译SQL查询会生成一组指令来访问和返回查询指定的数据。 这些指令表示为. int例程中的ObjectScript代码。

指令及其执行顺序受到SQL编译器中有关查询中涉及的表的结构和内容的数据的影响。 编译器尝试使用表大小和可用索引等信息,以使指令集尽可能高效。

查询访问计划(ShowPlan)是对结果指令集的可读翻译。 查询的作者可以使用这个查询访问计划来查看将如何访问数据。 虽然SQL编译器试图最有效地利用查询指定的数据,但有时查询的作者对存储的数据的某些方面的了解要比编译器清楚得多。 在这种情况下,作者可以利用查询计划修改原始查询,为查询编译器提供更多的信息或更多的指导。

阅读计划

“ShowPlan”的结果是一系列关于访问和显示查询中指定的数据的处理的语句。 下面提供了关于如何解释ShowPlan语句的信息。

访问映射

一个查询计划可以访问多个表。 当访问一个表时,计划可以访问单个映射(索引或主映射)、两个映射(索引映射后面跟着主映射),或者,对于多索引计划,可以访问多个映射。

在通过映射访问数据时,计划指示使用的下标。 它还指示实际的下标值是什么:一个给定值、一组给定值、一个值范围,或该下标在表中显示的所有值。 选择哪一个取决于查询中指定的条件。 显然,访问单个或几个下标值要比访问该下标级别上的所有值快得多。

条件和表达式

当查询运行时,将测试查询指定的各种条件。 除了前面提到的某些限制下标的条件外,ShowPlan输出没有显式地指示条件的测试。 尽早测试条件总是最好的。 测试各种条件的最佳地点可以从计划细节中推断出来。

类似地,ShowPlan不详细描述表达式和子表达式的计算。 除了简单之外,主要原因是在大多数数据库环境中,表和索引访问构成了处理的更重要方面; 检索表数据的成本占总体查询成本的主要地位,因为磁盘访问速度仍然比CPU处理慢几个数量级。

循环

当访问一个表中的数据时,经常需要迭代地检查多个行。 这样的访问是通过一个循环来指示的。 每一次传递要执行的指令称为循环体。 它们可以通过缩进直观地显示出来。 涉及多个表的数据库访问通常需要循环中的循环。 在这种情况下,每个循环级别都通过与前一个级别相比的进一步缩进表示。

临时文件

定义

查询计划还可能指示需要构建和使用中间临时文件(TEMP-FILE)。这是本地数组中的“临时”区域。它用于保存临时结果以用于各种目的,如排序。就像映射一样,临时文件有一个或多个下标,可能还有节点数据。

使用

一些临时文件包含处理单个表的数据。在这种情况下,可以将构建临时文件视为对该表中的数据进行预处理。在读取这样的临时文件之后,可以访问源表的主映射,也可以不访问源表的主映射。在其他情况下,临时文件可能包含处理多个表的结果。在其他情况下,临时文件用于存储分组的聚合值、检查DISTINCT等。

模块

临时文件的构建,以及其他处理,可以委托给一个称为模块的独立工作单元。 每个模块都被命名。 当列出单独的模块时,该计划将指明调用每个模块的位置。 当模块执行结束时,处理将在模块调用之后的下一条语句中继续进行。

发送给处理的查询

对于通过ODBC或JDBC网关连接链接的外部表,该计划显示发送到远程SQL gateway connection的查询文本,以从远程表检索所请求的数据。

对于并行查询处理和分片,该计划显示发送到并行处理或在分片上处理的各种查询。 还将显示用于每个查询的计划。

子查询、连接和联合

给定查询中的一些子查询(和视图)也可以单独处理。 它们的计划在单独的子查询部分中指定。 在计划中没有指明子查询部分被调用的精确位置。 这是因为它们经常作为条件或表达式处理的一部分被调用。

对于指定OUTER JOIN的查询,如果没有找到匹配的行,该计划可能指示可能生成的null行,以满足外部连接语义的要求。

对于UNION,该计划可能指示将来自不同UNION子查询的结果行组合到一个单独的模块中,在该模块中可以对这些结果行进行进一步处理。

计划分析

在分析给定查询的计划时,应用程序开发人员有时可能会觉得不同的计划会更有效率。 应用程序开发人员有多种方法来影响计划。

首先,计划将受到在包含实际应用程序数据的环境中正确运行调优表的影响。 在类源定义中手动定义一些Tune Table通常计算的值——例如表EXTENTSIZE、字段SELECTIVITY和映射BlockCount——也可以用于实现所需的计划。

此外,分析计划可能表明对类定义的某些更改可能导致更有效的计划,例如:

添加一个索引

在某些情况下(尽管不总是),使用一个临时文件进行预处理可能意味着向原始表添加一个与临时文件具有相同或类似结构的索引将消除构建临时文件的需要。 从查询计划中删除这个处理步骤显然可以使查询运行得更快,但这必须与更新表时维护索引所需的工作量进行平衡。

添加字段到索引数据

当计划显示正在使用的索引,然后是对主映射的访问时,这意味着将查询中使用的主映射字段添加到索引节点数据可能会为该查询生成更快的计划。 同样,这必须与额外的更新时间以及添加到处理使用该索引的其他查询的额外时间进行平衡,因为索引会更大,因此需要更多的读取时间。

添加连接索引

当计划显示以特定顺序连接两个表时(例如,首先检索t1,然后使用连接条件t1.a=t2.b连接到t2),可能相反的表顺序会产生一个更快的计划。例如,如果t2有额外的条件,可以显著限制符合条件的行数。 在这种情况下,在t1上添加一个t1索引。 a将允许这样一个连接顺序。

0
0 166
文章 姚 鑫 · 四月 26, 2021 9m read

第六章 SQL性能分析工具包

本章介绍可用于主动分析特定SQL语句的分析工具。这些工具收集有关这些SQL语句执行的详细信息。使用这些信息,开发人员可以采取措施提高低效SQL语句的性能。

根据请求的详细程度,此活动分析可能会显著增加服务器上的负载。因此,SQL性能分析工具包旨在进行协调一致的代码分析工作。它不是用来连续监视执行代码的。

分析工具界面

SQL性能分析工具包为开发人员和支持专家提供了分析特定SQL语句或语句组的能力。通过在执行特定SQL语句期间使用这些工具,它们可以收集详细信息,这些信息可用于单独或跨活动工作负载分析有问题的语句。

要记录的细节级别是可配置的,最细粒度的设置在模块级别收集信息,为语句的查询计划中的不同“步骤”提供信息。

  • %SYSTEM.SQL.PTools类方法。提供以下方法来收集以下各项的性能统计信息:
    • 整个系统:setSQLStatsFlag()
    • 特定的命名空间:setSQLStatsFlagByNS()
    • 当前进程或JOB:setSQLStatsFlagJob()
    • 指定的进程或JOB:setSQLStatsFlagByPID()`
    • 可以在SELECTINSERTUPDATEDELETE语句中使用%PROFILE关键字(等效于setSQLStatsFlagJob(2))或%PROFILE_ALL关键字(等效于setSQLStatsFlagJob(3))来仅收集该语句的性能分析统计信息。

使用性能分析工具包方法

可以使用%SYSTEM.SQL.PTools类方法执行以下操作:

  • 激活SQL性能统计信息。
  • 获取当前的SQL统计信息设置。
  • 导出收集的SQL性能统计信息。显示或导出到文件。
  • 删除SQL性能统计信息。

本节还包含使用这些方法的程序示例。

注意:%SYSTEM.SQL.PTools类方法是所有新编码的首选类接口。较旧的%SYS.PTools.StatsSQL类方法类似,并将继续受到支持。

激活统计信息收集

可以使用%SYSTEM.SQL.PTools类方法激活统计信息(Stats)代码生成以收集性能统计信息。提供以下方法来收集以下各项的性能统计信息:

  • 整个系统:setSQLStatsFlag()
  • 特定的命名空间:setSQLStatsFlagByNS()
  • 当前进程或JOB:setSQLStatsFlagJob()
  • 指定的进程或JOB:setSQLStatsFlagByPID()。如果第一个参数未指定,或指定为$JOB或空字符串(“”),则调用setSQLStatsFlagJob()。因此,SET SQLStatsFlag=$SYSTEM.SQL.SetSQLStatsFlagByPID($JOB,3)等同于SET SQLStatsFlag=$SYSTEM.SQL.SetSQLStatsFlagJob(3).

这些方法采用整数操作选项。它们返回一个冒号分隔的字符串,其第一个元素是先前的统计操作选项。可以使用getSQLStatsFlag()getSQLStatsFlagByPID()方法确定当前设置。

可以从ObjectScript或SQL调用这些方法,如以下示例所示:

  • 从ObjectScript:rtn=##class(%SYSTEM.SQL.PTools).setSQLStatsFlag(2,,8)
  • 从SQL : SELECT %SYSTEM_SQL.PTools_setSQLStatsFlag(2,,8)

操作选项

对于setSQLStatsFlag()setSQLStatsFlagByNS(),可以指定以下Action选项之一:0关闭统计信息代码生成;1打开所有查询的统计信息代码生成,但不收集统计信息(默认设置);2只记录查询外部循环的统计信息(在查询打开和关闭时收集统计信息);3记录查询的所有模块级别的统计信息。模块可以嵌套。如果是,则主模块统计信息是包含数字,即完整查询的总体结果。

对于setSQLStatsFlagJob()setSQLStatsFlagByPID(),操作选项略有不同。它们是:-1关闭此job的统计信息;0使用系统设置值。选项1、2和3与setSQLStatsFlag()相同,并覆盖系统设置。默认值为0。

要收集SQL Stats数据,需要在启用统计代码生成的情况下编译(准备)查询(选项1,默认设置):

  • 从0到1:更改SQL Stats选项后,需要编译包含SQL的例程和类以执行统计代码生成。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。
  • 要从1变为2:只需更改SQL Stats选项即可开始收集统计信息。这使可以在运行的生产环境中启用SQL性能分析,并将中断降至最低。
  • 从1到3(或从2到3):更改SQL Stats选项后,需要编译包含SQL的例程和类,以记录所有模块级别的统计信息。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。选项3通常仅用于非生产环境中已识别的性能较差的查询。
  • 从1、2或3变为0:要关闭统计代码生成,不需要清除缓存的查询。

收集选项

如果操作选项为2或3,则在调用这些方法之一时,可以指定收集选项值,以指定要收集的性能统计信息。默认情况下收集所有统计信息。

通过将与要收集的每种类型的统计信息相关联的整数值相加,可以指定收集选项。默认值为15(1+2+4+8)

这些方法将此Collect选项的前值作为第二个冒号分隔的元素返回。可以使用getSQLStatsFlag()getSQLStatsFlagByPID()方法确定当前设置。默认情况下,收集所有统计信息,返回15作为第二个元素值。

终止选项

统计数据收集将继续,直到终止。默认情况下,收集将无限期继续,直到通过发出另一个setSQLStatsFlag[nnn]()方法终止收集。或者,如果Action选项是1、2或3,可以指定setSQLStatsFlag[nnn]()Terminate选项,可以是经过的时间段(以分钟为单位),也可以是指定的时间戳。然后指定在该时间段过后重新设置操作选项。例如,字符串“M:120:1”将M(已用分钟)设置为120分钟,结束时操作选项将重置为1。所有其他选项将重置为适用于该操作选项的默认值。

这些方法将此终止选项值的先前值作为第五个冒号分隔的元素作为编码值返回。请参见获取统计信息设置。

获取统计信息设置

第一个冒号分隔值是操作选项设置。第二个冒号分隔值是Collect选项。第三个和第四个冒号分隔值用于特定于名称空间的统计信息收集。第五个冒号分隔值编码终止选项。第六个冒号分隔值指定FlagType:0=系统标志,1=进程/作业标志。

  KILL
  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL("USER")
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagByNS("USER",3,,7,"M:5:1")
DisplaySettings
  SET SQLStatsFlag = ##class(%SYSTEM.SQL.PTools).getSQLStatsFlag(0,.ptInfo)
  WRITE "ptInfo array of SQL Stats return value:",!
  ZWRITE ptInfo,SQLStatsFlag

导出查询性能统计信息

可以使用%SYSTEM.SQL.PTools的exportStatsSQL()方法将查询性能统计信息导出到文件。此方法用于将统计数据从%SYSTEM.SQL.PTools类导出到文件。

可以调用exportSQLStats(),如以下示例所示:

  • 从对象脚本:SET status=##class(%SYSTEM.SQL.PTools).exportSQLStats("$IO")(默认为格式T)。
  • 从 SQL: CALL %SYSTEM_SQL.PTools_exportSQLStats('$IO') (默认为格式H).

如果不指定文件名参数,此方法将导出到当前目录。默认情况下,此文件名为PT_StatsSQL_exportSQLStats_,后跟当前本地日期和时间YYYYMMDD_HHMMSS。可以指定$IO将数据输出到终端或管理门户显示。如果指定FileName参数,此方法将在当前命名空间的Mgr子目录或您指定的路径位置中创建一个文件。此导出仅限于当前命名空间中的数据。

可以将输出文件格式指定为P(文本)、D(逗号分隔数据)、X(XML标记)、H(HTML标记)或Z(用户定义分隔符)。

默认情况下,此方法导出查询性能统计信息。可以指定将其改为导出SQL查询文本或SQL查询计划数据,如以下示例所示:

  • 查询文本: CALL %SYSTEM_SQL.PTools_exportStatsSQL('$IO',,0,1,0)
  • 查询计划:CALL %SYSTEM_SQL.PTools_exportStatsSQL('$IO',,0,1,1)

ExportSQLStats()通过去除注释并执行文字替换来修改查询文本。

ExportSQLQuery()可以返回相同的查询文本和查询计划数据。

统计值

返回以下统计信息:

  • RowCount-给定查询在主模块中返回的总行数。
  • RunCount-自上次编译/准备查询以来已运行的总次数。
  • ModuleCount-在查询运行期间输入给定模块的总次数。
  • TimeToFirstRow-将给定查询的第一个结果集行返回到主模块所用的总时间。
  • TimeSpent-给定查询在给定模块中花费的总时间。
  • GlobalRefs-在给定模块中为给定查询完成的全局引用总数。
  • LinesOfCode-在给定模块中为给定查询执行的ObjectScript代码的总行数。
  • DiskWait(也称为磁盘延迟)-在给定模块中等待给定查询的磁盘读取所花费的总毫秒数。

删除查询性能统计信息

可以使用clearSQLStatsALL()方法删除性能统计信息。默认情况下,它删除为当前命名空间中的所有例程收集的统计信息。可以指定不同的命名空间,和/或将删除限制为特定例程。

性能统计示例

以下示例收集当前进程准备的查询主模块(操作选项2)的性能统计信息,然后使用exportSQLStats()将性能统计信息显示给终端。

  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL()
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagJob(2)
  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 failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET pStatus = ##class(%SYSTEM.SQL.PTools).exportSQLStats("$IO")
    IF pStatus'=1 {WRITE "Performance stats display failed:"
       DO $System.Status.DisplayError(qStatus) QUIT}

下面的示例收集当前进程准备的查询(操作选项3)的所有模块的性能统计信息,然后从嵌入式SQL调用exportSQLStats()以向终端显示性能统计信息:

  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL()
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagJob(3)
  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 failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  &sql(CALL %SYSTEM_SQL.PTools_exportSQLStats('$IO'))

以下示例收集当前进程准备的查询主模块(操作选项2)的性能统计信息,然后使用StatsSQLView查询显示这些统计信息:

  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL()
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagJob(2)
  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 failed:" DO $System.Status.DisplayError(qStatus) QUIT}
GetStats
  SET qStatus = tStatement.%Prepare("SELECT * FROM %SYS_PTools.StatsSQLView")
   IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET rsstats = tStatement.%Execute()
  DO rsstats.%Display()
  WRITE !!,"End of SQL Statistics"

以下示例收集用户命名空间中所有查询的所有模块(操作选项3)的性能统计信息。当统计信息收集时间在1分钟后到期时,它将重新设置为操作选项2,并且所有命名空间的收集范围默认为15(所有统计信息):

  DO ##class(%SYSTEM.SQL.PTools).clearSQLStatsALL("USER")
  DO ##class(%SYSTEM.SQL.PTools).setSQLStatsFlagByNS("USER",3,,7,"M:1:2")
  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 failed:" DO $System.Status.DisplayError(qStatus) QUIT}
GetStats
  SET qStatus = tStatement.%Prepare("SELECT * FROM %SYS_PTools.StatsSQLView")
   IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
  SET rsstats = tStatement.%Execute()
  DO rsstats.%Display()
  WRITE !!,"End of SQL Statistics",!
TerminateResetStats
  WRITE "returns:  ",##class(%SYSTEM.SQL.PTools).getSQLStatsFlag(),!
  HANG 100
  WRITE "reset to: ",##class(%SYSTEM.SQL.PTools).getSQLStatsFlag()
0
0 118
文章 姚 鑫 · 四月 25, 2021 10m read

第五章 优化查询性能(四)

注释选项

可以在SELECTINSERTUPDATEDELETETRUNCATE表命令中为查询优化器指定一个或多个注释选项。 注释选项指定查询优化器在编译SQL查询期间使用的选项。 通常,注释选项用于覆盖特定查询的系统范围默认配置。

语法

语法/*#OPTIONS */(在/*#之间没有空格)指定了一个注释选项。 注释选项不是注释; 它为查询优化器指定一个值。 注释选项使用JSON语法指定,通常是“key:value”对,例如: /*#OPTIONS {"optionName":value} */。 支持更复杂的JSON语法,比如嵌套值。

注释选项不是注释; 除了JSON语法之外,它可能不包含任何文本。 包含非json文本在/* ... */分隔符导致SQLCODE -153错误。 InterSystems SQL不验证JSON字符串的内容。

#OPTIONS关键字必须用大写字母指定。 JSON的大括号语法中不应该使用空格。 如果SQL代码用引号括起来,比如动态SQL语句,JSON语法中的引号应该是双引号。 例如:myquery="SELECT Name FROM Sample.MyTest /*#OPTIONS {""optName"":""optValue""} */".

可以在SQL代码中任何可以指定注释的地方指定/*#OPTIONS */ comment选项。 在显示的语句文本中,注释选项总是作为注释显示在语句文本的末尾。

你可以在SQL代码中指定多个/*#OPTIONS */ comment选项。 它们按照指定的顺序显示在返回的语句文本中。 如果为同一个选项指定了多个注释选项,则使用last指定的选项值。

以下的注释选项被记录在案:

  • /*#OPTIONS {"BiasAsOutlier":1} */

  • /*#OPTIONS {"DynamicSQLTypeList":"10,1,11"}

  • /*#OPTIONS {"NoTempFile":1} */

显示

/*#OPTIONS */ comment选项显示在SQL语句文本的末尾,而不管它们是在SQL命令中指定的位置。 一些显示的/*#OPTIONS */ comment选项没有在SQL命令中指定,而是由编译器的预处理器生成的。 例如 /*#OPTIONS {"DynamicSQLTypeList": ...} */

/*#OPTIONS */ comment选项显示在Show Plan语句文本、缓存的查询查询文本和SQL语句语句文本中。

为仅在/*#OPTIONS */ comment选项中不同的查询创建一个单独的缓存查询。

并行查询处理

并行查询提示指示系统在多处理器系统上运行时执行并行查询处理。 这可以极大地提高某些类型查询的性能。 SQL优化器确定一个特定的查询是否可以从并行处理中受益,并在适当的时候执行并行处理。 指定并行查询提示并不强制对每个查询进行并行处理,只强制那些可能从并行处理中受益的查询。 如果系统不是多处理器系统,则此选项无效。 要确定当前系统上的处理器数量,请使用 %SYSTEM.Util.NumberOfCPUs() 方法。

可以通过两种方式指定并行查询处理:

  • 在系统范围内,通过设置auto parallel选项。
  • 在每个查询的FROM子句中指定%PARALLEL关键字。

并行查询处理应用于SELECT查询。 它不应用于插入、更新或删除操作。

系统范围的并行查询处理

可以使用以下选项之一来配置系统范围的自动并行查询处理:

  • 在管理门户中选择System Administration,然后选择Configuration,然后选择SQL和对象设置,最后选择SQL。 查看或更改在单个进程中执行查询复选框。 注意,该复选框的默认值是未选中的,这意味着并行处理在默认情况下是激活的。
  • 调用$SYSTEM.SQL.Util.SetOption()方法,如下: SET status=$SYSTEM.SQL.Util.SetOption("AutoParallel",1,.oldval). 默认值是1(自动并行处理激活)。 要确定当前的设置,调用$SYSTEM.SQL.CurrentSettings(),它会显示为%PARALLEL选项启用自动提示。

注意,更改此配置设置将清除所有名称空间中的所有缓存查询。

当激活时,自动并行查询提示指示SQL优化器对任何可能受益于这种处理的查询应用并行处理。 在IRIS 2019.1及其后续版本中,自动并行处理是默认激活的。 从IRIS 2018.1升级到IRIS 2019.1的用户需要明确激活自动并行处理。

SQL优化器用于决定是否对查询执行并行处理的一个选项是自动并行阈值。 如果激活了系统范围的自动并行处理(默认),可以使用$SYSTEM.SQL.Util.SetOption()方法将自动并行处理的优化阈值设置为整数值,如下所示: SET status=$SYSTEM.SQL.Util.SetOption("AutoParallelThreshold",n,.oldval)n阈值越高,将此特性应用于查询的可能性就越低。 此阈值用于复杂的优化计算,但可以将此值视为必须驻留在已访问映射中的元组的最小数量。 默认值为3200。 最小值为0。 要确定当前的设置,调用$SYSTEM.SQL.CurrentSettings(),它显示%PARALLEL选项的自动提示阈值。

当自动并行处理被激活时,在分片环境中执行的查询将始终使用并行处理执行,而不管并行阈值是多少。

针对特定查询的并行查询处理

可选的%PARALLEL关键字在查询的FROM子句中指定。 它建议跨系统的IRIS使用多个处理器(如果适用的话)并行处理查询。 这可以显著提高使用一个或多个COUNTSUMAVGMAXMIN聚合函数和/groupby子句的查询的性能,以及许多其他类型的查询。 这些通常是处理大量数据并返回小结果集的查询。 例如,SELECT AVG(SaleAmt) FROM %PARALLEL User.AllSales GROUP BY Region都可使用并行处理。

仅指定聚合函数、表达式和子查询的“一行”查询执行并行处理,无论是否带有GROUP BY子句。 但是,同时指定单个字段和一个或多个聚合函数的“多行”查询不会执行并行处理,除非它包含GROUP BY子句。 例如,SELECT Name,AVG(Age) FROM %PARALLEL Sample.Person不执行并行处理,但是 SELECT Name,AVG(Age) FROM %PARALLEL Sample.Person GROUP BY Home_State 执行并行处理。

如果在运行时模式下编译指定%PARALLEL的查询,则所有常量都被解释为ODBC格式。

指定%PARALLEL可能会降低某些查询的性能。 在一个有多个并发用户的系统上运行%PARALLEL查询可能会降低整体性能。

在查询视图时可以执行并行处理。 但是,即使显式地指定了%parallel关键字,也不会对指定%VID的查询执行并行处理。

%PARALLEL的子查询

%PARALLEL用于SELECT查询及其子查询。 插入命令子查询不能使用%PARALLEL

当应用于与外围查询相关的子查询时,%PARALLEL将被忽略。 例如:

SELECT name,age FROM Sample.Person AS p 
WHERE 30<(SELECT AVG(age) FROM %PARALLEL Sample.Employee where Name = p.Name)

image

当应用于包含复杂谓词的子查询,或优化为复杂谓词的谓词时,%PARALLEL将被忽略。 被认为复杂的谓词包括FOR SOMEFOR SOME %ELEMENT谓词。

并行查询处理被忽略

无论AUTO PARALLEL选项设置如何,或者FROM子句中是否存在%PARALLEL关键字,某些查询都可能使用线性处理,而不是并行处理。InterSystems IRIS在优化查询后决定是否对该查询使用并行处理,并应用其他查询优化选项(如果指定)。RIS可以确定优化形式的查询不适合并行处理,即使用户指定的形式的查询似乎受益于并行处理。可以使用Show Plan确定InterSystems IRIS是否以及如何对查询进行分区以进行并行处理。

在以下情况下,指定%PARALLEL不会执行并行处理。查询成功执行,没有发出错误,但没有执行并行化:

  • 该查询包含FOR某些谓词。
  • 该查询包含一个TOP子句和一个ORDER BY子句。 这种子句组合优化了不使用并行处理的最快时间到第一行。 添加FROM子句%NOTOPOPT optimization -option关键字可优化以最快速度检索完整结果集。 如果查询不包含聚合函数,%PARALLEL%NOTOPOPT的组合将执行查询的并行处理。
  • 包含左外连接或内连接(其中ON子句不是相等条件)的查询。例如, FROM %PARALLEL Sample.Person p LEFT OUTER JOIN Sample.Employee e ON p.dob > e.dob. 这是因为SQL优化将这种类型的连接转换为完整的外部连接。 对于完整的外部连接,%PARALLEL将被忽略。
  • %PARALLEL%INORDER优化不能同时使用; 如果两者都指定,%PARALLEL将被忽略。
  • 查询引用一个视图并返回一个视图ID (%VID)。
  • 如果表有BITMAPEXTENT索引,COUNT(*)不使用并行处理。
  • %PARALLEL用于使用标准数据存储定义的表。 可能不支持将其与自定义存储格式一起使用。 %PARALLEL不支持全局临时表或具有扩展全局引用存储的表。
  • %PARALLEL用于可以访问一个表的所有行的查询,使用行级安全(ROWLEVELSECURITY)定义的表不能执行并行处理。
  • %PARALLEL用于存储在本地数据库中的数据。 它不支持映射到远程数据库的全局节点。

共享内存的考虑

对于并行处理,IRIS支持多个进程间队列(IPQ)。 每个IPQ处理单个并行查询。 它允许并行工作单元子流程将数据行发送回主流程,这样主流程就不必等待工作单元完成。 这使得并行查询能够尽可能快地返回第一行数据,而不必等待整个查询完成。 它还改进了聚合函数的性能。

并行查询执行使用来自通用内存堆(gmheap)的共享内存。 如果使用并行SQL查询执行,用户可能需要增加gmheap大小。 一般来说,每个IPQ的内存需求是4 x 64k = 256k。 InterSystems IRIS将一个并行SQL查询拆分为可用的CPU核数。 因此,用户需要分配这么多额外的gmheap:

<Number of concurrent parallel SQL requests> x <Number cores> x 256 = <required size increase (in kilobytes) of gmheap>

注意,这个公式不是100%准确的,因为一个并行查询可以产生同样是并行的子查询。 因此,明智的做法是分配比这个公式指定的更多的额外gmheap

分配足够的gmheap失败将导致错误报告给messages.log。 SQL查询可能会失败。 其他子系统尝试分配gmheap时也可能出现其他错误。

要查看一个实例的gmheap使用情况,特别是IPQ使用情况,请在管理门户的主页上选择System Operation,然后选择System usage,然后单击Shared Memory Heap usage链接;

image

要更改通用内存堆或gmheap(有时称为共享内存堆或SMH)的大小,请从管理门户的主页选择“系统管理”,然后是“配置”,然后是“附加设置”,最后是“高级内存”;

image

image

缓存查询注意事项

如果你正在运行一个缓存的SQL查询,使用%PARALLEL,当这个查询被初始化时,你做了一些事情来清除缓存的查询,那么这个查询可能会从一个工人作业报告一个<NOROUTINE>错误。 导致缓存查询被清除的典型情况是调用$SYSTEM.SQL.Purge()或重新编译该查询引用的类。 重新编译类将自动清除与该类相关的任何缓存查询。

如果发生此错误,再次运行查询可能会成功执行。 从查询中删除%PARALLEL可以避免出现此错误。

SQL语句和计划状态

使用%PARALLEL的SQL查询可以产生多条SQL语句。 这些SQL语句的计划状态是Unfrozen/Parallel。 计划状态为“已冻结”/“并行”的查询不能通过用户操作进行冻结。

生成报告

可以使用生成报告工具向InterSystems Worldwide Response Center (WRC) customer support提交查询性能报告,以便进行分析。 可以使用以下任意一种方式从管理门户运行生成报告工具:

  1. 必须首先从WRC获得WRC跟踪号。可以使用每个管理门户页面顶部的Contact按钮从管理门户联系WRC。在WRC编号区域中输入此跟踪编号。可以使用此跟踪编号来报告单个查询或多个查询的性能。
  2. 在“SQL语句”区域中,输入查询文本。右上角将显示一个X图标。可以使用此图标清除SQL语句区。查询完成后,选择保存查询按钮。系统生成查询计划并收集指定查询的运行时统计信息。无论系统范围的运行时统计信息设置如何,生成报告工具始终使用收集选项3:记录查询的所有模块级别的统计信息进行收集。由于在此级别收集统计信息可能需要时间,因此强烈建议您选中“在后台运行保存查询进程”复选框。默认情况下,此复选框处于选中状态。

当后台任务启动时,该工具显示“请等待……”,禁用页面上的所有字段,并显示一个新的视图进程按钮。 单击View Process按钮将在新选项卡中打开Process Details页面。 在流程详细信息页面,您可以查看该流程,并可以“暂停”、“恢复”或“终止”该流程。 进程的状态反映在Save查询页面上。 当流程完成时,当前保存的查询表将被刷新,View process按钮将消失,页面上的所有字段将被启用。

  1. 对每个查询执行步骤2。 每个查询将被添加到当前保存的Queries表中。 注意,该表可以包含具有相同WRC跟踪号的查询,也可以包含具有不同跟踪号的查询。 完成所有查询后,继续步骤4。

对于列出的每个查询,可以选择Details链接。 该链接将打开一个单独的页面,其中显示完整的SQL语句、属性(包括WRC跟踪号和IRIS软件版本),以及包含每个模块的性能统计信息的查询计划。

  • 要删除单个查询,请从“当前保存的查询”表中选中这些查询的复选框,然后单击“清除”按钮。
  • 要删除与WRC跟踪编号关联的所有查询,请从当前保存的查询表中选择一行。WRC编号显示在页面顶部的WRC编号区域。如果您随后单击清除按钮,则对该WRC编号的所有查询都将被删除。
  1. 使用查询复选框选择要报告给WRC的查询。要选择与WRC跟踪编号关联的所有查询,请从当前保存的查询表中选择一行,而不是使用复选框。在这两种情况下,都可以选择Generate Report按钮。生成报告工具创建一个XML文件,其中包括查询语句、具有运行时统计信息的查询计划、类定义以及与每个所选查询相关联的SQL int文件。

如果选择与单个WRC跟踪编号关联的查询,则生成的文件将具有默认名称,如WRC12345.xml。如果选择与多个WRC跟踪编号关联的查询,则生成的文件将具有默认名称WRCMultiple.xml

将出现一个对话框,要求指定保存报告的位置。保存报告后,可以单击Mail to链接将报告发送给WRC客户支持。使用邮件客户端的附加/插入功能附加文件。

0
0 162
文章 姚 鑫 · 四月 24, 2021 12m read

第五章 优化查询性能(三)

查询执行计划

可以使用解释或显示计划工具来显示SELECTDECLAREUPDATEDELETETRUNCATE TABLE和一些INSERT操作的执行计划。这些操作统称为查询操作,因为它们使用SELECT查询作为其执行的一部分。InterSystems IRIS在准备查询操作时生成执行计划;不必实际执行查询来生成执行计划。

默认情况下,这些工具显示InterSystems IRIS认为的最佳查询计划。对于大多数查询,有多个可能的查询计划。除了InterSystems IRIS认为最佳的查询计划外,还可以生成和显示备用查询执行计划。

InterSystems IRIS提供以下查询计划工具:

  • $SYSTEM.SQL.ExPlan()方法可用于生成和显示XML格式的查询计划以及备选查询计划(可选)。
  • SQL EXPLAIN命令可用于生成XML格式的查询计划,还可以选择生成备选查询计划和SQL统计信息。所有生成的查询计划和统计信息都包含在名为Plan的单个结果集字段中。请注意,EXPLAIN命令只能与SELECT查询一起使用。
  • 管理门户 - >系统资源管理器 - >SQL界面显示计划按钮。
  • 管理门户 — >系统资源管理器 — >工具—>SQL性能工具。

image

对于生成的%PARALLEL和分片查询,这些工具显示所有适用的查询计划。

使用Explain()方法

可以通过运行$SYSTEM.SQL.Explain()方法生成查询执行计划,示例如下:

/// w ##class(PHA.TEST.SQL).SQLExplain()
ClassMethod SQLExplain()
{
	SET mysql=2
	SET mysql(1)="SELECT TOP 10 Name,DOB FROM Sample.Person "
	SET mysql(2)="WHERE Name [ 'A' ORDER BY Age"
	SET status=$SYSTEM.SQL.Explain(.mysql,{"all":0,"quiet":1,"stats":0,"preparse":0},,.plan)
	IF status'=1 {WRITE "Explain() failed:" DO $System.Status.DisplayError(status) QUIT}
	ZWRITE plan
}

设置“all”:0选项会生成InterSystems IRIS认为最优的查询计划。 设置“all”:1选项会生成最佳的查询计划和备选的查询计划。 默认值为“all”:0

结果被格式化为表示xml格式文本的下标数组。

如果指定单个查询计划("all":0),上述方法调用中的plan变量将具有以下格式:

  • plan:显示结果中的下标总数。
  • plan(1):总是包含XML格式标签“<plan>”。 最后一个下标总是包含XML格式标记“</plan>”
  • plan(2):总是包含XML格式标签"<sql>"
  • plan(3): 总是包含查询文本的第一行。 如果""preparse":0(默认值),则返回字面查询文本,并为多行查询的每一行使用额外的下标;在上面的例子中,查询有两行,因此使用了两个下标(plan(3)plan(4))。如果"prepare":1,则规范化查询文本返回为单行:plan(3)
  • plan(n):总是包含XML格式标签“</sql>”; 在上面的例子中,3+mysql = plan(5)
  • plan(n+1):总是包含XML格式的查询cost"<cost value=""407137""/>".
  • plan(n+2):总是包含执行计划的第一行。 这个plan可以是任何长度,可以包含<module>…</module>标签作为单独的下标行,包含生成的执行模块的查询计划。

如果指"all":1 Explain()将生成备用查询计划。计划变量遵循相同的格式,不同之处在于它们使用第一级下标来标识查询计划,而使用第二级下标来标识查询计划的行。因此,plan(1)包含第一个查询计划结果中的二级下标计数,plan(2)包含第二个查询计划结果中的二级下标计数,依此类推。在此格式中,plan(1,1)包含第一个查询计划的XML格式标记 "<plan>"plan(2,1)包含第二个查询计划的XML格式标记 "<plan>",依此类推。唯一不同的是,备用查询计划包含二级零下标(plan(1,0)变量,该变量包含成本和索引信息;此零下标不计入一级下标(plan(1))值。

如果指"stats":1, Explain()将为每个查询计划模块生成性能统计信息。 每个模块的这些统计数据都使用<stats> ... </stats>标记,并立即出现在查询成本之后("<cost value=""407137""/>")和查询计划文本之前。 如果查询计划包含额外的<module>标记,则生成的模块的<stats>将紧接在<module>标记之后,在该模块的查询计划之前列出。 对于每个模块,将返回以下项:

  • <ModuleName>:模块名。
  • <TimeSpent>:模块的总执行时间,以秒为单位。
  • <GlobalRefs>:全局引用的计数。
  • <LinesOfCode>:执行的代码行数。
  • <DiskWait>:磁盘等待时间,单位为秒。
  • <RowCount>:结果集中的行数。
  • <ModuleCount>:此模块被执行的次数。
  • <Counter>:这个程序被执行的次数。

使用显示计划从InterSystems SQL工具

可以使用Show Plan以以下任何一种方式显示查询的执行计划

  • 从管理门户SQL接口。 选择System Explorer,然后选择SQL。 在页面顶部选择带有Switch选项的名称空间。 (可以为每个用户设置管理门户的默认名称空间。) 编写查询,然后按Show Plan按钮。 (还可以通过单击列出查询的Plan选项,从Show History列表调用Show Plan。)
  • 从管理门户工具界面。 选择“系统资源管理器”,然后选择“工具”,然后选择“SQL性能工具”,然后选择“SQL运行时统计信息”:
    • Query Test选项卡中:在页面顶部选择一个带有Switch选项的名称空间。 在文本框中写入查询。 然后按下Show Plan with SQL Stats按钮。 这将在不执行查询的情况下生成一个显示计划。
    • View Stats选项卡中:对于列出的查询之一,按Show Plan按钮。 列出的查询包括在执行查询时编写的查询和在查询测试时编写的查询。
  • SQL Shell中,可以使用SHOW PLANSHOW PLANALT Shell命令来显示最近执行的查询的执行计划。
  • 通过对缓存的查询结果集运行Show Plan,使用:i%Prop语法将文本替换值存储为属性:
  SET cqsql=2
  SET cqsql(1)="SELECT TOP :i%PropTopNum Name,DOB FROM Sample.Person "
  SET cqsql(2)="WHERE Name [ :i%PropPersonName ORDER BY Age"
  DO ShowPlan^%apiSQL(.cqsql,0,"",0,$LB("Sample"),"",1)

默认情况下,Show Plan以逻辑模式返回值。但是,当从管理门户或SQL Shell调用Show Plan时,Show Plan使用运行时模式。

执行计划:语句文本和查询计划

显示计划执行计划由两个组件组成,即语句文本和查询计划:

语句文本复制了原始查询,但进行了以下修改:管理门户SQL界面中的显示计划按钮显示删除了注释和换行符的SQL语句。空格是标准化的。显示计划按钮显示还执行文字替换,将每个文字替换为,除非已通过将文字值括在双圆括号中来取消文字替换。使用EXPLAIN()方法显示显示计划时,或者使用SQL运行时统计信息或备用显示计划工具显示显示计划时,不会执行这些修改。

查询计划显示将用于执行查询的计划。查询计划可以包括以下内容:

  • 如果查询计划已经冻结,则查询计划的第一行为冻结计划,否则第一行为空。
  • “Relative cost”是一个整数值,它是从许多因素中计算出来的抽象数字,用于比较同一查询的不同执行计划的效率。 这种计算考虑了查询的复杂性、索引的存在和表的大小(以及其他因素)。 相对成本对于比较两个不同的查询是没有用的。 " Relative cost not available"由某些聚合查询返回,例如COUNT(*)MAX(%ID)不带WHERE子句。
  • 查询计划由一个主模块和(在需要时)一个或多个子组件组成。 可以显示一个或多个模块子组件,按字母顺序命名, B: Module:B, Module:C,等等开始,并按执行顺序列出(不一定按字母顺序)。

默认情况下,模块执行处理并使用其结果填充内部临时文件(内部临时表)。 通过指定 /*#OPTIONS {"NoTempFile":1} */,可以强制查询优化器创建不生成内部临时文件的查询计划,如注释选项中所述。

对于查询中的每个子查询,都会显示一个命名子查询模块。子查询模块按字母顺序命名。子查询命名在每个命名子查询之前跳过一个或多个字母。因此 Module:B, Subquery:F or Module:D, Subquery:G.当到达字母表末尾时,会对其他子查询进行编号,解析Z=26并使用相同的跳过序列。下面的示例是以Subquery开头的每三个子查询命名序列:F:F,I,L,O,R,U,X,27,30,33。下面的示例是以Subquery开头的每秒一次的子查询命名序列:G:G,I,K,M,O,Q,S,U,W,Y,27,29。如果子查询调用模块,模块将按字母顺序放在子查询之后,不会跳过。因此,Subquery:H calls Module:I

  • “Read master map”作为主模块中的第一个项目符号表示查询计划效率低下。查询计划使用以下映射类型语句之一开始执行Read master map... (no available index), Read index map... (use available index), or Generate a stream of idkey values using the multi-index combination...因为master map读取的是数据本身,而不是数据的索引,所以Read master map...。几乎总是指示低效的查询计划。除非表相对较小,否则应该定义一个索引,以便在重新生成查询计划时,第一个映射显示为read index map...

某些操作会创建表示无法生成查询计划的显示计划:

  • 非查询插入:INSERT... VALUES()命令不执行查询,因此不生成查询计划。
  • 查询总是FALSE:在少数情况下,InterSystems IRIS可以在准备查询时确定查询条件总是FALSE,因此不能返回数据。“显示计划”会在“查询计划”组件中通知这种情况。例如,包含条件的查询WHERE %ID IS NULL 或 WHERE Name %STARTSWITH('A') AND Name IS NULL 不能返回数据,因此,InterSystems IRIS不生成执行计划。查询计划没有生成执行计划,而是表示“Output no rows”。如果查询包含具有这些条件之一的子查询,则查询计划的子查询模块表示“Subquery result NULL, found no rows”。这种条件检查仅限于涉及NULL的几种情况,并不是为了捕捉所有自相矛盾的查询条件。
  • 无效的查询:Show Plan为大多数无效查询显示SQLCODE错误消息。然而,在少数情况下,Show Plan显示为空。例如, WHERE Name = $$$$$ or WHERE Name %STARTSWITH('A")。在这些情况下,Show Plan不显示语句文本,而Query Plan[没有为该语句创建的计划]。这通常发生在分隔文字的引号不平衡时。 当为用户定义的(“外部”)函数指定了两个或多个前置美元符号而没有指定正确的语法时,也会出现这种情况。

交替显示计划

可以使用管理门户或Explain()方法显示查询的替代执行计划。

使用以下任意一种方法,从管理门户显示查询的备选执行计划:

  • 选择系统资源管理器,选择工具,选择SQL性能工具,然后选择备用的显示计划。
  • 选择System Explorer,选择SQL,然后从Tools下拉菜单中选择Alternate Show Plans

image

image

使用备用的“显示计划”工具:

  1. 输入一个SQL查询文本,或使用Show History按钮检索一个。 可以通过单击右边的圆形“X”圆来清除查询文本字段。
  2. 按显示计划选项按钮以显示多个备用显示计划。 Run ... in the background...默认情况下不选中复选框,这是大多数查询的首选设置。建议选择RUN...。对于大型或复杂的查询,请在后台复选框中。当一个长查询在后台运行时,会显示一个View process按钮。单击查看进程将在新选项卡中打开进程详细信息页面。在“进程详细信息”页中,可以查看进程,还可以挂起、继续或终止进程。
  3. 可能的计划按成本升序列出,并带有映射类型和起始映射。
  4. 从可能的计划列表中,使用复选框选择要比较的计划,然后按比较显示计划与统计信息按钮以运行这些计划并显示其SQL统计信息。

带有ALL限定符的EXPLAIN()方法显示查询的所有执行计划。它首先显示IRIS认为最优(成本最低)的计划,然后显示备选计划。备选计划按成本升序列出。

以下示例显示最佳执行计划,然后列出备选计划:

  DO $SYSTEM.SQL.SetSQLStatsFlagJob(3)
  SET mysql=1
  SET mysql(1)="SELECT TOP 4 Name,DOB FROM Sample.Person ORDER BY Age"
  DO $SYSTEM.SQL.Explain(.mysql,{"all":1},,.plan)
  ZWRITE plan

Stats

显示计划选项列表为每个备用显示计划分配一个成本值,使可以在执行计划之间进行相对比较。

Alternate Show Plan Details为每个查询计划提供了一组查询总数的统计信息(统计信息),以及(如果适用)每个查询计划模块的统计信息。每个模块的统计信息包括时间(整体性能,以秒为单位)、全局引用(全局引用数)、命令(执行的行数)和读取延迟(磁盘等待,以毫秒为单位)。查询总计统计信息还包括返回的行数。

将查询优化计划写入文件

以下实用程序列出了针对文本文件的一个或多个查询的查询优化计划。

QOPlanner^%apiSQL(infile,outfile,eos,schemapath)
  • infile 包含缓存查询列表的文本文件的文件路径名。指定为带引号的字符串。
  • outfile 要列出查询优化计划的文件路径名。指定为带引号的字符串。如果该文件不存在,系统将创建该文件。如果该文件已存在,则InterSystems IRIS会覆盖该文件。
  • eos 可选-语句末尾分隔符,用于分隔Infile列表中的各个缓存查询。指定为带引号的字符串。默认值为“GO”。如果此EOS字符串与缓存的查询分隔符不匹配,则不会生成输出文件。
  • schemapath 可选-以逗号分隔的方案名列表,用于为未限定的表名、视图名或存储过程名指定方案搜索路径。可以包括DEFAULT_SCHEMA,这是当前系统范围内的默认架构。如果infile包含#Import指令,QOPlanner会将这些#Import包/架构名称添加到schemapath的末尾。

以下是调用此查询优化计划列表实用程序的示例。该实用程序将ExportSQL^%qarDDLExport()实用程序生成的文件作为输入,如“缓存查询”一章的“将缓存查询列出到文件”一节中所述。可以生成此查询列表文件,也可以将一个(或多个)查询写入文本文件。

  DO QOPlanner^%apiSQL("C:\temp\test\qcache.txt","C:\temp\test\qoplans.txt","GO")

从终端命令行执行时,进度会显示在终端屏幕上,如下例所示:

Importing SQL Statements from file: C:\temp\test\qcache.txt
 
Recording any errors to principal device and log file: C:\temp\test\qoplans.txt
  
  SQL statement to process (number 1):
      SELECT TOP ? P . Name , E . Name FROM Sample . Person AS P , 
      Sample . Employee AS E ORDER BY E . Name
  Generating query plan...Done
 
  SQL statement to process (number 2):
      SELECT TOP ? P . Name , E . Name FROM %INORDER Sample . Person AS P 
      NATURAL LEFT OUTER JOIN Sample . Employee AS E ORDER BY E . Name
  Generating query plan...Done
 
Elapsed time: .16532 seconds

创建的查询优化计划文件包含如下条目:

<pln>
<sql>
 SELECT TOP ? P . Name , E . Name FROM Sample . Person AS P , Sample . Employee AS E ORDER BY E . Name
</sql>
Read index map Sample.Employee.NameIDX.
Read index map Sample.Person.NameIDX.
</pln>
######
<pln>
<sql>
 SELECT TOP ? P . Name , E . Name FROM %INORDER Sample . Person AS P 
    NATURAL LEFT OUTER JOIN Sample . Employee AS E ORDER BY E . Name
</sql>
Read master map Sample.Person.IDKEY.
Read extent bitmap Sample.Employee.$Employee.
Read master map Sample.Employee.IDKEY.
Update the temp-file.
Read the temp-file.
Read master map Sample.Employee.IDKEY.
Update the temp-file.
Read the temp-file.
</pln>
######

可以使用查询优化计划文本文件来比较使用不同查询变体生成的优化计划,或者比较不同版本的InterSystems IRIS之间的优化计划。

将SQL查询导出到文本文件时,来自类方法或类查询的查询将以代码行开头:

#import <package name>

这个#Import语句告诉QOPlanner实用程序使用哪个默认包/模式来生成查询计划。从例程导出SQL查询时,例程代码中SQL语句之前的任何#import行也将位于导出文件中的SQL文本之前。假设从缓存查询导出到文本文件的查询包含完全限定的表引用;如果文本文件中的表引用不是完全限定的,则QOPlanner实用程序使用在运行QOPlanner时在系统上定义的系统范围的默认模式。

0
0 155
文章 姚 鑫 · 四月 23, 2021 6m read

第五章 优化查询性能(二)

使用索引

索引通过维护常见请求数据的排序子集,提供了一种优化查询的机制。 确定哪些字段应该被索引需要一些思考:太少或错误的索引和关键查询将运行太慢; 太多的索引会降低插入和更新性能(因为必须设置或更新索引值)。

什么索引

要确定添加索引是否会提高查询性能,请从管理门户SQL接口运行查询,并在性能中注意全局引用的数量。 添加索引,然后重新运行查询,注意全局引用的数量。 一个有用的索引应该减少全局引用的数量。 可以通过在WHERE子句或ON子句条件前使用%NOINDEX关键字来防止使用索引。

应该为联接中指定的字段(属性)编制索引。左外部联接从左表开始,然后查看右表;因此,应该为右表中的字段建立索引。在下面的示例中,应该为T2.f2编制索引:

   FROM Table1 AS T1 LEFT OUTER JOIN Table2 AS T2 ON T1.f1 = T2.f2

内部联接应该在两个ON子句字段上都有索引。

执行“显示计划”,然后找到第一张map。 如果查询计划中的第一个项目是“Read master map”,或者查询计划调用的模块的第一个项目是“Read master map”,则查询的第一个映射是主映射,而不是索引映射。 因为主映射读取数据本身,而不是数据索引,这总是表明查询计划效率低下。 除非表相对较小,否则应该创建一个索引,以便在重新运行该查询时,查询计划的第一个映射表示“读取索引映射”。

应该索引在WHERE子句equal条件中指定的字段。

可能希望索引在WHERE子句范围条件中指定的字段,以及GROUP BYORDER BY子句中指定的字段。

在某些情况下,基于范围条件的索引可能会使查询变慢。如果绝大多数行满足指定的范围条件,则可能会发生这种情况。例如,如果将QUERY子句WHERE Date < CURRENT_DATE 用于大多数记录来自以前日期的数据库,则在DATE上编制索引实际上可能会降低查询速度。这是因为查询优化器假定范围条件将返回相对较少的行数,并针对此情况进行优化。可以通过在范围条件前面加上%noindex来确定是否发生这种情况,然后再次运行查询。

如果使用索引字段执行比较,则比较中指定的字段的排序规则类型应与其在相应索引中的排序规则类型相同。例如,SELECTWHERE子句或联接的ON子句中的Name字段应该与为Name字段定义的索引具有相同的排序规则。如果字段排序规则和索引排序规则之间存在不匹配,则索引可能效率较低或可能根本不使用。

image

image

索引配置选项

以下系统范围的配置方法可用于优化查询中索引的使用:

  • 要将主键用作IDKey索引,请设置$SYSTEM.SQL.Util.SetOption()方法,如下所示 SET status=$SYSTEM.SQL.Util.SetOption("DDLPKeyNotIDKey",0,.oldval). 默认为1
  • 要将索引用于SELECT DISTINCT查询,请设置$SYSTEM.SQL.Util.SetOption()方法,如下所示: SET status=$SYSTEM.SQL.Util.SetOption("FastDistinct",1,.oldval). 默认为1

索引使用情况分析

可以使用以下任一方法按SQL缓存查询分析索引使用情况:

  • 管理门户索引分析器SQL性能工具。
  • %SYS.PTools.UtilSQLAnalysis方法indexUsage()tableScans()tempIndices()joinIndices()outlierIndices()。、

索引分析

可以使用以下任一方法从管理门户分析SQL查询的索引使用情况:

  • 选择系统资源管理器,选择工具,选择SQL性能工具,然后选择索引分析器。
  • 选择系统资源管理器,选择SQL,然后从工具下拉菜单中选择索引分析器。

索引分析器提供当前命名空间的SQL语句计数显示和五个索引分析报告选项。 image

image

image

SQL语句计数

在SQL索引分析器的顶部有一个对命名空间中的所有SQL语句进行计数的选项。按收集SQL语句按钮。SQL索引分析器显示“正在收集SQL语句...”当计票进行时,然后“完成!”当清点完毕后。SQL语句分为三类进行计数:缓存查询计数、类方法计数和类查询计数。这些计数针对整个当前命名空间,不受架构选择选项的影响。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的getSQLStmts()

可以使用清除语句按钮删除当前命名空间中收集的所有语句。该按钮调用clearSQLStatements()方法。

报告选项

可以检查当前命名空间中选定架构的缓存查询报告,也可以(通过不选择架构)检查当前命名空间中所有缓存查询的报告。可以在此分析中跳过或包括系统类查询、INSERT语句和/或IDKEY索引。“架构选择”和“跳过选项”复选框是用户自定义的。

指数分析报告选项包括:

  • 索引使用:此选项获取当前名称空间中的所有缓存查询,为每个查询生成显示计划,并记录每个查询使用每个索引的次数以及名称空间中所有查询对每个索引的总使用量。这可用于显示未使用的索引,以便可以删除或修改这些索引以使其更有用。结果集从最少使用的索引到最常使用的索引排序。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的indexUsage()。要导出此方法生成的分析数据,请使用exportIUAnalysis()方法。

  • 使用表扫描的查询:此选项标识当前名称空间中执行表扫描的所有查询。如果可能,应避免表扫描。表扫描并不总是可以避免的,但是如果一个表有大量的表扫描,那么应该检查为该表定义的索引。通常,表扫描列表和临时索引列表会重叠;修复其中一个会删除另一个。结果集按从最大块计数到最小块计数的顺序列出表格。提供了显示计划链接以显示对帐单文本和查询计划。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的tableScans()。要导出此方法生成的分析数据,请使用exportTSAnalysis()方法。

  • 带临时索引的查询:此选项标识当前名称空间中构建临时索引以解析SQL的所有查询。有时,使用临时索引会有所帮助并提高性能,例如,基于范围条件构建一个小索引,然后InterSystems IRIS可以使用该索引按顺序读取主映射。有时,临时索引只是不同索引的子集,可能非常有效。其他情况下,临时索引会降低性能,例如,扫描master may以在具有条件的特性上构建临时索引。这种情况表明缺少所需的索引;应该向与临时索引匹配的类添加索引。结果集按从最大块计数到最小块计数的顺序列出表格。提供了显示计划链接以显示对帐单文本和查询计划。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的tempIndices()。要导出此方法生成的分析数据,请使用exportTIAnalysis()方法。

  • 缺少联接索引的查询:此选项检查当前名称空间中具有联接的所有查询,并确定是否定义了支持该联接的索引。它将可用于支持联接的索引从0(不存在索引)排序到4(索引完全支持联接)。外部联接需要一个单向索引。内联接需要双向索引。默认情况下,结果集只包含JoinIndexFlag<4的行。JoinIndexFlag=4表示有完全支持联接的索引。

相应的方法是%SYS.PTools.UtilSQLAnalysis类中的joinIndices(),它提JoinIndexFLAG值的描述。要导出此方法生成的分析数据,请使用exportJIAnalysis()方法。默认情况下,exportJIAnalysis()不会列出JoinIndexFlag=4值,但可以选择列出这些值。

  • 带离群值索引的查询:此选项标识当前名称空间中具有离群值的所有查询,并确定是否定义了支持该离群值的索引。它将可用于支持离群值的索引从0(不存在索引)到4(索引完全支持离群值)进行排序。默认情况下,结果集只包含OutlierIndexFlag<4的行。OutlierIndexFlag=4表示存在完全支持离群值的索引。

对应的方法是%SYS.PTools.UtilSQLAnalysis类中的outlierIndices()。要导出此方法生成的分析数据,请使用exportOIAnalysis()方法。默认情况下,exportOIAnalysis()不会列出OutlierIndexFlag=4值,但可以选择列出这些值。

当选择其中一个选项时,系统自动执行操作并显示结果。 第一次选择一个选项或调用相应的方法时,系统生成结果数据; 如果选择该选项或再次调用该方法,InterSystems IRIS将重新显示相同的结果。 要生成新的结果数据,必须使用Gather SQL Statements按钮重新初始化Index Analyzer结果表。 为%SYS.PTools生成新的结果数据。 在UtilSQLAnalysis方法中,必须调用gettsqlstmts()来重新初始化索引分析器结果表。 更改“跳过所有系统类和例程”或“跳过插入语句”复选框选项也会重新初始化索引分析器结果表。

IndexUsage()方法

下面的示例演示indexUsage()方法的用法:

/// w ##class(PHA.TEST.SQL).CountingCachedQueries2()
ClassMethod CountingCachedQueries2()
{
	DO ##class(%SYS.PTools.UtilSQLAnalysis).indexUsage(1,1)
	SET utils = "SELECT %EXACT(Type), Count(*) As QueryCount "_
	          "FROM %SYS_PTools.UtilSQLStatements GROUP BY Type"
	SET utilresults = "SELECT SchemaName, Tablename, IndexName, UsageCount "_
	                "FROM %SYS_PTools.UtilSQLAnalysisDB ORDER BY UsageCount"
	SET tStatement = ##class(%SQL.Statement).%New()
	SET qStatus = tStatement.%Prepare(utils)
	IF qStatus'=1 {
		WRITE "%Prepare 失败:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	SET rset = tStatement.%Execute()
	DO rset.%Display()
	WRITE !,"实用程序结束数据",!!
	SET qStatus = tStatement.%Prepare(utilresults)
	IF qStatus'=1 {
		WRITE "%Prepare 失败:" DO $System.Status.DisplayError(qStatus) QUIT
	}
	SET rset = tStatement.%Execute()
	DO rset.%Display()
	WRITE !,"实用程序结束数据"
}

注意,由于结果是按UsageCount排序的,因此带有UsageCount > 0的索引列在结果集的末尾。

可以从ObjectScript或SQL调用或SELECT命令调用该类中的方法。 SQL命名约定是指定包名%SYS_PTools,然后在以小写字母开头的方法名前加上前缀“PT_”。 如下面的例子所示:

ObjectScript:

  DO ##class(%SYS.PTools.UtilSQLAnalysis).indexUsage()

SQL:

  CALL %SYS_PTools.PT_indexUsage()
  SELECT %SYS_PTools.PT_indexUsage()

索引优化选项

默认情况下,InterSystems SQL查询优化器使用复杂而灵活的算法来优化涉及多个索引的复杂查询的性能。在大多数情况下,这些默认值可提供最佳性能。但是,在极少数情况下,可能希望通过指定OPTIMIZE-OPTION关键字为查询优化器提供“提示”。

FROM子句支持%ALLINDEX%IGNOREINDEX OPTIME-OPTION关键字。这些优化选项关键字控制查询中使用的所有索引。

可以使 %NOINDEX条件级别提示指定对特定条件使用索引的例外情况。 %NOINDEX提示放在每个不应使用索引的条件之前。例如,WHERE %NOINDEX hiredate < ?当绝大多数数据被条件选中(或未选中)时,这是最常用的。对于小于(<)或大于(>)的条件,使用%NOINDEX条件级别提示通常是有益的。对于相等条件,使用%NOINDEX条件级别提示不会带来任何好处。使用联接条件时,ON子句联接支持%NOINDEX

%NOINDEX关键字可用于覆盖在FROM子句中建立的索引优化。在下面的示例中,%ALLINDEX优化关键字适用于除E.Age条件之外的所有条件测试:

  SELECT P.Name,P.Age,E.Name,E.Age
  FROM %ALLINDEX Sample.Person AS P LEFT OUTER JOIN Sample.Employee AS E
       ON P.Name=E.Name
  WHERE P.Age > 21 AND %NOINDEX E.Age < 65

image

0
0 147
文章 姚 鑫 · 四月 22, 2021 8m read

第五章 优化查询性能(一)

InterSystems SQL自动使用查询优化器创建在大多数情况下提供最佳查询性能的查询计划。该优化器在许多方面提高了查询性能,包括确定要使用哪些索引、确定多个AND条件的求值顺序、在执行多个联接时确定表的顺序,以及许多其他优化操作。可以在查询的FROM子句中向此优化器提供“提示”。本章介绍可用于评估查询计划和修改InterSystems SQL将如何优化特定查询的工具。

InterSystems IRIS®Data Platform支持以下优化SQL查询的工具:

  • SQL Runtime Statistics用于生成查询执行的运行时性能统计信息
  • 索引分析器,用于显示当前命名空间中所有查询的各种索引分析器报告。这显示了InterSystems SQL将如何执行查询,可以全面了解索引是如何使用的。此索引分析可能表明应该添加一个或多个索引以提高性能。
  • 查询执行计划:显示SQL查询(查询计划)的最佳(默认)执行计划,并可选地显示该SQL查询的备用查询计划以及统计信息。用于显示查询计划的工具包括SQL EXPLAIN命令、$SYSTEM.SQL.ExPlan()方法以及管理门户和SQL Shell中的各种Show Plan工具。查询计划和统计数据是在准备查询时生成的,不需要执行查询。

可以使用以下选项来指导查询优化器,方法是设置配置默认值或在查询代码中编码优化器“提示”:

  • 管理所有条件的子句选项中提供的索引优化选项,或单个条件前面的%NOINDEX
  • SQL代码中指定的注释选项,使优化器覆盖该查询的系统范围编译选项。
  • 在每个查询或系统范围的基础上可用的并行查询处理允许多处理器系统在处理器之间划分查询执行。

以下SQL查询性能工具将在本手册的其他章节中介绍:

  • 缓存查询,使动态SQL查询能够重新运行,而无需在每次执行查询时准备查询的开销。
  • SQL语句来保留最新编译的嵌入式SQL查询。在“SQL语句和冻结计划”一章中。
  • 冻结计划以保留嵌入式SQL查询的特定编译。使用此编译,而不是使用较新的编译。在“SQL语句和冻结计划”一章中。

以下工具用于优化表数据,因此可以对针对该表运行的所有查询产生重大影响:

  • 定义索引可以显著提高对特定索引字段中数据的访问速度。
  • ExtentSizeSelectiveBlockCount用于在用数据填充表之前指定表数据估计;此元数据用于优化未来的查询。
  • Tune Table用于分析已填充的表中的代表性表数据;生成的元数据用于优化未来的查询。

本章还介绍如何将查询优化计划写入文件,以及如何生成SQL故障排除报告以提交给InterSystems WRC。

管理门户SQL性能工具

IRIS管理门户提供对以下SQL性能工具的访问。有两种方式可以从管理门户系统资源管理器选项访问这些工具:

  • 选择工具,然后选择SQL性能工具。
  • 选择SQL,然后选择工具下拉菜单。

从任一界面中您都可以选择以下SQL性能工具之一:

  • SQL运行时统计信息,以生成查询执行的性能统计信息。
  • 索引分析器,用于显示当前命名空间中所有查询的各种索引分析器报告。这显示了InterSystems SQL将如何执行查询,可以全面了解索引是如何使用的。此索引分析可能表明应该添加一个或多个索引以提高性能。
  • 备用显示计划:显示SQL查询的可用备用查询计划以及统计信息。
  • 生成报告以向InterSystems Worldwide Response Center(WRC)客户支持部门提交SQL查询性能报告。要使用此报告工具,必须首先从WRC获得WRC跟踪号。
  • 导入报告允许查看SQL查询性能报告。

SQL运行时统计信息

可以使用SQL运行时统计信息来衡量系统上运行的SQL查询的性能。SQL运行时统计信息衡量SELECTINSERTUPDATEDELETE操作(统称为查询操作)的性能。SQL运行时统计信息(SQL Stat)是在准备查询操作时收集的。请参阅使用SQL运行时统计信息工具。

默认情况下,SQL运行时统计信息的收集处于关闭状态。必须激活统计信息收集。强烈建议指定超时以结束统计信息收集。激活统计信息收集后,必须重新编译(准备)现有的动态SQL查询,并重新编译包含嵌入式SQL的类和例程。

性能统计信息包括ModuleNameModuleCount(模块被调用的次数)、RowCount(返回的行数)、TimeSpent(执行性能,单位为秒)、GlobalRefs(全局引用数)、LinesOfCode(执行的行数)和ReadLatency(磁盘读取访问时间,单位为毫秒)。

可以显式清除SQL Stats数据。清除缓存查询会删除所有相关的SQL统计数据。删除表或视图会删除所有相关的SQL Stats数据。

注意:系统任务在所有名称空间中每小时自动运行一次,以将特定于进程的SQL查询统计信息聚合到全局统计信息中。因此,全局统计信息可能不会反映一小时内收集的统计信息。可以使用管理门户监视此每小时一次的聚合或强制其立即发生。要查看此任务上次完成和下次调度的时间,请依次选择系统操作、任务管理器、任务调度,然后查看更新SQL查询统计信息任务。可以单击任务名称查看任务详细信息。在Task Details(任务详细信息)显示中,可以使用Run(运行)按钮强制立即执行任务。

使用SQL运行时统计信息工具

可以使用以下任一方法从管理门户显示系统范围内的SQL查询的性能统计信息:

  • 选择系统资源管理器,选择工具,选择SQL性能工具,然后选择SQL运行时统计信息。

image

  • 选择系统资源管理器,选择SQL,然后从工具下拉菜单中选择SQL运行时统计信息。

Settings

“设置”选项卡显示当前系统范围的SQL运行时统计信息设置以及此设置的过期时间。

Change Settings(更改设置)按钮允许设置以下统计信息收集选项:

  • 收集选项:可以将统计信息收集选项设置为0、1、2或3.0。0=关闭统计信息代码生成;1=为所有查询打开统计信息代码生成,但不收集统计信息;2=仅记录查询外部循环的统计信息(在主模块打开和关闭时收集统计信息);3=记录查询的所有模块级别的统计信息。
    • 从0到1:更改SQL Stats选项后,需要编译包含SQL的例程和类以执行统计代码生成。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。
    • 要从1变为2:只需更改SQL Stats选项即可开始收集统计信息。这使可以在运行的生产环境中启用SQL性能分析,并将中断降至最低。
    • 从1到3(或从2到3):更改SQL Stats选项后,需要编译包含SQL的例程和类,以记录所有模块级别的统计信息。对于xDBC和动态SQL,必须清除缓存查询以强制重新生成代码。选项3通常仅用于非生产环境中已识别的性能较差的查询。
    • 从1、2或3变为0:要关闭统计代码生成,不需要清除缓存的查询。
  • 超时选项:如果收集选项为2或3,可以按已用时间(小时或分钟)或按完成日期和时间指定超时。可以用分钟或小时和分钟指定运行时间;该工具将指定的分钟值转换为小时和分钟(100分钟=1小时40分钟)。默认值为50分钟。日期和时间选项默认为当天午夜(23:59)之前。强烈建议指定超时选项。
  • 重置选项:如果收集选项为2或3,则可以指定超时值到期时要重置为的收集选项。可用选项为0和1。

查询测试

查询测试选项卡允许输入SQL查询文本(或从历史记录中检索),然后显示该查询的SQL统计信息和查询计划。查询测试包括查询的所有模块级别的SQL统计信息,而与收集选项设置无关。

输入一个SQL查询文本,或使用Show History按钮检索一个。 可以通过单击右边的圆形“X”圆来清除查询文本字段。

使用Show Plan With SQL Stats按钮执行。

默认情况下,后台复选框中的“运行Show Plan进程”未被选中,这是大多数查询的首选设置。 仅对长时间、运行缓慢的查询选择此复选框。 当这个复选框被选中时,你会看到一个进度条显示“请等待…”的消息。 当运行一个长查询时,带有SQL Stats和Show History按钮的Show Plan消失,而显示一个View Process按钮。 单击View Process将在新选项卡中打开流程详细信息页面。 在流程详细信息页面中,可以查看该流程,并可以暂停、恢复或终止该流程。 流程的状态应该反映在显示计划页面上。 当流程完成后,显示计划会显示结果。 View Process按钮消失,带有SQL Stats的Show PlanShow History按钮重新出现。

使用查询测试显示的语句文本包括注释,不执行文字替换。

查看统计信息

View Stats(查看统计信息)选项卡为提供了在此系统上收集的运行时统计信息的总体视图。

可以单击任何一个View Stats列标题对查询统计信息进行排序。然后,可以单击SQL语句文本以查看所选查询的详细查询统计信息和查询计划。

使用此工具显示的语句文本包括注释,不执行文字替换。ExportStatsSQL()Show Plan显示的语句文本会去掉注释并执行文字替换。

清除统计信息按钮

清除统计信息按钮清除当前名称空间中所有查询的所有累积统计信息。它会在SQL运行时统计信息页上显示一条消息。如果成功,则会显示一条消息,指示已清除的统计信息数量。如果没有统计信息,则会显示无要清除的消息。如果清除不成功,则会显示一条错误消息。

运行时统计信息和显示计划

SQL运行时统计信息工具可用于显示包含运行时统计信息的查询的显示计划。

可以使用Alternate Show Plans工具将显示计划与统计数据进行比较,从而显示查询的运行时统计信息。备用显示计划工具在其显示计划选项中显示查询的估计统计信息。如果激活了收集运行时统计信息,则其Compare Show Plans with Stats选项将显示实际的运行时统计信息;如果运行时统计信息未处于活动状态,则此选项将显示估计统计信息。

0
0 208
文章 姚 鑫 · 四月 21, 2021 13m read

第四章 缓存查询(二)

运行时计划选择

运行时计划选择(RTPC)是一个配置选项,它允许SQL优化器利用运行时(查询执行时)的离群值信息。运行时计划选择是系统范围的SQL配置选项。

RTPC被激活时,准备查询包括检测查询是否包含具有离群值的字段上的条件。如果PREPARE检测到一个或多个异常值字段条件,则不会将查询发送到优化器。相反,SQL会生成一个运行时计划选择存根。在执行时,优化器使用此存根选择要执行的查询计划:忽略离群值状态的标准查询计划,或针对离群值状态进行优化的替代查询计划。如果有多个异常值条件,优化器可以从多个备选运行时查询计划中进行选择。

  • 准备查询时,SQL将确定它是否包含离群值字段条件。如果是这样,它将推迟选择查询计划,直到执行查询。在准备时,它创建一条标准SQL语句和(对于动态SQL)相应的缓存查询,但将选择是使用此查询计划还是创建不同的查询计划,直到查询执行。在准备时,它创建看起来像是标准SQL语句的内容,如下所示:DECLARE QRS CURSOR FOR SELECT Top ? Name,HaveContactInfo FROM Sample.MyTest WHERE HaveContactInfo=?,用问号表示文字替代变量。但是,如果查看SQL语句详细资料,则查询计划在准备时包含语句“执行可能导致创建不同的计划”,动态SQL查询还会创建看似标准的缓存查询;但是,缓存查询显示计划选项使用SELECT %NORUNTIME关键字显示查询文本,表明这是不使用RTPC的查询计划。
  • 执行查询(在嵌入式SQL中打开)时,SQL将创建第二个SQL语句和相应的缓存查询。SQL语句具有散列生成的名称并生成RTPC存根,如下所示: DECLARE C CURSOR FOR %NORUNTIME SELECT Top :%CallArgs(1) Name,HaveContactInfo FROM Sample.MyTest WHERE HaveContactInfo=:%CallArgs(2).然后,优化器使用它来生成相应的缓存查询。如果优化器确定离群值信息没有提供性能优势,它将创建一个与准备时创建的缓存查询相同的缓存查询,并执行该缓存查询。但是,如果优化器确定使用离群值信息可提供性能优势,则它会创建一个缓存查询,以禁止对缓存查询中的离群值字段进行文字替换。例如,如果HaveContactInfo字段是异常值字段(绝大多数记录的值为‘Yes’),查询SELECT Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=?将导致缓存查询:SELECT Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=(('Yes')).

请注意,RTPC查询计划的显示根据SQL代码的源代码而有所不同:

管理门户SQL界面显示计划按钮可能会显示另一个运行时查询计划,因为此显示计划从SQL界面文本框中获取其SQL代码。

选中该SQL语句后,将显示包括查询计划的语句详细资料。此查询计划不显示替代运行时查询计划,而是包含文本“执行可能导致创建不同的计划”,因为它从语句索引中获取其SQL代码。

如果RTPC未激活,或者查询不包含适当的离群值字段条件,优化器将创建标准SQL语句和相应的缓存查询。

如果一个RTPC存根被冻结,那么所有相关的备用运行时查询计划也会被冻结。 即使关闭了RTPC配置选项,对于冻结的查询,RTPC处理仍然是活动的。

在写查询时,可以通过指定圆括号来手动抑制文字替换: SELECT Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=(('Yes')).如果在条件中抑制离群值字段的文字替换,则RTPC不会应用于查询。 优化器创建一个标准的缓存查询。

激活RTPC

可以使用管理门户或类方法在系统范围内配置RTPC。 注意,更改RTPC配置设置将清除所有缓存的查询。

使用管理门户,根据参数值SQL设置配置系统范围的优化查询。 该选项将运行时计划选择(RTPC)优化和作为离群值(BQO)优化的偏差查询设置为合适的组合。 选择系统管理、配置、SQL和对象设置、SQL来查看和更改此选项。 可用的选择有:

  • 假设查询参数值不是字段离群值(BQO=OFF, RTPC=OFF,初始默认值)
  • 假设查询参数值经常匹配字段离群值(BQO=ON, RTPC=OFF)
  • 在运行时优化实际查询参数值(BQO=OFF, RTPC=ON)

要确定当前设置,调用$SYSTEM.SQL.CurrentSettings()

$SYSTEM.SQL.Util.SetOption()方法可以在系统范围内激活所有进程的RTPC,如下所示:SET status=$SYSTEM.SQL.Util.SetOption("RTPC",flag,.oldval)flag参数是一个布尔值,用于设置(1)或取消设置(0)RTPC。 oldvalue参数以布尔值的形式返回之前的RTPC设置。

应用RTPC

系统对SELECTCALL语句应用RTPC。 它不应用RTPC插入、更新或删除语句。

当在以下查询上下文中指定了一个离群值时,系统将RTPC应用于调优表确定的任何字段。

在与文字比较的条件中指定离群值字段。 这个比较条件可以是:

  • 使用相等(=)、非相等(!=)、IN%INLIST谓词的WHERE子句条件。
  • 具有相等(=)、非相等(!=)、IN%INLIST谓词的ON子句连接条件。

如果应用了RTPC,优化器将在运行时确定是应用标准查询计划还是备选查询计划。

如果查询中包含unresolved ? 输入参数。

如果查询指定了用双括号括起来的文字值,则不应用RTPC,从而抑制了文字替换。

如果文字是由子查询提供给离群字段条件的,则RTPC不会被应用。 但是,如果子查询中存在离群字段条件,则应用RTPC

Overriding RTPC

通过指定%NORUNTIMErestrict关键字,可以覆盖特定查询的RTPC。如果查询SELECT Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=? 会导致RTPC处理,查询 SELECT %NORUNTIME Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=?将覆盖RTPC,从而产生一个标准的查询计划。

缓存查询结果集

当执行缓存的查询时,它会创建一个结果集。 缓存的查询结果集是一个对象实例。 这意味着为文字替换输入参数指定的值被存储为对象属性。 这些对象属性使用i%PropName语法引用。

List缓存查询

计算缓存查询

通过调用%Library.SQLCatalog类的GetCachedQueryTableCount()方法,可以确定表的当前缓存查询数。下面的示例显示了这一点:

/// w ##class(PHA.TEST.SQL).CountingCachedQueries()
ClassMethod CountingCachedQueries()
{
	SET tbl="Sample.Person"
	SET num=##class(%Library.SQLCatalog).GetCachedQueryTableCount(tbl)
	IF num=0 {
		WRITE "没有缓存的查询 ",tbl 
	} ELSE {
		WRITE tbl," 与以下内容相关联 ",num," 缓存查询" 
	}
	q ""
}
DHC-APP>w ##class(PHA.TEST.SQL).CountingCachedQueries()
Sample.Person 与以下内容相关联 2 缓存查询

请注意,引用多个表的查询将创建单个缓存查询。但是,这些表中的每一个都单独计算该缓存查询的数量。因此,按表计数的缓存查询数可能大于实际缓存查询数。

显示缓存的查询

可以使用IRIS管理门户查看(和管理)查询缓存的内容。从系统资源管理器中,选择SQL。使用页面顶部的切换选项选择一个命名空间;这将显示可用命名空间的列表。在屏幕左侧打开Cached Queries文件夹。选择其中一个缓存查询将显示详细信息。

查询类型可以是下列值之一:

  • %SQL.Statement Dynamic SQL:使用%SQL.Statement的动态SQL查询。
  • Embedded cached SQL :嵌入式缓存SQL
  • ODBC/JDBC Statement:来自ODBC或JDBC的动态查询。

成功准备SQL语句后,系统会生成一个实现该语句的新类。如果已经设置了Retention Cached Query Source-System-wide配置选项,那么这个生成的类的源代码将被保留,并且可以使用Studio打开以供检查。要执行此操作,请转到IRIS管理门户。从系统管理中,依次选择配置、SQL和对象设置、SQL。在此屏幕上,可以设置保留缓存的查询源选项。如果未设置此选项(默认设置),系统将生成并部署类,并且不保存源代码。

也可以使用$SYSTEM.SQL.Util.SetOption()方法设置这个系统范围的选项,如下所示:SET status=$SYSTEM.SQL.Util.SetOption("CachedQuerySaveSource",flag,.oldval)Flag参数是一个布尔值,用于在编译缓存查询后保留(1)或不保留(0)查询源代码;默认值为0。要确定当前设置,请调用$SYSTEM.SQL.CurrentSettings()

使用^rINDEXSQL列出缓存查询

  ZWRITE ^rINDEXSQL("sqlidx",2)

此列表中的典型全局变量如下所示:

^rINDEXSQL("sqlidx",2,"%sqlcq.USER.cls4.1","oRuYrsuQDz72Q6dBJHa8QtWT/rQ=")="".

第三个下标是位置。例如,"%sqlcq.USER.cls4.1"是用户名称空间中的缓存查询;"Sample.MyTable.1"是一条SQL语句。第四个下标是语句散列。

将缓存查询导出到文件

以下实用程序将当前名称空间的所有缓存查询列出到文本文件中。

ExportSQL^%qarDDLExport(file,fileOpenParam,eos,cachedQueries,classQueries,classMethods,routines,display)
  • file 要列出缓存查询的文件路径名。指定为带引号的字符串。如果该文件不存在,系统将创建该文件。如果该文件已存在,则InterSystems IRIS会覆盖该文件。
  • fileOpenParam 可选-文件的打开模式参数。指定为带引号的字符串。默认值为“WNS”“W”指定正在打开文件以进行写入。“N”指定如果该文件不存在,则使用此名称创建一个新的顺序文件。“S”指定以回车符、换行符或换页符作为默认终止符的流格式。
  • eos 可选-用于分隔清单中各个缓存查询的语句结尾分隔符。指定为带引号的字符串。默认值为“GO”
  • cachedQueries 可选—从查询缓存导出所有SQL查询到文件。一个布尔标志。默认值为1。
  • classQueries 可选-从SQL类查询导出所有SQL查询到文件。一个布尔标志。默认值为1。
  • classMethods 可选-从类方法导出嵌入式SQL查询到文件。一个布尔标志。默认值为1。
  • routines 可选-从MAC例程导出嵌入式SQL查询到文件。这个清单不包括系统例程、缓存查询或生成的例程。一个布尔标志。默认值为1。
  • display 可选-在终端屏幕上显示导出进度。一个布尔标志。默认值为0。

下面是一个调用这个缓存查询导出工具的示例:

  DO ExportSQL^%qarDDLExport("C:\temp\test\qcache.txt","WNS","GO",1,1,1,1,1)

当在终端命令行中执行display=1时,导出进度显示在终端屏幕上,示例如下:

Export SQL Text for Cached Query: %sqlcq.USER.cls14..                Done
Export SQL Text for Cached Query: %sqlcq.USER.cls16..                Done
Export SQL Text for Cached Query: %sqlcq.USER.cls17..                Done
Export SQL Text for Cached Query: %sqlcq.USER.cls18..                Done
Export SQL Text for Cached Query: %sqlcq.USER.cls19..                Done
Export SQL statement for Class Query: Cinema.Film.TopCategory...        Done
Export SQL statement for Class Query: Cinema.Film.TopFilms...           Done
Export SQL statement for Class Query: Cinema.FilmCategory.CategoryName...Done
Export SQL statement for Class Query: Cinema.Show.ShowTimes...          Done

20 SQL statements exported to script file C:\temp\test\qcache.txt

创建的导出文件包含如下条目:

  -- SQL statement from Cached Query %sqlcq.USER.cls30
  SELECT TOP ? Name , Home_State , Age , AVG ( Age ) AS AvgAge FROM Sample . Person ORDER BY Home_State
GO
  -- SQL statement from Class Query Cinema.Film.TopCategory
#import Cinema
SELECT TOP 3 ID, Description, Length, Rating, Title, Category->CategoryName
  FROM Film
  WHERE (PlayingNow = 1) AND (Category = :P1)
  ORDER BY TicketsSold DESC
GO
  -- SQL statement(s) from Class Method Aviation.EventCube.Fact.%Count
#import Aviation.EventCube
SELECT COUNT(*) INTO :tCount FROM Aviation_EventCube.Fact
GO

这个缓存的查询列表可以用作查询优化计划实用程序的输入。

执行缓存查询

  • 从动态SQL:%SQL.Statement准备操作(%PrepareClassQuery()%ExecDirect())创建缓存查询。使用同一实例的动态SQL%Execute()方法执行最近准备的缓存查询。
  • 从终端:可以使用$SYSTEM.SQL类的ExecuteCachedQuery()方法直接执行缓存查询。此方法允许指定输入参数值并限制要输出的行数。可以从终端命令行执行动态SQL%SQL.Statement缓存查询或xDBC缓存查询。此方法主要用于测试有限数据子集上的现有缓存查询。
  • 在管理门户SQL界面中:按照上面的“显示缓存的查询”说明进行操作。从所选缓存查询的目录详细资料选项卡中,单击执行链接。

缓存查询锁

在更新缓存的查询元数据时,发出PREPAREPURCESS语句会自动请求独占的系统范围锁。SQL支持$SYSTEM.SQL.Util.SetOption()方法的系统范围CachedQueryLockTimeout选项。此选项控制在尝试获取对缓存查询元数据的锁定时的锁定超时。默认值为120秒。这比标准的SQL锁定超时(默认为10秒)要长得多。系统管理员可能需要在具有大量并发准备和清除操作的系统上修改此缓存查询锁定超时,尤其是在执行涉及大量(数千)缓存查询的批量清除的系统上。

SET status=$SYSTEM.SQL.Util.SetOption("CachedQueryLockTimeout",seconds,.oldval)方法设置系统范围的超时值:

SetCQTimeout
   SET status=$SYSTEM.SQL.Util.SetOption("CachedQueryLockTimeout",150,.oldval)
   WRITE oldval," initial value cached query seconds",!!
SetCQTimeoutAgain
   SET status=$SYSTEM.SQL.Util.SetOption("CachedQueryLockTimeout",180,.oldval2)
   WRITE oldval2," prior value cached query seconds",!!
ResetCQTimeoutToDefault
   SET status=$SYSTEM.SQL.Util.SetOption("CachedQueryLockTimeout",oldval,.oldval3)

CachedQueryLockTimeout设置系统范围内所有新进程的缓存查询锁定超时。它不会更改现有进程的缓存查询锁定超时。

清除缓存的查询

每当修改(更改或删除)表定义时,基于该表的任何查询都会自动从本地系统上的查询缓存中清除。如果重新编译持久类,则使用该类的任何查询都会自动从本地系统上的查询缓存中清除。

可以使用清除缓存查询选项之一通过管理门户显式清除缓存查询。可以使用SQL命令PURGE Cached Queries显式清除缓存查询。可以使用SQL Shell清除命令显式清除缓存查询。

可以使用$SYSTEM.SQL.Push(N)方法显式清除最近未使用的缓存查询。指定n天数将清除当前命名空间中在过去n天内未使用(准备)的所有缓存查询。将n值指定为0“”将清除当前命名空间中的所有缓存查询。例如,如果在2018年5月11日发出$SYSTEM.SQL.Push(30)方法,则它将仅清除在2018年4月11日之前最后准备的缓存查询。不会清除恰好在30天前(在本例中为4月11日)上次准备的缓存查询。

还可以使用以下方法清除缓存的查询:

  • $SYSTEM.SQL.PurgeCQClass()按名称清除当前命名空间中的一个或多个缓存查询。可以将缓存的查询名称指定为逗号分隔的列表。缓存查询名称区分大小写;命名空间名称必须以全大写字母指定。指定的缓存查询名称或缓存查询名称列表必须用引号引起来。
  • $SYSTEM.SQL.PurgeForTable()清除当前命名空间中引用指定表的所有缓存查询。架构和表名称不区分大小写。
  • $SYSTEM.SQL.PurgeAllNamespaces()清除当前系统上所有名称空间中的所有缓存查询。请注意,删除命名空间时,不会清除与其关联的缓存查询。执行PurgeAllNamespaces()检查是否有任何与不再存在的名称空间相关联的缓存查询;如果有,则清除这些缓存查询。

要清除当前命名空间中的所有缓存查询,请使用管理门户清除此命名空间的所有查询选项。

清除缓存的查询还会清除相关的查询性能统计信息。

清除缓存的查询还会清除相关的SQL语句列表条目。管理门户中列出的SQL语句可能不会立即清除,可能需要按清除陈旧按钮才能从SQL语句列表中清除这些条目。

注意:当您更改系统范围的默认架构名称时,系统会自动清除系统上所有名称空间中的所有缓存查询。

远程系统

在本地系统上清除缓存的查询不会清除该缓存查询在镜像系统上的副本。 必须手动清除远程系统上已清除的缓存查询的副本。

当修改和重新编译持久性类时,基于该类的本地缓存查询将被自动清除。 IRIS不会自动清除远程系统上缓存的查询的副本。 这可能意味着远程系统上缓存的一些查询是“过时的”(不再有效)。 但是,当远程系统尝试使用缓存的查询时,远程系统会检查查询引用的任何持久类是否已重新编译。 如果重新编译了本地系统上的持久化类,则远程系统在尝试使用它之前会自动清除并重新创建过时的缓存查询。

没有缓存的SQL命令

以下非查询SQL命令不会缓存;它们在使用后会立即清除:

  • 数据定义语言(DDL):CREATE TABLE, ALTER TABLE, DROP TABLE, CREATE VIEW, ALTER VIEW, DROP VIEW, CREATE INDEX, DROP INDEX, CREATE FUNCTION, CREATE METHOD, CREATE PROCEDURE, CREATE QUERY, DROP FUNCTION, DROP METHOD, DROP PROCEDURE, DROP QUERY, CREATE TRIGGER, DROP TRIGGER, CREATE DATABASE, USE DATABASE, DROP DATABASE
  • 用户、角色和权限:CREATE USER, ALTER USER, DROP USER, CREATE ROLE, DROP ROLE, GRANT, REVOKE, %CHECKPRIV
  • 锁 :LOCK TABLE, UNLOCK TABLE
  • 其他: SAVEPOINT, SET OPTION

请注意,如果从管理门户执行查询界面发出这些SQL命令之一,性能信息将包括如下文本:缓存查询:%sqlcq.USER.cls16。这将显示在中,表示已分配缓存的查询名称。但是,此缓存查询名称不是链接。未创建缓存查询,并且未保留增量缓存查询编号.cls16。 SQL将此缓存的查询号分配给下一个发出的SQL命令。

0
0 167
文章 姚 鑫 · 四月 20, 2021 13m read

第四章 缓存查询(一)

系统自动维护已准备好的SQL语句(“查询”)的缓存。这允许重新执行SQL查询,而无需重复优化查询和开发查询计划的开销。缓存查询是在准备某些SQL语句时创建的。准备查询发生在运行时,而不是在编译包含SQL查询代码的例程时。通常,PREPARE紧跟在SQL语句的第一次执行之后,但在动态SQL中,可以准备查询而不执行它。后续执行会忽略PREPARE语句,转而访问缓存的查询。要强制对现有查询进行新的准备,必须清除缓存的查询。

所有SQL调用都会创建缓存查询,无论是在ObjectScript例程中调用还是在类方法中调用。

  • 动态SQL、ODBC、JDBC和$SYSTEM.SQL.DDLImport()方法在准备查询时创建缓存查询。管理门户执行SQL接口、InterSystems SQL Shell和%SYSTEM.SQL.Execute()方法使用动态SQL,因此使用准备操作来创建缓存查询。

它们列在命名空间(或指定方案)的Management Portal常规缓存查询列表、每个正在访问的表的Management Portal Catalog Details缓存查询列表以及SQL语句列表中。动态SQL遵循本章中介绍的缓存查询命名约定。

  • 类查询在准备(%PrepareClassQuery()方法)或第一次执行(调用)时创建缓存查询。

它们列在命名空间的管理门户常规缓存查询列表中。如果类查询是在持久类中定义的,则缓存的查询也会列在该类的Catalog Details缓存查询中。它没有列在正在访问的表的目录详细信息中。它没有列在SQL语句清单中。类查询遵循本章中介绍的缓存查询命名约定。

  • 嵌入式SQL在第一次执行SQL代码或通过调用声明游标的OPEN命令启动代码执行时创建缓存查询。嵌入式SQL缓存查询列在管理门户缓存查询列表中,查询类型为嵌入式缓存SQL,SQL语句列表。嵌入式SQL缓存查询遵循不同的缓存查询命名约定。

所有清除缓存查询操作都会删除所有类型的缓存查询。

生成缓存查询的SQL查询语句包括:

  • SELECTSELECT缓存查询显示在其表的目录详细资料中。如果查询引用了多个表,则会为每个被引用的表列出相同的缓存查询。从这些表中的任何一个清除缓存的查询都会将其从所有表中清除。从表的目录详细资料中,可以选择缓存的查询名称以显示高速缓存的查询详细资料,包括执行和显示计划选项。由$SYSTEM.SQL.Schema.ImportDDL(“IRIS”)方法创建的选择缓存查询不提供ExecuteShow Plan选项。

SELECTDECLARE NAME CURSOR创建缓存查询。但是,缓存的查询详细信息不包括执行和显示计划选项。

  • CALL:为其架构创建缓存查询列表中显示的缓存查询。
  • INSERTUPDATEINSERTUPDATEDELETE:创建其表的Catalog Details中显示的缓存查询。
  • TRUNCATE TABLE:为其表创建一个缓存查询,该查询显示在目录详细信息中。 注意,$SYSTEM.SQL.Schema.ImportDDL("IRIS")不支持截断表。
  • SET TRANSACTION, START TRANSACTION%INTRANSACTION, COMMIT, ROLLBACK:为命名空间中的每个模式创建一个缓存查询,显示在缓存查询列表中。

当准备查询时,将创建一个缓存的查询。 因此,不要将%Prepare()方法放入循环结构中是很重要的。 同一个查询的后续%Prepare()(仅在指定的文字值上有所不同)使用现有的缓存查询,而不是创建新的缓存查询。

更改表的SetMapSelectability()值将使所有引用该表的现有缓存查询失效。 现有查询的后续准备将创建一个新的缓存查询,并从清单中删除旧的缓存查询。

清除缓存查询时,缓存查询将被删除。修改表定义会自动清除引用该表的所有查询。在更新查询缓存元数据时,发出准备或清除命令会自动请求独占的系统范围锁。系统管理员可以修改缓存查询锁定的超时值。

创建缓存的查询不是事务的一部分。缓存查询的创建不会被记录下来。

缓存查询提高了性能

第一次准备查询时,SQL引擎会对其进行优化,并生成将执行该查询的程序(一个或多个InterSystems IRIS®Data Platform例程的集合)。然后将优化的查询文本存储为缓存查询类。如果随后尝试执行相同(或类似)的查询,SQL引擎将找到缓存的查询并直接执行该查询的代码,从而绕过优化和代码生成的需要。

缓存查询提供以下好处:

  • 频繁使用的查询的后续执行速度更快。更重要的是,无需编写繁琐的存储过程即可自动获得这种性能提升。大多数关系数据库产品建议仅使用存储过程访问数据库。对于IRIS,这不是必需的。
  • 单个缓存的查询用于类似的查询,这些查询只是在字面值上有所不同。例如,SELECT TOP 5 Name FROM Sample.Person WHERE Name %STARTSWITH 'A' and SELECT TOP 1000 Name FROM Sample.Person WHERE Name %STARTSWITH 'Mc',只是top%startswith条件的文本值不同。为第一查询准备的缓存查询自动用于第二查询。
  • 查询缓存在所有数据库用户之间共享;如果用户1准备查询,则用户1023可以利用它。
  • 查询优化器可以自由地使用更多的时间为给定的查询找到最佳解决方案,因为这个代价只需要在第一次准备查询时支付。

InterSystems SQL将所有缓存的查询存储在一个位置,即IRISLOCALDATA数据库。但是,缓存查询是特定于名称空间的。每个缓存的查询都由准备(生成)它的名称空间标识。只能从准备缓存查询的命名空间中查看或执行缓存查询。可以清除当前命名空间或所有命名空间的缓存查询。

缓存查询不包括注释。但是,它可以在查询文本后面包含注释选项,例如/*#OPTIONS {"optionName":value} */

因为缓存查询使用现有的查询计划,所以它为现有查询提供了操作的连续性。对基础表的更改(如添加索引或重新定义表优化统计信息)不会对现有缓存查询产生任何影响。

创建缓存查询

当InterSystems IRIS准备查询时,它会确定:

  • 如果查询与查询缓存中已有的查询匹配。如果不是,则向查询分配递增计数。
  • 如果查询准备成功。如果不是,则不会将递增计数分配给缓存的查询名称。
  • 否则,递增计数被分配给缓存的查询名称,并且该查询被缓存。

动态SQL的缓存查询名称

SQL引擎为每个缓存查询分配唯一的类名,格式如下:

%sqlcq.namespace.clsnnn

其中,NAMESPACE为当前名称空间(大写),NNN为连续整数。例如,%sqlcq.USER.cls16

缓存的查询以每个命名空间为基础按顺序编号,从1开始。下一个可用的nnn序列号取决于已保留或释放的编号:

  • 如果查询与现有缓存查询不匹配,则在开始准备查询时会保留一个数字。如果查询与现有的缓存查询仅在文字值上不同,则查询与现有的缓存查询匹配-这取决于某些其他注意事项:隐藏的文本替换、不同的注释选项或“单独的缓存查询”中描述的情况。
  • 如果查询准备不成功,则保留但不分配号码。只有准备成功的查询才会被缓存。
  • 如果缓存查询准备成功,则会保留一个编号并将其分配给缓存查询。无论是否从该表访问任何数据,都会为查询中引用的每个表列出该缓存查询。如果查询未引用任何表,则会创建缓存查询,但不能按表列出或清除。
  • 清除缓存查询时会释放一个数字。该号码将作为下一个NNN序列号可用。清除与表关联的单个缓存查询或清除表的所有缓存查询将释放分配给这些缓存查询的编号。清除命名空间中的所有缓存查询会释放分配给缓存查询的所有编号,包括未引用表的缓存查询,以及保留但未分配的编号。

清除缓存查询将重置nnn整数。整数会被重复使用,但剩余的缓存查询不会重新编号。例如,缓存查询的部分清除可能会留下cls1、cls3、cls4和cls7。后续缓存查询将编号为cls2、cls5、cls6和cls8

一条CALL语句可能会导致多个缓存查询。例如,SQL语句CALL Sample.PersonSets('A','MA') 生成以下缓存查询:

%sqlcq.USER.cls1: CALL Sample . PersonSets ( ? , ? )
%sqlcq.USER.cls2: SELECT name , dob , spouse FROM sample . person 
                     WHERE name %STARTSWITH ? ORDER BY 1
%sqlcq.USER.cls3: SELECT name , age , home_city , home_state 
                     FROM sample . person WHERE home_state = ? ORDER BY 4 , 1

在动态SQL中,准备SQL查询(使用%PrepareClassQuery()%PrepareClassQuery()实例方法)后,可以使用%display()实例方法或%GetImplementationDetails()实例方法返回缓存的查询名称。查看成功准备的结果。

缓存的查询名称也是由%SQL.Statement类的%Execute()实例方法(以及%CurrentResult属性)返回的结果集OREF的一个组件。以下示例显示了这两种确定缓存查询名称的方法:

/// w ##class(PHA.TEST.SQL).CacheQuery()
ClassMethod CacheQuery(c)
{
	SET randtop=$RANDOM(10)+1
	SET randage=$RANDOM(40)+1
	SET myquery = "SELECT TOP ? Name,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 x = tStatement.%GetImplementationDetails(.class,.text,.args)
	IF x=1 { 
		WRITE "cached query name is: ",class,! 
	}
	SET rset = tStatement.%Execute(randtop,randage)
	WRITE "result set OREF: ",rset.%CurrentResult,!
	DO rset.%Display()
	WRITE !,"A sample of ",randtop," rows, with age < ",randage
}
DHC-APP>w ##class(PHA.TEST.SQL).CacheQuery()
cached query name is: %sqlcq.DHCdAPP.cls51
result set OREF: 5@%sqlcq.DHCdAPP.cls51
Name    Age
姚鑫    7
姚鑫    7
O'Rielly,Chris H.       7
Orwell,John V.  4
Zevon,Heloisa O.        11
Smith,Kyra P.   7
 
6 Rows(s) Affected
A sample of 6 rows, with age < 19

在本例中,选定的行数(TOP子句)和WHERE子句谓词值会随着每次查询调用而改变,但缓存的查询名称不会改变。

嵌入式SQL的缓存查询名称

SQL引擎为每个嵌入式SQL缓存查询分配一个唯一的类名,格式如下:

%sqlcq.namespace.hash

其中,NAMESPACE是当前的名称空间(大写),HASH是唯一的哈希值。例如,%sqlcq.USER.xEM1h5QIeF4l3jhLZrXlnThVJZDh

管理门户为每个表列出了嵌入式SQL缓存查询,目录详细信息为每个表列出了具有这个类名的缓存查询,查询类型为嵌入式缓存SQL。

单独的缓存查询

两个不应该影响查询优化的查询之间的差异仍然会生成单独的缓存查询:

  • 同一函数的不同语法形式会生成单独的缓存查询。因此,ASCII(‘x’){fn ASCII(‘x’)}生成单独的缓存查询,而{fn CURDATE()}{fn CURDATE}生成单独的缓存查询。
  • 区分大小写的表别名或列别名值以及可选的AS关键字的存在或不存在将生成单独的缓存查询。因此,ASCII('x'), ASCII('x') AChar, and ASCII('x') AS AChar会生成单独的缓存查询。
  • 使用不同的ORDER BY子句。
  • 使用top all代替具有整数值的top

文字替换

当SQL引擎缓存一个SQL查询时,它会执行文字替换。 查询缓存中的查询用“?” 字符,表示输入参数。 这意味着,仅在文字值上不同的查询由单个缓存的查询表示。 例如,两个查询:

SELECT TOP 11 Name FROM Sample.Person WHERE Name %STARTSWITH 'A'
SELECT TOP 5 Name FROM Sample.Person WHERE Name %STARTSWITH 'Mc'

都由单个缓存查询表示:

SELECT TOP ? Name FROM Sample.Person WHERE Name %STARTSWITH ?

这最小化了查询高速缓存的大小,并且意味着不需要对仅在字面值上不同的查询执行查询优化。

使用输入主机变量(例如:myvar)和? 输入参数也在相应的缓存查询中用“?” ”字符。 因此, SELECT Name FROM t1 WHERE Name='Adam', SELECT Name FROM t1 WHERE Name=?, and SELECT Name FROM t1 WHERE Name=:namevar ,都是匹配查询,并生成单个缓存查询。

可以使用%GetImplementationDetails()方法来确定这些实体中的哪些实体由每个“?”特定准备的字符。

以下注意事项适用于文字替换:

  • 指定为文字一部分的加号和减号将生成单独的缓存查询。因此,ABS(7)ABS(-7)ABS(+7)各自生成一个单独的缓存查询。多个符号也会生成单独的缓存查询:ABS(+?)ABS(++?)。因此,最好使用无符号变量ABS(?)。或ABS(:Num),可以为其提供有符号或无符号数字,而无需生成单独的缓存查询。
  • 精度和小数值通常不接受文字替换。因此,ROUND(567.89,2)被缓存为ROUND(?,2)。但是,CURRENT_TIME(N)CURRENT_TIMESTAMP(N)GETDATE(N)GETUTCDATE(N)中的可选精度值不接受文字替换。
  • IS NULLIS NOT NULL条件中使用的文字不接受文字替换。
  • ORDER BY子句中使用的任何文字都不接受文字替换。这是因为ORDER BY可以使用整数来指定列位置。更改此整数将导致根本不同的查询。
  • 字母文字必须用单引号引起来。某些函数允许指定带引号或不带引号的字母格式代码;只有带引号的字母格式代码才接受文字替换。因此,DATENAME(MONTER,64701)DATENAME(‘MONTER’,64701)在功能上是相同的,但是对应的缓存查询是DATENAME(MONTER,?)。和DATENAME(?,?)
  • 接受可变数量参数的函数会为每个参数计数生成单独的缓存查询。因此,Coalesce(1,2)Coalesce(1,2,3)会生成单独的缓存查询。

DynamicSQLTypeList Comment Option

当匹配查询时,注释选项被视为查询文本的一部分。 因此,在注释选项中不同于现有缓存查询的查询与现有缓存查询不匹配。 注释选项可以作为查询的一部分由用户指定,也可以由SQL预处理器在准备查询之前生成并插入。

如果SQL查询包含文字值,SQL预处理器将生成DynamicSQLTypeList注释选项,并将其附加到缓存的查询文本的末尾。此注释选项为每个文字分配数据类型。数据类型按照文字在查询中出现的顺序列出。只列出实际文字,而不是输入主机变量或?输入参数。下面是一个典型的例子:

SELECT TOP 2 Name,Age FROM Sample.MyTest WHERE Name %STARTSWITH 'B' AND Age > 21.5

生成缓存的查询文本:

SELECT TOP ? Name , Age FROM Sample . MyTest WHERE Name %STARTSWITH ? AND Age > ? /*#OPTIONS {"DynamicSQLTypeList":"10,1,11"} */

在本例中,文字2被列为类型10(整数),文字“B”被列为类型1(字符串),而文字21.5被列为类型11(数字)。

请注意,数据类型分配仅基于文字值本身,而不是关联字段的数据类型。例如,在上面的示例中,Age被定义为数据类型INTEGER,但是文字值21.5被列为NUMERIC。因为InterSystems IRIS将数字转换为规范形式,所以文字值21.0将被列为整数,而不是数字。

DynamicSQLTypeList返回以下数据类型值:

数字描述
1长度为1到32(包括1到32)的字符串
2长度为33到128(含)的字符串
3长度为129到512(含)的字符串
4长度大于512的字符串
10Integer
11Numeric

由于DynamicSQLTypeList注释选项是查询文本的一部分,因此更改文本以使其产生不同的数据类型会导致创建单独的缓存查询。例如,增加或减少文字字符串的长度,使其落入不同的范围。

文字替换和性能

SQL引擎对IN谓词的每个值执行文字替换。大量IN谓词值可能会对缓存查询性能产生负面影响。可变数量的IN谓词值可能会导致多个缓存查询。将IN谓词转换为%INLIST谓词会导致谓词只有一个文字替换,而不管列出的值有多少。%INLIST还提供了一个数量级大小参数,SQL使用该参数来优化性能。

取消文字替换

可以取消这种文字替换。在某些情况下,可能希望对文字值进行优化,并为具有该文字值的查询创建单独的缓存查询。若要取消文字替换,请将文字值括在双圆括号中。下面的示例显示了这一点:

SELECT TOP 11 Name FROM Sample.Person WHERE Name %STARTSWITH (('A'))

指定不同的 %STARTSWITH值将生成单独的缓存查询。请注意,对每个文字分别指定禁止文字替换。在上面的示例中,指定不同的TOP值不会生成单独的缓存查询。

要取消有符号数字的文字替换,请指定诸如 ABS(-((7)))之类的语法。

注意:在某些情况下,不同数量的括号也可能会抑制文字替换。InterSystems建议始终使用双圆括号作为此目的最清晰和最一致的语法。

共分注释选项

如果一个SQL查询指定了多个分割表,则SQL预处理器会生成一个共分片注释选项,并将该选项附加到缓存的查询文本的末尾。此共分选项显示是否对指定的表进行共分。

在下面的示例中,所有三个指定的表都进行了编码共享:

/*#OPTIONS {"Cosharding":[["T1","T2","T3"]]} */

在以下示例中,指定的三个表均未进行编码共享:

/*#OPTIONS {"Cosharding":[["T1"],["T2"],["T3"]]} */

在以下示例中,表T1未被编分,但表T2T3被编分:

/*#OPTIONS {"Cosharding":[["T1"],["T2","T3"]]} */
0
1 332
文章 姚 鑫 · 四月 19, 2021 11m read

第三章 优化表(二)

调整表计算值

调优表操作根据表中的代表性数据计算和设置表统计信息:

  • ExtentSize,它可能是表中的实际行数(行数),也可能不是。
  • 表中每个属性(字段)的选择性。 可以选择性地阻止单个属性的选择性计算。
  • 属性的离群选择性,其中一个值比其他值出现得更普遍。 有效的查询可以利用离群值优化。
  • 标识某些属性特征的每个属性的注释。
  • 每个属性的平均字段大小。
  • 表的SQL Map NameBlockCountSource of BlockCount

区段大小和行计数

从管理门户运行Tune Table工具时,ExtentSize是表中当前行的实际计数。默认情况下,GatherTableStats()方法还将实际行数用作ExtentSize。当表包含大量行时,最好对较少的行执行分析。可以使用SQL tune table命令并指定%SAMPLE_PERCENT来仅对总行的一定百分比执行分析。在针对包含大量行的表运行时,可以使用此选项来提高性能。此%SAMPLE_PERCENT值应该足够大,以便对代表性数据进行采样。如果ExtentSize<1000,则无论%SAMPLE_PERCENT值如何,TUNE TABLE都会分析所有行。

指定的ExtentSize可以小于或大于实际行数。但是,ExtentSize不应显著超过当前表数据中的实际行数。指定ExtentSize时,Tuning Table会为该数量的行外推行ID,然后执行采样。如果ExtentSize大大超过实际行数,则大多数采样的行ID将与实际行数据不对应。如果是这种情况,则无法计算字段选择性;相反,Tune Table将列出指定的ExtentSize作为计算的ExtentSize,并列出一个较小的数字作为SAMPLESIZE;Tune Table为这些不存在的计算值返回<Not Specified>

可以将ExtentSize设置为0。如果表从来不打算填充数据,但用于其他目的(如查询联接),则可能需要这样做。当将ExtentSize设置为0时,InterSystems IRIS会将每个字段的选择性设置为100%,并将每个字段的平均字段大小设置为0

选择性和异常值选择性

Tune Table以百分比计算每个属性(字段)值的选择性。 它通过对数据进行抽样来实现这一点,因此选择性总是一种估计,而不是一个精确的值。 选择性是基于所有属性值都是(或可能是)等可能的假设。 对于大多数数据来说,这是一个合理的假设。 例如,在一个普通人口表中,大多数数据值都是典型的:任何特定的出生日期都会出现在大约0.27%的数据中(365分之一); 大约一半是女性,一半是男性(50%)。 被定义为Unique的字段的选择性为1(不应与选择性为1.0000(1%)混淆)。 对于大多数性质,选择性百分比就足够了。

对于一些属性,Tune Table还计算离群值选择性。 这是单个属性值的百分比,与其他数据值相比,该属性值在示例中出现的频率更高。 只有当一个数据值的频率与其他数据值的频率存在显著差异时,调优表才会返回离群值选择性。 无论数据值的分布情况如何,Tune Table最多为表返回一个离群值。 如果选择了一个离群值,那么调优表将此值显示为离群值。 NULL表示为< NULL >

如果TuneTable返回异常值选择性,则正常选择性仍然是整个行集内每个非异常值数据值的百分比。例如,如果在1000个随机选择的值中检测到11个不同的值,其中一个是异常值,则选择性为1/11(9.09%):平均每个条目出现的几率为十一分之一。如果异常值选择性是80%,常规选择性是1%,那么除了异常值之外,还可以找到大约20((1-0.80)/0.01)个额外的非异常值。

如果优化表初始采样仅返回单个值,但附加采样返回多个不同的值,则这些采样结果会修改正常选择性。例如,990个值的初始随机采样仅检测一个值,但后续采样检测其他不同值的10个单个实例。在这种情况下,初始离群值会影响选择性值,该值现在被设置为1/1000(0.1%),因为10个非离群值中的每一个在1000个记录中只出现一次。

异常值选择性的最常见示例是允许NULL的属性。如果某个特性具有NULL的记录数大大超过该特性具有任何特定数据值的记录数,则NULL为异常值。以下是FavoriteColors字段的选择性和异常值选择性:

SELECTIVITY of FIELD FavoriteColors
   CURRENT =     1.8966%
   CALCULATED =  1.4405%
   CURRENT OUTLIER = 45.0000%, VALUE = <Null>
   CALCULATED OUTLIER = 39.5000%, VALUE = <Null> 

如果一个字段只包含一个不同的值(所有行都具有相同的值),则该字段的选择性为100%。选择性为100%的值不被视为异常值。调谐表通过采样数据来建立选择性和异常值选择值。为了确定这一点,优选表首先测试少量或几条记录,如果这些记录都具有相同的字段值,它将测试多达100,000条随机选择的记录,以支持非索引字段的所有值都相同的假设。只有在字段已编制索引,字段是索引的第一个字段,并且字段和索引具有相同的排序规则类型的情况下,优化表才能完全确定该字段的所有值是否相同。

  • 如果已知未编制索引的字段具有在测试100,000条随机选择的记录中可能检测不到的其他值,则应手动设置选择性和离群值选择性。
  • 如果已知非索引字段没有其他值,则可以手动指定100%的选择性,删除任何异常值选择性,并设置CALCSELECTIVITY=0以防止优选表尝试计算选择性或将此值指定为异常值。

要修改这些选择性、异常值选择性和异常值计算值,请从调谐表显示中选择单个字段。这会在显示屏右侧的详细信息区域中显示该字段的这些值。可以将选择性、异常值选择性和/或异常值修改为更适合预期完整数据集的值。

  • 可以将选择性指定为带有百分号(%)的行的百分比,也可以指定为整数行(没有百分号)。如果指定为整数行数,InterSystems IRIS将使用区大小来计算选择性百分比。
  • 可以为以前没有异常值的字段指定异常值选择性和异常值。将异常值选择性指定为带百分号(%)的百分比。如果仅指定异常值选择性,则Tune Table假定异常值为<Null>。如果仅指定异常值,则除非还指定异常值选择性,否则调谐表不会保存此值。

CALCSELECTIVITY参数与不计算选择性

在某些情况下,可能不希望优化表工具计算属性的选择性。要防止计算选择性,请将属性的CALCSELECTIVITY参数的值指定为0(默认值为1)。在Studio中,可以在“新建属性向导”的“属性参数”页上设置CALCSELECTIVITY,也可以在检查器中的属性参数列表中设置CALCSELECTIVITY(可能需要收缩并重新展开属性参数列表才能显示它)。

应该指定CALCSELECTIVITY=0的一种情况是,如果该字段未编制索引,则已知该字段在所有行中只包含一个值(选择性=100%)。

离群值的优化

默认情况下,查询优化器假定查询不会选择离群值。 例如,查询通常选择特定的字段值并从数据库返回少量记录,而不是返回大量记录,其中该字段值是离群值。 查询优化器总是使用选择性来构造查询计划,除非执行一些要求考虑离群选择性的操作。

根据选择离群值,可以执行以下几个操作来调整查询优化:

  • 如果异常值是<null>,则在查询WHERE子句中为该字段指定一个is nullis NOT null条件。 这将导致查询优化器在构造查询时使用离群值选择性。

  • 如果离群值是一个数据值,查询优化器会假定选择的字段值不是离群值。 例如,总部位于马萨诸塞州的公司的员工记录可能有Office_State字段离群值MA (Massachusetts)。 优化器假设查询不会选择' MA ',因为这将返回数据库中的大多数记录。 但是,如果正在编写一个查询来选择离群值,可以通过将离群值封装在双括号中来通知优化器。 在该字段上查询时,指定一个WHERE子句,如下所示:WHERE Office_State=(('MA'))。 这种技术抑制了文字替换,并迫使查询优化器在构建查询计划时使用离群值选择性。 对于动态SQL查询,以及在使用ODBC/JDBC提供的InterSystems IRIS之外编写的查询,这种语法是必需的。 对于类查询、嵌入式SQL查询或通过视图访问的查询,则不需要这样做。

  • 根据参数值SQL设置配置系统范围的优化查询。 该选项为离群值设置了运行时计划选择(RTPC)优化和作为离群值(BQO)优化的偏差查询的适当组合。 注意,更改此配置设置将清除所有名称空间中的所有缓存查询。 使用管理门户,选择System Administration、Configuration、SQL和Object Settings、SQL来查看和更改此选项。 可用的选择有:

    • 假设查询参数值不是字段离群值(BQO=OFF, RTPC=OFF,初始默认值)
    • 假设查询参数值经常匹配字段离群值(BQO=ON, RTPC=OFF)
    • 在运行时优化实际查询参数值(BQO=OFF, RTPC=ON)

要确定当前设置,调用$SYSTEM.SQL.CurrentSettings()

  • 覆盖查询的系统范围配置设置。

通过指定%NORUNTIME restrict关键字,可以覆盖单个查询的RTPC。 如果查询SELECT Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=? 将导致RTPC处理,查询SELECT %NORUNTIME Name,HaveContactInfo FROM t1 WHERE HaveContactInfo=? 将覆盖RTPC,从而产生一个标准的查询计划。

通过指定注释选项/*#OPTIONS {"BiasAsOutlier":1} */,可以覆盖偏见查询作为单个查询的离群值。

“备注”列

管理门户优化表信息选项为每个字段显示一个备注列。此字段中的值是系统定义的,不可修改。它们包括以下内容:

  • RowID字段:一个表有一个RowID,由系统定义。它的名称通常是ID,但可以有不同的系统分配的名称。由于其所有值(根据定义)都是唯一的,因此其选择性始终为1。如果类定义包括SqlRowIdPrivate,则Notes列值为RowID字段、Hidden字段。
  • 隐藏字段:隐藏字段定义为私有,SELECT*不显示。默认情况下,CREATE TABLERowID字段定义为隐藏;可以指定%PUBLICROWID关键字以使RowID不隐藏和公开。默认情况下,由持久化类定义定义的表将RowID定义为非隐藏;可以指定SqlRowIdPrivateRowID定义为隐藏和私有。容器字段定义为隐藏。
  • 流字段:表示使用流数据类型定义的字段,可以是字符流(CLOB),也可以是二进制流(BLOB)。流文件没有平均字段大小。
  • 父引用字段:引用父表的字段。

注释列中未标识标识字段、ROWVERSION字段、序列字段或UNIQUEIDENTIFIER(GUID)字段。

平均字段大小

运行调谐表根据当前表格数据集计算所有非流字段的平均字段大小(以字符为单位)。这与AVG($length(Field))相同(除非另有说明),四舍五入到小数点后两位。可以更改各个字段的平均字段大小,以反映字段数据的预期平均大小。

  • NULL:因为$LENGTH函数将NULL字段视为长度为0,所以将长度为0的NULL字段取平均值。这可能会导致平均字段大小小于一个字符。
  • 空列:如果列不包含数据(所有行都没有字段值),则平均字段大小值为1,而不是0。对于不包含数据的列,AVG($length(Field))为0。
  • ExtentSize=0:将ExtentSize设置为0时,所有字段的平均字段大小将重置为0。
  • 逻辑字段值:平均字段大小始终根据字段的逻辑(内部)值计算。
  • 列表字段:InterSystems IRIS列表字段根据其逻辑(内部)编码值计算。此编码长度大于列表中元素的总长度。
  • 容器字段:集合的容器字段大于其集合对象的总长度。例如,在Sample.Person中,Home容器字段的Average Field Size大于Home_StreetHome_CityHome_StateHome_Zip平均字段大小的总和。
  • 流字段:流字段没有平均字段大小。

如果特性/字段的特性参数CALCSELECTIVITY设置为0,则调谐表不会计算该特性/字段的平均字段大小。

可以通过从调谐表显示中选择单个字段来修改平均字段大小计算值。这将在显示屏右侧的详细信息区域中显示该字段的值。可以将“平均字段大小”修改为更适合预期的完整数据集的值。由于设置此值时优化表不执行验证,因此应确保该字段不是流字段,并且指定的值不大于最大字段大小(MaxLen)。

平均字段大小还显示在管理门户目录详细信息选项卡字段选项表中。必须已为字段选项表运行了调整表,才能显示平均字段大小值。

map BlockCount选项卡

调优表Map BlockCount选项卡显示SQL映射名称、BlockCount(作为正整数)和BlockCount的来源。 块计数的来源可以在类定义中定义、由类编译器估计或由TuneTable度量。 将类编译器估计的调优表更改运行到TuneTable测量; 它不影响在类定义中定义的值。

通过从调优表显示中选择单个SQL映射名称,可以修改BlockCount计算值。 这将在显示器右侧的详细信息区域中显示该地图名称的块计数。 可以将块计数修改为一个更适合预期的完整数据集的值。 因为在设置该值时,Tune Table不执行验证,所以应该确保块计数是一个有效值。 修改BlockCount会将BlockCount的来源更改为类定义中定义的。

导出和重新导入调优表统计信息

可以从一个表或一组表导出调优表统计信息,然后将这些调优表统计信息导入一个表或一组表。 以下是可能希望执行此导出/导入的三种情况。 (为简单起见,这些描述了从单个表导出/导入统计数据; 在实际使用中,通常会从多个相互关联的表中导出/导入统计数据):

  • 为生产系统建模:生产表完全填充了实际数据,并使用Tune table进行优化。 在测试环境中,创建的表具有相同的表定义,但数据少得多。 通过从生产表导出调优表统计信息并将它们导入测试表,可以在测试表上对生产表优化建模。
  • 要复制生产系统:生产表完全填充了实际数据,并使用tune Table进行了优化。将创建具有相同表定义的第二个生产表。(例如,生产环境及其备份环境,或者多个相同的表定义,每个表包含不同医院的患者记录。)。通过从第一个表导出调优表统计信息并将其导入第二个表,您可以为第二个表提供与第一个表相同的优化,而无需第二次运行调优表或等待第二个表填充有代表性的数据。
  • 要恢复到以前的统计信息集:可以通过运行tune Table或显式设置统计信息来创建表的优化统计信息。通过导出这些统计信息,可以在尝试其他统计信息设置时保留它们。一旦确定了最佳统计信息集,就可以将它们重新导入到表中。

可以使用$SYSTEM.SQL.Stats.Table.Export()方法将调优表统计信息导出到XML文件。此方法可以导出名称空间中一个、多个或所有表的优化表统计信息,如以下示例所示:

  DO $SYSTEM.SQL.Stats.Table.Export("C:\AllStats.xml")
  /* 导出当前命名空间中所有架构/表的TuneTable统计信息 */
  DO $SYSTEM.SQL.Stats.Table.Export("C:\SampleStats.xml","Sample")
  /*  导出Sample模式中所有表的可调统计信息 */
  DO $SYSTEM.SQL.Stats.Table.Export("C:\SamplePStats.xml","Sample","P*")
  /* 导出Sample模式中所有以字母“P”开头的表的可调统计信息 */
  DO $SYSTEM.SQL.Stats.Table.Export("C:\SamplePersonStats.xml","Sample","Person")
  /*  导出Sample的可调统计信息Person */

可以使用$SYSTEM.SQL.Stats.Table.Import()方法重新导入使用$SYSTEM.SQL.Stats.Table.Import()方法导出的调优表统计信息。

$SYSTEM.SQL.Stats.Table.Import()有一个KeepClassUpToDate boolean选项。 如果为真(并且update为真),$SYSTEM.SQL.Stats.Table.Import()将用新的EXTENTSIZE和选择性值更新类定义,但类定义将保持最新。 但是,在许多情况下,最好在调优了类表之后重新编译类,这样类定义中的查询就可以重新编译,SQL查询优化器就可以使用更新后的数据统计信息。 默认值为FALSE(0)。请注意,如果该类已部署,则类定义不会更新。

$SYSTEM.SQL.Stats.Table.Import()有一个ClearCurrentStats boolean选项。 如果为TRUE$SYSTEM.SQL.Stats.Table.Import()将在导入统计信息之前从现有表中清除所有先前的区段大小、选择性、块计数和其他调优表统计信息。 如果您想要完全清除导入文件中没有指定的那些表状态,而不是让它们在表的persistent类中定义,则可以使用此方法。 默认值是FALSE(0)

如果$SYSTEM.SQL.Stats.Table.Import()没有找到相应的表,它将跳过该表并继续导入文件中指定的下一个表。 如果找到了一个表,但是没有找到一些字段,那么这些字段将被跳过。

无法继承类存储定义中映射的BlockCountBlockCount只能出现在映射起源的类的存储定义中。如果映射源自超类,则$SYSTEM.SQL.Stats.Table.Import()仅设置投影表的BlockCount元数据,而不设置类存储BlockCount元数据。

0
0 161
文章 姚 鑫 · 四月 18, 2021 13m read

第三章 优化表(一)

要确保InterSystems IRIS®Data Platform上的InterSystems SQL表的最高性能,可以执行多种操作。优化可以对针对该表运行的任何查询产生重大影响。本章讨论以下性能优化注意事项:

  • ExtentSizeSelectiveBlockCount用于在用数据填充表之前指定表数据估计;此元数据用于优化未来的查询。
  • 运行tune Table来分析填充表中的代表表数据;生成的元数据用于优化未来的查询。
  • 优化表计算的值包括扩展大小、选择性、异常值选择性、平均字段大小和块计数
  • 导出和重新导入优选表统计数据

扩展大小、选择性和块数(ExtentSize, Selectivity, and BlockCount)

当查询优化器决定执行特定SQL查询的最有效方式时,它会考虑以下三种情况:

  • 查询中使用的每个表的ExtentSize行计数。
  • Selectivity为查询使用的每列计算的DISTINCT值的百分比。
  • 查询使用的每个SQL映射的块计数。

为了确保查询优化器能够做出正确的决策,正确设置这些值非常重要。

  • 在用数据填充表之前,可以在类(表)定义期间显式设置这些统计信息中的任何一个。
  • 在用代表性数据填充表之后,可以运行tune Table来计算这些统计数据。
  • 运行TuneTable之后,可以通过指定显式值来覆盖计算的统计信息。

可以将显式设置的统计信息与优化表生成的结果进行比较。如果优化表所做的假设导致查询优化器的结果不是最优的,则可以使用显式设置的统计信息,而不是优化表生成的统计信息。

在Studio中,类编辑器窗口显示类源代码。在源代码的底部,它显示了Storage定义,其中包括类ExtentSize和每个属性的选择性(如果合适,还包括OutlierSelectivity)。

ExtentSize

表的ExtentSize值就是表中存储的行数(大致)。

在开发时,可以提供初始ExtentSize值。如果未指定ExtentSize,则默认值为100,000

通常,会提供一个粗略的估计,即在填充数据时该表的大小是多少。 有一个确切的数字并不重要。 此值用于比较扫描不同表的相对成本; 最重要的是确保关联表之间的ExtentSize的相对值代表一个准确的比例(也就是说,小表的值应该小,大表的值应该大)。

  • CREATE TABLE提供了一个%EXTENTSIZE参数关键字来指定表中的预期行数,示例如下:
CREATE TABLE Sample.DaysInAYear (%EXTENTSIZE 366, 
                                 MonthName VARCHAR(24),Day INTEGER,
                                 Holiday VARCHAR(24),ZodiacSign VARCHAR(24))

表的持久类定义可以在存储定义中指定ExtentSize参数:

<Storage name="Default">
<Data name="MyClassDefaultData">
...
<ExtentSize>200</ExtentSize>
...
</Storage>

在本例中,片段是MyClass类的存储定义,它为ExtentSize指定了200的值。

如果表有真实的(或真实的)数据,可以使用管理门户中的调优表功能自动计算和设置它的区段大小值;

Selectivity

在InterSystems SQL表(类)中,每个列(属性)都有一个与之相关联的选择性值。 列的选择性值是在查询该列的典型值时返回的表中的行的百分比。 选择性为1/D,其中D是字段不同值的数目,除非检测到异常值。

选择性基于大致相等的不同值的数量。例如,假设一个表包含一个性别列,其值大致均匀分布在“M”“F”之间。性别栏的选择值将为50%。更具区分性的特性(例如街道名称Street Name)的选择性值通常只有很小的百分比。

所有值都相同的字段的选择性为100%。为了确定这一点,优化器首先测试一小部分或几条记录,如果这些记录都具有相同的字段值,它将测试多达100,000条随机选择的记录,以支持非索引字段的所有值都相同的假设。如果在对100,000条随机选择的记录进行的测试中可能未检测到某个字段的其他值,则应手动设置选择性。

定义为唯一(所有值都不同)的字段的选择性为1(不应与1.0000%的选择性混淆)。例如,RowID的选择性为1。

在开发时,可以通过在存储定义中定义一个选择性参数来提供此值,该参数是表的类定义的一部分:

<Storage name="Default">
<Data name="MyClassDefaultData">
...
<Property name="Gender">
<Selectivity>50%</Selectivity>
</Property>
...
</Storage>

若要查看类的存储定义,请在Studio中,从“视图”菜单中选择“查看存储”;Studio在类的源代码底部包含存储。

image

通常,需要提供在应用程序中使用时预期的选择性的估计值。与ExtentSize一样,拥有确切的数字并不重要。InterSystems IRIS提供的许多数据类型类将为选择性提供合理的默认值。

还可以使用SetFieldSelectivity()方法设置特定字段(属性)的选择值。

如果表中有真实的(或真实的)数据,则可以使用管理门户中的Tune table工具自动计算和设置其选择性值。 调优表确定一个字段是否有一个离群值,这个值比任何其他值都常见得多。 如果是这样,Tune Table将计算一个单独的离群值选择性百分比,并根据这个离群值的存在来计算选择性。 异常值的存在可能会极大地改变选择性值。

选择性用于查询优化。 在SELECT查询中指定的字段和在视图的SELECT子句中指定的字段使用相同的选择性值。 请注意,视图的行分布可能与源表不同。 这可能会影响视场选择性的精度。

BlockCount

当编译一个持久化类时,类编译器会根据区段大小和属性定义计算每个SQL映射使用的映射块的大致数量。 可以在调优表工具的Map BlockCount选项卡中查看这些BlockCount值。 块计数在调优表中由类编译器估计。 注意,如果更改了区段大小,则必须关闭并重新打开SQL Tune Table窗口,以查看该更改反映在BlockCount值中。

当运行Tune Table时,它会测量每个SQL映射的实际块计数。 除非另有指定,调优表测量值将替换类编译器的近近值。 这些调优表测量值在类定义中表示为负整数,以区别于指定的BlockCount值。 如下面的例子所示:

<SQLMap name="IDKEY">
 <BlockCount>-4</BlockCount>
</SQLMap>

调优表测量值在调优表中表示为正整数,标识为由调优表测量。

可以在类定义中定义显式的块计数值。 可以显式地指定块计数为正整数,如下面的示例所示:

<SQLMap name="IDKEY">
 <BlockCount>12</BlockCount>
</SQLMap>

当定义一个类时,可以省略为map定义BlockCount,显式地指定一个BlockCount为正整数,或显式地定义BlockCountNULL

  • 如果不指定块计数,或指定块计数为0,则类编译器估计块计数。 运行Tune Table将替换类编译器的估计值。
  • 如果指定一个显式的正整数BlockCount,运行Tune Table不会替换此显式的BlockCount值。 在调优表中,显式的类定义块计数值表示为正整数,标识为在类定义中定义的。 这些块计数值不会通过随后运行Tune Table而更改。
  • 如果将显式BlockCount指定为NULL,则SQL Map将使用类编译器估计的BlockCount值。因为BlockCount在类定义中是“定义的”,所以运行Tune Table不会替换这个估计的BlockCount值。

所有InterSystems SQL映射块的大小为2048字节(2K字节)。

在以下情况下,优化表不测量块计数:

  • 如果表是由数组或列表集合投影的子表。这些类型的子表的BlockCount值与父表数据映射的BlockCount值相同。
  • 如果全局映射是远程全局(不同名称空间中的全局)。取而代之的是使用在类编译期间使用的估计的BlockCount

Tune Table

Tune Table是一个实用程序,它检查表中的数据,并返回关于区段大小(表中的行数)、每个字段中不同值的相对分布以及平均字段大小(每个字段中值的平均长度)的统计信息。 它还为每个SQL映射生成块计数。 可以指定该调优表,使用此信息更新与表及其每个字段相关联的元数据。 查询优化器随后可以使用这些统计信息来确定最有效的查询执行计划。

在外部表上使用Tune Table将只计算区段大小。 调优表无法计算外部表的字段选择性值、平均字段大小或映射块计数值。

何时运行调优表

应该在每个表填充了具有代表性的实际数据之后,在该表上运行tune Table。通常,在数据“激活”之前,只需要运行一次tune Table,这是应用程序开发的最后一步。Tune Table不是维护实用程序;它不应对实时数据定期运行。

注:在极少数情况下,运行调优表会降低SQL性能。虽然TuneTable可以在实时数据上运行,但建议在具有实际数据的测试系统上运行TuneTable,而不是在生产系统上运行。可以使用可选的系统模式配置参数来指示当前系统是测试系统还是活动系统。设置后,系统模式将显示在管理门户页面的顶部,并可由$SYSTEM.Version.SystemMode()方法返回。

通常,在添加、修改或删除表数据时不应重新运行Tune Table,除非当前数据的特征发生了数量级的更改,如下所示:

  • 相对表大小:Tune Table假设它正在分析具有代表性的数据子集。如果该子集是代表性子集,则该子集只能是整个数据集的一小部分。如果联接或其他关系中涉及的表的ExtentSize保持大致相同的相对大小,则当表中的行数发生变化时,Tune Table结果仍然是相关的。如果连接表之间的比率更改了一个数量级,则需要更新ExtentSize。这对于JOIN语句很重要,因为SQL优化器在优化表连接顺序时使用ExtentSize。一般来说,无论查询中指定的联接顺序如何,都会先联接较小的表,然后再联接较大的表。因此,如果tableAtableB中的行比从1000:2000更改为10000:2000,可能在一个或多个表上重新运行tune Table,但如果更改为2100:4000,则不需要重新运行tune Table。
  • 均匀值分布:优化表假设每个数据值的可能性都是相等的。如果它检测到离群值,它会假定除离群值之外的每个数据值的可能性都是相等的。调谐表通过分析每个字段的当前数据值来建立选择性。真实数据的可能性相等始终是一个粗略的近似值;不同数据值的数量及其相对分布的正态变化不应保证重新运行调优表。但是,字段可能值的数量(不同值与记录的比率)的数量级变化或单个字段值的总体可能性可能会导致不准确的选择性。大幅更改具有单个字段值的记录的百分比可能会导致TuneTable指定一个离群值或删除指定的离群值,从而显著改变计算的选择性。如果字段的选择性不再反映数据值的实际分布,则应重新运行调优表。
  • 重大升级或新的站点安装可能需要重新运行Tune Table。

运行 Tune Table

运行调优表有三个接口:

  • 使用Management Portal SQL interface Actions下拉列表,它允许在单个表或多个表上运行Tune Table。

  • 为单个表或当前命名空间中的所有表调用$SYSTEM.SQL.Stats.Table.GatherTableStats()方法。

  • 对单个表发出SQL命令调优表。

Tune Table清除引用正在调优的表的缓存查询。 调优表命令提供了一个recompile缓存查询选项,以使用新的调优表计算值重新生成缓存的查询。

如果表映射到只读数据库,则无法执行调优表,并生成错误消息。

在运行了调优表工具之后,生成的区段大小和选择性值将保存在类的存储定义中。 要查看存储定义,在Studio中,从“视图”菜单中选择“视图存储”; Studio在类源代码的底部包含存储。

从管理门户调优表

要从管理门户运行Tune Table:

  1. 选择System Explorer,然后选择SQL。 通过单击页面顶部的Switch选项选择一个名称空间,然后从显示的列表中选择一个名称空间。 (可以为每个用户设置管理门户的默认名称空间。)
  2. 从屏幕左侧的下拉列表中选择模式,或者使用筛选器。
  3. 执行下列操作之一:
  • 优化单个表:展开表类别,然后从列表中选择一个表。选择表格后,单击操作下拉列表,然后选择调整表格信息。这将显示表的当前ExtentSize和选择性信息。如果从未运行过调谐表,ExtentSize=100000,则不会显示任何选择性、异常值选择性、异常值或平均字段大小信息(除了选择性为1的行ID),并且会按照类编译器的估计列出映射块计数信息。

从选择性选项卡中,选择调谐表按钮。这将在表上运行tune Table,根据表中的数据计算ExtentSize、选择性、异常值选择性、异常值和Average Field Size值。Map BlockCount(地图块计数)信息按Tune Table(调谐表)测量列出。

单个表上的Tune Table始终作为后台进程运行,并在完成后刷新该表。这可以防止超时问题。当此后台进程正在运行时,将显示一条正在进行的消息。在后台进程执行时,关闭按钮可用于关闭调谐表窗口。

  • 优化方案中的所有表:单击操作下拉列表,然后选择优化方案中的所有表。这将显示调谐表方框。选择Finish按钮在方案中的所有表上运行Tune Table。调谐表完成后,此框显示完成按钮。选择Done(完成)退出Tune Table(调谐表)框。

SQL优化表窗口有两个选项卡:选择性和映射块计数。这些选项卡显示由调谐表生成的当前值。它们还允许手动设置与Tune Table生成的值不同的值。

选择性选项卡包含以下字段:

  • 当前表扩展大小。此字段有一个编辑按钮,允许输入不同的表格扩展大小。
  • “使类保持最新”复选框。对Tune Table生成的统计数据的任何更改,或由Tune Table界面或Tune Table方法中的用户输入值生成的任何更改,都会立即表示在类定义中:
    • 如果未选中此框(否),则不会设置修改后的类别定义上的最新标志。这表明类定义已过期,应该重新编译。这是默认设置。
    • 如果选中此框(是),类定义将保持标记为最新。在活动系统上更改统计信息时,这是首选选项,因为它降低了重新编译表类定义的可能性。
  • 字段表,其中包含字段名称、选择性、备注、异常值选择性、异常值和平均字段大小等列。通过单击Fields表格标题,可以按该列的值进行排序。通过单击Fields表行,您可以手动设置该字段的选择性、异常值选择性、异常值和平均字段大小的值。

Map BlockCount选项卡包含以下字段:

  • 包含SQL Map Name、BlockCount和Source of BlockCount列的映射名称表。索引的SQL映射名称是SQL索引名;这可能不同于持久类索引属性名。
  • 通过单击单个map名称,可以手动设置该地图名称的BlockCount值。

在选择性选项卡中,可以单击优化表按钮在此表上运行优化表。

使用方法调整表

可以使用$SYSTEM.SQL.Stats.Table.GatherTableStats()方法在当前名称空间中运行Tune Table工具。

  • GatherTableStats(“Sample.MyTable”)在单个表上运行TuneTable。
  • GatherSchemaStats(“Sample”)在指定模式中的所有表上运行tune Table。
  • GatherTableStats(“*”)在当前命名空间中的所有表上运行TuneTable。

使用GatherTableStats()方法时,可能会生成以下错误消息:

  • 不存在的表:
DO $SYSTEM.SQL.Stats.Table.GatherTableStats("NoSuchTable")
No such table 'SQLUser.NoSuchTable'
  • View视图:
DO $SYSTEM.SQL.Stats.Table.GatherTableStats("Sample.MyView")
'Sample.MyView' is a view, not a table. No tuning will be performed.

当运行GatherTableStats(“*”)GatherSchemaStats(“SchemaName”)时,如果系统支持并行处理,系统将使用多个进程并行调优多个表。

在分片表上运行Tune table

如果在一个分片表上运行调优表,那么调优表操作将被转发到每个碎片,并针对该表的那个碎片运行。 调优表不会在调用它的主名称空间中执行。 如果在导出到碎片的类定义的非分片表上运行调优表,因为该表已连接到一个分片表,调优表操作将转发到每个碎片,并且它也在主名称空间中执行。

在分片表上运行Tune Table时,应该遵循以下准则:

  • 优化分片主表,而不是分片本地表。
  • 区段大小和块计数值是每个分片的值,而不是所有分片的总和。
  • 如果使用$SYSTEM.SQL.Stats.Table.Export()$SYSTEM.SQL.Stats.Table.Import(),则导出/导入分片主表的调优统计,而不是分片本地表。
  • 调优切分表将在切分主类和切分本地类/表定义中定义调优统计。 如果手动编辑类定义中的调优表元数据,建议的过程是修改碎片主类的定义,然后重新编译碎片主类。 在编译碎片主类时,碎片主调优统计信息将被复制到类的碎片本地版本。

如果GatherTableStats()GatherSchemaStats()指定了一个logFile参数,shard master实例中的日志文件有一个针对指定表的条目,例如:

  • Sharded table: TABLE: <tablename> Invoking TuneTable on shards for sharded table <tablename>
  • Non-sharded table: TABLE: <tablename> Invoking TuneTable on shards for mapped non-sharded table <tablename>

在每个分片实例上,在mgr/<shard-namespace>目录中创建一个同名的日志文件,记录这个分片上这个表的调优表信息。 如果为日志文件指定了目录路径,那么分片将忽略该路径,并且该文件始终存储在mgr/<shard-namespace>中。

0
0 250
文章 姚 鑫 · 四月 17, 2021 6m read

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

验证索引

可以使用以下任一方法验证索引

  • $SYSTEM.OBJ.ValidateIndices()验证表的索引,还验证该表的集合子表中的任何索引。
  • %Library.Storage.%ValidateIndices()验证表的索引。集合子表索引必须使用单独的%ValidateIndices()调用进行验证。

这两种方法都会检查指定表的一个或多个索引的数据完整性,并可以选择更正发现的任何索引完整性问题。他们分两步执行索引验证:

  1. 确认为表(类)中的每一行(对象)正确定义了索引实体。
  2. 遍历每个索引,对于索引的每个条目,确保表(类)中有一个值和匹配的条目。

如果这两种方法中的任何一种发现不一致,它都可以有选择地更正索引结构和/或内容。它可以验证标准索引、位图索引、位图范围索引和位片索引,并可选择对其进行校正。默认情况下,这两种方法都会验证索引,但不会更正索引。

如果满足以下条件,%ValidateIndices()只能用于更正(生成)读写活动系统上的索引:如上所述,使用了SetMapSelecability()%ValidateIndices()参数必须包括AUTOCORRECT=1lockOption>0。由于%ValidateIndices()速度明显较慢,因此%BuildIndices()是在活动系统上构建索引的首选方法。

%ValidateIndices()通常从终端运行。它显示当前设备的输出。此方法可以应用于指定的索引名称%List,也可以应用于为指定表(类)定义的所有索引。它只对那些源自指定类的索引进行操作;如果索引源自超类,则可以通过在超类上调用%ValidateIndices()来验证该索引。

ReadONLY类不支持%ValidateIndices()

分片类和分片主类表(Sharded=1)支持%ValidateIndices()。可以直接作为类方法调用%ValidateIndices,也可以从碎片主类上的$SYSTEM.OBJ.ValidateIndices调用%ValidateIndices。然后对每个分片上的分片本地类执行索引验证,并将结果返回给分片主机上的调用者。在分片类上使用%ValidateIndices()时,详细标志被强制为0。当前设备没有输出。任何发现/更正的问题都会在byreference error()数组中返回。

下面的示例使用%ValidateIndices()来验证和更正表Sample.Person的所有索引:

/// w ##class(PHA.TEST.SQL).ValIndex()
ClassMethod ValIndex(c)
{
	SET status=##class(Sample.Person).%ValidateIndices("",1,2,1)
	IF status=1 {
		WRITE !,"成功索引验证/更正" 
	} ELSE {
		WRITE !,"索引验证/更正失败",!
		DO $System.Status.DisplayError(status) 
		QUIT
	}
}
DHC-APP>w ##class(PHA.TEST.SQL).ValIndex()
 
 
Checking index integrity for class 'Sample.Person'
Begin time:  04/15/2021 21:25:40
 
Verifying data from data map 'IDKEY' is indexed correctly...
Data Map evaluation complete, 208 rows checked, 0 errors found, elapsed time: .007243 seconds
 
Verifying data from index map "$Person" is correct...
Index map "$Person" evaluation complete, 0 errors, elapsed time: .000359 seconds
 
Verifying data from index map NameIDX is correct...
Index map NameIDX evaluation complete, 0 errors, elapsed time: .000713 seconds
 
Verifying data from index map SSNKey is correct...
Index map SSNKey evaluation complete, 0 errors, elapsed time: .000623 seconds
 
Verifying data from index map ZipCode is correct...
Index map ZipCode evaluation complete, 0 errors, elapsed time: .000596 seconds
 
%ValidateIndices is complete, total elapsed time: .009726 seconds
 
成功索引验证/更正

在本例中,第一个参数(“”)指定要验证所有索引;第二个参数(1)指定应该纠正索引差异;第三个参数(2)指定对整个表进行独占锁定;第四个参数(1)指定使用多个进程(如果可用)来执行验证。该方法返回%Status值。

列表索引

INFORMATION.SCHEMA.INDEXES持久类显示有关当前命名空间中所有列索引的信息。它为每个索引列返回一条记录。它提供了许多索引属性,包括索引映射到的索引名、表名和列名。每个列记录还提供该列在索引映射中的序号位置;除非索引映射到多个列,否则此值为1。它还提供了布尔属性PRIMARYKEYNONUNIQUE(0=索引值必须是唯一的)。

下面的示例为参与当前命名空间中所有非系统索引的索引的每列返回一行:

SELECT Index_Name,Table_Schema,Table_Name,Column_Name,Ordinal_Position,
Primary_Key,Non_Unique
FROM INFORMATION_SCHEMA.INDEXES WHERE NOT Table_Schema %STARTSWITH '%'

image

Open、Existes和Delete方法

按索引键打开实例

对于ID键、主键或唯一索引,indexnameOpen()方法(其中indexname是索引的名称)允许打开其索引属性值与提供的一个或多个值匹配的对象。由于此方法与索引中的每个属性都有一个对应的参数,因此该方法有三个或更多参数:

  • 第一个参数分别对应于索引中的属性。
  • 倒数第二个参数指定要用来打开对象的并发值
  • 最后一个参数可以接受%Status代码,以防该方法无法打开实例。

如果找到匹配的实例,则该方法返回OREF

例如,假设一个类包括以下索引定义:

 Index SSNKey On SSN [ Unique ];

然后,如果引用的对象已存储到磁盘,并且具有唯一的ID值,则可以按如下方式调用该方法:

  SET person = ##class(Sample.Person).SSNKeyOpen("111-22-3333",2,.sc)

成功完成后,该方法将Person的值设置为其SSN属性值为111-22-3333Sample.Person实例的OREF。

该方法的第二个参数指定并发值,此处为2(共享)。第三个参数包含一个可选的%Status代码;如果该方法没有找到与提供的值匹配的对象,则会向状态参数sc写入一条错误消息。

此方法作为%Compiler.Type.Index.Open()方法实现;此方法类似于%Persistent.Open()%Persistent.OpenId()方法,不同之处在于它使用索引定义中的属性,而不是OIDID参数。

检查实例是否存在

IndexnameExists()方法(其中indexname是索引的名称)检查是否存在一个实例,该实例具有一个或多个由该方法的参数指定的索引属性值。该方法有一个与索引中的每个属性相对应的参数;如果对象的ID与提供的值匹配,则它的最后一个可选参数可以接收该对象的ID。该方法返回一个布尔值,表示成功(1)或失败(0)。此方法作为%Compiler.Type.Index.Exists()方法实现。

例如,假设一个类包括以下索引定义:

 Index SSNKey On SSN [ Unique ];

然后,如果引用的对象已存储到磁盘,并且具有唯一的ID值,则可以按如下方式调用该方法:

  SET success = ##class(Sample.Person).SSNKeyExists("111-22-3333",.id)

成功完成后,Success等于1id包含与找到的对象匹配的ID

此方法返回除以下索引之外的所有索引的值:

  • 位图索引或位图范围索引。
  • 当索引包括(元素)或(键)表达式时。

删除实例

IndexnameDelete()方法(其中indexname是索引的名称)用于唯一、PrimaryKey和/或IdKey索引;它删除键值与提供的键属性/列值匹配的实例。有一个可选参数,可用于指定操作的并发设置。该方法返回%Status代码。它作为%Compiler.Type.Index.Delete()方法实现。

0
0 163
文章 姚 鑫 · 四月 16, 2021 12m read

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

位片索引

当数字数据字段用于某些数值运算时,位片索引用于该字段。位片索引将每个数值数据值表示为二进制位串。位片索引不是使用布尔标志来索引数值数据值(如在位图索引中那样),而是以二进制值表示每个值,并为二进制值中的每个数字创建一个位图,以记录哪些行的该二进制数字具有1。这是一种高度专门化的索引类型,可以显著提高以下操作的性能:

  • SUMCOUNTAVG Aggregate计算。(位片索引不用于COUNT(*)计算。)。位片索引不用于其他聚合函数。
  • 指定的字段 TOP n ... ORDER BY field
  • 在范围条件运算中指定的字段,WHERE field > nWHERE field BETWEEN lownum AND highnum

SQL优化器确定是否应该使用定义的位片索引。通常,优化器仅在处理大量(数千)行时才使用位片索引。

可以为字符串数据字段创建位片索引,但位片索引将这些数据值表示为规范数字。换句话说,任何非数字字符串(如“abc”)都将被索引为0。这种类型的位片索引可用于快速计数具有字符串字段值的记录,而不计算那些为空的记录。

在下面的例子中,Salary是位片索引的候选项:

SELECT AVG(Salary) FROM SalesPerson

位片索引可用于使用WHERE子句的查询中的聚合计算。如果WHERE子句包含大量记录,则这是最有效的。在下面的示例中,SQL优化器可能会使用Salary上的位片索引(如果已定义);如果定义了位片索引,它还会使用REGION上的位图索引,使用定义的位图或为REGION生成位图临时文件:

SELECT AVG(Salary) FROM SalesPerson WHERE Region=2

但是,当索引无法满足WHERE条件时,不使用位片索引,而必须通过读取包含要聚合的字段的表来执行。以下示例将不使用Salary的位片索引:

SELECT AVG(Salary) FROM SalesPerson WHERE Name LIKE '%Mc%'

可以为任何包含数值的字段定义位片索引。InterSystems SQL使用Scale参数将小数转换为位字符串,如ObjectScript $factor函数中所述。可以为数据类型字符串的字段定义位片索引;在这种情况下,出于位片索引的目的,非数字字符串数据值被视为0

可以为系统分配的行ID为正整数值的表中的字段定义位片索引,也可以为使用%BID属性定义以支持位图(和位片)索引的表中的字段定义位片索引。

位片索引只能为单个字段名定义,不能为多个字段的连接定义。 不能指定WITH DATA子句。

下面的例子比较了位片索引和位图索引。 如果你为1、5和22行创建一个位图索引,它会为这些值创建一个索引:

^gloI("bitmap",1,1)= "100"
^gloI("bitmap",5,1)= "010"
^gloI("bitmap",22,1)="001"

如果为第1、2和3行的值1、5和22创建位切片索引,则会首先将这些值转换为位值:

1 =   00001
5 =   00101
22 = 10110

然后,它为这些位创建索引:

^gloI("bitslice",1,1)="110"
^gloI("bitslice",2,1)="001"
^gloI("bitslice",3,1)="011"
^gloI("bitslice",4,1)="000"
^gloI("bitslice",5,1)="001"

在本例中,位图索引中的值22需要设置1个全局节点;位片索引中的值22需要设置3个全局节点。

请注意,插入或更新需要在所有n个位片中设置一个位,而不是设置单个位串。这些附加的全局设置操作可能会影响涉及填充位片索引的插入和更新操作的性能。使用INSERTUPDATEDELETE操作填充和维护位片索引比填充位图索引或常规索引慢。维护多个位片索引和/或在频繁更新的字段上维护位片索引可能具有显著的性能成本。

在易失性表(执行许多插入、更新和删除操作)中,位片索引的存储效率可能会逐渐降低。%SYS.Maint.Bitmap实用程序方法同时压缩位图索引和位片索引,从而提高了还原效率。

重建索引

可以按如下方式构建/重新构建索引:

  • 使用BUILD INDEX SQL命令构建指定索引,或构建为表、架构或当前命名空间定义的所有索引。
  • 使用管理门户重建指定类(表)的所有索引。
  • 使用%BuildIndices()(或%BuildIndicesAsync())方法,如本节所述。

当前数据库访问确定应如何重建现有索引:

  • 非活动系统(在索引构建或重建期间没有其他进程访问数据)
  • READONLY活动系统(能够在索引构建或重建期间查询数据的其他进程)
  • 读写活动系统(能够在索引构建或重建期间修改数据和查询数据的其他进程)

构建索引的首选方法是使用%BuildIndices()方法或%BuildIndicesAsync()方法。

  • %Library.Persistent.%BuildIndices()%BuildIndices()作为后台进程执行,但调用方必须等待%BuildIndices()完成才能接收回控制。
  • %Library.Persistent.%BuildIndicesAsync()%BuildIndicesAsync()%BuildIndices()作为后台进程启动,调用方立即收到控制权。%BuildIndicesAsync()的第一个参数是eueToken输出参数。其余参数与%BuildIndices()相同。

%BuildIndicesAsync()返回%Status值:Success表示%BuildIndices()辅助作业已成功排队;失败表示该辅助作业未成功排队。

%BuildIndicesAsync()eueToken输出参数返回一个值,该值指示%BuildIndices()完成状态。要获取完成状态,请通过引用将eueToken值传递给%BuildIndicesAsyncResponse()方法。还可以指定等待布尔值。如果wait=1,则%BuildIndicesAsyncResponse()将等待,直到由eueToken标识的%BuildIndices() JOB 完成。如果wait=0%BuildIndicesAsyncResponse()将尽快返回状态值。如果返回时%BuildIndicesAsyncResponse() ``eueToken不为空,则%BuildIndices() job尚未完成。在这种情况下,可以使用eueToken再次调用%BuildIndicesAsyncResponse()。当%BuildIndicesAsyncResponse()``eueToken最终为NULL时,返回的%BuildIndicesAsyncResponse()``%Status值是%BuildIndicesAsync()调用的job的完成状态。

在非活动系统上构建索引

系统自动生成方法(由%Persistent类提供),这些方法构建或清除为类(表)定义的每个索引。可以通过以下两种方式之一使用这些方法:

  • 通过管理门户进行交互。
  • 以编程方式,作为方法调用。

构建索引执行以下操作:

  1. 删除索引的当前内容。
  2. 扫描(读取每一行)主表,并为表中的每一行添加索引项。如果可能,使用特殊的$SortBegin$SortEnd函数来确保高效地构建大型索引。在构建标准索引时,除了在内存中缓存数据之外,使用$SortBegin/$SortEnd还可以使用IRISTEMP数据库中的空间。因此,在构建非常大的标准索引时,InterSystems IRIS可能需要IRISTEMP中大致等于最终索引大小的空间。

注:构建索引的方法仅为使用InterSystems IRIS默认存储结构的类(表)提供。映射到遗留存储结构的类不支持索引构建,因为它假定遗留应用程序管理索引的创建。

使用管理门户构建索引

可以通过执行以下操作来构建表的现有索引(重建索引):

  1. 从管理门户中选择系统资源管理器,然后选择SQL。使用页面顶部的切换选项选择一个命名空间;这将显示可用命名空间的列表。选择命名空间后,选择屏幕左侧的Schema下拉列表。这将显示当前名称空间中的模式列表,其中带有布尔标志,指示是否有任何表或视图与每个模式相关联。
  2. 从此列表中选择一个架构;该架构将显示在架构框中。它的正上方是一个下拉列表,允许选择属于该模式的表、系统表、视图、过程或所有这些。选择“表”或“全部”,然后打开“表”文件夹以列出此架构中的表。如果没有表,则打开文件夹将显示空白页。(如果未选择“表”或“全部”,则打开“表”文件夹将列出整个命名空间的表。)
  3. 选择其中一个列出的表。这将显示表的目录详细信息。
  • 要重建所有索引:单击操作下拉列表,然后选择重建表的索引。
  • 要重建单个索引:单击索引按钮以显示现有索引。每个列出的索引都有重建索引的选项。

注意:当其他用户正在访问表的数据时,不要重建索引。要在活动系统上重建索引,请参阅在活动系统上构建索引。

以编程方式构建索引

为非活动表构建索引的首选方法是使用随表的Persistent类提供的%BuildIndices()(或%BuildIndicesAsync())方法。

若要以编程方式生成一个或多个索引,请使用%Library.Persistent.%BuildIndices()方法。

生成所有索引:调用%BuildIndices(),不带参数生成为给定类(表)定义的所有索引(为其提供值):

    SET sc = ##class(MyApp.SalesPerson).%BuildIndices()
    IF sc=1 {
        WRITE !,"成功构建索引" 
    } ELSE {
    WRITE !,"索引构建失败",!
        DO $System.Status.DisplayError(sc) QUIT
    } 

生成指定索引:调用%BuildIndices(),并将$LIST索引名作为第一个参数,为给定类(表)生成指定的已定义索引(为其提供值):

    SET sc = ##class(MyApp.SalesPerson).%BuildIndices($ListBuild("NameIDX","SSNKey"))
    IF sc=1 {
        WRITE !,"成功构建索引" 
    } ELSE {
    WRITE !,"索引构建失败",!
        DO $System.Status.DisplayError(sc) QUIT
    } 

生成除以下项之外的所有索引:调用%BuildIndices(),并将索引名称的$LIST作为第七个参数来构建(为其提供值)给定类(表)的所有已定义索引(指定索引除外):

    SET sc = ##class(MyApp.SalesPerson).%BuildIndices("",,,,,,$ListBuild("NameIDX","SSNKey"))
    IF sc=1 {
        WRITE !,"成功构建索引" 
    } ELSE {
    WRITE !,"索引构建失败",!
        DO $System.Status.DisplayError(sc) QUIT
    } 

%BuildIndices()方法执行以下操作:

  1. 对要重建的任何(非位图)索引调用$SortBegin函数(这将启动对这些索引的高性能排序操作)。
  2. 循环遍历类(表)的主要数据,收集索引使用的值,并将这些值添加到索引(通过适当的排序转换)。
  3. 调用$SortEnd函数来完成索引排序过程。

如果索引已经有值,则必须使用两个参数调用%BuildIndices(),其中第二个参数的值为1。 为此参数指定1将导致该方法在重新生成值之前清除这些值。 例如:

    SET sc = ##class(MyApp.SalesPerson).%BuildIndices(,1)
    IF sc=1 {
        WRITE !,"成功构建索引" 
    } ELSE {
    WRITE !,"索引构建失败",!
        DO $System.Status.DisplayError(sc) QUIT
    } 

清除并重建所有的索引。 你也可以清除并重建索引的子集,例如:

    SET sc = ##class(MyApp.SalesPerson).%BuildIndices($ListBuild("NameIDX","SSNKey"),1)
    IF sc=1 {
        WRITE !,"成功构建索引" 
    } ELSE {
    WRITE !,"索引构建失败",!
        DO $System.Status.DisplayError(sc) QUIT
    } 

注意:当表的数据被其他用户访问时,不要重建索引。 若要在活动系统上重建索引,请参见在活动系统上构建索引。

在活动系统上构建索引

在活动系统上构建(或重建)索引时,有两个问题:

  • 除非正在构建的索引对SELECTQuery隐藏,否则活动Query可能返回不正确的结果。这是在构建索引之前使用SetMapSelecability()方法处理的。
  • 索引构建期间对数据的活动更新不会反映在索引条目中。这是通过在生成索引时使生成操作锁定单个行来处理的。

注意:如果应用程序在单个事务内对数据执行大量更新,则可能会出现锁表争用问题。

在Readonly主动系统上构建索引

如果表当前仅用于查询操作(READONLY),则可以在不中断查询操作的情况下构建新索引或重建现有索引。这是通过在重建索引时使索引对查询优化器不可用来实现的。

如果要为其构建一个或多个索引的所有类当前都是READONLY,请使用“在读写活动系统上构建索引”中描述的相同系列操作,但有以下区别:使用%BuildIndices()时,设置pLockFlag=3(共享区锁定)。

在读写活动系统上构建索引

如果持久化类(表)当前正在使用并且可用于读写访问(查询和数据修改),则可以在不中断这些操作的情况下构建新索引或重建现有索引。如果要为其重建一个或多个索引的类当前可读写访问,则构建索引的首选方法是使用与表的持久类一起提供的%BuildIndices()(或%BuildIndicesAsync())方法。

注意:以下信息适用于动态SQL查询,而不适用于嵌入式SQL。嵌入式SQL在编译时(而不是在运行时)检查MapSelecability设置。因此,关闭索引的MapSelecability对已经编译的嵌入式SQL查询没有任何影响。因此,嵌入式SQL查询仍可能尝试使用禁用的索引,并将给出不正确的结果。

在并发读写访问期间,需要执行以下一系列操作来构建一个或多个索引:

  1. 望构建的索引对查询不可用(读取访问权限)。这是使用SetMapSelecability()完成的。这使得查询优化器无法使用该索引。在重建现有索引和创建新索引时都应执行此操作。例如:
  SET status=$SYSTEM.SQL.Util.SetMapSelectability("Sample.MyStudents","StudentNameIDX",0)
  • 第一个参数是Schema.Table名称,它是SqlTableName,而不是持久类名称。例如,默认模式是SQLUser,而不是User。该值区分大小写。
  • 第二个参数是SQL索引映射名称。这通常是索引的名称,指的是磁盘上存储索引的名称。对于新索引,这是在创建索引时将使用的名称。该值不区分大小写。
  • 第三个参数是MapSelecability标志,其中0将索引映射定义为不可选择(OFF),1将索引映射定义为可选择(ON)。指定0。

可以通过调用GetMapSelecability()方法来确定索引是否不可选。如果已将索引显式标记为不可选,则此方法返回0。在所有其他情况下,它返回1;它不执行表或索引是否存在的验证检查。请注意,Schema.Table名称是SqlTableName,并且区分大小写。

SetMapSelecability()GetMapSelecability()仅适用于当前命名空间中的索引映射。如果该表映射到多个命名空间,并且需要在每个命名空间中构建索引,则应该在每个命名空间中调用SetMapSelecability()

  1. 在索引构建期间建立并发操作:
  • 对于新索引:在类中创建索引定义(或在类的%Storage.SQL中创建新的SQL Index Map规范)。编译类。此时,索引存在于表定义中;这意味着对象保存、SQL INSERT操作和SQL UPDATE操作都记录在索引中。但是,由于在步骤1中调用了SetMapSelecability(),因此不会为任何数据检索选择此索引映射。SetMapSelecability()阻止查询使用区索引,但是数据映射将被投影到SQL以使用索引全局和数据全局。对于新索引,这是合适的,因为索引尚未填充。在对表运行查询之前,需要填充区索引。
  • 对于现有索引:清除任何引用该表的缓存查询。索引构建执行的第一个操作是终止索引。因此,在重新生成索引时,不能依赖任何经过优化以使用该索引的代码。
  1. 使用pLockFlag=2(行级锁定)的持久化类(表)的%BuildIndices()方法构建一个或多个索引。PLockFlag=2标志在重建过程中在单个行上建立独占写锁,以便并发数据修改操作与构建索引操作相协调。

默认情况下,%BuildIndices()构建为持久类定义的所有索引;可以使用pIgnoreIndexList从重建中排除索引。

默认情况下,%BuildIndices()为所有ID构建索引项。但是,可以使用pStartIDpEndID来定义ID范围。%BuildIndices()将仅为该范围内(含)的ID构建索引项。例如,如果使用带有%NOINDEX限制的INSERT将一系列新记录添加到表中,则可以稍后使用具有ID范围的%BuildIndices()为这些新记录构建索引项。还可以使用pStartIDpEndID在节中构建极大的索引。

%BuildIndices()返回%Status值。如果%BuildIndices()因检索数据时出现问题而失败,系统将生成一个SQLCODE错误和一条消息(%msg),其中包含遇到错误的%ROWID

  1. 构建完索引后,启用映射以供查询优化器选择。将第三个参数MapSelecability标志设置为1,如下例所示:
  SET status=$SYSTEM.SQL.Util.SetMapSelectability("Sample.MyStudents","StudentNameIDX",1)
  1. 再次清除引用该表的所有缓存查询。这将消除在此程序中创建的缓存查询,这些查询无法使用索引,因此不如使用索引的相同查询最佳。

这就完成了这个过程。索引已完全填充,查询优化器能够考虑该索引。

注意:%BuildIndices()只能用于重建ID值为正整数的表的索引。如果父表具有正整数ID值,还可以使用%BuildIndices()重建子表中的索引。对于其他表,请使用%ValidateIndices()方法,如验证索引中所述。因为%ValidateIndices()是构建索引的最慢方法,所以只有在没有其他选项的情况下才应该使用它。

0
0 154
文章 姚 鑫 · 四月 15, 2021 10m read

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

位图索引

位图索引是一种特殊类型的索引,它使用一系列位串来表示与给定索引数据值相对应的一组ID值。

位图索引具有以下重要功能:

  • 位图是高度压缩的:位图索引可以比标准索引小得多。这大大减少了磁盘和缓存的使用量。
  • 位图操作针对事务处理进行了优化:与使用标准索引相比,可以在表中使用位图索引,而不会降低性能。
  • 位图上的逻辑操作(countingANDOR)经过优化以获得高性能。
  • SQL引擎包括许多可以利用位图索引的特殊优化。

位图索引的创建取决于表的唯一标识字段的性质:

  • 如果表的ID字段定义为具有正整数值的单个字段,则可以使用此ID字段为字段定义位图索引。此类型的表使用系统分配的唯一正整数ID,或使用IdKey定义自定义ID值,其中IdKey基于类型为%IntegerMINVAL>的单个属性,或类型%Numeric型且Scale=0MINVA>0
  • 如果表的ID字段未定义为具有正整数值的单个字段(例如,子表),则可以定义采用正整数的%BID(位图ID)字段作为代理ID字段;这允许为该表中的字段创建位图索引。

受下列限制,位图索引的操作方式与标准索引相同。 索引值将被整理,可以在多个字段的组合上建立索引。

位图索引操作

位图索引的工作方式如下。 假设Person表,其中包含一些列:

image

此表中的每一行都有一个系统分配的RowID号(一组递增的整数值)。位图索引使用一组位字符串(包含1和0值的字符串)。在位串中,位的序号位置对应于索引表的RowID。对于给定值,假设State“NY”,则有一个位串,每个位置对应一个包含“NY”的行,其他位置为0。

例如,State上的位图索引可能如下所示:

image

而关于Age 年龄的索引可能如下所示:

image

注:此处显示的年龄字段可以是普通数据字段,也可以是其值可以可靠派生(CalculatedSQLComputed)的字段。

除了将位图索引用于标准操作外,SQL引擎还可以使用位图索引来使用多个索引的组合来高效地执行特殊的基于集合的操作。例如,要查找居住在纽约的24岁Person的所有实例,SQL引擎只需执行AgeState索引的逻辑与:

image

生成的位图包含匹配搜索条件的所有行的集合。SQL引擎使用它从这些行返回数据。

SQL引擎可以将位图索引用于以下操作:

  • 对给定表上的多个条件进行AND运算。
  • 对给定表上的多个条件进行OR运算。
  • 给定表上的RANGE范围条件。
  • 对给定表上的操作进行计数COUNT

使用类定义定义IdKey位图索引

如果表的ID是值限制为唯一正整数的字段,则可以使用新建索引向导或通过与创建标准索引相同的方式编辑类定义的文本,将位图索引定义添加到类定义中。唯一的区别是需要将索引类型指定为“位图”:

Class MyApp.SalesPerson Extends %Persistent [DdlAllowed]
{
 Property Name As %String;
 Property Region As %Integer;

 Index RegionIDX On Region [Type = bitmap];
}

使用类定义定义%BID位图索引

如果表的ID不限于正整数,则可以创建%BID属性以用于创建位图索引定义。可以将此选项用于具有任何数据类型的ID字段的表,以及由多个字段组成的IDKEY(包括子表)。可以为以下任一数据存储类型创建%BID位图:默认结构表或%Storage.SQL表。此功能称为“任意表的位图”或BAT

要在这样的表上启用位图索引,必须执行以下操作:

  1. 为类定义%BID属性/字段。这可以是类的现有属性,也可以是新属性。它可以有任何名称。如果这是新属性,则必须为表中的所有现有行填充此属性/字段。此%BID字段必须定义为将字段数据值限制为唯一正整数的数据类型。例如,将MyBID属性设置为%Counter
  2. 定义新的类参数以定义哪个属性是%BID字段。此参数被命名为BIDField。此参数设置为%BID属性的SQLFieldName。例如,参数BIDField=“MyBID”
  3. 定义%BID的索引。例如,MyBID上的Index BIDIdx[Type=Key,Unique]
  4. 定义%BID定位器索引。 这将%BID索引绑定到表的ID键字段。 下面的例子是一个表的一个复合IDKey组成两个字段:
Index IDIdx On (IDfield1, IDfield2) [ IdKey, Unique ];
Index BIDLocIdx On (IDfield1, IDfield2, MyBID) [ Data = IdKey, Unique ]; 

此表现在支持位图索引。可以使用标准语法根据需要定义位图索引。例如: Index RegionIDX On Region [Type = bitmap];

此表现在还支持位片索引。可以使用标准语法定义位片索引。

注意:要构建或重新生成%BID位图索引,必须使用%BuildIndices()%BID位图索引不支持%ConstructIndicesParallel()方法。

使用DDL定义位图索引

如果使用DDL语句定义表,还可以使用以下DDL命令为ID为正整数的表格创建和删除位图索引:

CREATE BITMAP INDEX RegionIDX ON TABLE MyApp.SalesPerson (Region)

生成位图范围索引

编译包含位图索引的类时,如果类中存在任何位图索引,并且没有为该类定义位图范围索引,则类编译器会生成位图范围索引。如果位图范围索引存在(无论是定义的还是生成的),该类从主超类继承位图范围索引。为类构建索引时,如果要求构建位图范围索引,或者正在构建另一个位图索引并且位图范围索引结构为空,则会构建位图范围索引。

除非存在位图索引,否则InterSystems IRIS不会生成位图范围索引。位图范围索引定义为:type = bitmap, extent = true。这意味着从主要超类继承的位图范围索引被认为是位图索引,并且如果在该子类中没有显式定义位图范围索引,则将触发在子类中生成位图范围索引。

InterSystems IRIS不会基于未来的可能性在超类中生成位图范围索引。这意味着,除非存在type=bitmap的索引,否则InterSystems IRIS永远不会在持久类中生成位图范围索引。假设将来的某个子类可能引入type=bitmap的索引是不够的。

注意:在将位图索引添加到生产系统上的类的过程中需要特别小心(在生产系统中,用户正在使用特定的类,编译所述类,然后为其构建位图索引结构)。在这样的系统上,位图范围索引可以在编译完成和索引构建进行之间的过渡期间被填充。这可能导致索引构建过程未隐式构建位图范围索引,这导致部分完整的位图范围索引。

选择索引类型

下面是在位图和标准索引之间选择的一般准则。 一般来说,所有类型的键和引用都要使用标准索引:

  • Primary key
  • Foreign key
  • Unique keys
  • Relationships
  • Simple object references

否则,位图索引通常更可取(假设表使用系统分配的数字ID号)。

其他因素:

  • 每个属性上的单独位图索引通常比多个属性上的位图索引具有更好的性能。这是因为SQL引擎可以使用ANDOR操作有效地组合单独的位图索引。
  • 如果一个属性(或确实需要一起编制索引的一组属性)有超过10,000-20,000个不同的值(或值组合),请考虑标准索引。但是,如果这些值的分布非常不均匀,以至于很少的值只占行的很大一部分,那么位图索引可能会更好。一般来说,目标是减少索引所需的总体大小。

位图索引的限制

所有位图索引都有以下限制:

  • 不能在唯一列上定义位图索引。
  • 不能在位图索引中存储数据值。
  • 除非字段的SqlCategoryINTEGER, DATE, POSIXTIME, or NUMERIC(scale=0),否则不能在字段上定义位图索引。
  • 对于包含超过100万条记录的表,当惟一值的数量超过10,000时,位图索引的效率低于标准索引。 因此,对于大型表,建议避免为任何包含(或可能包含)超过10,000个惟一值的字段使用位图索引; 对于任意大小的表,避免对任何可能包含超过20,000个惟一值的字段使用位图索引。 这些是一般的近似值,不是确切的数字。

必须创建一个%BID属性来支持一个表上的位图索引:

  • 使用非整数字段作为唯一的ID键。
  • 使用一个多字段ID键。
  • 是父子关系中的子表。

可以使用$SYSTEM.SQL.Util.SetOption()方法SET status=$SYSTEM.SQL.Util.SetOption("BitmapFriendlyCheck",1,.oldval) 设置系统范围的配置参数,以便在编译时检查此限制,从而确定%Storage.SQL类中是否允许定义的位图索引。此检查仅适用于使用%Storage.SQL的类。默认值为0可以使用$SYSTEM.SQL.Util.GetOption(“BitmapFriendlyCheck”)来确定此选项的当前配置。

应用程序逻辑限制

位图结构可以由位串数组表示,其中数组的每个元素表示具有固定位数的"chunk"。因为UNDEFINED等同于一个全为0位的块,所以该数组可以是稀疏的。表示全部0位的块的数组元素根本不需要存在。因此,应用程序逻辑应该避免依赖于0值位的$BITCOUNT(str,0)计数。

由于位串包含内部格式,因此应用程序逻辑不应依赖于位串的物理长度,也不应依赖于将具有相同位值的两个位串相等。在回滚操作之后,位串恢复到事务之前的位值。然而,由于内部格式化,回滚的位串可能不等于或不具有与事务之前的位串相同的物理长度。

维护位图索引

在易失性表(执行许多插入和删除操作)中,位图索引的存储效率可能会逐渐降低。要维护位图索引,可以运行%SYS.Maint.Bitmap实用程序方法来压缩位图索引,使其恢复到最佳效率。可以使用OneClass()方法压缩单个类的位图索引。或者,可以使用Namespace()方法来压缩整个命名空间的位图索引。这些维护方法可以在带电系统上运行。

运行%SYS.Maint.Bitmap实用程序方法的结果将写入调用该方法的进程。这些结果还会写入%SYS.Maint.BitmapResults类。

位图块的SQL操作

InterSystems SQL提供了以下扩展来直接操作位图索引:

  • %CHUNK函数
  • %Bitpos函数
  • %BITMAP聚合函数
  • %BITMAPCHUNK聚合函数
  • %SETINCHUNK谓词条件

所有这些扩展都遵循InterSystems SQL位图表示约定,将一组正整数表示为一系列位图块,每个块最多包含64,000个整数。

这些扩展允许在查询和嵌入式SQL中更轻松、更高效地操作某些条件和筛选器。在嵌入式SQL中,它们支持位图的简单输入和输出,特别是在单个块级别。它们支持处理完整的位图,这些位图由%bitmap()%SQL.Bitmap类处理。它们还支持非RowID值的位图处理,例如外键值、子表的父引用、关联的任一列等。

例如,要输出指定块的位图,请执行以下操作:

SELECT %BITMAPCHUNK(Home_Zip) FROM Sample.Person
WHERE %CHUNK(Home_Zip)=2

要输出整个表的所有块,请执行以下操作:

SELECT %CHUNK(Home_Zip),%BITMAPCHUNK(Home_Zip) FROM Sample.Person
GROUP BY %CHUNK(Home_Zip) ORDER BY 1

%CHUNK函数

%%CHUNK(F)返回位图索引字段f值的块分配。这被计算为f\64000+1.%%CHUNK(F)非位图索引字段的任何字段或值f%chunk(F)始终返回1。

%BITPOS函数

%Bitpos(F)返回分配给其区块内的位图索引字段f值的位位置。这被计算为f#64000+1。对于不是位图索引字段的任何字段或值f%Bitpos(F)返回的值比其整数值多1。字符串的整数值为0

%BITMAP聚合函数

聚合函数%bitmap(F)将许多f值组合到一个%SQL.Bitmap对象中,在该对象中,对于结果集中的每个值f,与适当块中的f相对应的位被设置为1。上述所有参数中的f通常是正整数字段(或表达式),通常(但不一定)是RowID

%BITMAPCHUNK聚合函数

聚合函数%BITMAPCHUNK(F)将字段f的许多值组合成64,000位的InterSystems SQL标准位图字符串,其中对于集合中的每个值f,位f#64000+1=%Bitpos(F)被设置为1。请注意,无论%chunk(F)的值是多少,都会在结果中设置该位。%BITMAPCHUNK()为空集生成NULL,并且与任何其他聚合一样,它忽略输入中的NULL值。

%SETINCHUNK谓词条件

当且仅当($BIT(BM,%Bitpos(F)=1时,条件(f%SETINCHUNK BM)为真。Bm可以是任何位图表达式字符串,例如输入主机变量:bm,或%BITMAPCHUNK()聚合函数的结果,等等。请注意,无论%chunk(F)的值是多少,都会检查<bm> 位。如果<bm> 不是位图或为NULL,则条件返回FALSE。(F%SETINCHUNK NULL)生成FALSE(非未知)。

0
0 149
文章 姚 鑫 · 四月 14, 2021 8m read

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

定义索引

使用带有索引的Unique、PrimaryKey和IdKey关键字

与典型的SQL一样,InterSystems IRIS支持惟一键和主键的概念。 InterSystems IRIS还能够定义IdKey,它是类实例(表中的行)的唯一记录ID。 这些特性是通过UniquePrimaryKeyIdKey关键字实现的:

  • Unique -在索引的属性列表中列出的属性上定义一个唯一的约束。 也就是说,只有这个属性(字段)的唯一数据值可以被索引。 唯一性是根据属性的排序来确定的。 例如,如果属性排序是精确的,则字母大小写不同的值是唯一的; 如果属性排序是SQLUPPER,则字母大小写不同的值不是唯一的。 但是,请注意,对于未定义的属性,不会检查索引的惟一性。 根据SQL标准,未定义的属性总是被视为唯一的。
  • PrimaryKey -在索引的属性列表中列出的属性上定义一个主键约束。
  • IdKey -定义一个唯一的约束,并指定哪些属性用于定义实例(行)的唯一标识。 IdKey总是具有精确的排序规则,即使是数据类型为string时也是如此。

这些关键字的语法出现在下面的例子中:

Class MyApp.SampleTable Extends %Persistent [DdlAllowed]
{
  Property Prop1 As %String;
  Property Prop2 As %String;
  Property Prop3 As %String;

  Index Prop1IDX on Prop1 [ Unique ];
  Index Prop2IDX on Prop2 [ PrimaryKey ];
  Index Prop3IDX on Prop3 [ IdKey ];
}

注意:IdKeyPrimaryKeyUnique关键字只在标准索引下有效。 不能将它们与位图或位片索引一起使用。

同时指定IdKeyPrimaryKey关键字也是有效的语法,例如:

 Index IDPKIDX on Prop4 [ IdKey, PrimaryKey ];

这个语法指定IDPKIDX索引既是类(表)的IdKey,也是它的主键。 这些关键字的所有其他组合都是多余的。

对于使用这些关键字之一定义的任何索引,都有一个方法允许打开类的实例,其中与索引关联的属性有特定的值;

定义SQL搜索索引

可以在表类定义中定义SQL搜索索引,如下所示:

Class Sample.TextBooks Extends %Persistent [DdlAllowed]
{
 Property BookName As %String;
 Property SampleText As %String(MAXLEN=5000);

 Index NameIDX On BookName [ IdKey ];
 Index SQLSrchIDXB On (SampleText) As %iFind.Index.Basic;
 Index SQLSrchIDXS On (SampleText) As %iFind.Index.Semantic;
 Index SQLSrchIDXA On (SampleText) As %iFind.Index.Analytic; 
}

用索引存储数据

可以使用index Data关键字指定一个或多个数据值的副本存储在一个索引中:

Class Sample.Person Extends %Persistent [DdlAllowed]
{
 Property Name As %String;
 Property SSN As %String(MAXLEN=20);

 Index NameIDX On Name [Data = Name];
}

在本例中,索引NameIDX的下标是各种Name值的排序(大写)值。名称的实际值的副本存储在索引中。当通过SQL更改Sample.Person表或通过对象更改对应的Sample.Person类或其实例时,将维护这些副本。

在经常执行选择性(从许多行中选择一些行)或有序搜索(从许多列中返回一些列)的情况下,在索引中维护数据副本会很有帮助。

例如,考虑以下针对Sample.Person表的查询:

SQL引擎可以通过读取NameIDX而从不读取表的主数据来决定完全满足此请求。

image

注意:不能使用位图索引存储数据值。

索引null

如果一个索引字段的数据为NULL(没有数据存在),相应的索引使用索引NULL标记来表示这个值。 默认情况下,索引空标记值为-1E14 使用索引空标记可以使空值排序在所有非空值之前。

%Library.BigInt数据类型存储小于-1E14的小负数。默认情况下,%BigInt索引空标记值为-1E14,因此与现有BigInt索引兼容。如果索引的%BigInt数据值可能包括这些极小的负数,则可以使用INDEXNULLMARKER属性参数更改特定字段的索引NULL标记值,作为特性定义的一部分,如下例所示:

Property ExtremeNums As %Library.BigInt(INDEXNULLMARKER = "-1E19");

还可以在数据类型类定义中更改索引NULL标记的默认值。

此参数属性在IRIS里有,Cache里没有。

索引集合

为属性编制索引时,放在索引中的值是整个已整理属性值。对于集合,可以通过将(Elements)或(Key)附加到属性名称来定义与集合的元素和键值相对应的索引属性。(元素)和(键)允许指定从单个属性值生成多个值,并对每个子值进行索引。当属性是集合时,Elements令牌通过值引用集合的元素,Key令牌通过位置引用它们。当元素和键都出现在单个索引定义中时,索引键值包括键和关联的元素值。

例如,假设有一个基于Sample.Person类的FavoriteColors属性的索引。对此属性集合中的项进行索引的最简单形式是以下任一种:

 INDEX fcIDX1 ON (FavoriteColors(ELEMENTS));

 INDEX fcIDX2 ON (FavoriteColors(KEYS));

其中,FavoriteColor(Elements)是指FavoriteColors属性的元素,因为它是一个集合。一般形式是PropertyName(元素)或PropertyName(键),其中该集合的内容是定义为某个数据类型的列表或数组的属性中包含的一组元素)。

若要索引文本属性,可以创建一个由PropertyNameBuildValueArray()方法生成的索引值数组(在下一节中介绍)。与集合本身一样,(Elements)和(Key)语法对索引值数组有效。

如果属性集合被投影为数组,则索引必须遵守以下限制才能被投影到集合表。索引必须包括(键)。索引不能引用集合本身和对象ID值以外的任何属性。如果投影索引还定义了要存储在索引中的数据,则存储的数据属性也必须限制为集合和ID。否则,不会投影索引。此限制适用于投影为数组的集合属性上的索引;不适用于投影为列表的集合上的索引。

与集合的元素或键值对应的索引还可以具有所有标准索引功能,例如将数据与索引一起存储、特定于索引的排序规则等。

InterSystems SQL可以通过指定FOR SOME%ELEMENT谓词来使用集合索引。

使用(Elements)和(Key)索引数据类型属性

为了索引数据类型属性,还可以使用BuildValueArray()方法创建索引值数组。此方法将属性值解析为键和元素的数组;它通过生成从与其关联的属性的值派生的元素值集合来实现这一点。使用BuildValueArray()创建索引值数组时,其结构适合索引。

BuildValueArray()方法的名称为PropertyNameBuildValueArray(),其签名为:

ClassMethod propertynameBuildValueArray(value, ByRef valueArray As %Library.String) As %Status
  • BuildValueArray()方法的名称以组合方法的典型方式派生于属性名。
  • 第一个参数是属性值。
  • 第二个参数是通过引用传递的数组。 这是一个包含键-元素对的数组,键下标的数组等于元素。
  • 该方法返回一%Status 值。

这个例子:

/// DescriptiveWords是一个以逗号分隔的单词字符串
Property DescriptiveWords As %String;

/// 基于描述词的索引
Index dwIDX On DescriptiveWords(ELEMENTS);

/// 方法的作用是:演示如何在属性的子值上建立索引。
///
/// (如果DescriptiveWords被定义为一个集合,则不需要此方法。)
ClassMethod DescriptiveWordsBuildValueArray(
     Words As %Library.String = "",
     ByRef wordArray As %Library.String)
   As %Status {
 If Words '= "" {
   For tPointer = 1:1:$Length(Words,",") {
     Set tWord = $Piece(Words,",",tPointer)
     If tWord '= "" {
       Set wordArray(tPointer) = tWord
     }
   }
 }
 Else {
   Set wordArray("TODO") = "Enter keywords for this person"
 }
 Quit $$$OK
}

在本例中,dwIDX索引基于DescriptiveWords属性。 DescriptiveWordsBuildValueArray()方法接受由Words参数指定的值,基于该值创建一个索引值数组,并将其存储在wordArray中。 InterSystems IRIS在内部使用BuildValueArray()实现; 不调用此方法。

注意:没有必要将任何元素/键值建立在属性值的基础上。 唯一的建议是,每次向该方法传递给定值时,都创建相同的元素和键数组。

为各种实例的描述性词所属性设置值和检查这些值的属性涉及活动(如以下):

SAMPLES>SET empsalesoref = ##class(MyApp.Salesperson).%OpenId(3)

SAMPLES>SET empsalesoref.DescriptiveWords = "Creative"

SAMPLES>WRITE empsalesoref.%Save()
1
SAMPLES>SET empsalesoref = ##class(MyApp.Salesperson).%OpenId(4)

SAMPLES>SET empsalesoref.DescriptiveWords = "Logical,Tall"

SAMPLES>WRITE empsalesoref.%Save()
1

sample index内容,例如:

DescriptiveWords(ELEMENTS)IDData
" CREATIVE"3""
" ENTER KEYWORDS FOR THIS PERSON"1""
" ENTER KEYWORDS FOR THIS PERSON"2""
" LOGICAL"4""
" TALL"4""

注意:此表显示抽象中的索引内容。磁盘上的实际存储形式可能会有所变化。

将数组(元素)上的索引投影到子表

要在嵌入式对象中索引属性,需要在引用该嵌入式对象的持久化类中创建索引。 属性名必须指定表(%Persistent类)中的引用字段的名称和嵌入对象(%SerialObject)中的属性的名称,如下面的示例所示:

Class Sample.Person Extends (%Persistent) [ DdlAllowed ]
{  Property Name As %String(MAXLEN=50);
   Property Home As Sample.Address;
   Index StateInx On Home.State;
} 

此处HomeSample.Person中引用嵌入对象Sample.Address的属性,该对象包含State属性,如下例所示:

Class Sample.Address Extends (%SerialObject)
{  Property Street As %String;
   Property City As %String;
   Property State As %String;
   Property PostalCode As %String;
 }

只有与持久类属性引用相关联的嵌入对象的实例中的数据值被索引。不能直接索引%SerialObject属性%Library.SerialObject(以及%SerialObject的所有未显式定义SqlCategory的子类)的SqlCategory为字符串。

还可以使用SQL CREATE INDEX语句在嵌入式对象属性上定义索引,如下例所示:

CREATE INDEX StateIdx ON TABLE Sample.Person (Home_State)

类中定义的索引注释

当在类定义中使用索引时,需要记住以下几点:

  • 索引定义仅从主(第一个)超类继承。
  • 如果使用Studio添加(或删除)数据库中存储数据的类的索引定义,则必须使用“构建索引”中描述的过程之一来手动填充索引。

使用DDL定义索引

如果你使用DDL语句来定义表,也可以使用以下DDL命令来创建和删除索引:

  • CREATE INDEX
  • DROP INDEX

DDL index命令执行以下操作:

  1. 它们更新在其上添加或删除索引的相应类和表定义。 重新编译修改后的类定义。
  2. 它们根据需要在数据库中添加或删除索引数据:CREATE index命令使用当前存储在数据库中的数据填充索引。 类似地,DROP INDEX命令从数据库中删除索引数据(即实际索引)。
0
0 202
文章 姚 鑫 · 四月 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