acos функция в Sqlite

У меня вопрос о запросах SQLite. Мне нужно выполнить запрос sqlite следующим образом:

SELECT id, ( 6371 * acos ( cos ( radians(78.3232) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(65.3234) ) + sin ( radians(78.3232) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 30 ORDER BY distance LIMIT 0 , 20; 

Но я получаю ошибку, потому что Sqlite не поддерживает такие функции, как acos . Есть ли способ выполнить мой запрос в Sqlite? Мне нужно использовать это на iOS. Любое практическое предложение приветствуется.

Вы можете реализовать свои собственные функции в SQLite. Например, напишите оболочку функции SQLite C для acos :

 void sqlite_acos(sqlite3_context *context, int argc, sqlite3_value **argv) { int dataType = sqlite3_value_numeric_type(argv[0]); if (dataType == SQLITE_INTEGER || dataType == SQLITE_FLOAT) { double value = sqlite3_value_double(argv[0]); sqlite3_result_double(context, acos(value)); } else { sqlite3_result_null(context); } } 

Вы можете повторить этот процесс для каждой из этих тригонометрических функций, которые вам нужны.

Затем вызовите sqlite3_create_function для сопоставления этой функции C с выражением SQL:

 - (BOOL)createFunctions:(sqlite3 *)db { int rc; if ((rc = sqlite3_create_function(db, "acos", 1, SQLITE_ANY, NULL, sqlite_acos, NULL, NULL)) != SQLITE_OK) { NSLog(@"%s: sqlite3_create_function acos error: %s (%d)", __FUNCTION__, sqlite3_errmsg(db), rc); } // repeat this for all of the other functions you define return rc; } 

Честно говоря, вместо того, чтобы писать эти отдельные тригонометрические функции, я бы написал один, который вычисляет расстояние на более высоком уровне. Это будет немного более эффективно. Это может выглядеть примерно так:

 double radians(double degrees) { return degrees * M_PI / 180.0; } void sqlite_distance(sqlite3_context *context, int argc, sqlite3_value **argv) { double values[4]; // get the double values for the four arguments for (int i = 0; i < 4; i++) { int dataType = sqlite3_value_numeric_type(argv[i]); if (dataType == SQLITE_INTEGER || dataType == SQLITE_FLOAT) { values[i] = sqlite3_value_double(argv[i]); } else { sqlite3_result_null(context); return; } } // let's give those values meaningful variable names double lat = radians(values[0]); double lng = radians(values[1]); double lat2 = radians(values[2]); double lng2 = radians(values[3]); // calculate the distance double result = 6371.0 * acos(cos(lat2) * cos(lat) * cos(lng - lng2) + sin(lat2) * sin(lat)); sqlite3_result_double(context, result); } 

И тогда вы определяете функцию distance в SQLite, например:

 int rc; if ((rc = sqlite3_create_function(db, "distance", 4, SQLITE_ANY, NULL, sqlite_distance, NULL, NULL)) != SQLITE_OK) { NSLog(@"%s: sqlite3_create_function distance error: %s (%d)", __FUNCTION__, sqlite3_errmsg(db), rc); } 

Итак, вы открываете базу данных, вызываете sqlite3_create_function для этой функции distance , а затем можете писать SQL, который использует эту новую функцию distance :

 const char *sql = "SELECT " "id, distance(lat, lng, 65.3234, 78.3232) AS distance " "FROM markers " "WHERE distance < 30 " "ORDER BY distance"; if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) != SQLITE_OK) NSLog(@"prepare failed: %s", sqlite3_errmsg(db)); while (sqlite3_step(statement) == SQLITE_ROW) { sqlite3_int64 rowid = sqlite3_column_int64(statement, 0); double distance = sqlite3_column_double(statement, 1); NSLog(@"%lld %f", rowid, distance); } sqlite3_finalize(statement); 

Эта функция distance действительна до закрытия базы данных, поэтому при каждом открытии базы данных обязательно вызывайте sqlite3_create_function .