9

崖:stardate / enddate / pagesize / pagenumパラメーターをカプセル化するために標準の「フィルター」タイプをストアドプロシージャに渡すための既知のパターンはありますか?

この質問の正しい場所がわかりません。共通のフィルタリングパラメータ(startdate、enddate、pagenumber、pagesize、intのリストなど)をカプセル化するストアドプロシージャにフィルタリングオブジェクトパラメータを渡すというアイデアを模索しています。この理由は、同様のパラメーターの量を減らし、プロシージャ全体に広がる定型SQLを減らすためです。これにより、最初から各手順のより標準的なインターフェイスと開始点が得られます。私はそのトピックに関する多くの情報を見つけることができませんでした。

私が気付いたパターン-ほとんどのSPを最初に構築するとき、それらはwhere句で使用される単一のidパラメーターで始まります。後で、日付範囲パラメーター(開始日、終了日、またはダイナミックレンジ「ytd、mtd、dtd」)のパラメーターを追加する必要がある場合があります。データセットが十分に大きい場合は、サーバー側のページングにpagesize/pagenumを導入する必要がある場合もあります。しばらくすると、単一のIDではなくIDのリストの結果が必要であることに気付く場合があるため、IDをエンベロープするためにCSVまたはXMLパラメーターを追加します。

最終的に、多くのストアドプロシージャは、これらの標準的なフィルタリングパラメータを処理するために、多くの類似したボイラープレートと(うまくいけば)同一のパラメータで終わります。カプセル化されたフィルターオブジェクトパラメーターをプロシージャに渡すための既知のパターンを調査しようとしています。これは、理想的にはC#側で強く型付けされます。これは、(レポート固有のクエリパラメータに加えて)すべて同じフィルタリングオプションを必要とするレポートを強化するプロシージャのグループを管理する場合に特に役立ちます。

私の目標は、必要なパラメーターの数をWHERE句に必要な最小限に減らし、汎用フィルタリングオプションをプロシージャに渡し、プロシージャ内でそれらの値を使用するための標準メカニズムを作成することです。これは、XML、CLR、またはUDTパラメーターを介してどのように実現できますか?

この質問のコンテキストでは、C#2.0からADO.Net経由でSQLServer2008を使用しています。残念ながら、現時点ではLINQ / EFはこのプロジェクトのオプションではないため、既存のRDBMSを使用する必要があります。テクノロジーの変更を必要とする既知のパターンがある場合、私はそれについて聞きたいと思います。

編集:これまでの返信に感謝します。50ポイントの報奨金を追加しました。これをさらに数日間実行して、さらに議論を促進しようとします。私の質問が十分に明確でない場合は、コメントを残してください。

4

5 に答える 5

5

個人的には、削減する必要のないものを考えすぎたり、削減しようとしていると思います。ストアドプロシージャのパラメータをそのままにしておくか、パラメータのセットをコマンドオブジェクトに追加できる基本クラスとヘルパー関数を作成することをお勧めします。

ただし、そうは言っても、私はあなたの質問に対する解決策を投げかけ、それがあなたのニーズに合うかどうかを確認します。

TSQLユーザー定義型を使用することをお勧めします。1つ以上のタイプを作成します。1つは日付範囲用で、もう1つはページングと並べ替え用です。複数行のデータをストアドプロシージャに渡すために、同様のプロセスを使用します。(このコードの一部は、すでに作成したコードを変更しているだけで、かなり長い間DataTableフィールドを操作していないため、少し調整する必要がある場合があります。)

最終的には、これは、アプリケーションメソッドと一致するストアドプロシージャのパラメータのリストを短縮することだけです。ストアドプロシージャは、テーブル変数の情報を抽出または結合する役割を果たします。以下にリストされているクラスは、これらのパラメーターを.NETアプリケーション側で強く型付けされたままにする機能を提供します。

