リスト ビューのしきい値によって発生する現象と対処策

こんにちは SharePoint サポートの森 健吾 (kenmori) です。

今回の投稿では、リスト ビューのしきい値によって発生する現象をご説明します。
前回の投稿では、リスト ビューのしきい値が実装されている理由についてご案内いたしました。

タイトル : リスト ビューのしきい値に関する FAQ
アドレス : https://blogs.technet.microsoft.com/sharepoint_support/2015/04/14/faq/

リスト ビューのしきい値はホスティング環境などで様々なユーザーが利用する環境下において、データベース サーバーおよび SharePoint の安定稼働を守るために、一度に多数のレコードにアクセスする可能性のある操作をデータ アクセス前にブロックするという処理によって問題を根本的に対処する仕組みです。

リスト ビューのしきい値の影響を最小限にするためには、リストまたはライブラリにフォルダーを作成し、フォルダー内にアイテムを 5000 件以上格納しないよう運用します。この方法で、しきい値から生じるほとんどの制約を回避できます。

今回の投稿では、より具体的に実際にどのようなシナリオで、どのようにブロックされるかについて簡単にご説明し、しきい値の影響を受けない運用方法についてご紹介します。本投稿の内容は、2015 年 5 月時点での SharePoint Online の動作をベースに検証した結果をもとにしておりますが、一部の動作は今後変更になる可能性がございますのでご了承くださいますようお願いいたします。

なお、リスト インフラストラクチャを使用したすべてのカスタム ソリューションも同様に影響を受けるため、今回の記事ですべてを網羅することはできませんが、標準機能で通常運用するにあたり可能な限り発生する現象をお伝えします。

 

リスト ビューのしきい値の影響を受けない操作

まずは、リスト ビューのしきい値の影響を全く受けない操作を下記に例示します。上記の通り、すべての影響を受けない操作を列挙しているわけではありません。

・アイテムの追加、変更、削除、バージョン管理 (5000 アイテムを超えての操作も問題ありません)
・リスト、ライブラリの検索ボックスの使用
・スプレッドシートへのエクスポート
・Access で開く (分割したデータ操作のため、しきい値を超えた操作が可能です、50000 アイテムまで操作可能です)
・OneDrive for Business 同期クライアントへの同期 (注意 : アイテム数、サイズの観点で別の制限があります)
・ビューの作成
・列の作成、変更 (集計列を除く)
・単一アイテムに対する権限の変更

それでは、これより影響を受ける操作と、その際に画面上に現れる現象を実際に見ていきます。

 

シナリオ 1 : フォルダー分けされていないリスト・ライブラリ (または単一のフォルダー) 内に 5000 アイテム以上存在する場合

(補足 : 図内の数はフォルダー内のアイテム数)

 

下記のような操作が影響を受けることを確認しております。

1-1) リスト ビューの表示  (条件 : インデックス付きの列以外で並べ替えまたは絞り込み条件を変更した場合)

フォルダー分けされていないリストやライブラリ、あるいはフォルダー分けされていても一つのフォルダー内に 5000 アイテムより多く存在する場合は、リスト ビューのしきい値によってブロックされます。

(エラーメッセージ抜粋)
このビューは、管理者が設定したリスト ビューのしきい値 (アイテム 5000 個) を超えるため、表示できません。

アイテムを表示するには、別のビューを選択するか、新しいビューを作成してください。このリストのビューを作成できる適切な権限を持っていない場合は、システム管理者に依頼して、リスト ビューのしきい値に準拠するようにビューを変更してください。

インデックス付きの列を使用した絞り込みや、並べ替えを使用することで問題を回避できます。ただし、インデックス付きの列を使用して絞り込みを行う場合は、取得データを 5000 件以内に収める必要があります。

※標準で用意されているすべてのアイテム ビューなどのデフォルトの設定は、ライブラリの名前 (フォームで使用) FileLeafRef 列と、リストの ID 列が暗黙的にインデックス付きの列として指定されているため、エラーとなりません。

また、フォルダー分けしていても、”フォルダーなしですべてのアイテムを表示する” といった設定を含めると、リスト ビューのしきい値の影響を受けます。

 

1-2) リスト ビューの操作で、列フィールドのプルダウンからフィルターや並べ替え

リスト ビュー上で、フィルターをかける場合は、フィルター候補を生成する際などに、同一階層のすべてのアイテム データを取得して表示、操作する必要があるため、しきい値の影響を受けます。

