文章
Louis Lu · 五月 30 阅读大约需 7 分钟

如何保存、查询 List 类型数据

本文主要总结了在InterSystems IRIS 中如何保存、查询List类型数据

假设我们设计的对象中包含姓名,同时每个姓名下可以包含多个电话。我们可以使用下面方法进行处理。

1. 传统方式

我们可以把每一个姓名和电话放在不同列中。

Class Test.Person Extends %Persistent
{

Property Name As %String;

Property Phone As %String;

}

我们使用SQL语句插入数据:

insert into Test.Person values ('a','111-111-1111');

insert into Test.Person values ('b','222-111-1111');

insert into Test.Person values ('a','111-222-1111');

insert into Test.Person values ('c','333-111-1111');

insert into Test.Person values ('b','222-222-1111');

数据在表中是这样的:

Name Phone
a 111-111-1111
b 222-111-1111
a 111-222-1111
c 333-111-1111
b 222-222-1111

这种情况下,我们可以使用下面的sql语句将结果返回:

SELECT
   distinct %exact(Name) Name,
   LIST(phone %foreach(Name)) Phonestr
FROM test.person
Name Phonestr
a 111-111-1111,111-222-1111
b 222-111-1111,222-222-1111
c 333-111-1111

我们可以为电话号码创建索引,以提高搜索速度,如下:

Index IdxP On Phone;

使用这种方式保存list数据比较简单,当是当list数据非常多时,这种方法会使表格臃肿。

 

2. 保存在一个字符串字段中,使用分隔符区分

这里我们将所有电话号码保存在一个字符串字段中,每个号码之间用逗号区分

Class Test.Person2 Extends %Persistent
{

Property Name As %String;

Property PhoneStr As %String;

}

填充数据后,类似于这样

Name PhoneStr
a 111-111-1111,111-222-1111
b 222-111-1111,222-222-1111
c 333-111-1111
d 333-111-1111,222-222-1111

这种情况下我们可以用下面方法实现对每个电话的索引

Index idxP On PhoneStr(ELEMENTS);


ClassMethod PhoneStrBuildValueArray(value, ByRef array) As %Status
{
  if value="" {
    s array(0)=value
  }else{
    s list=$lfs(value,","),ptr=0
    while $listnext(list,ptr,item){
      s array(ptr)=item
    }
  }
  q $$$OK
}

这里用到了一个函数

ClassMethod propertynameBuildValueArray(value, ByRef valueArray) As %Status

其中:

  • value – 需要拆分的内容;
  • valueArray – 返回array类型的值,其中包含拆分的内容,格式为 array(key1)=value1, array(key2)=value2...

这时候我们的数据是这样:

USER>zw ^Test.Person2D
^Test.Person2D=4
^Test.Person2D(1)=$lb("","a","111-111-1111,111-222-1111")
^Test.Person2D(2)=$lb("","b","222-111-1111,222-222-1111")
^Test.Person2D(3)=$lb("","c","333-111-1111")
^Test.Person2D(4)=$lb("","d","333-111-1111,222-222-1111")

索引是这样:

USER>zw ^Test.Person2I
^Test.Person2I("idxP"," 111-111-1111",1)=""
^Test.Person2I("idxP"," 111-222-1111",1)=""
^Test.Person2I("idxP"," 222-111-1111",2)=""
^Test.Person2I("idxP"," 222-222-1111",2)=""
^Test.Person2I("idxP"," 222-222-1111",4)=""
^Test.Person2I("idxP"," 333-111-1111",3)=""
^Test.Person2I("idxP"," 333-111-1111",4)=""

这种情况下我们可以通过下面的SQL 语句查找 包含电话号码 333-111-1111 的姓名

