iPhoneでSQLite3を使う4(データの削除とバインド変数)

今回は、削除についてです。SQLではDELETE文を使います。


DELETE文の例
DELETE文では、どのテーブルを削除したいかを記述します。また、削除するときにこんなデータだけ削除するという、条件をつけることもできます。

DELETE FROM TABLE名 WHERE 条件

例えば、userinfoテーブルから、すべてのデータを削除したいのであれば、条件をつけずに

DELETE FROM userinfo

となります。
userinfoテーブルから、friends_countが10000以上のものだけを削除するなら

DELETE FROM userinfo WHERE friends_count >= 10000

となります。
Objective-Cでの実装を書くと下記のようになります。前回のSELECT文と使うメソッドは同じなので解説は省きます。DELETE文の場合は、値を取得するわけではないので成功か失敗だけを見れば良いです。

-(BOOL)userInfoDeleteAll {
	// 削除用 SQL 文を作成
	NSString *deleteSQL = @"DELETE FROM userInfo";

	// SQL文のコンパイルと実行
	sqlite3_stmt *statement = Nil;
	if( sqlite3_prepare_v2(db_, [deleteSQL UTF8String], -1, &statement, NULL ) != SQLITE_OK) {
		NSLog( @"Failed to prepare statement with '%s'.", sqlite3_errmsg( db_ ));
		return NO;
	}
	int wasSucceeded = sqlite3_step(statement);

	//PREPARE済みSTATEMENTの廃棄
	sqlite3_finalize(statement);
	if( wasSucceeded != SQLITE_DONE ) {
		NSLog( @"Failed to delete from database with '%s'.", sqlite3_errmsg( db_ ));
		return NO;
	}
	return YES;
}



次は、指定のIDのみを削除する例です。引数にとして渡されたidを、SQLのwhere句に条件として指定します。

-(BOOL)userInfoDeleteID:(NSString*)id {
	// 削除用 SQL 文を作成
	NSString *deleteSQL = [NSString stringWithFormat:@"DELETE FROM userInfo WHERE id = %@", id];
	// SQL文のコンパイルと実行
	sqlite3_stmt *statement = Nil;
	if( sqlite3_prepare_v2(db_, [deleteSQL UTF8String], -1, &statement, NULL ) != SQLITE_OK) {
		NSLog( @"Failed to prepare statement with '%s'.", sqlite3_errmsg( db_ ));
		return NO;
	}
	int wasSucceeded = sqlite3_step(statement);

	//PREPARE済みSTATEMENTの廃棄
	sqlite3_finalize(statement);
	if( wasSucceeded != SQLITE_DONE ) {
		NSLog( @"Failed to delete from database with '%s'.", sqlite3_errmsg( db_ ));
		return NO;
	}
	return YES;
}

userInfoDeleteAllと同じですね。


次のuserInfoDeleteIDを見てください。さきほどのuserInfoDeleteIDとやれることは同じです。何が違うのでしょう?

sqlite3_stmt* statementDelete_;

-(BOOL)userInfoDeleteID:(NSString*)id {
	sqlite3_reset(statementDelete_);
	sqlite3_bind_text(statementDelete_, 1, [id UTF8String], -1, SQLITE_TRANSIENT);
	int wasSucceeded = sqlite3_step(statementDelete_);

	if( wasSucceeded != SQLITE_DONE ) {
		NSLog( @"Failed to delete from database with '%s'.", sqlite3_errmsg( db_ ));
		return NO;
	}
	return YES;
}

-(BOOL)prepareSQL {
	NSString* deleteSQL = @"delete from userInfo where id = ?";

	if(sqlite3_prepare_v2(db_, [deleteSQL UTF8String], -1, &statementDelete_, NULL) != SQLITE_OK)
	{
		NSLog( @"Failed to prepare statement with '%s'.", sqlite3_errmsg( db_ ));
		return NO;
	}
}

-(void)finalizeSQL {
	if (db_) {
		// PREPARE済みSTATEMENTの廃棄
		sqlite3_finalize(statementDelete_);
	}
}

使い方のイメージは、こんな感じです。

[self prepareSQL];
for ( ) {
	[self userInfoDeleteID:xxx];
}
[self finalizeSQL];

prepareSQLメソッドのSQL文を見てください。"?"という文字が入っています。これは、バインド変数といいます。prepareSQL実行時には値が決まっておらず、SQL実行時に値が決まるということを意味します。
では値はどこで決まっているかというと、sqlite3_bind_textというメソッドがあると思います。ここでセットしています。

sqlite3_bind_text(statementDelete_, 1, [id UTF8String], -1, SQLITE_TRANSIENT);

1というのが何番目の"?"かを意味して、そこに id の値をセットしてねということになります。


で、何がいいのか?


何度も実行する場合にメリットがあります。SQLコンパイルする必要がある。sqlite3_prepare_v2でコンパイルするといいました。SQLコンパイルする時に、そのテーブルの存在、指定の列が存在するのかチェックしたり結構な手間がかかります。バインド変数を使うとSQLコンパイルが1回で済むので、パフォーマンス上のメリットがあるということになります。