(エラーメッセージ抜粋)
フィルターの値を表示できません。フィルター処理できないフィールドであるか、管理者が設定したリスト表示のしきい値を超える数のアイテムが返された可能性があります。

1-3) エクスプローラでのファイル一覧表示

エクスプローラでは、しきい値によるエラーを表示するインターフェースが用意されていないため、実際にデータは存在していても、下図のようにフォルダーが空という表示結果となります。

 

 

シナリオ 2 : フォルダー分けされていても配下に 5000 アイテム以上存在すると制限を受ける場合 (特殊な例)

フォルダーごとに 5000 件のアイテムを保存するというルールを守っていても、下記の特殊な制限事項があります。これらの操作は、頻繁に実施するものではありませんが、事前に認識しておく必要があります。
なお、運用上頻繁に実施することないこれらの例に関して、SharePoint オンプレミスにおいては、この記事に記載した方法よりも適切な対処策があります。詳しくは以下のサイトをご確認ください。

タイトル : リスト ビューのしきい値に関する SharePoint オンプレミス版の対処策
アドレス : https://blogs.technet.com/b/sharepoint_support/archive/2015/06/13/sharepoint-on-prem-workaround-to-the-list-view-threshold.aspx

 

2-1) 配下に 5000 件以上のアイテムを持つフォルダーを名前変更、移動する。

配下に 5000 件以上のアイテムを持つフォルダー名前やフォルダー移動は、リスト ビューしきい値による制限の対象となります。

これは、フォルダーの名前変更や移動によって、配下の各アイテムがコンテンツ データベース内に保持するアイテムの URL を書き換える必要が生じることに起因します。

一度にリスト ビューのしきい値である 5000 件以上のデータを書き換える操作は、ブロックの対象となります。

下記に具体的なエラーを記載します。

 

エクスプローラを使用したフォルダーの名前変更、フォルダーの移動

(エラーメッセージ抜粋)
送り側のファイルまたはディスクから読み取れません。

 

フォルダーの [プロパティの編集]

(エラーメッセージ抜粋)
管理者によって設定されたリスト ビューのしきい値を超えているので、実行しようとした処理は禁止されています。

 

SharePoint Designer からのフォルダー名の変更、フォルダーの移動

(エラーメッセージ抜粋)
サーバー エラー : 管理者によって設定されたリスト ビューのしきい値を超えているので、実行しようとした処理は禁止されています。

対処策

移動先に新しいフォルダーを作成し、フォルダー内のファイルを複数選択して移動することで、フォルダーの移動 (名前変更) は可能となります。

2-2) 配下に 5000 件以上のアイテムを持つリスト・フォルダーの権限を継承・切断する。

リスト・ライブラリやフォルダーの権限を継承・切断すると、配下のアイテムが持つ ScopeId (権限スコープの ID) を書き換える必要が生じます。
この動作においても、一度にリスト ビューのしきい値である 5000 件以上のデータを書き換える操作は、ブロックの対象となります。

(エラーメッセージ抜粋)
管理者によって設定されたリスト ビューのしきい値を超えているので、実行しようとした処理は禁止されています。

[固有の権限の削除] (再継承) は、配下のアイテムが 5000 を超えるとエラーとなります。しかし、[権限の継承の中止] に関しては配下のアイテムが 10000 でエラーとなります。この操作については意図された変則的な実装がなされております。

 

対処策

移動先に新しいフォルダーを作成し、あらかじめ権限設定を実施した上で、上記 1) と同様にフォルダー内のファイルを複数選択して移動することで、フォルダーの [権限継承の中止] と [固有の権限の削除] と同じ結果を作ることは可能となります。

 

3) リストに集計列を追加する。

集計列は表示時に再計算するのではなく、常にデータとして集計後の値を保持しておく実装となります。そのため、集計列を作成する時点で集計を行い、その結果を列の値に格納する動作となります。

そのため、リスト内に 5000 件以上のデータが存在する場合は、一度にリスト ビューのしきい値である 5000 件以上のデータを書き換える操作となるため、しきい値によるブロックの対象となります。

 (エラーメッセージ抜粋)
管理者によって設定されたリスト ビューのしきい値を超えているので、実行しようとした処理は禁止されています。

対処策

データを移動させ、一時的にリスト・ライブラリ内のアイテム数を 5000 以下とした上で、集計列を作成します。設定後、データを戻します。

しきい値を意識した運用方法について