select Name from test.person2 where phonestr ['333-111-1111' 
select Name from test.person2 where phonestr like '%333-111-1111%'

但是当你检查查询计划的时候,却发现它并没有使用任何索引

我们只能通过类似于下面SQL语句的写法才能使用该索引

select Name from test.person2 where for some %element(Phonestr) (%value = '333-111-1111')

类似的还有下面的写法

(%Value %STARTSWITH 'а')
(%Value [ 'a' and %Value [ 'b')
(%Value in ('c','d'))
(%Value is null)

3. 使用 %List 类型

Class Test.Person3 Extends %Persistent
{

Property Name As %String;

Property PhoneList As %List;

Index idxP On PhoneList(ELEMENTS);

ClassMethod PhoneListBuildValueArray(value, ByRef array) As %Status
{
  if value="" {
    s array(0)=value
  }else{
    s ptr=0
    while $listnext(value,ptr,item){
      s array(ptr)=item
    }
  }
  q $$$OK
}

}

插入数据

insert into Test.Person3 (Name,PhoneList) select 'a', $LISTBUILD('111-111-1111','111-222-1111') 

insert into Test.Person3 (Name,PhoneList) select 'b', $LISTBUILD('222-111-1111','222-222-1111') 

insert into Test.Person3 (Name,PhoneList) select 'c', $LISTBUILD('333-111-1111') 

insert into Test.Person3 (Name,PhoneList) select 'd', $LISTBUILD('333-111-1111','222-222-1111') 

数据和索引保存为

USER>zw ^Test.Person3D
^Test.Person3D=4
^Test.Person3D(1)=$lb("","a",$lb("111-111-1111","111-222-1111"))
^Test.Person3D(2)=$lb("","b",$lb("222-111-1111","222-222-1111"))
^Test.Person3D(3)=$lb("","c",$lb("333-111-1111"))
^Test.Person3D(4)=$lb("","d",$lb("333-111-1111","222-222-1111"))
 
USER>zw ^Test.Person3I
^Test.Person3I("idxP","111-111-1111",1)=""
^Test.Person3I("idxP","111-222-1111",1)=""
^Test.Person3I("idxP","222-111-1111",2)=""
^Test.Person3I("idxP","222-222-1111",2)=""
^Test.Person3I("idxP","222-222-1111",4)=""
^Test.Person3I("idxP","333-111-1111",3)=""
^Test.Person3I("idxP","333-111-1111",4)=""

同样可以使用下面的SQL语句查找包含电话333-111-1111的姓名

select Name from test.person2 where for some %element(phonelist) (%value = '333-111-1111')

4 使用 List Of、Array Of 保存

不需要定义propertynameBuildValueArray函数

Class Test.Person4 Extends %Persistent
{

Property Name As %String;

Property PhoneList As list Of %String;

Index idxP On PhoneList(ELEMENTS);

}

使用同样的方式插入数据

insert into Test.Person4 (Name,PhoneList) select 'a', $LISTBUILD('111-111-1111','111-222-1111') 
insert into Test.Person4 (Name,PhoneList) select 'b', $LISTBUILD('222-111-1111','222-222-1111') 
insert into Test.Person4 (Name,PhoneList) select 'c', $LISTBUILD('333-111-1111') 
insert into Test.Person4 (Name,PhoneList) select 'd', $LISTBUILD('333-111-1111','222-222-1111')  

数据和索引保存为

USER>zw ^Test.Person4D
^Test.Person4D=4
^Test.Person4D(1)=$lb("","a",$lb("111-111-1111","111-222-1111"))
^Test.Person4D(2)=$lb("","b",$lb("222-111-1111","222-222-1111"))
^Test.Person4D(3)=$lb("","c",$lb("333-111-1111"))
^Test.Person4D(4)=$lb("","d",$lb("333-111-1111","222-222-1111"))
 
USER>zw ^Test.Person4I
^Test.Person4I("idxP"," 111-111-1111",1)=""
^Test.Person4I("idxP"," 111-222-1111",1)=""
^Test.Person4I("idxP"," 222-111-1111",2)=""
^Test.Person4I("idxP"," 222-222-1111",2)=""
^Test.Person4I("idxP"," 222-222-1111",4)=""
^Test.Person4I("idxP"," 333-111-1111",3)=""
^Test.Person4I("idxP"," 333-111-1111",4)=""

使用同样的SQL查询可以得到结果

select Name from test.person4 where for some %element(Phonelist) (%value = '333-111-1111')

 

引申话题:针对日期字段的索引

日期格式通常是yyyy-mm-dd,我们经常要求按照某年或者某月查询数据,我们可以使用propertynameBuildValueArray函数设定保存的索引方式实现这个目的

Class Test.Person5 Extends %Persistent
{

Property Name As %String;

Property DOB As %Date;

Index idxD On (DOB(KEYS), DOB(ELEMENTS));

ClassMethod DOBBuildValueArray(value, ByRef array) As %Status
{
  if value="" {
    s array(0)=value
  }else{
    s d=$zd(value,3)
    s array("yy")=+$p(d,"-",1)
    s array("mm")=+$p(d,"-",2)
    s array("dd")=+$p(d,"-",3)

  }
  q $$$OK
}

}

插入数据

insert into Test.Person5 (Name,DOB) 
  select 'a', {d '2000-01-01'} union all
  select 'b', {d '2000-01-02'} union all
  select 'c', {d '2000-02-01'} union all
  select 'd', {d '2001-01-02'} union all
  select 'e', {d '2001-01-01'} union all
  select 'f', {d '2001-02-01'}

查看数据以及索引保存的内容

USER>zw ^Test.Person5D
^Test.Person5D=6
^Test.Person5D(1)=$lb("","a",58074)
^Test.Person5D(2)=$lb("","b",58075)
^Test.Person5D(3)=$lb("","c",58105)
^Test.Person5D(4)=$lb("","d",58441)
^Test.Person5D(5)=$lb("","e",58440)
^Test.Person5D(6)=$lb("","f",58471)
 
USER>zw ^Test.Person5I
^Test.Person5I("idxD","dd",1,1)=""
^Test.Person5I("idxD","dd",1,3)=""
^Test.Person5I("idxD","dd",1,5)=""
^Test.Person5I("idxD","dd",1,6)=""
^Test.Person5I("idxD","dd",2,2)=""
^Test.Person5I("idxD","dd",2,4)=""
^Test.Person5I("idxD","mm",1,1)=""
^Test.Person5I("idxD","mm",1,2)=""
^Test.Person5I("idxD","mm",1,4)=""
^Test.Person5I("idxD","mm",1,5)=""
^Test.Person5I("idxD","mm",2,3)=""
^Test.Person5I("idxD","mm",2,6)=""
^Test.Person5I("idxD","yy",2000,1)=""
^Test.Person5I("idxD","yy",2000,2)=""
^Test.Person5I("idxD","yy",2000,3)=""
^Test.Person5I("idxD","yy",2001,4)=""
^Test.Person5I("idxD","yy",2001,5)=""
^Test.Person5I("idxD","yy",2001,6)=""

执行下面 SQL 可以显示所有2月出生的信息

select * from Test.Person5 where for some %element(DOB) (%key='mm' and %value = 2)

 

 

这篇文章源自 这里,作者 Vitaliy Serdtsev

 

 

 

00
1 0 0 25
Log in or sign up to continue