8

PDFファイルのレコードを表示するGridViewを作成したいと思います。これらのレコードには、ユーザーがカスタマイズ可能なメタデータを添付できるため、独自の列を作成して、そこに独自の情報を入力できます。次に、GridViewに表示して、各列を並べ替えることができます。列の順序が-1の場合、GridViewには表示されません。

たとえば、静的テーブルがあります

DocumentsTable:
ID int
PDF_Folder varchar
UserID int

次に、ユーザーが独自の列を作成できる別のテーブルがあります

MetaDataColumns:
ID int
userid int foreign key
KeyName varchar
Order int

および値を保持するテーブル

MetaDataValues:
ID int
UserID int foreign key
DocumentID int foreign key
MetaDataID int foreign key
value varchar(100)

ここで問題となるのは、MetaDataColumnから列を取得してGridViewを作成し、MetaDataValueテーブルの値を入力する必要があることです。私の当初の計画では、GridViewを動的に作成して列を追加する関数を使用する予定でしたが、MetaDataValueの値を列として使用する方法に固執しています。または、GridView AutoGenerate列を使用することもできますが、カスタムデータを表示するようにSQLをカスタマイズする必要があります。私はこれにアプローチする方法に少し立ち往生しています。

私が思いついたアプローチの1つは、この擬似コードです。

private DataTable CreateColumns()
{
   var columns =  select * from MetaDataColumns 
                  where userid = UserId;

   DataTable dt = new DataTable();

   foreach (column in columns)
   {
       dt.Columns.Add(new DataColumn(column[keyName], typeof(string));  //assumes all string
   }

 return dt
}

private void PopulateDG(DataGrid dg)
{
    var documents = select * from DocumentsTable
                     where userid=UserId;

    foreach (document in documents)
    {            
        var columnValues = select * from MetaDatavalues 
                           documentID == document.id;

        DataRow dr = dg.NewRow();
        dr[columnValues.KeyName] = columnValues.value;

    }

 }

 private void LoadGV()
 {  
   DataGrid dg = CreateColumns();
   PopulateDG(dg);
   GridView.datasource = dg;
   GridView.DataBind();
  }

このデザインについて私が気に入らないことの1つは、ドキュメントテーブルのすべての行に対して別のクエリを作成することです。これがSQLの問題かどうかわかりませんか?

4

3 に答える 3

2

あなたの問題は主にデータベースの設計に起因しています。( 3NFの)列となるものをテーブルの行に変換したため、列を動的に追加する必要があります。明らかに、これはユーザーが独自の列を追加できるようにするためです-私の心は震えますが、それがアプリの動作方法です:-)。

の構造によりMetaDataColumns、ユーザーは列名のセットを定義して、必要に応じて個々のドキュメントに適用することを選択できると想定します。

問題は、すべてを適切に正規化しようとすると、完全に非正規化されたデータベースで、多くの手間がかかることだと思います。私の解決策は、テーブルを非正規化することMetaDataValuesです。使用しているRDBMSについては言及していませんが、MySQLには4096列または65kバイトのハード制限があります。Oracleでの制限は、SQLServerでは10001024です。

の構造を次のように変更すると、少なくとも332セットの情報をそこMetaDataValuesに収めることができるはずです。これはで個別に一意になるため、理論的には代理キーを削除できます。UserIDDocumentIDID

MetaDataValues:
ID int
UserID int foreign key
DocumentID int foreign key
KeyName1 varchar
Order1 int
Value1 varchar(100) 
...
KeyNameN varchar
OrderN int
ValueN varchar(100)

もちろん、これにより、個々のユーザーが作成できる列数の上限が332に設定されます。しかし、ユーザーが気が狂う能力を制限するのは普通のことであり、単一のPDFに保存するために332の別々のメタデータを考えることができる人は、何らかの形で制限されるに値します。

特に情報に夢中になっているユーザーがいる場合は、いつでも同じ構造の2番目のテーブルを宣言し、それを入力し続けることができます。

