文章
姚 鑫 · 三月 29, 2021
# 第十三章 使用动态SQL(七)
# SQL元数据
动态SQL提供以下类型的元数据:
- 在“准备”之后,描述查询类型的元数据。
- 在“准备”之后,描述查询中选择项的元数据(“列”和“扩展列信息”)。
- 在准备之后,描述查询参数的元数据:参数,`:var`参数和常量。 (语句参数,形式参数和对象)
- 执行之后,描述查询结果集的元数据。在执行Prepare操作(`%Prepare()`,`%PrepareClassQuery()`或`%ExecDirect()`)之后,可以使用`%SQL.StatementMetadata`属性值。
- 可以直接为最新的`%Prepare()`返回`%SQL.Statement`元数据属性。
- 可以返回包含`%SQL.StatementMetadata`属性的oref的`%SQL.Statement%Metadata`属性。这使可以返回多个准备操作的元数据。
`SELECT`或`CALL`语句返回所有这些元数据。 `INSERT`,`UPDATE`或`DELETE`返回语句类型元数据和形式参数。
## 语句类型元数据
使用`%SQL.Statement`类进行`Prepare`之后,可以使用`%SQL.StatementMetadata statementType`属性来确定准备哪种类型的SQL语句,如以下示例所示。本示例使用`%SQL.Statement%Metadata`属性来保存和比较两`个Prepare`操作的元数据:
```java
/// d ##class(PHA.TEST.SQL).MetaData()
ClassMethod MetaData()
{
SET tStatement = ##class(%SQL.Statement).%New()
SET myquery1 = "SELECT TOP ? Name,Age,AVG(Age),CURRENT_DATE FROM Sample.Person"
SET myquery2 = "CALL Sample.SP_Sample_By_Name(?)"
SET qStatus = tStatement.%Prepare(myquery1)
IF qStatus'=1 {
WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
}
SET meta1 = tStatement.%Metadata
SET qStatus = tStatement.%Prepare(myquery2)
IF qStatus'=1 {
WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
}
SET meta2 = tStatement.%Metadata
WRITE "语句类型query 1: ",meta1.statementType,!
WRITE "语句类型query 2: ",meta2.statementType,!
WRITE "End of metadata"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).MetaData()
语句类型query 1: 1
语句类型query 2: 45
End of metadata
```
`statementType`属性的“类引用”条目列出了语句类型整数代码。最常见的代码是1(`SELECT`查询)和45(`CALL`到存储的查询)。
可以使用`%GetImplementationDetails()`实例方法返回相同的信息,如成功准备的结果中所述。
执行查询后,可以从结果集中返回语句类型名称(例如`SELECT`)。
## 选择项目Select-item元数据
使用`%SQL.Statement`类准备`SELECT`或`CALL`语句之后,可以通过显示所有元数据或指定各个元数据项来返回有关查询中指定的每个选择项列的元数据。此列元数据包括ODBC数据类型信息,以及客户端类型和InterSystems Objects属性的起源以及类类型信息。
以下示例返回最近准备的查询中指定的列数:
```java
/// d ##class(PHA.TEST.SQL).MetaData1()
ClassMethod MetaData1()
{
SET myquery = "SELECT %ID AS id,Name,DOB,Age,AVG(Age),CURRENT_DATE,Home_State FROM Sample.Person"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
WRITE "Number of columns=",tStatement.%Metadata.columnCount,!
WRITE "End of metadata"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).MetaData1()
Number of columns=7
End of metadata
```
以下示例返回列名称(或列别名),ODBC数据类型,最大数据长度(精度),以及每个`SELECT`项目字段的比例:
```java
/// d ##class(PHA.TEST.SQL).MetaData2()
ClassMethod MetaData2()
{
SET $NAMESPACE="SAMPLES"
SET myquery=2
SET myquery(1)="SELECT Name AS VendorName,LastPayDate,MinPayment,NetDays,"
SET myquery(2)="AVG(MinPayment),$HOROLOG,%TABLENAME FROM Sample.Vendor"
SET rset = ##class(%SQL.Statement).%New()
SET qStatus = rset.%Prepare(.myquery)
IF qStatus'=1 {
WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
}
SET x=rset.%Metadata.columns.Count()
SET x=1
WHILE rset.%Metadata.columns.GetAt(x) {
SET column=rset.%Metadata.columns.GetAt(x)
WRITE !,x," ",column.colName," 是数据类型 ",column.ODBCType
WRITE " 大小为 ",column.precision," 规模 = ",column.scale
SET x=x+1
}
WRITE !,"End of metadata"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).MetaData2()
1 VendorName 是数据类型 12 大小为 50 规模 = 0
2 LastPayDate 是数据类型 9 大小为 10 规模 = 0
3 MinPayment 是数据类型 8 大小为 6 规模 = 0
4 NetDays 是数据类型 4 大小为 3 规模 = 0
5 Aggregate_5 是数据类型 8 大小为 20 规模 = 0
6 Expression_6 是数据类型 12 大小为 255 规模 = 0
7 Literal_7 是数据类型 12 大小为 13 规模 = 0
End of metadata
```
下面的示例使用`%SQL.StatementMetadata%Display()`实例方法显示所有列元数据:
```java
/// d ##class(PHA.TEST.SQL).MetaData3()
ClassMethod MetaData3()
{
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare("SELECT %ID AS id,Name,DOB,Age,AVG(Age),CURRENT_DATE,Home_State FROM Sample.Person")
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
DO tStatement.%Metadata.%Display()
WRITE !,"End of metadata"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).MetaData3()
Columns (SQLRESULTCOL, property 'columns'):
Column Name Type Prec Scale Null Label Table Schema CType
----------- ---- ---- ----- ---- ------------ ------------ ------------ -----
id 4 10 0 0 id Person Sample 5
Name 12 50 0 0 Name Person Sample 10
DOB 9 10 0 1 DOB Person Sample 2
Age 4 10 0 1 Age Person Sample 5
Aggregate_5 2 20 8 1 Aggregate_5 14
Expression_6 9 11 0 2 Expression_6 2
Home_State 12 2 0 1 Home_State Person Sample 10
Extended Column Info (SQLRESULTCOL)
Flags: 1:AutoIncrement,2:CaseSensitive,3:Currency,4:ReadOnly,5:RowVersion,
6:Unique,7:Aliased,8:Expression,9:Hidden,10:Identity,11:KeyColumn,
12:RowId
Column Name Linked Prop Type Class Flags
------------ --------------------- --------------------- -----------------------
id Sample.Person Y,N,N,Y,N,Y,Y,N,N,Y,Y,Y
Name Sample.Person.Name %Library.String N,N,N,N,N,N,N,N,N,N,N,N
DOB Sample.Person.DOB %Library.Date N,N,N,N,N,N,N,N,N,N,N,N
Age Sample.Person.Age %Library.Integer N,N,N,N,N,N,N,N,N,N,N,N
Aggregate_5 %Library.Numeric N,N,N,Y,N,N,Y,N,N,N,N,N
Expression_6 %Library.Date N,N,N,Y,N,N,Y,Y,N,N,N,N
Home_State Sample.Address.State
%Library.String N,N,N,N,N,N,N,N,N,N,N,N
Statement Parameters (property 'parameters'):
Nbr. Type precision scale nullable colName columntype
---- ---- --------- ----- -------- ------------ ----------
Formal Parameters (property 'formalParameters'):
Nbr. Type precision scale nullable colName columntype
---- ---- --------- ----- -------- ------------ ----------
Objects:
Col Column Name Extent ExportCall
--- ----------- ----------------- -----------------------------
1 id Sample.Person ##class(Sample.Person).%SQLQuickLoad
```
这将返回所选字段的两个表列表。第一列元数据表列出了列定义信息:
显示标题 | `%SQL.StatementColumn`属性 | 描述
---|---|---
Column Name | colName |列的SQL名称。如果为该列提供了别名,则会在此处列出该列的别名,而不是字段名称。名称和别名将被截断为12个字符。对于表达式,聚合,文字,主机变量或子查询,列出了分配的`“ Expression_n”`,`“ Aggregate_n”`,`“ Literal_n”`,`“ HostVar_n”`或`“ Subquery_n”`标签(`n`为`SELECT`项序列号)。如果为表达式,聚合,文字,主机变量或子查询分配了别名,则在此处列出该别名。
Type| ODBCType |ODBC数据类型的整数代码。请注意,这些ODBC数据类型代码与CType数据类型代码不同。
Prec| precision|精度或最大长度(以字符为单位)。日期,时间,PosixTime和TimeStamp数据类型中描述了TIME数据类型的精度和小数位元数据。
Scale| scale| 小数位数的最大数目。对于整数或非数值返回0。日期,时间,PosixTime和TimeStamp数据类型中描述了`TIME`数据类型的精度和小数位元数据。
Null| isNullable| 一个整数值,指示是否将列定义为`Non-NULL(0)`,或者是否允许`NULL(1)`。 RowID返回0。如果`SELECT`项是可能导致`NULL`的聚合或子查询,或者如果它指定`NULL`文字,则该项设置为1。如果`SELECT`项是表达式或主机变量,则设置此项到2(无法确定)。
Label| label| 列名或列别名(与列名相同)。
Table| tableName| SQL表名称。即使为表指定了别名,也始终在此处列出实际的表名。如果`SELECT`项是表达式或聚合,则不会列出任何表名。如果`SELECT`项是子查询,则列出子查询表名称。
Schema| schemaName|表的架构名称。如果未指定架构名称,则返回系统范围的默认架构。如果`SELECT`项是表达式或聚合,则不会列出任何模式名称。如果SELECT项是子查询,则不会列出任何架构名称。
CType| clientType| 客户端数据类型的整数代码。
第二列元数据表列出了扩展列信息。扩展列信息表列出了具有十二个布尔标志(SQLRESULTCOL)的每一列,这些标志被指定为Y(是)或N(否):
显示标题 | `%SQL.StatementColumn`属性 | 描述
---|---|---
1: AutoIncrement| isAutoIncrement| TRowID和IDENTITY字段返回Y。
2: CaseSensitive| isCaseSensitive |具有`%EXACT`归类的字符串数据类型字段返回Y。引用`%SerialObject`嵌入式对象的属性返回Y。
3: Currency| isCurrency| 使用%Library.Currency数据类型定义的字段,例如`MONEY`数据类型。
4: ReadOnly |isReadOnly|表达式,聚合,文字,`HostVar`或子查询返回Y。RowID,IDENTITY和RowVersion字段返回Y。
5: RowVersion| isRowVersion|RowVersion字段返回Y。
6: Unique| isUnique| 定义为具有唯一值约束的字段。 RowID和IDENTITY字段返回Y。
7: Aliased| isAliased| 系统为非字段选择项提供别名。因此,无论用户是否通过指定列别名替换了系统别名,表达式,聚合,文字,HostVar或子查询都将返回Y。此标志不受用户指定的列别名的影响。
8: Expression| isExpression| 表达式返回Y。
9: Hidden| isHidden| 如果使用`%PUBLICROWID`或`SqlRowIdPrivate = 0`(默认值)定义表,则RowID字段返回N。否则,RowID字段返回Y。引用`%SerialObject`嵌入式对象的属性返回Y。
10: Identity| isIdentity| 定义为IDENTITY字段的字段返回Y。如果未隐藏RowID,则RowID字段返回Y。
11: KeyColumn| isKeyColumn| 定义为主键字段或外键约束目标的字段。 RowID字段返回Y。
12: RowID| isRowId |ROWID和Identity字段返回Y.
扩展列信息元数据表列出了每个选定字段的列名称(SQL名称或列别名),链接属性(链接的持久性类属性)和类型类(数据类型类)。请注意,链接属性列出了持久性类名(不是SQL表名)和属性名(不是列别名)。
- 对于普通表字段(`SELECT Name FROM Sample.Person`): `Linked Prop=Sample.Person.Name, Type Class=%Library.String`.
- 对于表格的RowID (`SELECT %ID FROM Sample.Person`): `Linked Prop= [none], Type Class=Sample.Person`.
- 对于表达式,聚合,文字,`HostVar`或子查询 (`SELECT COUNT(Name) FROM Sample.Person`): `Linked Prop= [none], Type Class=%Library.BigInt`.
- 供参考`%Serial Object`嵌入式对象属性 (`SELECT Home_State FROM Sample.Person`). `Linked Prop=Sample.Address.State, Type Class=%Library.String.`
- 对于引用`%SerialObject`嵌入式对象的字段(`SELECT Home FROM Sample.Person`). `Linked Prop=Sample.Person.Home, Type Class=Sample.Address`.
在此示例中,`Sample.Person`中的`Home_State`字段引用`%SerialObject`类`Sample.Address`的`State`属性。
下面的示例返回带有一个形式参数(也就是语句参数)的被调用存储过程的元数据:
```java
/// d ##class(PHA.TEST.SQL).MetaData4()
ClassMethod MetaData4()
{
SET $NAMESPACE="SAMPLES"
SET mysql = "CALL Sample.SP_Sample_By_Name(?)"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(.mysql)
IF qStatus'=1 {
WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
}
DO tStatement.%Metadata.%Display()
WRITE !,"End of metadata"
}
```
它不仅返回列(字段)信息,还返回语句参数,形式参数和对象的值。
以下示例返回具有三个形式参数的的元数据。这三个参数之一用问号(`?`)指定,使其成为语句参数:
```java
/// d ##class(PHA.TEST.SQL).MetaData5()
ClassMethod MetaData5()
{
SET $NAMESPACE="SAMPLES"
SET mycall = "CALL personsets(?,'MA')"
SET tStatement = ##class(%SQL.Statement).%New(0,"sample")
SET qStatus = tStatement.%Prepare(mycall)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
DO tStatement.%Metadata.%Display()
WRITE !,"End of metadata"
}
```
```java
DHC-APP>d ##class(PHA.TEST.SQL).MetaData5()
Columns (SQLRESULTCOL, property 'columns'):
Column Name Type Prec Scale Null Label Table Schema CType
----------- ---- ---- ----- ---- ------------ ------------ ------------ -----
Extended Column Info (SQLRESULTCOL)
Flags: 1:AutoIncrement,2:CaseSensitive,3:Currency,4:ReadOnly,5:RowVersion,
6:Unique,7:Aliased,8:Expression,9:Hidden,10:Identity,11:KeyColumn,
12:RowId
Column Name Linked Prop Type Class Flags
------------ --------------------- --------------------- -----------------------
Statement Parameters (property 'parameters'):
Nbr. Type precision scale nullable colName columntype
---- ---- --------- ----- -------- ------------ ----------
1 12 50 0 2 name 1
Formal Parameters (property 'formalParameters'):
Nbr. Type precision scale nullable colName columntype
---- ---- --------- ----- -------- ------------ ----------
1 4 4 0 2 _isc_sp_ret_val 5
2 12 50 0 2 name 1
3 12 50 0 2 state 1
Objects:
Col Column Name Extent ExportCall
--- ----------- ----------------- -----------------------------
End of metadata
```
请注意,此元数据不返回任何列信息,但是“语句参数”,“形式参数”列表包含列名称和数据类型。
## Query参数元数据
使用`%SQL.Statement`类进行`Prepare`之后,您可以返回有关查询参数的元数据:输入参数(指定为问号(`?`)),输入主机变量(指定为`:varname`)和常量(文字值)。可以返回以下元数据:
- `?`参数`:parameterCount`属性
- ODBC数据类型为`?`参数`:%SQL.StatementMetadata%Display()`实例方法“语句参数”列表。
- ?,v(:var)和c(常量)参数的列表:`%GetImplementationDetails()`实例方法,如成功准备的结果中所述。
- ?,v(:var)和c(常量)参数的ODBC数据类型:`formalParameters`属性。
`%SQL.StatementMetadata%Display()`实例方法“形式参数”列表。
- 查询文本,其中显示以下参数:`%GetImplementationDetails()`实例方法,如成功准备结果中所述。
语句元数据`%Display()`方法列出了“语句参数”和“形式参数”。对于每个参数,它列出了顺序参数号,ODBC数据类型,精度,小数位数,该参数是否可为空(2表示始终提供一个值)及其对应的属性名称(colName)和列类型。
请注意,某些ODBC数据类型以负整数形式返回。
下面的示例按顺序返回每个查询参数(`?`,`:var`和常量)的ODBC数据类型。请注意,`TOP`参数以数据类型12(`VARCHAR`)而不是数据类型4(`INTEGER`)返回,因为可以指定`TOP ALL`:
```java
/// d ##class(PHA.TEST.SQL).MetaData6()
ClassMethod MetaData6()
{
SET myquery = 4
SET myquery(1) = "SELECT TOP ? Name,DOB,Age+10 "
SET myquery(2) = "FROM Sample.Person"
SET myquery(3) = "WHERE %ID BETWEEN :startid :endid AND DOB=?"
SET myquery(4) = "ORDER BY $PIECE(Name,',',?)"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(.myquery)
IF qStatus'=1 {
WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
}
SET prepmeta = tStatement.%Metadata
WRITE "Number of ? parameters=",prepmeta.parameterCount,!
SET formalobj = prepmeta.formalParameters
SET i=1
WHILE formalobj.GetAt(i) {
SET prop=formalobj.GetAt(i)
WRITE prop.colName," type= ",prop.ODBCType,!
SET i=i+1
}
WRITE "End of metadata"
}
```
执行`Execute`之后,无法从查询结果集元数据中获取参数元数据。在结果集中,所有参数均已解析。因此`parameterCount = 0`,`formalParameters`不包含任何数据。
## Query结果集元数据
使用`%SQL.Statemen`t类执行`Execute`之后,可以通过调用返回结果集元数据:
- `%SQL.StatementResult`类的属性。
- `%SQL.StatementResult%GetMetadata()`方法,访问`%SQL.StatementMetadata`类属性。
### %SQL.StatementResult属性
执行查询操作后,`%SQL.StatementResult`返回:
- `%StatementType`属性返回与最近执行的SQL语句相对应的整数代码。以下是这些整数代码的部分列表:`1 = SELECT; 2 = INSERT; 3 = UPDATE; 4 = DELETE or TRUNCATE TABLE; 9 = CREATE TABLE; 15 = CREATE INDEX; 45 = CALL`.
- `%StatementTypeName`计算的属性基于`%StatementType`返回最近执行的SQL语句的命令名称。此名称以大写字母返回。请注意,`TRUNCATE TABLE`操作将作为`DELETE`返回。即使执行了更新操作,`INSERT OR UPDATE`也将作为`INSERT`返回。
- `%ResultColumnCount`属性返回结果集行中的列数。
下面的示例显示这些属性:
```java
/// d ##class(PHA.TEST.SQL).MetaData7()
ClassMethod MetaData7()
{
SET myquery = "SELECT TOP ? Name,DOB,Age FROM Sample.Person WHERE Age > ?"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {
WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
}
SET rset = tStatement.%Execute(10,55)
IF rset.%SQLCODE=0 {
WRITE "Statement type=",rset.%StatementType,!
WRITE "Statement name=",rset.%StatementTypeName,!
WRITE "Column count=",rset.%ResultColumnCount,!
WRITE "End of metadata"
} ELSE {
WRITE !,"SQLCODE=",rset.%SQLCODE," ",rset.%Message
}
}
```
### %SQL.StatementResult %GetMetadata()
执行之后,可以使用`%SQL.StatementResult %GetMetadata()`方法访问`%SQL.StatementMetadata`类属性。这些是在Prepare之后由`%SQL.Statement%Metadata`属性访问的相同属性。
以下示例显示了属性:
```java
/// d ##class(PHA.TEST.SQL).MetaData8()
ClassMethod MetaData8()
{
SET myquery=2
SET myquery(1)="SELECT Name AS VendorName,LastPayDate,MinPayment,NetDays,"
SET myquery(2)="AVG(MinPayment),$HOROLOG,%TABLENAME FROM Sample.Vendor"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(.myquery)
IF qStatus'=1 {
WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT
}
SET rset = tStatement.%Execute()
IF rset.%SQLCODE=0 {
SET rsmeta=rset.%GetMetadata()
SET x=rsmeta.columns.Count()
SET x=1
WHILE rsmeta.columns.GetAt(x) {
SET column=rsmeta.columns.GetAt(x)
WRITE !,x," ",column.colName," is data type ",column.ODBCType
WRITE " with a size of ",column.precision," and scale = ",column.scale
SET x=x+1 }
} ELSE {
WRITE !,"SQLCODE=",rset.%SQLCODE," ",rset.%Message
}
WRITE !,"End of metadata"
}
```
请注意,结果集元数据不提供参数元数据。这是因为`Execute`操作会解析所有参数。因此,在结果集中,`parameterCount = 0`,而`formalParameters`不包含任何数据。
# 审核动态SQL
InterSystems IRIS支持动态SQL语句的可选审核。启用%System /%SQL / DynamicStatement系统审核事件时,将执行动态SQL审核。默认情况下,未启用此系统审核事件。
如果启用%System /%SQL / DynamicStatement,则系统将自动审核在系统范围内执行的每个`%SQL.Statement`动态语句。审核将信息记录在审核数据库中。
要查看审核数据库,请依次转到管理门户,系统管理,选择安全性,审核,然后查看审核数据库。可以将“事件名称”过滤器设置为DynamicStatement,以将View Audit Database限制为Dynamic SQL语句。审核数据库列出了时间(本地时间戳),用户,PID(进程ID)和事件的描述。说明指定动态SQL语句的类型。例如,SQL SELECT语句(`%SQL.Statement`)或SQL CREATE VIEW语句(`%SQL.Statement`)。
通过选择事件的详细信息链接,可以列出其他信息,包括事件数据。事件数据包括执行的SQL语句和该语句的任何参数的值。例如:
```java
SELECT TOP ? Name , Age FROM Sample . MyTest WHERE Name %STARTSWITH ?
/*#OPTIONS {"DynamicSQLTypeList":",1"} */
Parameter values:
%CallArgs(1)=5
%CallArgs(2)="Fred"
```
事件数据的总长度(包括语句和参数)为3,632,952个字符。如果该语句和参数长于3632952,则事件数据将被截断。
InterSystems IRIS还支持ODBC和JDBC语句的审核(事件名称= XDBCStatement),以及嵌入式SQL语句的审核(事件名称= EmbeddedStatement)。
文章
Frank Ma · 三月 2, 2022
好人不需要规则。
神秘博士
要成为日期和时间的主人并不是一件容易的事,在任何编程语言中,这总是一个问题,有时会让人感到困惑,我们将澄清并提出一些提示,使这项任务尽可能简单。
坐上TARDIS,我将把你变成一个时间领主。
让我们从基本知识开始
如果你通常使用其他语言,请记住,Intersystems Object Script(以下简称IOS,不要与苹果手机混淆)的日期有点特殊。当我们在终端运行$HOROLOG 命令时,为了得到当前的日期和时间,你会看到它被分为两部分:
WRITE $HOROLOG
> 66149,67164
第一个值是天数,确切地说,是自1840年12月31日以来的天数,也就是说,值1是1841年1月1日;第二个值是自今天00:00以来的秒钟。
在这个例子中,66149对应于09/02/2022(欧洲格式的日/月/年的2月9日),67164对应于18:39:24。我们将这种格式称为数据和时间的内部格式。
感到困惑吗?好吧,我们将开始揭示宇宙的伟大秘密(日期和时间)。
如何将内部格式转换为更清晰的格式?
为此,我们将用到命令 $ZDATETIME
基本的命令是
SET RightNow = $HOROLOG
WRITE RightNow
> 66149,67164
WRITE $ZDATETIME(RightNow)
> 02/09/2022 18:39:24
默认情况下,它使用美国格式月/日/年(mm/dd/yyyy)。如果你想使用其他格式的日期,我们将使用第二个参数,比如欧洲格式日/月/年(dd/mm/yyyy),在这种情况下,我们将给它一个值4(关于更多的格式,见文档$ZDATETIME.dformat)。
SET RightNow = $HOROLOG
WRITE RightNow
> 66149,67164
WRITE $ZDATETIME(RightNow,4)
> 09/02/2022 18:39:24
该选项使用我们在本地变量中定义的分隔符和年份格式。
如果我们还想放另一种时间格式,例如12小时格式(AM/PM)而不是24小时格式,我们使用第三个参数,其值为3,如果我们不想显示秒,我们将使用值4(见文件$ZDATETIME.tformat)。
SET RightNow = $HOROLOG
WRITE RightNow
> 66149,67164
WRITE $ZDATETIME(RightNow,4,3)
> 09/02/2022 06:39:24PM
WRITE $ZDATETIME(RightNow,4,4)
> 09/02/2022 06:39PM
现在是不是更清楚了?那么让我们更深入地了解一下。
ODBC 格式
这个格式与你的本地配置无关,它将始终显示为年/月/日格式 yyyy-mm-dd,其值为3。 如果我们想创建要导出文件的数据,如CSV、HL7文件等,建议使用它。
SET RightNow = $HOROLOG
WRITE RightNow
> 66149,67164
WRITE $ZDATETIME(RightNow,3)
> 2022-02-09 18:39:24
一周的日子,星期名称,一年中的某天
Value 值
描述
10
一周的日子将是一个介于0和6之间的值,0代表星期天,6代表星期六。
11
星期的缩写名称,它将根据你定义的本地配置返回,IRIS的默认安装是 enuw (English, United States, Unicode)
12
长格式的星期名称,与11相同。
14
一年中的某一天,自1月1日以来的天数。
如果我们只是想分别处理日期和时间,应该分别使用$ZDATE和$ZTIME命令。格式的参数与 $ZDATETIME.dformat 和 $ZDATETIME.tformat中定义的参数相同。
SET RightNow = $HOROLOG
WRITE RightNow
> 66149,67164
WRITE $ZDATE(RightNow,10)
> 3
WRITE $ZDATE(RightNow,11)
> Wed
WRITE $ZDATE(RightNow,12)
> Wednesday
那我如何将日期转换为内部格式?
好了,现在我们来看看相反的步骤,即有一个带有日期的文本,并将其转换成IOS格式。对于这个任务,我们将使用命令 $ZDATETIMEH。
这一次,我们必须指出日期和时间的格式(如果我们使用$ZDATETIMEH),或者分别指出日期($ZDATEH)和时间($ZTIMEH)。
格式是相同的,也就是说,如果我们有一个ODBC格式(yyyy-mm-dd)的日期字符串,那么我们将使用值3。
SET MyDatetime = "2022-02-09 18:39:24"
SET Interna1 = $ZDATETIMEH(MyDatetime, 3, 1) // ODBC Format
SET MyDatetime = "09/02/2022 18:39:24"
SET Interna2 = $ZDATETIMEH(MyDatetime, 4, 1) // European format
SET MyDatetime = "02/09/2022 06:39:24PM"
SET Interna3 = $ZDATETIMEH(MyDatetime, 1, 3) // American format with time in 12h AM/PM
WRITE Interna1,!,Interna2,!,Interna3
> 66149,67164
66149,67164
66149,67164
从逻辑上讲,如果我们说字符串使用的是一种特殊的格式,而我们给它提供了错误的参数,那么任何事情都可能发生,比如它理解为2月9日,而不是9月2日。
不要混合格式,这样以后会出现问题。
SET MyDatetime = "09/02/2022"
/// American format
SET InternalDate = $ZDATEH(MyDatetime, 1)
/// European format
SET OtherDate = $ZDATETIME(InternalDate, 4)
WRITE InternalDate,!,OtherDate
> 66354
02/09/2022
不用说,如果我们试图设定一个欧洲的日期并试图将其转化为美国的日期...... 在情人节会发生什么?
SET MyDatetime = "14/02/2022"
SET InternalDate = $ZDATEH(MyDatetime, 1) // American format. month 14 doesn't exists!!!
^
<ILLEGAL VALUE>
嗯,就像所有的情人节一样......破碎的心,嗯......在这种情况下,破碎的代码。
好吧,让我们用你已经学到的东西做一些事情。
READ !,"Please indicate your date of birth (dd/mm/yyyy): ",dateOfBirth
SET internalFormat = $ZDATEH(dateOfBirth, 4)
SET dayOfWeek= $ZDATE(internalFormat, 10)
SET nameOfDay = $ZDATE(internalFormat, 12)
WRITE !,"The day of the week of your birth is: ",nameOfDay
IF dayOfWeek = 5 WRITE "you always liked to party!!!" // was born on friday
以后我们将看到其他的做事方法,以及如何处理错误。
下一章:如何进行时间旅行
好奇
如果你想知道为什么01/01/1841的值被当作1的值,那是因为选择这个日期是因为它是在世的最年长的美国公民出生前的非闰年,当MUMPS编程语言被设计时,他是一个121岁的内战老兵,它从这个语言中扩展了对象脚本。