リスト ビューのしきい値による各種操作のブロックの影響を最小限に抑えるために、まずはファイル数が増加傾向にあるライブラリにおいて、シナリオ 1 に記載の問題が発生しないようフォルダー内のアイテムが 5000 件を超えないように確認をする必要があります。

以下にいくつかの方法をご案内します。

1) 目視で子アイテム数を確認する方法

最も少ない工数で、この問題に対して運用的に検出できる方法として、ビュー上で ”子アイテムの数” を表示する方法があります。下記にドキュメント ライブラリを例に手順を記載します。

1. ライブラリにアクセスします。
2. リボン メニューより [ライブラリ] – [ビューの変更] をクリックします。
3. "子アイテムの数" にチェックを入れて [OK] をクリックします。

上図の例では営業資料フォルダー内にファイルが 5001 個あり、しきい値を超えている状況が確認できます。ファイルの数を 5000 以内に減らしてエラーを防ぐ必要があります。
このように視覚的に、どれくらいの子アイテムがフォルダーにあるかを日々確認できるようにし、子アイテムの数が 5000 を超えないよう事前にフォルダー分け管理するよう促すことで問題に対して事前に対処することが可能です。

 

2) 子アイテムが 5000 を超えたフォルダーを通知する PowerShell スクリプト

子アイテムが 5000 を超えたフォルダーをプログラムで検出方法になります。
下記の PowerShell スクリプトは、指定されたサイト URL 内に存在するドキュメント ライブラリ配下のフォルダーを参照していき、直下および配下のアイテム数を取得してテキスト ファイルに出力するサンプル コードとなります。

ドキュメント ライブラリでのお問い合わせが最も多いため、ドキュメント ライブラリに限定した PowerShell スクリプトとなりますことをご了承ください。

コード内で 5000 件より多い場合などと分岐処理などを加えることで、運用で様々な活用方法が考えられます。是非とも、お役立ていただけますと幸いです。

param (
  $siteUrl,
  $outfilename,
  $username,
  $password 
)

[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")

$context = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
$pwd = convertto-securestring -string $password -asplaintext -force
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $pwd)
$context.Credentials = $credentials

function enumFolderCounts($fld)
{
  $context.Load($fld.Folders)
  $context.ExecuteQuery()
  $filescnt = $fld.ItemCount
  foreach ($afld in $fld.Folders)
  {
    $filescnt += enumFolderCounts($afld)
  }

  $sb = new-object System.Text.StringBuilder
  $cnt = $sb.Append($fld.ServerRelativeUrl)
  $cnt = $sb.Append("`t")
  $cnt = $sb.Append($fld.ItemCount)
  $cnt = $sb.Append("`t")
  $cnt = $sb.Append($filescnt)
  $sb.ToString() | Out-File $outfilename -Append
  return $filescnt
}

$rootweb = $context.Web
$context.Load($rootweb)
$context.ExecuteQuery()
$context.Load($rootweb.Lists)
$context.ExecuteQuery() 

"URL`tItemCount`tDescendentcount" | Out-File $outfilename

foreach ($list in $rootweb.Lists)
{
  if ($list.BaseType -eq [Microsoft.SharePoint.Client.BaseType]::DocumentLibrary)
  {
    $context.Load($list)
    $context.Load($list.RootFolder)
    $context.ExecuteQuery()
    $cnt = enumFolderCounts $list.RootFolder
  }
}

上記のコードを GetFolderFilesCount.ps1 として保存し、下記の様に実行します。

.\GetFolderFilesCount.ps1 -siteUrl https://tenant.sharepoint.com –outfile C:\TEMP\FilesCount.txt -username user@tenant.onmicrosoft.com -password password

 

実行時引数

siteUrl 対象サイトの URL
outfile 出力先テキストファイル

3) 子アイテムが 5000 を超えたフォルダーから、制限値を超えた数だけファイルを移動できる PowerShell スクリプト

SharePoint Online では、子アイテムが 5000 を超えた場合、子アイテムを 5000 件以内に収めるためには、既定のビューから必要な数のファイルをダウンロードした上で該当フォルダーからファイルを削除して 5000 件以内に収める必要があります。

ただし、ファイルを削除すると、ファイルに指定された列情報や権限、ワークフローの履歴やバージョン情報などの情報も紛失します。この状況を防ぐため、ファイルの削除ではなく、移動を行う PowerShell スクリプトを作成いたしました。

