Table

Table can build query interface requests.

// SELECT * FROM player WHERE score >= 1000
let table = Table("player")
let rows: [Row] = try dbQueue.read { db in
    table.all()
        .filter(Column("score") >= 1000)
        .fetchAll(db)
}

Request Derivation

  • Creates a request for all rows of the table.

    // Fetch all players
    let table = Table<Player>("player")
    let request = table.all()
    let players: [Player] = try request.fetchAll(db)
    
  • Creates a request which fetches no row.

    // Fetch no players
    let table = Table<Player>("player")
    let request = table.none()
    let players: [Player] = try request.fetchAll(db) // Empty array
    
  • Creates a request which selects selection.

    // SELECT id, email FROM player
    let table = Table("player")
    let request = table.select(Column("id"), Column("email"))
    
  • Creates a request which selects sql.

    // SELECT id, email FROM player
    let table = Table("player")
    let request = table.select(sql: "id, email")
    
  • Creates a request which selects an SQL literal.

    Literals allow you to safely embed raw values in your SQL, without any risk of syntax errors or SQL injection:

    // SELECT id, email, score + 1000 FROM player
    let table = Table("player")
    let bonus = 1000
    let request = table.select(literal: """
        id, email, score + \(bonus)
        """)
    
  • Creates a request which selects selection, and fetches values of type type.

    try dbQueue.read { db in
        // SELECT max(score) FROM player
        let table = Table("player")
        let request = table.select([max(Column("score"))], as: Int.self)
        let maxScore: Int? = try request.fetchOne(db)
    }
    
  • Creates a request which selects sql, and fetches values of type type.

    try dbQueue.read { db in
        // SELECT max(score) FROM player
        let table = Table("player")
        let request = table.select(sql: "max(score)", as: Int.self)
        let maxScore: Int? = try request.fetchOne(db)
    }
    
  • Creates a request which selects an SQL literal, and fetches values of type type.

    Literals allow you to safely embed raw values in your SQL, without any risk of syntax errors or SQL injection:

    // SELECT IFNULL(name, 'Anonymous') FROM player
    let table = Table("player")
    let defaultName = "Anonymous"
    let request = table.select(
        literal: "IFNULL(name, \(defaultName))",
        as: String.self)
    let name: String? = try request.fetchOne(db)
    
  • Creates a request which appends selection.

    // SELECT id, email, name FROM player
    let table = Table("player")
    let request = table
        .select([Column("id"), Column("email")])
        .annotated(with: [Column("name")])
    
  • Creates a request with the provided predicate.

    // SELECT * FROM player WHERE email = 'arthur@example.com'
    let table = Table<Player>("player")
    let request = table.filter(Column("email") == "arthur@example.com")
    
  • Creates a request with the provided primary key predicate.

    // SELECT * FROM player WHERE id = 1
    let table = Table<Player>("player")
    let request = table.filter(key: 1)
    
  • Creates a request with the provided primary key predicate.

    // SELECT * FROM player WHERE id IN (1, 2, 3)
    let table = Table<Player>("player")
    let request = table.filter(keys: [1, 2, 3])
    
  • Creates a request with the provided predicate.

    // SELECT * FROM player WHERE email = 'arthur@example.com'
    let table = Table<Player>("player")
    let request = table.filter(sql: "email = ?", arguments: ["arthur@example.com"])
    
  • Creates a request with the provided predicate added to the eventual set of already applied predicates.

    Literals allow you to safely embed raw values in your SQL, without any risk of syntax errors or SQL injection:

    // SELECT * FROM player WHERE name = 'O''Brien'
    let table = Table<Player>("player")
    let name = "O'Brien"
    let request = table.filter(literal: "email = \(email)")
    
  • Creates a request sorted according to the provided orderings.

    // SELECT * FROM player ORDER BY name
    let table = Table<Player>("player")
    let request = table.order(Column("name"))
    
  • Creates a request sorted by primary key.

    // SELECT * FROM player ORDER BY id
    let table = Table<Player>("player")
    let request = table.orderByPrimaryKey()
    
    // SELECT * FROM country ORDER BY code
    let request = Country.orderByPrimaryKey()
    
  • Creates a request sorted according to sql.

    // SELECT * FROM player ORDER BY name
    let table = Table<Player>("player")
    let request = table.order(sql: "name")
    
  • Creates a request sorted according to an SQL literal.

    // SELECT * FROM player ORDER BY name
    let table = Table<Player>("player")
    let request = table.order(literal: "name")
    
  • Creates a request which fetches limit rows, starting at offset.

    // SELECT * FROM player LIMIT 1
    let table = Table<Player>("player")
    let request = table.limit(1)
    
  • Creates a request that allows you to define expressions that target a specific database table.

    See TableRecord.aliased(_:) for more information.

  • Returns a request which embeds the common table expression.

    See TableRecord.with(_:) for more information.

  • Creates a request which selects the primary key.

    All primary keys are supported:

    // SELECT id FROM player
    let table = Table("player")
    let request = try table.selectPrimaryKey(as: Int64.self)
    
    // SELECT code FROM country
    let table = Table("country")
    let request = try table.selectPrimaryKey(as: String.self)
    
    // SELECT citizenId, countryCode FROM citizenship
    let table = Table("citizenship")
    let request = try table.selectPrimaryKey(as: Row.self)
    

