2016-11-30 13 views
1

SharePointにWebパーツがあり、リスト内の特定のフィールドの一意の値または異なる値をドロップダウンコントロールに入力しようとしています。LINQで一意の値を取得する方法は簡単ですか?

残念ながら、システムの性質上、それはテキストフィールドなので、データ値を得るための他の決定的なソースはありません(つまり、選択フィールドの場合はフィールド定義を取得できますそこから値を取得する)、後でCAMLクエリでドロップダウンの値を使用しているので、の値はでなければなりません。現在のところリストにはアープロックスがあります。 4Kのアイテムが含まれていますが、ゆっくりと成長しています。

そして、それはサンドボックスソリューションの一部なので、ユーザーコードサービスの制限時間によって制限されています。タイムアウトが頻繁に発生します。私の開発環境では、私はデバッグでコードを踏んで、LINQの行のように私は実際に別の値を取得するのが最も時間がかかり、私はこのメソッドの呼び出しを完全にコメントアウトし、だから私はこれがどこに問題があるのか​​かなり確信している。ここで

は私のコードです:

private void AddUniqueValues(SPList list, SPField filterField, DropDownList dropDownControl) 
{ 
    SPQuery query = new SPQuery(); 
    query.ViewFields = string.Format("<FieldRef Name='{0}' />", filterField.InternalName); 
    query.ViewFieldsOnly = true; 

    SPListItemCollection results = list.GetItems(query); // retrieves ~4K items 

    List<string> uniqueValues = results.Cast<SPListItem>().Select(item => item[filterField.Id].ToString()).Distinct().ToList(); // this takes too long with 4K items 

    uniqueValues.Sort(); 

    dropDownControl.Items.AddRange(uniqueValues.Select(itm => new ListItem(itm)).ToArray()); 
} 

は、私の知る限り、CAMLクエリで直接「明確な」値を取得する方法はありませんので、どのように私はより迅速にこれを行うことができますか?より速く実行するためにLINQを再構成する方法はありますか?

これをクライアント側から簡単に行う方法はありますか? (RESTが好まれますが、必要ならJSOMをやります)。


私はさらにテストをして興味深い結果を見つけたので、ここにいくつかの追加情報を追加したいと思います。

まず、Cast()Select()が必要かどうかの質問に答えるには:はい、そうです。

SPListItemCollectionIEnumerableですが、IEnumerable<T>ではなく、LINQをまったく使用できるようにキャストする必要があります。

そして、それはIEnumerable<SPListItem>にキャストだ後、SPListItemはかなり複雑なオブジェクトであり、そして私は、そのオブジェクトのちょうどプロパティは異なる値を見つけるために探しています。 IEnumerable<SPListItem>に直接Distinct()を使用すると、それらのすべてが得られます。だから私はSelect()私は比較したい単一の値にする必要があります。

はい、Cast()Select()は絶対に必要です。

M.kazem Akhgaryさんのコメントで指摘されているように、私の元のコード行では、毎回ToString()(4Kアイテム用)を呼び出すのに時間がかかりました。しかし、他のいくつかのバリエーションをテストするには:

// original 
List<string> uniqueValues = results.Cast<SPListItem>().Select(item => item[filterField.Id].ToString()).Distinct().ToList(); 

// hash set alternative 
HashSet<object> items = new HashSet<object>(results.Cast<SPListItem>().Select(itm => itm[filterField.Id])); 

// don't call ToString(), just deal with base objects 
List<object> obs = results.Cast<SPListItem>().Select(itm => itm[filterField.Id]).Distinct().ToList(); 

// alternate LINQ syntax from Pieter_Daems answer, seems to remove the Cast() 
var things = (from SPListItem item in results select item[filterField.Id]).Distinct().ToList(); 

は、私はそれらのメソッドのすべてが完了するまでに秒の複数の数十を取ったことがわかりました。不思議なことに、DataTable/DataView私は私が欲しかった値を抽出するためにビットを追加するPieter_Daems answerからの方法であって、

DataTable dt = results2.GetDataTable(); 
DataView vw = new DataView(dt); 
DataTable udt = vw.ToTable(true, filterField.InternalName); 
List<string> rowValues = new List<string>(); 
foreach (DataRow row in udt.Rows) 
{ 
    rowValues.Add(row[filterField.InternalName].ToString()); 
} 
rowValues.Sort(); 

わずか1~2秒を取りました!

最後に、Thriggle's answerとします。これは、SharePointの5000アイテムリストビューのしきい値をうまく扱うためです。これは、おそらく一日で処理されますが、それはわずかに遅く(2-3秒) DataTable方法。 LINQよりもはるかに高速です。

