4

バックグラウンド

私が働いている主なアプリケーションは、InterSystemsのMUMPS風のCachéデータベースエンジンに大きく基づいています。すべてがグローバル配列に格納されます。外部レポートのためにシステムからデータを取得することは、単に苦痛であるものから、ひどく遅くて苦痛であるものまでさまざまです。

Cachéはデータベース用のODBCドライバーを提供しますが、関連するグローバルアレイがたまたま選択基準によってキー設定されていない限り、スキャンに頼り、単純なクエリの実行には数時間かかります。規模については、Caché実稼働名前空間全体が約100GBです。このような場合、ODBCドライバーよりもはるかに高速にデータをプルするObjectScript(IntersystemsのMUMPSの方言)プログラムを作成できます。

問題の一部は、アプリケーションベンダーがCachéのオブジェクト永続性サポートを使用せず、代わりにグローバル配列のファサードとして定義されたSQLテーブルを持っていることであり、バッチ要求ではうまく機能しないことがよくあります。

最も一般的なデータ(2.5GB相当)をプルするレポートデータベースをMS SQL Serverで構築しました。すべてのテーブルをスキャンする必要がある場合でも、すべての結果が3秒以内に返されます。残念ながら、データの更新には長い時間がかかるため、完全な更新は週に1回、アクティブな更新は1日1回しか実行できません。これはほとんどのニーズには十分ですが、もっとうまくやりたいと思っています。

私はCaché2007、SQL Server 2008 R2、Windows7およびWindowsServer2008R2上のVS2010を使用しています。

質問の範囲

ソースCachéデータベースのライブデータをSQLServer上の他のデータと統合する方法が必要です。ビューまたはテーブル値関数をSQLクエリに統合し、ソースデータベースからライブデータをプルできるようにしたい。

  • ライブデータは、SQLServer内で処理できる必要があります。セカンダリアプリケーションでそれを行うことは非常に苦痛であり、ODBCを介してクエリをプッシュし、適切な形式で最終的なデータセットを取得することを期待するレポートツールでは機能しません。

  • SQL Serverにデータを取り込む方法や、私がやりたいのと同じ一般的なことを実行する方法があることを理解しています。それはこの質問が何であるかではありません。

  • 必要なすべてのデータがSQL定義のテーブルを介して公開されるわけではなく、ObjectScriptでパフォーマンスを使用可能にするために必要なコントロールを取得するため、データはCachéで実行されるObjectScriptプログラムから取得する必要があります。

  • 新しいオプションや、試したり検討したりしたオプションの1つ、またはそれらのアプローチの他の長所や短所を改善する方法についてのアドバイスを探しています。

私がこれまでに試したこと

このプロジェクトは、私が調べたそれぞれの有望な道がひどいものであるか、何らかの理由で機能しないという欲求不満の練習でした。多くの場合、その理由はSQLCLRアセンブリに対する不要な制限です。

  1. リンクサーバーを介してInterSystemのCachéODBCドライバーを介してすべてをプルします。SQL Serverは、条件をリモートサーバーにプッシュできない場合、またはローカルで結合を実行する必要がある場合に、スキャンに頼ることがよくあります。重要なテーブルのスキャンには何時間もかかり、受け入れられません。また、多くの列の長さが、CachéのSQLテーブル定義によって誤って定義されています。SQL Serverはそれを嫌い、クエリを中止します。このSOの質問を参照してください。テーブルの定義を変更することはできません。ベンダーは、MS Accessで動作するため、問題はないと考えています。

  2. オンデマンドでOPENQUERYを使用します。これはある程度機能しますが、前の項目の列の長さの問題が発生する可能性があり、OPENQUERYクエリをパラメーター化する方法がないため、コンテキストデータを取得するのはかなり役に立ちません。

  3. SQLCLRを使用して、CLRテーブル値関数を介してODBCデータプロバイダーを呼び出します。これにより、パラメーター化とデータ長の問題が処理されますが、新しいデータが必要になるたびに関数を定義または変更する必要があります。残念ながら、私が興味を持っているすべてのデータ要素がSQLで利用できるわけではありません。いくつかのことについては、グローバルアレイに直接アクセスする必要があります。

  4. Intersystemsは、サーバー上でTCPを介してObjectScriptプログラムを実行し、結果を取得できるActiveXコントロールを提供します。これはスタンドアロンのC#アプリではうまく機能しますが、SQLCLRアセンブリから接続しようとすると、ばかげたURIエラーが発生します。

    ユーザー定義ルーチンまたはアグリゲート「GetActiveAccounts」の実行中に.NETFrameworkエラーが発生しました:System.UriFormatException:無効なURI:URIが空です。System.UriFormatException:at System.Uri.CreateThis(String uri、Boolean dontEscape、UriKind uriKind)at System.Uri..ctor(String uriString)at System.ComponentModel.Design.RuntimeLicenseContext.GetLocalPath(String fileName)atSystem.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 FacsAccess.GetActiveAccounts.E1.GetEnumerator()のctor()

    この未回答のSOの質問を参照してください。ネット上には他にも投稿がありますが、手がかりはないようです。これは、C++DLLに対する非常に単純なCOMラッパーです。ライセンスに関しては何もしておらず、管理されたライセンスライブラリに含まれる理由はありません。これは、SQLデータベースにロードされているために名前がないアセンブリの名前を取得しようとしている、ある種の定型文であるのではないかと思います。

  5. Intersystemsは、より直接的なアンマネージインターフェイスも提供しますが、これらのインターフェイスはすべてC ++であり、P / Invokeでは使用できず、SQLCLRでC ++/CLI混合モードの不純なアセンブリをロードできません。