MetaDataColumnsこれを行うと、それはユーザーオプションを表示する以外には使用されないことを意味します。変更が加えられるたびに更新する必要がありMetaDataValues、既存の情報を上書きしないようにするのは少し面倒かもしれません。KeyName1レコードを更新する前にレコードを選択し、 ..KeyNameNを繰り返して、データが含まれていない最初のレコードに入力するなどの操作を行う必要があると思います。あるいは、絶対に恐ろしいSQLクエリを書くこともできます。いずれにせよ、これが「チョークポイント」になります。

もう1つのオプションは、に追加の列を追加することMetaDataColumnsです。これは、列が関連するNを示しますが、これにより、ユーザーはドキュメントごとに332列ではなく、332列に制限されます。

ただし、データベースからの選択はめちゃくちゃ簡単になりました。

select d.*, m.*
  from DocumentsTable d
  join MetaDataValues m
    on d.ID = m.DocumentID
   and d.UserID = m.UserID
 where d.UserId = ?

1,000列のselectステートメントを動的に生成するテーブルを反復処理する必要はありません。すべての情報がすぐそこにあり、簡単にアクセスできます。

一日の終わりに、あなたの質問に対する「正しい」答えは、あなたが時間を過ごしたい場所に依存します。ドキュメントの作成または更新に0.5秒長くかかるか、そのドキュメントの情報を選択するのに0.5秒(おそらくそれ以上)かかるようにしますか。

個人的には、何かを作るには時間がかかることをユーザーは理解していると思いますが、何かが現れるのを何年も待たなければならないことほど迷惑なことはありません。

データベースソリューションではなく、別のソーシャルソリューションがあります。ユーザーが独自の列を作成することを許可しないでください。ユーザーが必要とする最も一般的なメタデータを選択し、データベースに正規化された形式で適切に作成します。正しいデータ型で列を作成することができ(これにより、長期的には多くの手間を省くことができます)、はるかに簡単な時間を過ごすことができます。あなたがそれを実現するのに十分幸運であるとは思えません。しかし、それは覚えておく価値があります。

于 2012-07-14T23:09:46.413 に答える
1

意味ですか

<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="false">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Path=Id}" Header="ID"/>
        <DataGridTextColumn Binding="{Binding Path=Name}" Header="Name"/>
    </DataGrid.Columns>
</DataGrid>

コードビハインドでクラスを作成します

public class MetadataSource
{
    public MetadataSource()
    { // use reflection to create properties/values }
}
于 2012-07-13T07:49:32.643 に答える
1

データの各行が個別のクエリを表す擬似コードアプローチに問題があることは間違いありません。1,000行のデータがある場合、データベースに1,000以上のクエリがヒットすることになり、ページは非常に遅くなります。

次のような状況を改善するための即時のステップとして、少なくとも2つのSQLクエリを組み合わせることができます。

var values = SELECT * FROM MetaDataValues
WHERE documentid IN (SELECT id FROM DocumentsTable WHERE userid = UserId)

foreach (val in values)
{            
    DataRow dr = dg.NewRow();
    ...
}

私は通常、「SELECT *」アプローチを好みません。これにより、データベースはすべての列に入力するために追加のクエリを実行する必要があります。あなたの場合、ユーザーが表示できる列に制限される可能性があるとすると、SQLはさまざまな方法で必要以上のデータを取り込むことになります。したがって、コードは次のようにさらに統合および最適化できます。

private void PopulateDG(DataGrid dg)
{
    var columns = SELECT columnKey FROM MetaDataColumns
            WHERE userid = UserId;

    // pseudo code, join column keys into a comma delimited string
    string columnFields = string.Join(",", columns);

    string getValueSql = string.Format("SELECT {0} FROM MetaDataValues
            WHERE documentid IN (SELECT id FROM DocumentsTable WHERE userid = UserId)", columnFields);

    var values = ExecuteSql(getValueSql);
于 2012-07-14T16:08:01.047 に答える