SelectObject を使用すると、Object Storage Service (OSS) 内のオブジェクトに対して直接 SQL クエリを実行し、対象データのみを取得できます。この機能により、オブジェクト全体を事前にダウンロードする必要がなくなり、帯域幅の消費量を削減するとともに、クライアント側の CPU 使用率およびメモリ使用量を低減できます。これにより、OSS を基盤としたデータ分析の効率性が向上します。
仕組み
GetObject を使用する場合、アプリケーションはフィルタリング処理を行う前にオブジェクト全体をダウンロードします。一方、SelectObject ではフィルタリング処理を OSS 側に移動させます。つまり、SQL 条件式およびプロジェクション(列選択)を OSS に送信すると、OSS が該当する行のみを返却します。
クエリのフローは以下のとおりです:
SQL
SELECT ... FROM ... WHEREステートメントおよび出力フォーマットを指定して SelectObject リクエストを送信します。OSS がオブジェクトをスキャンし、条件およびプロジェクションをサーバー側で適用します。
OSS が該当するレコードのみをストリーム形式でクライアントに返却します。
ラージオブジェクトの場合、まず CreateSelectObjectMeta を呼び出して分割数(splits)の合計値を取得した後、
split-rangeを使用して並列クエリを実行します。
サポートされるオブジェクトタイプ
| タイプ | 詳細 |
|---|---|
| CSV(TSV および類似フォーマットを含む) | RFC 4180 に準拠している必要があります。カスタムの行区切り文字、列区切り文字、および引用符文字をサポートします。 |
| JSON(UTF-8 エンコーディング) | 2 種類のフォーマットに対応: JSON DOCUMENT(単一の JSON オブジェクト)および JSON LINES(各行に 1 つの JSON オブジェクトを記述、\n または \r\n で区切る。デリミタの構成は不要)。 |
| 標準ストレージおよび低頻度アクセス(IA)オブジェクト | 直接クエリ可能です。アーカイブ、コールドアーカイブ、およびディープコールドアーカイブストレージのオブジェクトは、クエリ実行前に解凍する必要があります。 |
| 暗号化されたオブジェクト | OSS が管理する暗号化または Key Management Service (KMS) が管理するカスタマーマスターキー(CMK)による暗号化が適用されたオブジェクト。 |
制限事項
| 制限 | 詳細 |
|---|---|
| サポートされないバケットタイプ | ゾーン属性(no-header-region)を持たないバケットでは SelectObject はサポートされていません。 |
| コンソールでのデータ制限 | OSS コンソールでは、最大 128 MB のサイズのオブジェクトから最大 40 MB のデータを選択できます。 |
| SDK のサポート状況 | SelectObject は、OSS SDK for Java および OSS SDK for Python のみでサポートされています。 |
| 圧縮 | 圧縮済みオブジェクトのクエリでは、GZIP 圧縮のみがサポートされています。 |
| LIKE の大文字小文字の区別 | LIKE を使用したあいまい一致は、大文字小文字を区別します。 |
課金
SelectObject の課金は、スキャンされるデータ量に基づきます:
標準オブジェクト:データスキャン料金。
低頻度アクセス(IA)、アーカイブ、コールドアーカイブ、およびディープコールドアーカイブストレージのオブジェクト:データスキャン料金に加え、データ取得料金も発生します。
料金の詳細については、「データ処理料金」をご参照ください。
サポートされる SQL 構文
| カテゴリ | サポート状況 |
|---|---|
| ステートメント | SELECT ... FROM ossobject [WHERE ...] |
| データ型 | string、int(64 ビット)、double(64 ビット)、decimal(128 ビット)、timestamp、bool |
| 論理演算子 | AND、OR、NOT |
| 算術演算子 | +、-、*、/、% |
| 比較演算子 | >、=、<、>=、<=、!= |
| 文字列演算子 | LIKE、||(連結) |
| 集計関数 | COUNT、MAX、MIN、AVG、SUM |
| 型変換 | CAST 関数 |
CSV と JSON におけるデータ型の違い
CSV データはデフォルトで string 型として扱われます。CAST を使用して明示的に型変換を行います:
SELECT s.a FROM ossobject s WHERE CAST(s.a AS decimal) > 123456789.12345JSON オブジェクトは元の型(Null、Bool、Int64、Double、String)を保持します。高精度の浮動小数点数を処理する場合は、ParseJsonNumberAsString を true に設定し、decimal 型へキャストします:
SELECT s.a FROM ossobject s WHERE CAST(s.a AS decimal) > 123456789.12345SQL の使用例
CSV クエリ
CSV の列インデックスは _1 から始まります。CSV にヘッダー行が含まれる場合、列名で参照できます。
| 目的 | SQL ステートメント |
|---|---|
| 先頭 10 行を返す | SELECT * FROM ossobject LIMIT 10 |
| 列 1(int)> 列 3(int)を満たす行を返す | SELECT _1, _3 FROM ossobject WHERE CAST(_1 AS int) > CAST(_3 AS int) |
| 列 1 の値が「X」で始まる行の数をカウントする | SELECT COUNT(*) FROM ossobject WHERE _1 LIKE 'X%' |
| 列 2 の値が特定のタイムスタンプ以降であり、かつ列 3 の値が 200 より大きい行を返す | SELECT * FROM ossobject WHERE _2 > CAST('2018-08-09 11:30:25' AS timestamp) AND _3 > 200 |
| 列 6(double)に対する集計関数を実行する | SELECT AVG(CAST(_6 AS double)), SUM(CAST(_6 AS double)), MAX(CAST(_6 AS double)), MIN(CAST(_6 AS double)) FROM ossobject |
| パターンに一致する連結された列 | SELECT * FROM ossobject WHERE (_1 || _3) LIKE 'Tom%Anderson' |
| 列 1 の値が 3 で割り切れる行を返す | SELECT * FROM ossobject WHERE (_1 % 3) = 0 |
| 列 1 の値が 1995 ~ 2012 の範囲内である行を返す | SELECT * FROM ossobject WHERE _1 BETWEEN 1995 AND 2012 |
| 列 5 の値が N、M、G、L のいずれかである行を返す | SELECT * FROM ossobject WHERE _5 IN ('N', 'M', 'G', 'L') |
| (列 2 × 列 3)>(列 5 + 100)を満たす行を返す | SELECT * FROM ossobject WHERE _2 * _3 > _5 + 100 |
JSON クエリ
ネストされた構造体を操作するには、ドット表記および配列ワイルドカードを使用します。FROM 句でパスを絞り込むと、パフォーマンスが向上します。
次のサンプルオブジェクトを例に説明します:
{
"contacts": [
{
"firstName": "John",
"lastName": "Smith",
"age": 27,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{ "type": "home", "number": "212 555-1234" },
{ "type": "office", "number": "646 555-4567" },
{ "type": "mobile", "number": "123 456-7890" }
],
"children": [],
"spouse": null
}
]
}| 目的 | SQL ステートメント |
|---|---|
| 年齢が 27 の連絡先を取得する | SELECT * FROM ossobject.contacts[*] s WHERE s.age = 27 |
| すべての自宅の電話番号 | SELECT s.number FROM ossobject.contacts[*].phoneNumbers[*] s WHERE s.type = 'home' |
| 配偶者が null の連絡先を取得する | SELECT * FROM ossobject s WHERE s.spouse IS NULL |
| 子配列が空の連絡先 | SELECT * FROM ossobject s WHERE s.children[0] IS NULL |
| 郵便番号が「10021」で始まる住所を取得する | SELECT s.streetAddress FROM ossobject.contacts[*].address s WHERE s.postalCode LIKE '10021%' |
FROM 句のパスを絞り込む(例:ossobject.contacts[*].address)方が、広範なパスを指定した後にドット表記でアクセスする方法(例:ossobject.contacts[*] の後に s.address.streetAddress)よりも効率的です。
OSS コンソールを使用したオブジェクトのクエリ実行
コンソールでは、最大 128 MB のサイズのオブジェクトから最大 40 MB のデータを選択できます。
OSS コンソールにログインします。
左側のナビゲーションウィンドウで、バケット をクリックします。対象のバケットを検索してクリックします。
左側のナビゲーションツリーで、オブジェクト管理 > オブジェクト を選択します。
対象オブジェクトの 操作 列で、
> コンテンツの選択 を選択します。コンテンツの選択 パネルで、以下のパラメーターを設定します:
パラメーター 説明 オブジェクトタイプ CSVまたはJSONデリミタ CSV のみ対応。 ,またはカスタムタイトル行 CSV のみ対応。1 行目が列ヘッダーを含むかどうか JSON 表示モード JSON のみ対応。 JSON_LINESまたはJSON_DOCUMENT圧縮フォーマット GZIP のみ対応 プレビュー をクリックして、選択したデータをプレビューします。
重要標準ストレージオブジェクトのプレビューにはデータスキャン料金が発生します。低頻度アクセス(IA)、アーカイブ、コールドアーカイブ、およびディープコールドアーカイブストレージのオブジェクトのプレビューには、データスキャン料金に加えてデータ取得料金も発生します。詳細については、「データ処理料金」をご参照ください。
次へ をクリックし、SQL ステートメントを入力して実行します。例:CSV オブジェクト
Peopleには、Name、Company、Ageの 3 つの列があり、それぞれの列インデックスは_1(Name)、_2(Company)、_3(Age)です。名前が「Lora」で始まり、年齢が 50 を超える人物をクエリする:
SELECT * FROM ossobject WHERE _1 LIKE 'Lora*' AND _3 > 50行数、最大年齢、最小年齢を取得する:
SELECT COUNT(*), MAX(CAST(_3 AS int)), MIN(CAST(_3 AS int)) FROM oss_object
結果を表示します。ダウンロード をクリックして、出力をローカルマシンに保存します。
OSS SDK を使用したオブジェクトのクエリ実行
SelectObject は、OSS SDK for Java および OSS SDK for Python のみでサポートされています。
Java の使用例
Java SDK では、createSelectObjectMetadata を使用して分割メタデータを生成し、selectObject を使用してクエリを実行します。認証情報は環境変数 OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET から読み込まれます。
import com.aliyun.oss.model.*;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.OSSClientBuilder;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
public class SelectObjectSample {
// バケットのリージョンに対応するエンドポイントに置き換えます。
// 例: https://oss-cn-hangzhou.aliyuncs.com
private static String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
private static String bucketName = "examplebucket";
public static void main(String[] args) throws Exception {
// 環境変数 OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET から認証情報を読み込みます。
EnvironmentVariableCredentialsProvider credentialsProvider =
CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
String region = "cn-hangzhou";
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
// CSV オブジェクトをクエリします。
selectCsvSample("test.csv", ossClient);
// JSON LINES オブジェクトをクエリします。
selectJsonSample("test.json", ossClient);
ossClient.shutdown();
}
private static void selectCsvSample(String key, OSS ossClient) throws Exception {
// ヘッダー行を含むサンプル CSV データをアップロードします。
String content = "name,school,company,age\r\n" +
"Lora Francis,School A,Staples Inc,27\r\n" +
"Eleanor Little,School B,\"Conectiv, Inc\",43\r\n" +
"Rosie Hughes,School C,Western Gas Resources Inc,44\r\n" +
"Lawrence Ross,School D,MetLife Inc.,24";
ossClient.putObject(bucketName, key, new ByteArrayInputStream(content.getBytes()));
// オブジェクトの分割メタデータを生成します(マルチパートクエリの前に推奨)。
SelectObjectMetadata selectObjectMetadata = ossClient.createSelectObjectMetadata(
new CreateSelectObjectMetadataRequest(bucketName, key)
.withInputSerialization(
new InputSerialization().withCsvInputFormat(
new CSVFormat()
.withHeaderInfo(CSVFormat.Header.Use)
.withRecordDelimiter("\r\n"))));
System.out.println("Total lines: " + selectObjectMetadata.getCsvObjectMetadata().getTotalLines());
System.out.println("Total splits: " + selectObjectMetadata.getCsvObjectMetadata().getSplits());
// 4 列目(年齢)が 40 より大きい行をクエリします。
SelectObjectRequest selectObjectRequest =
new SelectObjectRequest(bucketName, key)
.withInputSerialization(
new InputSerialization().withCsvInputFormat(
new CSVFormat()
.withHeaderInfo(CSVFormat.Header.Use)
.withRecordDelimiter("\r\n")))
.withOutputSerialization(new OutputSerialization().withCsvOutputFormat(new CSVFormat()));
selectObjectRequest.setExpression("SELECT * FROM ossobject WHERE _4 > 40");
OSSObject ossObject = ossClient.selectObject(selectObjectRequest);
BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
ossClient.deleteObject(bucketName, key);
}
private static void selectJsonSample(String key, OSS ossClient) throws Exception {
// サンプル JSON LINES データ(1 行につき 1 つの JSON オブジェクト)をアップロードします。
final String content = "{\"name\":\"Lora Francis\",\"age\":27,\"company\":\"Staples Inc\"}\n" +
"{\"name\":\"Eleanor Little\",\"age\":43,\"company\":\"Conectiv, Inc\"}\n" +
"{\"name\":\"Rosie Hughes\",\"age\":44,\"company\":\"Western Gas Resources Inc\"}\n" +
"{\"name\":\"Lawrence Ross\",\"age\":24,\"company\":\"MetLife Inc.\"}";
ossClient.putObject(bucketName, key, new ByteArrayInputStream(content.getBytes()));
// 年齢が 40 より大きいレコードをクエリします。
SelectObjectRequest selectObjectRequest =
new SelectObjectRequest(bucketName, key)
.withInputSerialization(new InputSerialization()
.withCompressionType(CompressionType.NONE)
.withJsonInputFormat(new JsonFormat().withJsonType(JsonType.LINES)))
.withOutputSerialization(new OutputSerialization()
.withCrcEnabled(true)
.withJsonOutputFormat(new JsonFormat()))
.withExpression("SELECT * FROM ossobject AS s WHERE s.age > 40");
OSSObject ossObject = ossClient.selectObject(selectObjectRequest);
BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
ossClient.deleteObject(bucketName, key);
}
}Python の使用例
Python SDK では、create_select_object_meta、select_object、および select_object_to_file が提供されます。認証情報は環境変数 OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET から読み込まれます。
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
def select_callback(consumed_bytes, total_bytes=None):
print(f"Consumed bytes: {consumed_bytes}")
# 環境変数から認証情報を読み込みます。
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
endpoint = "https://oss-cn-hangzhou.aliyuncs.com" # バケットのエンドポイントに置き換えます。
region = "cn-hangzhou" # バケットのリージョンに置き換えます。
bucket = oss2.Bucket(auth, endpoint, "examplebucket", region=region)
# --- CSV クエリ ---
key = "python_select.csv"
content = "Tom Hanks,USA,45\r\n" * 1024
bucket.put_object(key, content)
# 分割メタデータを生成します。
csv_meta_params = {"RecordDelimiter": "\r\n"}
csv_header = bucket.create_select_object_meta(key, csv_meta_params)
print(f"Total rows: {csv_header.rows}, total splits: {csv_header.splits}")
# 列 3 の値が 44 より大きい行をクエリします。
select_csv_params = {
"CsvHeaderInfo": "None",
"RecordDelimiter": "\r\n",
"LineRange": (500, 1000),
}
result = bucket.select_object(
key, "SELECT * FROM ossobject WHERE _3 > 44", select_callback, select_csv_params
)
print(result.read())
# 結果をローカルファイルに保存します。
bucket.select_object_to_file(
key, "python_select.csv", "SELECT * FROM ossobject WHERE _3 > 44",
select_callback, select_csv_params
)
bucket.delete_object(key)
# --- JSON DOCUMENT クエリ ---
key = "python_select.json"
content = '{"contacts":[{"key1":1,"key2":"hello world1"},{"key1":2,"key2":"hello world2"}]}'
bucket.put_object(key, content)
select_json_params = {"Json_Type": "DOCUMENT"}
result = bucket.select_object(
key,
"SELECT s.key2 FROM ossobject.contacts[*] s WHERE s.key1 = 1",
None,
select_json_params,
)
print(result.read())
bucket.delete_object(key)
# --- JSON LINES クエリ ---
key = "python_select_lines.json"
content = '{"key1":1,"key2":"hello world1"}\n{"key1":2,"key2":"hello world2"}'
bucket.put_object(key, content)
select_json_params = {"Json_Type": "LINES"}
json_header = bucket.create_select_object_meta(key, select_json_params)
print(f"Total rows: {json_header.rows}, total splits: {json_header.splits}")
result = bucket.select_object(
key, "SELECT s.key2 FROM ossobject s WHERE s.key1 = 1", None, select_json_params
)
print(result.read())
bucket.delete_object(key)OSS API を使用したオブジェクトのクエリ実行
RESTful API を直接使用して SelectObject を呼び出す場合、署名計算をコードに組み込む必要があります。詳細については、「SelectObject」をご参照ください。
ラージオブジェクトに対する並列クエリの実行
ラージオブジェクトに対しては、split-range を使用して、複数の同時実行リクエストにクエリを分割します。
分割(split)とバイト範囲(byte range)の使い分け:
| 方法 | 使用タイミング |
|---|---|
| 分割単位(推奨) | 分割は複数の行を含み、おおよそ均等なサイズになります。JSON オブジェクトおよび列値に改行を含む可能性のある CSV オブジェクトに使用します。 |
| バイト範囲単位 | シンプルですが、メタデータは不要です。列値に改行を含まない CSV オブジェクトにのみ使用します。 |
分割単位の並列クエリの手順:
分割数の合計値を取得するために CreateSelectObjectMeta を呼び出します。最初のクエリ実行前に非同期で呼び出すことで、余分なスキャンオーバーヘッドを回避できます。
利用可能なクライアントリソースに基づいて、同時実行数
nを決定します。分割数の合計値を
nで割って、1 回のリクエストあたりの分割数を算出します。n個の同時実行 SelectObject リクエストを送信します。各リクエストにはsplit-rangeパラメーターを指定します。例:split-range=1-20。結果を順序通りに結合します。