私が検討したオプションですが、ちょっとひどいようです

  1. SQL ServerのCOMサポートを介してActiveXコントロールを試すことを検討しましたが、それは非常に遅く、非常に面倒です。

  2. トラフィックをプロキシするためのアウトプロセスサービスを作成することはできますが、SQLCLRからの.NETリモーティングを使用できません。また、WCFを使用することは想定されておらず、このような単純なインターフェイスではとにかく非常に重いものになります。私はすぐに自分のIPCイン​​ターフェイスをロールバックします。

  3. VisMまたはCacheDirectインターフェイス用のCスタイルのインターフェイスを使用して、ある種の追加のアンマネージラッパーを記述し、P/Invokeを介してそれにアクセスすることができます。

これはそれほど難しいことではないようですが、それは本当に私を壁に押し上げており、私はいくつかの視点が必要です。

4

1 に答える 1

2

リンクサーバーを介してODBCを使用できると思います。このサーバーは、ODBCドライバーに表示され、結果セットを返すキャッシュデータベース上のストアドプロシージャにアクセスしますが、SQLを使用して実装されていません。

このようなストアドプロシージャを作成してODBC経由でアクセスできることは100%確信していますが、SQLサーバーからリンクサーバーとしてアクセスしようとしたことはありません。リンクサーバーが機能しない場合でも、Active XコントロールやCacheDirectではなく、IntersystemのODBCドライバーを介してアクセスすることをお勧めします。

この質問に対するそのような手順の例があります。

リンクが切れた場合のコードは次のとおりです。

Query OneGlobal(GlobalName As %String) As %Query(ROWSPEC = "NodeValue:%String,Sub1:%String,Sub2:%String,Sub3:%String,Sub4:%String,Sub5:%String,Sub6:%String,Sub7:%String,Sub8:%String,Sub9:%String") [SqlProc]
{
}

ClassMethod OneGlobalExecute(ByRef qHandle As %Binary, GlobalName As %String) As %Status
{
    S qHandle="^"_GlobalName
    Quit $$$OK
}

ClassMethod OneGlobalClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = OneGlobalExecute ]
{
    Quit $$$OK
}

ClassMethod OneGlobalFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = OneGlobalExecute ]
{

    S Q=qHandle  
    S Q=$Q(@Q)  b  
    I Q="" S Row="",AtEnd=1 Q $$$OK
    S Depth=$QL(Q)
    S $LI(Row,1)=$G(@Q)
    F I=1:1:Depth S $LI(Row,I+1)=$QS(Q,I)
    F I=Depth+1:1:9 S $LI(Row,I+1)=""
    S AtEnd=0
    S qHandle=Q
    Quit $$$OK
}

参考までに、これはすべてのグローバルからのすべてのデータを公開するため、本番環境で使用する例ではありません。

ただし、とにかくデータを直接取得するためのキャッシュオブジェクトスクリプトを作成する準備ができているように聞こえました。これは、そのためのテンプレートです。理解しておくべき主なことは、qHandleは呼び出しごとにODBCドライバーによって返されるため、状態を格納するために使用できるということです。状態が多い場合は、qHandleを「実際の」状態を保持する一時的なグローバルへの整数インデックスにし、closeメソッドでクリーンアップします。

パフォーマンスが心配なので、

MyQueryFetchRows (ByRef qHandle As %Binary, FetchCount As %Integer = 0, ByRef RowSet As %List, ByRef ReturnCount As %Integer, ByRef AtEnd As %Integer) As %Status

メソッド-詳細については、%Library.Queryのドキュメントを参照してください。

これを本当にストアドプロシージャではなく(読み取り専用)テーブルとしてODBCに表示する必要がある場合は、それが可能かもしれないと思いますが、任意のストアドプロシージャを読み取りとして公開できるかどうかを確認しようとしたことはありません。テーブルだけで、それがどれほど簡単か、または実際に常に可能かどうかはわかりません。

于 2012-06-20T02:51:07.937 に答える