このスクリプトでは、既定のビューにおける最後のアイテムから順に (FileLeafRef で降順ソートして一番最後のアイテムから順に) 他のフォルダーにファイルを移動します。
事前にあらかじめ同じライブラリに移動先のフォルダーを作成しておく必要があります。フォルダーに権限を与える必要がある場合は事前に実施ください。

なお、こちらのサンプルもドキュメント ライブラリでのお問い合わせが最も多いため、ドキュメント ライブラリに限定した PowerShell スクリプトとなりますことをご了承ください。

 

param (
  $siteUrl,
  $srcfolderurl,
  $destfolderurl,
  $username,
  $password 
)

$ErrorActionPreference = "Stop"

[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")

$context = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
$pwd = convertto-securestring -string $password -asplaintext -force
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $pwd)
$context.Credentials = $credentials

$maxfilescount = 5000

$web = $context.Web
$context.Load($web)
$context.ExecuteQuery() 

$srcfolder = $web.GetFolderByServerRelativeUrl($srcfolderurl);
$context.Load($srcfolder)
$context.ExecuteQuery()

$destfolder = $web.GetFolderByServerRelativeUrl($destfolderurl);
$context.Load($destfolder)
$context.ExecuteQuery()

if ($destfolder.ItemCount -gt $maxfilescount)
{
  Write-Host "移動先のフォルダにはすでに" $maxfilescount "件以上のファイルが存在します。別のフォルダを移動先に指定して再度実行してください。"
  return
}

$movecount = 0
$mustbemovedcount = 0

if ($srcfolder.ItemCount -gt $maxfilescount)
{
  if ($srcfolder.ItemCount -lt ($maxfilescount * 2))
  {
    $movecount = $srcfolder.ItemCount - $maxfilescount
  }
  else
  {
    $movecount = $maxfilescount
    $mustbemovedcount = $srcfolder.ItemCount - $maxfilescount
  }

  $list = $web.GetList($srcfolderurl)
  $context.Load($list)
  $context.ExecuteQuery()

  $camlquery = New-Object Microsoft.SharePoint.Client.CamlQuery
  $camlquery.ViewXml = "<View><Query><OrderBy><FieldRef Name='FileLeafRef' Ascending='False'/></OrderBy></Query><RowLimit Paged='FALSE'>" + $movecount + "</RowLimit></View>"
  $camlquery.FolderServerRelativeUrl = $srcfolderurl

  $items = $list.GetItems($camlquery)
  $context.Load($items)
  $context.ExecuteQuery()

  foreach ($item in $items)
  {
    $context.Load($item)
    $context.Load($item.File)
    $context.ExecuteQuery() 

    $item.File.MoveTo($destfolderurl + "/" + $item["FileLeafRef"], [Microsoft.SharePoint.Client.MoveOperations]::None)
    $context.ExecuteQuery()
  }

  Write-Host $items.Count.ToString() "件のファイルを移動しました。"
  if ($mustbemovedcount -gt 0)
  {
    Write-Host "あと" $mustbemovedcount.ToString() "件のファイルを移動する必要があります。別のフォルダを移動先に指定して再度実行してください。"
  }
}
else
{
  Write-Host "指定されたフォルダ内のファイルを移動する必要はありません。"
}

上記のコードを MoveLastFiles.ps1 などとして保存し、下記の様に実行します。

.\MoveLastFiles.ps1 -siteUrl https://tenant.sharepoint.com/sites/siteA -srcfolderurl /sites/siteA/DocLibB/FolderC -destfolderurl /sites/siteA/DocLibB/FolderD -username user@tenant.onmicrosoft.com -password password

 

実行時引数

siteUrl 対象サイトの URL
srcfolderurl しきい値を超えたフォルダの URL (ドメイン相対 URL)
destfolderurl しきい値を超えたファイルの移動先 URL (ドメイン相対 URL)

 

5010 個のファイルが存在した場合は 10 個のファイルを移動します。10030 個のファイルが存在した場合は最大の 5000 個のファイルを移動した上で、残り 30 個のファイルを移動する必要がある旨をメッセージで表示します。

SharePoint Online に対して PowerShell スクリプトを実行する方法にご不明点がある場合は、下記の投稿をご参考にしていただけますと幸いです。

タイトル : SharePoint Online に対して PowerShell を使用する方法
アドレス : https://blogs.technet.com/b/sharepoint_support/archive/2014/10/20/sharepoint-online-powershell.aspx

以上の通りお伝えいたします。