Передача массива в предложение sqlite WHERE IN через FMDB?

Можно ли передать массив оператору SELECT … WHERE … IN через FMDB? Я попытался взорвать массив следующим образом:

NSArray *mergeIds; // An array with NSNumber Objects
NSString *mergeIdString = [mergeIds componentsJoinedByString:@","];

NSString *query = @"SELECT * FROM items WHERE last_merge_id IN (?)";
FMResultSet *result = [database executeQuery:query, mergeIdString];

Это работает только в том случае, если в массиве есть ровно 1 объект, что наводит меня на мысль, что FMDB добавляет кавычки ко всей сжатой строке.

Поэтому я попытался передать массив как есть методу FMDB:

NSArray *mergeIds; // An array with NSNumber Objects
NSString *query = @"SELECT * FROM items WHERE last_merge_id IN (?)";
FMResultSet *result = [database executeQuery:query, mergeIds];

Который вообще не работает.

Я ничего не нашел об этом ни в README, ни в примерах на странице FMDB на github.

Спасибо, Стефан


person Stefan    schedule 05.12.2011    source источник


Ответы (6)


У меня была такая же проблема, и я понял это, по крайней мере, для своего собственного приложения. Во-первых, структурируйте свой запрос следующим образом, сопоставив количество вопросительных знаков с объемом данных в массиве:

NSString *getDataSql = @"SELECT * FROM data WHERE dataID IN (?, ?, ?)";

Затем используйте вызов executeQuery:withArgumentsInArray:

FMResultSet *results = [database executeQuery:getDataSql withArgumentsInArray:dataIDs];

В моем случае у меня был массив объектов NSString внутри NSArray с именем dataID. Я пробовал все, что угодно, чтобы заставить этот SQL-запрос работать, и, наконец, с помощью этой комбинации вызова sql/функции я смог получить правильные результаты.

person dreadpirateryan    schedule 18.07.2012

Ну, я думаю, мне придется использовать executeQueryWithFormat (что, согласно документации FMDB, не рекомендуется). Во всяком случае, вот мое решение:

NSArray *mergeIds; // An array of NSNumber Objects
NSString *mergeIdString = [mergeIds componentsJoinedByString:@","];

NSString *query = @"SELECT * FROM items WHERE last_merge_id IN (?)";
FMResultSet *res = [self.database executeQueryWithFormat:query, mergeIdString];
person Stefan    schedule 12.12.2011
comment
Но почему-то это все еще не работает, когда объекты являются NSStrings вместо NSNumbers. :-( - person Stefan; 22.12.2011
comment
Это не сработает. Санитизация SQL увидит эту единственную переменную. - person Luke The Obscure; 28.03.2014

Добавив к Уэйну Лю, если вы знаете, что строки не содержат одинарных или двойных кавычек, вы можете просто сделать:

NSString * delimitedString = [strArray componentsJoinedByString:@"','"];
NSString * sql = [NSString stringWithFormat:@"SELECT * FROM tableName WHERE fieldName IN ('%@')", delimitedString];
person BrianEff    schedule 22.02.2014

Если ключи являются строками, я использую следующий код для генерации команды SQL:

(предположим, что strArray — это NSArray, содержащий элементы NSString)

NSString * strComma = [strArray componentsJoinedByString:@"\", \""];
NSString * sql = [NSString stringWithFormat:@"SELECT * FROM tableName WHERE fieldName IN (\"%@\")", strComma];

Обратите внимание: если какие-либо элементы в strArray потенциально могут содержать символы «двойных кавычек», вам необходимо написать дополнительные коды (перед этими двумя строками), чтобы избежать их, написав вместо этого 2 двойных кавычки.

person Wayne Liu    schedule 15.02.2012

Я создал простое расширение FMDB для решения проблемы:

FMDB+InOperator на GitHub

person Stefan    schedule 06.10.2015

Вот расширение Swift для FMDatabase, которое разбивает параметры запроса массива на несколько именованных параметров.

extension FMDatabase {

    func executeQuery(query: String, params:[String: AnyObject]) -> FMResultSet? {

        var q = query
        var d = [String: AnyObject]()
        for (key, val) in params {
            if let arr = val as? [AnyObject] {
                var r = [String]()
                for var i = 0; i < arr.count; i++ {
                    let keyWithIndex = "\(key)_\(i)"
                    r.append(":\(keyWithIndex)")
                    d[keyWithIndex] = arr[i]
                }
                let replacement = ",".join(r)
                q = q.stringByReplacingOccurrencesOfString(":\(key)", withString: "(\(replacement))", options: NSStringCompareOptions.LiteralSearch, range: nil)
            }
            else {
                d[key] = val
            }
        }

        return executeQuery(q, withParameterDictionary: d)
    }

}

Пример:

let sql = "SELECT * FROM things WHERE id IN :thing_ids"
let rs = db.executQuery(sql, params: ["thing_ids": [1, 2, 3]])
person Justin Driscoll    schedule 09.06.2015