Intersystems缓存 - 保持对象代码,以确保数据符合对象定义

问题描述:

我是新来使用intersytems缓存和面临的问题,我正在查询存储在缓存中的数据,暴露的类似乎不准确地代表数据基础系统。存储在全局变量中的数据几乎总是大于目标代码中定义的数据。Intersystems缓存 - 保持对象代码,以确保数据符合对象定义

因此,我经常遇到像下面这样的错误。

Msg 7347, Level 16, State 1, Line 2 
OLE DB provider 'MSDASQL' for linked server 'cache' returned data that does not match expected data length for column '[cache]..[namespace].[tablename].columname'. The (maximum) expected data length is 5, while the returned data length is 6. 

有没有人有实现某种类型的质量管理过程,以确保对象定义(SQL映射)在这样保持远离他们能够容纳这是在全局进行持久化数据的经验吗?

Property columname As %String(MAXLEN = 5, TRUNCATE = 1) [ Required, SqlColumnNumber = 2, SqlFieldName = columname ]; 

在这个特定的示例中,系统具有为5的最大LEN定义的列,但是存储在系统中的数据是长6个字符。

如何主动监控和修复此类情况。

/*

我没有在高速缓存

*/

+0

@psr - 澄清修复:我的意思是确定大于sql表定义的数据并重新定义sql表。或者最低限度,开始向系统管理员提供指标,指明预期数据类型(类型和长度)中的数据百分比。 – 2012-01-17 18:08:56

+0

如果您喜欢答案,则可以将其标记为已接受。 – psr 2012-01-19 23:43:17

这不是完全清楚什么是“监测和修复”将意味着你的,但是:

多少控制你有在数据库端?使用数据类型的LogicalToODBC方法,高速缓存运行代码以实现从全局到ODBC的数据类型转换。如果将属性类型从%String更改为您自己的类AppropriatelyNamedString,则可以重写该方法以自动截断。如果这就是你想要做的。可以使用%Library.CompiledClass类以编程方式更改所有%String属性类型。

也可以在Cache内运行代码来查找属性超过(理论上)最大长度的记录。这显然需要全表扫描。甚至可以将该代码作为存储过程公开。

再一次,我不知道你到底在做什么,但这些都是一些选择。他们可能需要更深入Cache方面比你更喜欢。

至于防止不良数据摆在首位,没有一般的答案。缓存允许程序员直接写入全局变量,绕过任何对象或表定义。如果发生这种情况,那么这样做的代码必须直接修复。

编辑:这是检测不良数据的代码。如果你在做有趣的事情,它可能不起作用,但它对我有用。这有点难看,因为我不想将它分解成方法或标签。这意味着从命令提示符运行,所以它可能必须根据您的目的进行修改。

{ 
    S ClassQuery=##CLASS(%ResultSet).%New("%Dictionary.ClassDefinition:SubclassOf") 
    I 'ClassQuery.Execute("%Library.Persistent") b q 
    While ClassQuery.Next(.sc) { 
    If $$$ISERR(sc) b Quit 
     S ClassName=ClassQuery.Data("Name") 
     I $E(ClassName)="%" continue 
     S OneClassQuery=##CLASS(%ResultSet).%New(ClassName_":Extent") 
     I '$IsObject(OneClassQuery) continue //may not exist 
     try { 
     I 'OneClassQuery.Execute() D OneClassQuery.Close() continue 
     } 
     catch 
     { 

      D OneClassQuery.Close() 
      continue 
     } 
     S PropertyQuery=##CLASS(%ResultSet).%New("%Dictionary.PropertyDefinition:Summary") 
     K Properties 
     s sc=PropertyQuery.Execute(ClassName) I 'sc D PropertyQuery.Close() continue 
     While PropertyQuery.Next() 
     { 
      s PropertyName=$G(PropertyQuery.Data("Name")) 
      S PropertyDefinition="" 
      S PropertyDefinition=##CLASS(%Dictionary.PropertyDefinition).%OpenId(ClassName_"||"_PropertyName) 
      I '$IsObject(PropertyDefinition) continue 
      I PropertyDefinition.Private continue 
      I PropertyDefinition.SqlFieldName=""  
      { 
       S Properties(PropertyName)=PropertyName 
      } 
      else 
      { 
       I PropertyName'="" S Properties(PropertyDefinition.SqlFieldName)=PropertyName 
      } 
     } 
     D PropertyQuery.Close() 

     I '$D(Properties) continue 

     While OneClassQuery.Next(.sc2) { 
      B:'sc2 
      S ID=OneClassQuery.Data("ID") 
      Set OneRowQuery=##class(%ResultSet).%New("%DynamicQuery:SQL") 
      S sc=OneRowQuery.Prepare("Select * FROM "_ClassName_" WHERE ID=?") continue:'sc 
      S sc=OneRowQuery.Execute(ID) continue:'sc 
      I 'OneRowQuery.Next() D OneRowQuery.Close() continue 
      S PropertyName="" 
      F S PropertyName=$O(Properties(PropertyName)) Q:PropertyName="" d 
      . S PropertyValue=$G(OneRowQuery.Data(PropertyName)) 
      . I PropertyValue'="" D 
      .. S PropertyIsValid=$ZOBJClassMETHOD(ClassName,Properties(PropertyName)_"IsValid",PropertyValue) 
      .. I 'PropertyIsValid W !,ClassName,":",ID,":",PropertyName," has invalid value of "_PropertyValue 
      .. //I PropertyIsValid W !,ClassName,":",ID,":",PropertyName," has VALID value of "_PropertyValue 
      D OneRowQuery.Close() 
     } 
     D OneClassQuery.Close() 
    } 
    D ClassQuery.Close() 
} 

最简单的办法是增加MAXLEN参数,以6或更大的创建这些对象定义。 Caché只在保存时执行MAXLEN和TRUNCATE。在其他Caché代码中,这通常很好,但不幸的是,ODBC客户端往往期望更严格地执行此操作。另一种选择是写你的SQL SELECT一样LEFT(列名,5)...

我使用所有的集成服务包,例如最简单的方法是创建所有为nvarchar或char数据投射到正确长度的查询。通过这种方式,我的数据不会因截断而失败。 可选: 首先运行就像一个查询:从cachenamespace.tablename.mycolumnName SELECT MAX(DATALENGTH(mycolumnName))

新的查询:SELECT CAST(mycolumnname为varchar(6))作为mycolumnname, 转换(VARCHAR( 8000),memo_field)AS memo_field from cachenamespace.tablename.mycolumnName

您获取数据的痛苦会减轻但不会被消除。 如果您使用任何类型的oledb提供程序,或者如果您在SQL Server中使用OPENQUERY,则 中的强制转换必须发生在发送到Intersystems CACHE db的查询中,而不是在从内部OPENQUERY中检索数据的外部查询中。

+0

一个合理但强力的解决方案。我已经进入了一个新的职位,因此不再处理这个问题,但是感谢您的时间和协助。就像所有其他选项一样,在哪里选择专业人士以及在哪里采取这些缺点。当我们这样做时,将所有内容定义为varchar(8000),这意味着我们将在内存分配和ETL性能方面失去很多效率。但在这种情况下,我认为它是可以的,因为选择是...高效,失败,或者完全没有效率,但完成工作。后者更具价值。 – 2017-09-22 16:08:27