GetListItemで大量の行列情報をエクスポートする

SharePointが標準で提供する「リストのデータをまとめてエクスポートする方法」は二通りあります。

ひとつは[データシートで編集]で、そのままエクセルにコピペできます。もうひとつは[スプレッドシートにエクスポート]で、owssvr.iqy ファイルとしてダウンロードすることもできます。この場合、ファイルを開いた際にクエリが走り、データを取得します。

ただ、どちらの操作も、実はサーバに結構な負荷がかかるようです。私の環境における事象ですが、どちらの方法でも、大量の行列のあるリストで実行すると、かなり頻繁に「失敗」します。データの一部しか読み込まれず、エラーになるのです。

この原因自体は皆目不明なのですが、

 1. 自宅環境では発生しない
 2. 会社環境では、私だけでなく他ユーザーでも発生する
 3. Office2003、Office2007でも発生する
 4. 経験則として早朝は成功しやすく午後~夜になると失敗率大

以上から、フロントエンドサーバのメモリ領域に問題があるのでは、と推測しています。サーバは深夜にリブート運用されており、この際、メモリ領域が開放されます。それが「早朝は成功する確率が高いが夕方は危険」な原因ではないだろうかと。

原因の究明はさておき、とりあえずは(成功する)エクスポート手段が必要です。そこで、List.aspx の GetListItem が利用出来ないかと考えました。

もちろん、Webサービス経由でも負荷が大きいことは変わりません。そこで、GetListItem でクエリを分割し、かつ逐次実行させ、結果をマージしてみました。

まず、前提としてリスト側にクエリのキー値が必要です。アイテム作成時に乱数を生成出来ればよいのですが。あいにく、SharePointにはその関数がありません。

まず、最初に ID 値の利用を考えました。完全にユニークですし、確実に存在します。
例えば、まず ID=1~100、次に 101~200、201~300 を取得すれば…。

しかし、SharePointの ID は、実はあまり信用がおけません。アイテムが削除された場合に「抜け番」ができてしまうからです。リストの運用次第では、最小値が10000番台、ということも珍しくありません。ID=1 からインクリメントした場合、10000に達するまで「空振り」を繰り返すことになります。

また、インクリメントをどこで止めるのかも問題で、IDの「最大値」が必要になります。出来なくはなさそうですが…もうすこし、簡単な方法を考えてみます。

集計値で、ID の下二桁をとる、というのはどうでしょう?

=RIGHT(ID,2)

ただ、これだけでは ID=100 が 00 になります。
そこで、Value 関数で数値に変換してみます。

=VALUE(RIGHT(ID,2))

これで、0~99 の(擬似的な)乱数が各アイテムに生成されました。
これをキーに、GetListItem でクエリを実行します。
例えば、”0″ のクエリは以下のようになります。

[code]<?xml version="1.0" encoding="utf&#45;8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema&#45;instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/"><listName>listGUID</listName><viewName></viewName><query><Query><Where><Eq><FieldRef Name="cal" /><Value Type="Calculated">0</Value></Eq></Where></Query></query><viewFields><ViewFields><FieldRef Name="Column1" /><FieldRef Name="Column2" /><FieldRef Name="Column3" /><FieldRef Name="Column4" /></ViewFields></viewFields><rowLimit>2000</rowLimit><queryOptions><QueryOptions/></queryOptions><webID></webID></GetListItems></soap:Body></soap:Envelope>[/code]─しかし。何故かこのクエリに対して、有効な値が返りません。
クエリはきちんと送信されるのですが、戻り値が空なのです。
これも原因が良く判りません…(汗)

どうも、SharePointの仕様的に、ID 列を集計値に利用しては駄目っぽいです。そういえば、集計値列に関数を入力する際、利用できる列が表示されますが、その中に ID列はありませんね(いつも手入力のため、気づきませんでした)

という訳で、ID は諦めて、別の方法を考えます。どんなアイテムにも確実に存在する値…。ということで、作成日時/更新日時はどうでしょうか?

以前のエントリでご紹介しましたが、SharePointは日付データを秒単位まで記録しています。そこで、

=TEXT(更新日時,”s”)

これで、各アイテムに 0~59 の(擬似)乱数が振られます。
形式は数値でもテキストでも大丈夫です。

なお、もっと細かい分割をしたい場合、以下のようになります。
…後者はやや、やり過ぎ感が否めませんが。

=RIGHT(MINUTE(更新日時),1) * 60 + TEXT(更新日時,”s”) → 0~660
=MINUTE(更新日時) * 60 + TEXT(更新日時,”s”) →0~3360

さて、準備に手間取りましたが、この値(ここでは 0~59)を利用して、60回にわけてクエリを逐次実行し、最終的に戻り値をcsv形式にマージするスクリプトを用意してみました。

例によってコンテンツエディタWebパーツにソースを直書きです。

一応、まちがって実行しないように警告。
まあ、Enter連打されたら無意味ですが(苦笑)

処理が開始されます。
画像がいつも見慣れたやつなのは、ご愛嬌です。

進行度合いを%で表示します。

完了しました。
なお、クリップボード出力は ie のみ対応です。

エディタにはりつけました。
もちろん、エクセルに貼りつけてから , で区切ってもOK。

ソースはこちら。
SharePointMANIACS_GetLargelistItem.txt

で、今更なのですが…(苦笑)
私はおよそコーディングに関してはまるきし門外漢です。
このソースも、うちの技術者さんが以前書かれたソースを参考にしたり、ネットで公開されているサンプルをつなぎ合わせてつくりました。
なので、「とりあえず動く」レベルだ、という点は、予めご承知おき下さい。
なお、こう書いた方がいい、的なツッコミは大歓迎です^^

