SharePointのドキュメントライブラリに5000件を超えるファイルが存在する場合、通常のSQLクエリでパスカラムによるフィルタリングを行うと、SharePointのリストビューしきい値制限に抵触し、クライアントサイドでの全件取得・フィルタリングが発生して非常に時間がかかります。CData SharePoint ドライバーが提供するListFilesFromFolderストアドプロシージャを利用することで、SharePointのRenderListDataAsStreamエンドポイントを経由したサーバーサイドフィルタリングが実現でき、取得速度を大幅に改善できます。
背景:なぜパスカラムフィルタは遅いのか
SharePoint Online には「リストビューしきい値」と呼ばれる制限があり、1回のクエリで取得・フィルタリングできるアイテム数は 5,000 件に制限されています。CData SharePoint ドライバーでドキュメントライブラリテーブル(例:ドキュメント)に対してパスカラム(FileDirRef)で絞り込むと、SharePoint の REST API がサーバー上での WHERE 句適用を拒否し、ライブラリ全件をクライアント側に転送してからドライバーがフィルタリングを行う動作になります。
-- この書き方はクライアントサイドフィルタリングになる(遅い)
SELECT * FROM ドキュメント WHERE パス = '/sites/mysite/ドキュメント/案件フォルダ/2025年度'
一方、IDカラム(SharePointのインデックス済み列)を条件にすると、SharePoint がサーバー上で効率よくフィルタリングを行います。ただし、5000件を超えるライブラリでIDフィルタをサーバーサイドで処理させるには、接続プロパティ DisableFilterLimit=true を設定する必要があります。
DisableFilterLimit の挙動は以下のとおりです。
設定値 | 動作 |
|---|
false(デフォルト)
| フィルタ結果が5000件を超える場合、リスト全件をクライアントに転送してからドライバーがフィルタリング(低速・高負荷) |
true
| 5000件超でもサーバーサイドフィルタを試みる。IDなどのインデックス列では有効に機能するが、非インデックス列ではSharePointがサーバーエラーを返す場合がある |
重要なのは、パスカラム(FileDirRef)は DisableFilterLimit=true にしてもサーバーサイドフィルタが効きません。SharePoint がパス列でのサーバーサイドフィルタをサポートしていないためです。パスカラムでフィルタする限り、ライブラリ全件の転送は避けられません。
-- DisableFilterLimit=true でもクライアントサイドフィルタになる(遅い)
SELECT * FROM ドキュメント WHERE パス = '/sites/mysite/ドキュメント/案件フォルダ/2025年度'
-- DisableFilterLimit=true + IDカラムフィルタ → サーバーサイドで処理される(速い)
SELECT * FROM ドキュメント WHERE ID = 12345
しかし現実には「特定フォルダ配下のファイルをIDで絞り込む」ためには、まずそのフォルダ内のIDをリストアップする手段が必要です。そこで活用するのが ListFilesFromFolder ストアドプロシージャです。
ListFilesFromFolderストアドプロシージャとは
ListFilesFromFolder は CData SharePoint ドライバーが提供するストアドプロシージャで、SharePoint の RenderListDataAsStream エンドポイントを内部的に呼び出し、指定フォルダ配下のファイル情報をサーバーサイドでフィルタリングして取得します。
入力パラメータ
パラメータ名 | 型 | 必須 | 説明 |
|---|
LibraryName
| String | 〇 | ドキュメントライブラリ名(例:Shared Documents)。FolderServerRelativePathのライブラリセグメントと一致させること |
FolderServerRelativePath
| String | 〇 | 対象フォルダのサーバー相対パス(例:/sites/Site/Shared Documents/SubFolder) |
Scope
| String | — | 取得スコープ。FilesOnly(デフォルト)= 指定フォルダ直下のファイルのみ、Recursive = 全サブフォルダを含むすべてのファイル |
返却カラム(Result Set Columns)
ListFilesFromFolder が返すカラムは以下のとおりです。ファイル名などの詳細情報は含まれないため、別途ライブラリテーブルへのクエリが必要です。
カラム名 | 型 | 説明 |
|---|
ID
| Int | リストアイテムの一意識別子 |
EncodedAbsUrl
| String | HTTPリクエスト用にエンコードされたファイルの絶対URL |
FileSizeDisplay
| String | 人が読めるファイルサイズ(例:12 KB) |
Modified
| Datetime | ファイルの最終更新日時 |
FileDirRef
| String | ファイルが格納されているフォルダのサーバー相対パス |
実装手順
ステップ1:ドライバーの接続設定
SharePoint ドライバーの接続文字列を設定します。SharePoint Onlineの場合、Azure AD認証(RESTスキーマ)を使用します。IDカラムによるサーバーサイドフィルタを有効にするため、DisableFilterLimit=true を必ず設定してください。
jdbc:sharepoint:URL=https://mycompany.sharepoint.com/sites/mysite;
SharePointEdition=SharePoint Online;
AuthScheme=AzureAD;
Schema=REST;
InitiateOAuth=GETANDREFRESH;
DisableFilterLimit=true;
ステップ2:ListFilesFromFolderでIDを取得する
EXEC(またはCALL)構文でストアドプロシージャを呼び出します。
-- ドキュメントライブラリ「案件資料」のフォルダ「2025年度/Q1」配下のファイルIDを取得
EXEC ListFilesFromFolder
@LibraryName = '案件資料',
@FolderServerRelativePath = '/sites/mysite/案件資料/2025年度/Q1',
@Scope = 'FilesOnly';
Javaコードで呼び出す場合は次のようになります。
import java.sql.*;
String url = "jdbc:sharepoint:URL=https://mycompany.sharepoint.com/sites/mysite;"
+ "AuthScheme=AzureAD;Schema=REST;InitiateOAuth=GETANDREFRESH;...";
try (Connection conn = DriverManager.getConnection(url);
CallableStatement cs = conn.prepareCall(
"{CALL ListFilesFromFolder(?, ?, ?)}")) {
cs.setString(1, "案件資料");
cs.setString(2, "/sites/mysite/案件資料/2025年度/Q1");
cs.setString(3, "FilesOnly"); // "Recursive" を指定するとサブフォルダも含む
try (ResultSet rs = cs.executeQuery()) {
while (rs.next()) {
int id = rs.getInt("ID");
String url2 = rs.getString("EncodedAbsUrl");
String size = rs.getString("FileSizeDisplay");
System.out.println("ID=" + id + ", URL=" + url2 + ", Size=" + size);
}
}
}
ステップ3:取得したIDでライブラリテーブルを検索する
ストアドプロシージャで得たIDを使い、ドキュメントライブラリテーブルからファイルの詳細情報(ファイル名、作成日時、更新日時、カスタム列など)をサーバーサイドフィルタリングで取得します。
-- ステップ2で取得したIDを使って詳細情報を取得
-- ※ DisableFilterLimit=true の設定により、IDカラムはサーバーサイドフィルタが適用される
SELECT
ID,
FileLeafRef AS ファイル名,
Author AS 作成者,
Created AS 作成日時,
Modified AS 更新日時,
File_x0020_Size AS ファイルサイズ
FROM 案件資料
WHERE ID IN (101, 102, 103, 104, 105);
Javaでストアドプロシージャの結果とJOINするパターンは次の通りです。
// ステップ2の結果から IDリストを構築
List ids = new ArrayList<>();
// ... (rs.getInt("ID") を順次追加)
// IDリストを IN句用文字列に変換
String inClause = ids.stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
// ライブラリテーブルからIDで詳細情報を取得(サーバーサイドフィルタ)
String query = "SELECT ID, FileLeafRef, Author, Created, Modified, File_x0020_Size "
+ "FROM 案件資料 WHERE ID IN (" + inClause + ")";
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
System.out.println(rs.getString("FileLeafRef") + " (" + rs.getLong("File_x0020_Size") + " bytes)");
}
}
よくある質問・トラブルシューティング
Q: DisableFilterLimit=true にすればパスカラムでも速くなるか
A: なりません。DisableFilterLimit=true はIDなどのインデックス済み列に対してのみ有効です。パスカラム(FileDirRef)はSharePoint側がサーバーサイドフィルタをサポートしていないため、DisableFilterLimit=true を設定しても全件転送+クライアントサイドフィルタになるか、サーバーエラーが発生します。パスを起点にファイルを絞り込みたい場合は、本記事で紹介した ListFilesFromFolder ストアドプロシージャを使うのが正しいアプローチです。
Q: ListFilesFromFolderでエラー「The attempted operation is prohibited because it exceeds the list view threshold」が出る
A: FolderServerRelativePathに指定したパスが正確かどうか確認してください。パスはサーバー相対URLの形式(例:/sites/mysite/ライブラリ名/フォルダ名)で指定する必要があります。またLibraryNameはパス中のライブラリセグメント名と一致していなければなりません。パスが正しければ、RenderListDataAsStreamエンドポイントがサーバーサイドフィルタリングを行うため、しきい値エラーは発生しないはずです。
Q: サブフォルダ内のファイルも含めて取得したい
A: ScopeパラメータにRecursiveを指定してください。指定しない場合のデフォルトはFilesOnlyで、指定フォルダ直下のファイルのみが返されます。
Q: ライブラリテーブルの名前がわからない
A: ドライバーでの接続後、クライアントツールのテーブル一覧を取得するか、SELECT * FROM sys_tablesを実行してください。SharePointのドキュメントライブラリ名がそのままテーブル名として公開されます。日本語名のライブラリの場合、そのまま日本語でクエリに記述できます。
Q: IN句に指定できるIDの上限はあるか
A: CData SharePoint ドライバーとSharePoint REST APIの仕様上、一度のリクエストで扱えるIDの数に実質的な制限はありませんが、可読性とメモリ効率のため、数百件ごとにバッチ処理することを推奨します。
Q: SOAPスキーマでも同じ方法が使えるか
A: ListFilesFromFolderストアドプロシージャはRESTスキーマ向けの機能です。SOAPスキーマではFullClientFilter=trueプロパティを活用する別のアプローチが推奨されます。詳細はCDataナレッジベースの5000件超対応ガイドを参照してください。
まとめ
5000件を超えるSharePointドキュメントライブラリからファイル情報を効率よく取得するには、以下の2ステップが有効です。
ListFilesFromFolderストアドプロシージャでフォルダパスを指定し、RenderListDataAsStreamによるサーバーサイドフィルタリングでIDとURLを取得する。
取得したIDを使い、ライブラリテーブルへのWHERE ID IN (...)クエリでインデックスによるサーバーサイドフィルタリングを活用しながら詳細情報を取得する。
この2段階アプローチにより、クライアントへの大量データ転送を回避しつつ、必要なファイル情報だけを効率的に取得できます。