まず第一に、PDO 拡張機能を備えた PHP 5.4.11 と MySQL 5.1.66 (Debian Squeeze 上) を使用した共有ホスティング サービスに取り組んでいます。
現在、ユーザーがデータベースにデータを保存するための制限付きのサービスを開発しています。今のところ、クォータに関して観察する必要があるユーザーデータが保存されるテーブルは 1 つだけです (ただし、これは変更される可能性があります)。すべてのテーブルは、テキスト列に InnoDB 格納エンジンと utf8_unicode_ci 照合を使用します。クォータに関連するテーブルに次の列があるとします。
+--------------+-----------+
| Column name | Type |
+--------------+-----------+
| id | int |
| userId | int |
| created | timestamp |
| lastModified | timestamp |
| description | varchar |
| content | text |
+--------------+-----------+
ここで、特定のユーザーに属するすべての行のサイズをバイト単位で計算する必要があります。ドキュメントを検索してググったところ、満足のいく答えが得られずに同様の質問をしている他の人しか見つかりませんでした。
MySQL 関数については知っていますLENGTH()
が、これは文字列関数であるため、(固定長の) 数値および日付/時刻フィールドが占めるスペースを返しません。また、文字列フィールドだけを考慮した場合、ユーザーはデータベースを空の文字列でいっぱいにするだけで、クォータに達することはありません。また、MySQL では各行の説明のためにオーバーヘッドがあることも知っていますが、それを計算に含めたくありません。(同様に、ディスク上のファイル サイズではなく、実際のファイル サイズを計算したいと思います。)
さらに、特定のテーブル構造に依存したくありません。これは変更される可能性があり、クォータを計算する関数を更新することを覚えておく必要があるためです。
既存の解決策がないため、独自の解決策を考え出しました (以下を参照)。ただし、いくつかの欠点があります。たとえば、次のとおりです。
- テーブルで使用されるデータ型とそれぞれのサイズのリストが必要です。
FLOAT(p)
、DECIMAL(M,D)
、NUMERIC(M,D)
およびデータ型を正確に処理できませんBIT(M)
(これは実装できますが)。- 2 つの別個のクエリが必要です。
だから今のところ、これは私が思いついたものです:
$db = new PDO(...);
$tablename = 'users';
$userId = 1;
// Make a list of type sizes in bytes - null indicates string types of
// varying size. If there is a type used in the database which is not
// listed here, an exception will be thrown.
$typeSizes = array(
'int' => 4,
'timestamp' => 4,
'varchar' => null,
'text' => null
);
// Get datatypes used in the table.
$sql = 'SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS '
. 'WHERE TABLE_NAME=?';
$stmt = $db->prepare($sql);
$stmt->bindValue(1, $tablename);
$stmt->execute();
$colTypes = array_map('reset', array_map('reset',
$stmt->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC)));
// Iterate over the existing columns. Sum up sizes of fixed size columns to
// get a 'fixed-size-factor' for a row. Make a list of varying size columns.
$fixedSizeFactor = 0;
$varyingSizeCols = array();
foreach ($colTypes as $colName => $colType) {
if (array_key_exists($colType, $typeSizes)) {
if ($typeSizes[$colType] !== null) {
$fixedSizeFactor += $typeSizes[$colType];
} else {
$varyingSizeCols[] = $colName;
}
} else {
$msg = "Unhandled column type '$colType' - unable to calculate used "
. 'storage. Probably the $typeSizes array needs to be updated.';
throw new Exception($msg);
}
}
// Get number of all records of the user and the size of his data in
// varying size columns.
$sumArgument = 0;
if (count($varyingSizeCols) > 0) {
$sumArgument = 'LENGTH(' . implode(') + LENGTH(', $varyingSizeCols) . ')';
}
$sql = 'SELECT SUM(' . $sumArgument . ') AS size, COUNT(*) AS count FROM '
. $tablename . ' WHERE userId=?';
$stmt = $db->prepare($sql);
$stmt->bindValue(1, $userId);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
// Calculate used storage.
$usedStorage = $result['count'] * $fixedSizeFactor + $result['size'];
私の質問は次のとおりです。これを行うための、より「ネイティブ」でハックの少ない方法はありますか? そうでない場合、パフォーマンスの最適化に関する提案はありますか?