SharePointと yyyy/mm/dd
SharePointのGetListitemで値を取得する


これまでのコメント

  1. mamao2 より:

    AGENT: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2) Gecko/20100115 Firefox/3.6 Jingoo/0.1.4
    これは面白いですね。
    計算式で議事乱数を作り出す発想はありませんでした。(笑)

    >─しかし。何故かこのクエリに対して、有効な値が返りません。
    >クエリはきちんと送信されるのですが、戻り値が空なのです。

    この部分ですが、どうやら集計列の条件指定は計算結果の型を指定する必要があるようです。

    <Where><Eq><FieldRef Name=\”cal\” /><Value Type=\”Calculated\”>0</Value></Eq></Where>
     ↓
    <Where><Eq><FieldRef Name=\”cal\” /><Value Type=\”Number\”>0</Value></Eq></Where>

    という感じに数値型を指定したところヒットするようになりました。
    ご参考までに。

  2. saruhiko より:

    AGENT: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; InfoPath.2)
    mamao2さん、こんばんは。

    >計算式で疑似乱数を作り出す発想はありませんでした。(笑)

    あはは。
    ある意味、素人ならではの発想だよな~と自分でも思います^^;;

    >どうやら集計列の条件指定は計算結果の型を指定する必要があるようです。

    ええっ?!
    な、なるほど!
    うわ~、騙された~。
    その発想はなかったです(苦笑)
    集計列なのに、Numberだなんて…。なんかズルイ(笑)

    ありがとうございます!勉強になりました!

  3. suzukix より:

    AGENT: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MDDR; InfoPath.1)
    はじめまして、いつも参考にさせて頂いております。

    いまさらのコメントになり申し訳ございませんが、
    上記SharePointMANIACS_GetLargelistItem.txtから
    自環境へ変更し実行してみましたが、
    rowsItem(158行目)でカウント0となってしまいます。
    (対象のライブラリのGUIDは、ライブラリの設定画面のURLの末尾から取得しました。)
    サーバー名、サイト名、ライブラリ(GUID)のいずれかが怪しいとにらんでおりますが、残念ながら未だ原因はつかめておりません。

    他に怪しい点や調べたほうがよい点など御座いましたらご教授頂けると幸いです。

  4. saruhiko より:

    AGENT: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3
    こんにちわ。
    カウントが0、ということは戻り値がない、つまりそもそもリストから
    データを抜けていない可能性が高いです。

    環境変数の設定書式がサンプルのとおりで間違いがない場合、
    次の確認ポイントは実際にどのようなXMLが戻っているかですね。
    【XMLを取得したい場合は】処理を利用してXML文を表示してみてください。

  5. saruhiko より:

    AGENT: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3
    > 対象のライブラリのGUIDは、ライブラリの設定画面のURLの末尾から取得しました。

    念のため。デコードはされていますか?

    あと、ライブブラリを抜く場合、確か記憶ではフォルダがあると、
    そのままではその配下までクエリしてくれなかったかと思います。
    この場合、(うろ覚えで恐縮ですが)、フォルダ無視のビューを設定し、
    そのビューを明示的に指定してやる必要があります。

  6. suzukix より:

    AGENT: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MDDR; InfoPath.1)
    早々のご回答ありがとうございます。

    原因はよく分かりませんが、以下の手順で成功致しました。

    ①環境変数のlistNameを存在しないGUIDを指定して実行。
     →httpInst.responseText(XML)にはファイルが見つかりません旨のメッセージが返り、値は取得できません。(0件)

    ②環境変数のlistNameを実在するGUIDを指定して実行。
     →httpInst.responseTextにはライブラリ内のデータが取得できていました。

    ※②の実在するGUIDは①以前にずっと指定していた
    GUID(ライブラリの設定画面のURLの末尾)です。

    サーバー及びクライアントの再起動は行っておりませんが、
    間違った情報がどこかしらに記録されていて②でエラーとなった拍子に
    クリアされたのでしょうか…。

    いずれにしても成功したので、引き続きビューや条件による絞込みなど試してみます。
    ありがとうございました。

  7. suzukix より:

    AGENT: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MDDR; InfoPath.1)
    こんにちは。以前は大変お世話になりました。

    いきなりの質問で申し訳ございません。
    Lists.asmxのGetListItemsでリッチテキスト項目を取得することは可能でしょうか?

    こちら↓でも同質問をしておりますが、なかなかレスが付かないので誠に勝手ながらコメントさせて頂きました。
    http://social.msdn.microsoft.com/Forums/ja-JP/sharepointdevelopja/thread/328f6bbe-3c88-4af9-9f48-7ca01b3a9035

  8. kazuhiko より:

    AGENT: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7
    suzukixさん、遅くなりまして申し訳ない。
    実際に確認してみるのにちょっと時間がかかりました。
    で、可能でした。
    ただし、HTMLはすべてエスケープ文字出力されるので、
    今度はそれを復号?してやる必要がありますね。

  9. R00tZer0 より:

    Webサービスのアイテム取得にはページング機能があります。

  10. 中村 和彦 より:

    ありがとうございます。
    お恥ずかしい、知りませんでした…
    それが指定出来ればそもそもこんな無理やりな仕掛けをしなくて済みますね。
    調べてみます。

login

Author

中村 和彦(シンプレッソ・コンサルティング株式会社 代表)が「ユーザ視点の SharePoint 情報」を発信します。元大手製造業 SharePoint 運用担当。現SharePoint コンサルタント。お仕事のお問い合わせはこちらまでお願いします。当ブログにおける発信内容は個人に帰属し所属組織の公式発信/見解ではありません。
FB : 中村 和彦
blog: Be・Better!
MS MVP SharePoint 2009/10-2011/9
MS MVP Office 365 2012/10-2014/9