しかし、興味があるのは、SPListItemCollectionから特定のフィールドから異なる値を取得する最も速い方法は、DataTable/DataViewの変換方法のようです。

+0

私はそれが 'item [filterField.Id] .ToString()'部分のためだと思います。 'ToString'メソッドはオーバーライドされていますか?そうでなければ、基本的に同じ文字列を何度も何度も返すので、ハッシングのメリットは得られません –

+0

検索にDistinctを追加できませんか? 'SPListItemCollection results = list.GetItems(クエリ).Distinct()'? –

+0

'.Cast.Select.Unique()' linqを実行するのにどれくらいの時間が必要ですか? – TripleEEE

答えて

2

あなたが潜在的に最初の明瞭さをチェックする前に、すべてのアイテムを取得することで、大幅な遅延を導入しています。

代替アプローチは、SharePointに対して複数のCAMLクエリを実行することです。これにより、一意の値ごとに1つのクエリが生成されます(結果を返さない1つの最終クエリ)。

  1. リストに値を列挙するフィールドに列インデックスが適用されていることを確認してください。
  2. 最初のCAMLクエリでは、に並べ替え、列挙するフィールドで行の値を1つだけに設定します。
  3. そのクエリによって返されたアイテムからフィールドの値を取得し、それを独自の値のコレクションに追加します。
  4. フィールドをソートして行の制限を1に設定しますが、今度はフィルタの条件を追加して、フィールド値が検出したフィールド値よりも大きいアイテムのみを取得します。
  5. 返された項目のフィールドの値を独自の値のコレクションに追加します。
  6. クエリーが空の結果セットを返すまで、ステップ4と5を繰り返します。その時点で、独自の値のコレクションにはフィールドの現在の値がすべて含まれている必要があります。

もっと速くなるでしょうか?それはあなたのデータと、重複する値がどのくらいの頻度で発生するかによって異なります。

4つのアイテムがあり、ユニークな値が5つのみの場合、6つの軽量CAMLクエリでこれらの5つの値を収集し、合計5つのアイテムを返すことができます。これは、すべての4000個のアイテムを照会し、一度に1つずつ列挙して一意の値を探すよりも、はるかに理にかなっています。

一方、4000個のアイテムと3000個のユニークな値がある場合、リストを3001回照会することを検討しています。これは、単一のクエリ内のすべてのアイテムを取得し、後処理を使用して一意の値を見つけるよりも遅くなる可能性があります。

+0

興味深いアプローチ、私はそれを試してみましょう。 FWIWには現在28個のユニークな値があり、ユニークな値が追加される割合は、アイテム全体が増加する割合よりはるかに小さくなるので、これが行く方法かもしれないと思っています。 –

+1

このアプローチは、SharePoint 2010または2013で、5000以上のアイテムを返すクエリを抑制するリストビューのしきい値によって制限される場合にも役立ちます。この場合、列の索引付けは特に重要です。 – Thriggle

+1

はい、5Kリストビューのしきい値を念頭に置いて、このテクニックを使用します。ありがとうございます!私が試したLINQメソッドよりもはるかに高速でしたが、興味深いことに最速ではありませんでした...(興味があれば更新された質問を見てください)。 –

0

Duplicate maybe?

.DistinctはO(n)の呼び出しです。 それ以上のスピードを得ることはできません。

これは、ユニークを取得するためにキャスト+セレクトが必要かどうかをチェックしたいと思うかもしれません。私はHashSetを試してみたいと思います。

2
var distinctItems = (from SPListItem item in items select item["EmployeeName"]).Distinct().ToArray(); 

かのDataViewにあなたの結果を変換するなど、何かの操作を行います。

SPList oList = SPContext.Current.Web.Lists["ListName"]; 
SPQuery query = new SPQuery(); 
query.Query = "<OrderBy><FieldRef Name='Name' /></OrderBy>"; 
DataTable dtcamltest = oList.GetItems(query).GetDataTable(); 
DataView dtview = new DataView(dtcamltest); 
DataTable dtdistinct = dtview.ToTable(true, "Name"); 

出典:https://sharepoint.stackexchange.com/questions/77988/caml-query-on-sharepoint-list-without-duplicates

+0

@Pieter_Diems最終的に私はもう一つの答えを出しますが、興味深いのは 'DataTable '/' DataView'変換方法が最も速かった。興味があれば私の更新された質問を見てください。 –

関連する問題