if not exists (select * from INFORMATION_SCHEMA.DOMAINS where DOMAIN_SCHEMA = 'dbo' and DOMAIN_NAME = 'DateRange' and DATA_TYPE = 'table type')
begin

    create type dbo.DateRange as table 
    (
        StartDate datetime2 null
        ,EndDate datetime2 null
    )

end
go


if not exists (select * from INFORMATION_SCHEMA.DOMAINS where DOMAIN_SCHEMA = 'dbo' and DOMAIN_NAME = 'Paging' and DATA_TYPE = 'table type')
begin

    create type dbo.Paging as table 
    (
        PageNumber int null
        ,PageSize int null
        ,SortField sysname null
        ,SortDirection varchar(4) null
    )

end
go

SQLユーザー定義型は、.NETアプリケーションでは強く型付けされたオブジェクトとして表すことができます。基本クラスから始めます。

    Imports System
    Imports System.Data
    Imports System.Data.SqlClient
    Imports System.Runtime.Serialization


    Namespace SqlTypes

        <Serializable()> _
        <System.ComponentModel.DesignerCategory("Code")> _
        Public MustInherit Class SqlTableTypeBase
            Inherits DataTable

            Public Sub New()

                MyBase.New()
                Initialize()

            End Sub


            Public Sub New(ByVal tableName As String)

                MyBase.New(tableName)
                Initialize()

            End Sub


            Public Sub New(ByVal tableName As String, ByVal tableNamespace As String)

                MyBase.New(tableName, tableNamespace)
                Initialize()

            End Sub


            Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)

                MyBase.New(info, context)

            End Sub


            ''' <summary>
            ''' Implement this method to create the columns in the data table to match the SQL server user defined table type
            ''' </summary>
            ''' <remarks></remarks>
            Protected MustOverride Sub Initialize()


            Public Function CreateParameter(parameterName As String) As SqlParameter

                Dim p As New SqlParameter(parameterName, SqlDbType.Structured)
                p.Value = Me

                Return p

            End Function

        End Class

    End Namespace

SQLタイプの実装を作成します。

Imports System
Imports System.Data
Imports System.Runtime.Serialization


Namespace SqlTypes

    <Serializable()> _
    <System.ComponentModel.DesignerCategory("Code")> _
    Public Class DateRange
        Inherits SqlTableTypeBase

        Public Sub New()

            MyBase.New()

        End Sub


        Public Sub New(ByVal tableName As String)

            MyBase.New(tableName)

        End Sub


        Public Sub New(ByVal tableName As String, ByVal tableNamespace As String)

            MyBase.New(tableName, tableNamespace)

        End Sub


        Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)

            MyBase.New(info, context)

        End Sub


        'TODO: throw some more overloaded constructors in here...

        Public Sub New(startDate As DateTime?, endDate As DateTime?)

            MyBase.New()

            Me.StartDate = startDate
            Me.EndDate = endDate

        End Sub


        Public Property StartDate As DateTime?
            Get
                Return CType(Me.Rows(0)(0), DateTime?)
            End Get
            Set(value As DateTime?)
                Me.Rows(0)(0) = value
            End Set
        End Property


        Public Property EndDate As DateTime?
            Get
                Return CType(Me.Rows(0)(1), DateTime?)
            End Get
            Set(value As DateTime?)
                Me.Rows(0)(1) = value
            End Set
        End Property


        Protected Overrides Sub Initialize()

            Me.Columns.Add(New DataColumn("StartDate", GetType(DateTime?)))
            Me.Columns.Add(New DataColumn("EndDate", GetType(DateTime?)))

            Me.Rows.Add({Nothing, Nothing})

        End Sub

    End Class

End Namespace

と:

Imports System
Imports System.Data
Imports System.Runtime.Serialization


