SQLite.Swift, как получить случайную строку, которая никогда не повторяется

Я экспериментирую с SQlite.Swift в iOS и пытаюсь случайным образом выбрать строку из таблицы в базе данных SQLite.

Таблица — movies, а ее столбцы — movieID, title, genre и seen.

Проблема: фильмы должны выбираться случайным образом, но как только я устанавливаю seen на «да», они больше не должны выбираться.

func randomRow() -> Int {

       let path = NSSearchPathForDirectoriesInDomains(
                 .documentDirectory,
                 .userDomainMask, true)
                 .first!

       let database = try! Connection("\(path)/movies.sqlite3")

       var randomRow = Int(arc4random_uniform(UInt32(try!
                       database.scalar(movies.where(seen != "yes")
                       .select(movieID.distinct.count)))))

       while previousNumber == randomRow {
             randomRow = Int(arc4random_uniform(UInt32(try!
             database.scalar(movies.where(seen != "yes")
             .select(movieID.distinct.count)))))
           }
             previousNumber = randomRow
             return randomRow
   }

      let destRow = randomRow()

Теперь я хочу использовать этот случайный номер строки, который будет получен из таблицы, чтобы вывести title и genre случайного фильма в let ("dispMovie" и "movieGenre") для вывода в UITextField.

          let path = NSSearchPathForDirectoriesInDomains(
                     .documentDirectory,
                     .userDomainMask, true)
                     .first!

          let database = try! Connection("\(path)/filme.sqlite3")

          for movie in try! database.prepare(
                            self.movies.where(
                            seen != "yes" && movieID == "\(destRow)")) {

dispMovie = movie[title]
movieGenre = movie[genre]
                }

    outputTextField.text = "\(dispMovie), \(movieGenre)"

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

Я также пытался вызвать Raw SQLite с помощью этого:

let newMovie = try! database.scalar(
                    "SELECT title, genre FROM movies WHERE seen != yes
                    ORDER BY RANDOM() LIMIT 1") as! String

outputTextField.text = newMovie

Но это отображает только название, а не жанр, и поиск жанра в дополнительной строке невозможен, потому что фильм выбирается из случайной строки. Я пытался сослаться на название фильма в коде Raw, но это приводило к сбою приложения.

Спасибо за помощь и подсказки.


person Aeger    schedule 20.12.2017    source источник


Ответы (2)


Никакого Рождества, пока это не будет сделано!

После нескольких дней размышлений и беготни по стенам мне вдруг пришла в голову старая добрая петля while. Оказалось, что это работает, поэтому я оставляю это как ответ на свой вопрос:

Забудьте о randomRow(). В этом нет необходимости. В первую очередь я сосредоточился на SQLite RAW и продолжил с комбинацией scalar, where и select из библиотеки SQLite.Swift. В итоге получил вот это:

Публикую весь мой UIViewController, чтобы вы могли проверить себя:

class TestView: UIViewController {

//Table
    let movies = Table("movies")

//Expressions
    let movieId = Expression<String>("ID")
    let movieTitle = Expression<String>("movieTitle")
    let genre = Expression<String>("genre")
    let seen = Expression<String>("seen")

//to find with database connection
    var randomMovie = String()
    var movieDetails = [String]()

//Array of all movies seen
    var seenMovies = [String]()

//TestLabel to check everything
    @IBOutlet weak var testLabel: UILabel!

    @IBAction func testButton(_ sender: Any) {

//Open database connection
        let path = NSSearchPathForDirectoriesInDomains(
            .documentDirectory, .userDomainMask, true
            ).first!

        let database = try! Connection("\(path)/movies.sqlite3")

//Check column "seen" and add all movie Titles to Array seenMovies        
        for movie in try! database.prepare(self.movies.where(seen == "yes")) {
            self.seenMovies.append(movie[movieTitle])
        }

//Select random row from movies Table with SQLite RAW as String
        self.randomMovie = "\(try! database.scalar("SELECT movieTitle FROM movies WHERE seen != yes ORDER BY RANDOM() LIMIT 1"))"

//Create Array of details for that randomly picked movie
//Use scalar, where and select to find details from the picked movie above
        self.movieDetails = ["\(randomMovie!)",
            "\(try! database.scalar(movies.where(movieTitle == "\(randomMovie!)").select(genre)))"]

//And here's the "unique" thing...
//while randomMovie is also an element in seenMovies, loop until it's not
        while seenMovies.contains("\(randomMovie!)") {
            randomMovie = "\(try! database.scalar("SELECT movieTitle FROM movies WHERE seen != yes ORDER BY RANDOM() LIMIT 1"))"
        }
//let testLabel show the results and see that no seen movie will be picked        
        testLabel.text = "\(details)\n\n\(erraten)"

//To prevent the Array from being appended over and over:
        self.seenMovies.removeAll()

        } 
    }

Поскольку я все еще считаю себя новичком (начал программировать в октябре 17 года), пожалуйста, не стесняйтесь оптимизировать это, если это возможно. Все мои тесты прошли очень хорошо, и ни один фильм не повторялся при переключении на «просмотр».

person Aeger    schedule 23.12.2017
comment
небольшое подтверждение из сегодняшнего тестового сеанса: тестировал этот код со 150 фильмами три раза (3 * 150, а не с 3 фильмами ;-)). Работает все хорошо. Ни один просмотренный фильм не отображался снова. - person Aeger; 24.12.2017

Найдено решение из ошибки SQLite.Swift, зарегистрированной в 2016 году.

let db = Connection(databasePath)
let movies = Table("movies")
let query = movies.order(Expressions<Int>.random()).limit(1)

for row in try! db.prepare(query) {
  // handle query result here
}
person Glass    schedule 16.09.2020