背景
我工作的主要应用程序主要基于 InterSystems 的 MUMPS 式 Caché 数据库引擎。一切都存储在全局数组中。从系统中获取数据以进行外部报告的范围从简单的痛苦到极其缓慢和痛苦。
Caché 为数据库提供了一个 ODBC 驱动程序,但除非涉及的全局数组碰巧由选择标准键入,否则它会求助于扫描,一个简单的查询将需要数小时才能运行。就规模而言,整个 Caché 生产命名空间约为 100GB。在这些情况下,我可以编写比 ODBC 驱动程序更快地提取数据的 ObjectScript(Intersystems 的 MUMPS 方言)程序。
我认为部分问题是应用程序供应商不使用 Caché 的对象持久性支持,而是将 SQL 表定义为全局数组的外观,并且它通常不适用于批处理请求。
我在 MS SQL Server 中构建了一个报告数据库,它提取最常见的数据(价值 2.5GB),即使它必须扫描每个表,所有结果也会在 3 秒内返回。不幸的是,刷新数据需要很长时间,所以我只能每周进行一次完全刷新,每天进行一次主动刷新。这足以满足大多数需求,但我想做得更好。
我在 Windows 7 和 Windows Server 2008 R2 上使用 Caché 2007、SQL Server 2008 R2、VS2010。
问题范围
我需要一种将源 Caché 数据库中的实时数据与 SQL Server 上的其他数据集成的方法。我希望能够将视图或表值函数集成到 SQL 查询中,并让它从源数据库中提取实时数据。
实时数据必须在 SQL Server 中可用以进行处理。使用辅助应用程序执行此操作将是一个巨大的痛苦,并且不适用于只希望通过 ODBC 推送查询并获得正确格式的最终数据集的报告工具。
我知道有一些方法可以将数据导入 SQL Server 或完成我想做的相同的一般事情。这不是这个问题的目的。
数据需要来自在 Caché 上运行的 ObjectScript 程序,因为并非我需要的所有数据都通过 SQL 定义的表公开,并且我获得了使性能可用于 ObjectScript 所需的控制。
我正在寻找有关任何新选项的建议,或者我如何改进我尝试或考虑过的选项之一,或者这些方法的其他优点或缺点。
到目前为止我尝试过的
这个项目是一个令人沮丧的练习,我研究过的每条有希望的途径要么很糟糕,要么由于某种原因不起作用。通常原因是对 SQLCLR 程序集的一些不必要的限制。
通过链接服务器通过 InterSystem 的 Caché ODBC 驱动程序提取所有内容。如果 SQL Server 无法将条件推送到远程服务器或必须在本地执行连接,则它通常会求助于扫描。对任何重要表的扫描都需要花费数小时并且是不可接受的。此外,Caché 中的 SQL 表定义错误地定义了许多列的长度;SQL Server 不喜欢这样并中止查询。请参阅这个 SO 问题。我无法更改表定义,供应商认为这不是问题,因为它可以与 MS Access 一起使用。
按需使用 OPENQUERY。这在某种程度上有效,但我仍然可以从上一项中遇到列长度问题,并且无法参数化 OPENQUERY 查询,因此提取上下文数据非常无用。
使用 SQLCLR 通过 CLR 表值函数调用 ODBC 数据提供程序。这解决了参数化和数据长度问题,尽管它确实需要我在每次需要新数据时定义或修改一个函数。不幸的是,并不是我感兴趣的所有数据元素都可以通过 SQL 获得。对于某些事情,我需要直接访问全局数组。
Intersystems 提供了一个ActiveX 控件,允许您在服务器上通过 TCP 运行 ObjectScript 程序并获得结果。这在独立的 C# 应用程序中效果很好,但是一旦我尝试从 SQLCLR 程序集建立连接,我就会收到一个荒谬的 URI 错误:
在执行用户定义的例程或聚合“GetActiveAccounts”期间发生 .NET Framework 错误:System.UriFormatException:无效的 URI:URI 为空。System.UriFormatException: 在 System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind) 在 System.ComponentModel.Design.RuntimeLicenseContext.GetLocalPath(String fileName) 在 System.ComponentModel. Design.RuntimeLicenseContext.GetSavedLicenseKey(Type type, Assembly resourceAssembly) at System.ComponentModel.LicenseManager.LicenseInteropHelper.GetCurrentContextInfo(Int32& fDesignTime, IntPtr& bstrKey, RuntimeTypeHandle rth) at FacsAccess.GetActiveAccounts.Client.connect() at FacsAccess.GetActiveAccounts.Client.. FacsAccess.GetActiveAccounts.E1.GetEnumerator() 处的 ctor()
请参阅这个未回答的 SO 问题。网上还有其他帖子,但似乎没有人知道。这是一个非常简单的 C++ DLL 的 COM 包装器;它对许可没有任何作用,也没有理由进入托管许可库。我想知道这是否是某种样板文件,它试图获取一个没有名称的程序集的名称,因为它已加载到 SQL 数据库中。
Intersystems 还提供了更直接的非托管接口,但这些接口都是 C++,我无法通过 P/Invoke 使用它,也无法在 SQLCLR 中加载 C++/CLI 混合模式不纯程序集。
我考虑过但似乎有点糟糕的选项
我考虑过通过 SQL Server 的 COM 支持来尝试 ActiveX 控件,但这非常慢而且非常麻烦。
我可以创建一个进程外服务来代理流量,但我不能使用来自 SQLCLR 的 .NET 远程处理,而且你不应该使用 WCF,而且对于这样一个简单的接口来说,它真的很重。我宁愿推出自己的 IPC 接口。
我可以为 VisM 或 CacheDirect 接口编写某种带有 C 风格接口的额外非托管包装器,并通过 P/Invoke 访问它。
看起来这不应该那么难,但它真的把我逼到了墙角,我需要一些观点。