Namespace SqlTypes

    <Serializable()> _
    <System.ComponentModel.DesignerCategory("Code")> _
    Public Class Paging
        Inherits SqlTableTypeBase

        Public Sub New()

            MyBase.New()

        End Sub


        Public Sub New(ByVal tableName As String)

            MyBase.New(tableName)

        End Sub


        Public Sub New(ByVal tableName As String, ByVal tableNamespace As String)

            MyBase.New(tableName, tableNamespace)

        End Sub


        Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)

            MyBase.New(info, context)

        End Sub


        'TODO: throw some more overloaded constructors in here...


        Public Sub New(pageNumber As Integer?, pageSize As Integer?)

            MyBase.New()

            Me.PageNumber = pageNumber
            Me.PageSize = pageSize

        End Sub


        Public Sub New(sortField As String, sortDirection As String)

            MyBase.New()

            Me.SortField = sortField
            Me.SortDirection = sortDirection

        End Sub


        Public Sub New(pageNumber As Integer?, pageSize As Integer?, sortField As String, sortDirection As String)

            Me.New(pageNumber, pageSize)

            Me.SortField = sortField
            Me.SortDirection = sortDirection

        End Sub


        Public Property PageNumber As Integer?
            Get
                Return CType(Me.Rows(0)(0), Integer?)
            End Get
            Set(value As Integer?)
                Me.Rows(0)(0) = value
            End Set
        End Property


        Public Property PageSize As Integer?
            Get
                Return CType(Me.Rows(0)(1), Integer?)
            End Get
            Set(value As Integer?)
                Me.Rows(0)(1) = value
            End Set
        End Property


        Public Property SortField As String
            Get
                Return CType(Me.Rows(0)(2), String)
            End Get
            Set(value As String)
                Me.Rows(0)(2) = value
            End Set
        End Property


        Public Property SortDirection As String
            Get
                Return CType(Me.Rows(0)(3), String)
            End Get
            Set(value As String)
                Me.Rows(0)(3) = value
            End Set
        End Property


        Protected Overrides Sub Initialize()

            Me.Columns.Add(New DataColumn("PageNumber", GetType(Integer?)))
            Me.Columns.Add(New DataColumn("PageSize", GetType(Integer?)))
            Me.Columns.Add(New DataColumn("SortField", GetType(String)))
            Me.Columns.Add(New DataColumn("SortDirection", GetType(String)))

            Me.Rows.Add({Nothing, Nothing, Nothing, Nothing})

        End Sub

    End Class

End Namespace

オブジェクトをインスタンス化し、コンストラクターで値を設定してから、オブジェクトからパラメーターを取得し、それをストアドプロシージャコマンドオブジェクトのパラメーターコレクションに追加します。

cmd.Parameters.Add(New DateRange(startDate, endDate).CreateParameter("DateRangeParams"))
cmd.Parameters.Add(New Paging(pageNumber, pageSize).CreateParameter("PagingParams"))

編集 この答えは強い型付けを中心に展開しているので、メソッドシグネチャに強い​​型付けの例を追加する必要があると思いました。

'method signature with UDTs
Public Function GetMyReport(customParam1 as Integer, timeFrame as DateRange, pages as Paging) as IDataReader

'method signature without UDTs
Public Function GetMyReport(customParam1 as Integer, startDate as DateTime, endDate as DateTime, pageNumber as Integer, pageSize as Integer)
于 2012-05-19T02:20:44.813 に答える
3

私たちもこの問題に直面しました。データベースのProgrammability/Typeセクションにユーザー定義のテーブルタイプを作成することで解決しました。

ユーザー定義のテーブルタイプSQLServer2008 R2

このテーブルは、さまざまなストアドプロシージャや関数を呼び出すときに、すべてのアプリケーションで使用されます。applクライアント側(vb.net 2010)でプログラムでこのテーブルに入力し、パラメーターとして渡します。ストアドプロシージャでは、テーブルを読み取り、必要な処理、フィルタリング、処理などを実行します。これがお役に立てば幸いです。

于 2012-05-17T07:24:06.400 に答える
1

