2014-05-12 56 views
1

我有一個關於SQLite查詢的問題。 我需要執行這樣一個SQLite查詢:Sqlite中的acos函數

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上使用它。 歡迎任何實際的建議。

+0

也許你可以從[貢獻的文件](http://www.sqlite.org/contrib) – VMai

+2

使用extension-functions.c我想你基本上想要太多。選擇在最小/最大範圍內具有經度/緯度值的行,然後讀取選定的行並對提取的值執行計算。如果你能完成上述工作,它可能會比上述更快。 –

+0

順便說一句,'HAVING'子句只能與'GROUP BY'子句一起使用。如果你不使用'GROUP BY',那麼只需使用'WHERE'子句而不是'HAVING'。 – Rob

回答

4

你可以在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); 
} 

然後你會在SQLite的定義distance功能,像這樣:

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_functiondistance功能,那麼你可以編寫一個使用這一新功能distance SQL:

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函數的n在您關閉數據庫之前是有效的,因此每次打開數據庫時一定要撥打sqlite3_create_function

+0

謝謝..我可以在我的SQL查詢中使用你的距離函數嗎?對不起,但我是新的鄰接此.. – Safari

+0

@Safari我已經添加了演示,你將如何使用'距離'功能。 – Rob

+1

非常好!謝謝!! – Safari