Counting All

Fetching Records from Table

  • A cursor over all records fetched from the database.

    // SELECT * FROM player
    let table = Table<Player>("player")
    let players = try table.fetchCursor(db) // Cursor of Player
    while let player = try players.next() {  // Player
        ...
    }
    

    Records are iterated in the natural ordering of the table.

    If the database is modified during the cursor iteration, the remaining elements are undefined.

    The cursor must be iterated in a protected dispatch queue.

    Throws

    A DatabaseError is thrown whenever an SQLite error occurs.
  • An array of all records fetched from the database.

    // SELECT * FROM player
    let table = Table<Player>("player")
    let players = try table.fetchAll(db) // [Player]
    

    Throws

    A DatabaseError is thrown whenever an SQLite error occurs.
  • The first found record.

    // SELECT * FROM player LIMIT 1
    let table = Table<Player>("player")
    let player = try table.fetchOne(db) // Player?
    

    Throws

    A DatabaseError is thrown whenever an SQLite error occurs.
  • A set of all records fetched from the database.

    // SELECT * FROM player
    let table = Table<Player>("player")
    let players = try table.fetchSet(db) // Set<Player>
    

    Throws

    A DatabaseError is thrown whenever an SQLite error occurs.

Associations to TableRecord

Associations to CommonTableExpression

  • Creates an association to a common table expression that you can join or include in another request.

    For more information, see TableRecord.association(to:on:).

  • Creates an association to a common table expression that you can join or include in another request.

    The key of the returned association is the table name of the common table expression.

“Through” Associations