私の意見では、この問題に対する本当に美しい解決策はありません。最大の問題は、ほとんどの場合、一部のパラメーターがnullになる可能性があることですが、そうでないものもあります(パラメーターがテーブル値パラメーターまたはXMLパラメーターのどちらからのものであるかは関係ありません)。次に、次のようなSQLになります。

Declare @Col1Value int = null
Declare @Col2Value int = null
Select * 
From dbo.MyTable
where (@Col1Value is Null Or Col1 = @Col1Value)
    And (@Col2Value is Null Or Col2 = @Col2Value)

もちろん、その効率的ではない+クエリプランは、はるかに最善ではありません。

この問題を解決するには、動的SQLが大いに役立ちます。ただし、その場合は、ユーザー権限を慎重に検討する必要があります(Execute As someProxyUser、Certificatesを使用する場合があります)。

その後、1つの入力XMLパラメータを使用してプロシージャを作成し、必要なすべてのパラメータを渡してからSQLを生成することができます。しかし、SQLがより複雑になるとコーディングが多くなるため、これはあまり良い方法ではありません。関与します。たとえば、複数のテーブルからデータを選択し、それらの複数のテーブルに同じ列がある場合。

要約すると、この問題に対する優れた洗練された解決策はないと思います。エンティティフレームワークとパラメータを渡す古典的な方法を使用してください:)。

于 2012-05-18T11:57:32.717 に答える
1

XMLをパラメーターとして使用し、関心のあるXMLの部分を解凍するのに役立つUDFをいくつか追加します。単一値パラメーターの場合はスカラー値のUDF、リストの場合はテーブル値のUDFです。

クエリにXMLを埋め込むと、クエリオプティマイザが混乱する傾向があり、UDFを使用すると、where句または結合になってしまうと、パフォーマンスが低下する可能性があるため、クエリ自体でXMLまたはUDFを使用しません。最初にXMLからローカル変数、テーブル変数、または一時テーブルに値を取得し、次にそれらをクエリで使用します。

于 2012-05-18T13:04:27.437 に答える
1

私も同様の状況に直面し、UDTはほぼ完璧に機能していることがわかりました。「このアカウントのデータを取得する」、「これらのアカウントのデータを取得する」、「これらの基準を使用する」など、非常によく似た問題から始めました。SPに入ると、XML文字列を渡す代わりにUDTを使用しました。 、UDTから直接参加でき、UDTはADO.NETでサポートされているため、便利でシンプルです。UDT(大量のアップサート)から数十万の行をSPに渡しており、1つの例外を除いて、パフォーマンスは問題になりませんでした。その数の行を送信するときにクエリをトレースしようとしないでください。 SQLサーバー内のスレッドスケジューラが爆発します。

ユーザー定義テーブルタイプを使用する際に注意すべき点が1つあります。何らかの理由で、Microsoftは、テーブルタイプを変更できないようにすることをお勧めします。ドロップ/追加することしかできません。それから他の誰かが、何かが彼らに依存しているならあなたがそれらを落とさないようにするのがさらに良いだろうと思ったので、あなたが手でそれをするならばそれらを変えるならばあなたはそれらを落とす/再構成する非常に苦痛なプロセスに終わります。

必要性が手順ごとにより具体的であるという理由だけで、すべてのパラメーターを単一のUDTにカプセル化することはしませんでした。したがって、物事のリストがある場合、そのパラメーターにUDTを使用しましたが、日付などのよく知られた値を引き出すためのいくつかの便利な関数を使用して、それらすべてをルール化する1つのUDTが便利であることが簡単にわかりました。私は同じコードを何度も書くことを軽蔑しています。これにより、わずかなコストでコードベースが確実に縮小され、複雑さが増します。副次的な利点は、すべての開発者が標準的な方法に固執することを余儀なくされることです。これは、クランチタイムが発生したときに常に強制されるとは限らないことが望ましいことです。また、コードの再利用のためにデータレイヤー内にいくつかの素晴らしい機会を開くでしょう。

于 2012-05-21T16:55:40.663 に答える