Joining Methods

  • Creates a request that prefetches an association.

  • Creates a request that includes an association. The columns of the associated record are selected. The returned association does not require that the associated database table contains a matching row.

  • Creates a request that includes an association. The columns of the associated record are selected. The returned association requires that the associated database table contains a matching row.

  • Creates a request that includes an association. The columns of the associated record are not selected. The returned association does not require that the associated database table contains a matching row.

  • Creates a request that includes an association. The columns of the associated record are not selected. The returned association requires that the associated database table contains a matching row.

  • Creates a request which appends columns of an associated record to the columns of the table.

    let playerTable = Table("player")
    let teamTable = Table("team")
    let playerTeam = playerTable.belongsTo(teamTable)
    
    // SELECT player.*, team.color
    // FROM player LEFT JOIN team ...
    let teamColor = playerTeam.select(Column("color")
    let request = playerTable.annotated(withOptional: teamColor))
    

    This method performs the same SQL request as including(optional:). The difference is in the shape of Decodable records that decode such a request: the associated columns can be decoded at the same level as the main record:

    struct PlayerWithTeamColor: FetchableRecord, Decodable {
        var player: Player
        var color: String?
    }
    let players = try dbQueue.read { db in
        try request
            .asRequest(of: PlayerWithTeamColor.self)
            .fetchAll(db)
    }
    

    Note: this is a convenience method. You can build the same request with TableAlias, annotated(with:), and joining(optional:):

    let teamAlias = TableAlias()
    let request = playerTable
        .annotated(with: teamAlias[Column("color")])
        .joining(optional: playerTeam.aliased(teamAlias))
    
  • Creates a request which appends columns of an associated record to the columns of the table.

    let playerTable = Table("player")
    let teamTable = Table("team")
    let playerTeam = playerTable.belongsTo(teamTable)
    
    // SELECT player.*, team.color
    // FROM player JOIN team ...
    let teamColor = playerTeam.select(Column("color")
    let request = playerTable.annotated(withRequired: teamColor))
    

    This method performs the same SQL request as including(required:). The difference is in the shape of Decodable records that decode such a request: the associated columns can be decoded at the same level as the main record:

    struct PlayerWithTeamColor: FetchableRecord, Decodable {
        var player: Player
        var color: String
    }
    let players = try dbQueue.read { db in
        try request
            .asRequest(of: PlayerWithTeamColor.self)
            .fetchAll(db)
    }
    

    Note: this is a convenience method. You can build the same request with TableAlias, annotated(with:), and joining(required:):

    let teamAlias = TableAlias()
    let request = playerTable
        .annotated(with: teamAlias[Column("color")])
        .joining(required: playerTeam.aliased(teamAlias))
    

Association Aggregates

  • Creates a request with the provided aggregate predicate.

    // SELECT player.*
    // FROM player LEFT JOIN book ...
    // HAVING COUNT(DISTINCT book.id) = 0
    let table = Table<Player>("player")
    var request = table.all()
    request = request.having(Player.books.isEmpty)
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

Batch Delete

  • Deletes all rows, and returns the number of deleted rows.

    Throws

    A DatabaseError is thrown whenever an SQLite error occurs.

Check Existence by Single-Column Primary Key

  • Returns whether a row exists for this primary key.

    try Table("player").exists(db, key: 123)
    try Table("country").exists(db, key: "FR")
    

    When the table has no explicit primary key, GRDB uses the hidden “rowid” column:

    try Table("document").exists(db, key: 1)
    

Deleting by Single-Column Primary Key

  • Delete rows identified by their primary keys, and returns the number of deleted rows.

    // DELETE FROM player WHERE id IN (1, 2, 3)
    try Table("player").deleteAll(db, keys: [1, 2, 3])
    
    // DELETE FROM country WHERE code IN ('FR', 'US', 'DE')
    try Table("country").deleteAll(db, keys: ["FR", "US", "DE"])
    

    When the table has no explicit primary key, GRDB uses the hidden “rowid” column:

    // DELETE FROM document WHERE rowid IN (1, 2, 3)
    try Table("document").deleteAll(db, keys: [1, 2, 3])
    
  • Delete a row, identified by its primary key, and returns whether a database row was deleted.

    // DELETE FROM player WHERE id = 123
    try Table("player").deleteOne(db, key: 123)
    
    // DELETE FROM country WHERE code = 'FR'
    try Table("country").deleteOne(db, key: "FR")
    

    When the table has no explicit primary key, GRDB uses the hidden “rowid” column:

    // DELETE FROM document WHERE rowid = 1
    try Table("document").deleteOne(db, key: 1)
    

Batch Update

  • Updates all rows, and returns the number of updated rows..

    For example:

    try dbQueue.write { db in
        // UPDATE player SET score = 0
        try Table("player").updateAll(db, [Column("score").set(to: 0)])
    }
    

    Throws

    A DatabaseError is thrown whenever an SQLite error occurs.