R Academy · leksjon

Parameteriserte spørringer og transaksjoner

Send parametere trygt og håndter transaksjoner med flere trinn i R.

Leksjon 4 av 413 trinn

Parameteriserte spørringer og transaksjoner er en gratis leksjon i R Academy på CoddyKit. Dette er leksjon 4 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i R Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i R Academy inneholder totalt 4 leksjoner.

Hvorfor bruke parametriserte spørringer?

Når brukerinndata limes direkte inn i en SQL-streng, kan en angriper injisere skadelig SQL-kode. Parametriserte spørringer skiller SQL-strukturen fra dataverdiene, noe som gjør injeksjonsangrep umulige og samtidig gjør koden ryddigere og enklere å lese.

library(DBI)

# DANGEROUS: string concatenation (SQL injection risk!)
user_input <- "'; DROP TABLE users; --"
# bad_sql <- paste0("SELECT * FROM users WHERE name = '", user_input, "'")
# dbGetQuery(con, bad_sql)   <- NEVER do this

# SAFE: parameterized query
# dbGetQuery(con, 'SELECT * FROM users WHERE name = $1',
#            params = list(user_input))

cat('Parameterized queries prevent SQL injection')

dbGetQuery() med parametere

dbGetQuery() utfører en SELECT-setning og returnerer resultatene som en dataramme. Send en params-liste for å binde verdier til plassholdere. Syntaksen for plassholdere varierer etter driver: $1 for PostgreSQL og ? for SQLite/MySQL.

library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
dbWriteTable(con, 'employees', data.frame(
  name       = c('Alice', 'Bob', 'Carol'),
  department = c('Engineering', 'Marketing', 'Engineering'),
  salary     = c(90000, 75000, 95000)
))

# Parameterized SELECT — SQLite uses ?
result <- dbGetQuery(
  con,
  'SELECT name, salary FROM employees WHERE department = ?',
  params = list('Engineering')
)
print(result)

dbDisconnect(con)

dbExecute() – INSERT, UPDATE, DELETE

dbExecute() kjører SQL-setninger som endrer data (INSERT, UPDATE, DELETE), og returnerer antallet berørte rader. Bruk params for trygg binding av verdier. Dette er riktig funksjon for skriveoperasjoner.

library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
dbWriteTable(con, 'users', data.frame(
  id     = 1:3,
  name   = c('Alice', 'Bob', 'Carol'),
  active = c(TRUE, FALSE, TRUE)
))

# Safe UPDATE with parameters
rows_affected <- dbExecute(
  con,
  'UPDATE users SET active = ? WHERE id = ?',
  params = list(TRUE, 2)
)
cat('Rows updated:', rows_affected, '\n')

# Verify
result <- dbGetQuery(con, 'SELECT * FROM users WHERE id = 2')
cat('Bob active:', result$active)

dbDisconnect(con)

Flere parametere i én spørring

De kan binde flere parametere ved å oppgi dem alle i params-listen. De bindes i rekkefølge til plassholderne (? eller $1, $2, ...) i SQL-strengen. Sørg alltid for at antallet listeelementer samsvarer med antallet plassholdere.

library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
dbWriteTable(con, 'products', data.frame(
  id    = 1:4,
  name  = c('Apple', 'Banana', 'Cherry', 'Date'),
  price = c(1.5, 0.8, 3.0, 5.0),
  stock = c(100, 200, 50, 30)
))

# Filter by two parameters
result <- dbGetQuery(
  con,
  'SELECT name, price FROM products WHERE price > ? AND stock > ?',
  params = list(1.0, 40)
)
print(result)

dbDisconnect(con)

Parametrisert INSERT

Parametriserte INSERT-setninger legger trygt til nye poster. Bind verdien i hver kolonne som en parameter. For masseinnsetting bør De i stedet bruke dbAppendTable() med en dataramme – det er raskere, og DBI håndterer bindingen automatisk.

library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
dbExecute(con, 'CREATE TABLE logs (level TEXT, message TEXT, ts TEXT)')

# Parameterized INSERT
dbExecute(
  con,
  'INSERT INTO logs (level, message, ts) VALUES (?, ?, ?)',
  params = list('INFO', 'User logged in', as.character(Sys.time()))
)

result <- dbGetQuery(con, 'SELECT * FROM logs')
print(result)

dbDisconnect(con)

dbBegin() og dbCommit() – Transaksjoner

En transaksjon samler flere SQL-setninger i én atomisk enhet: enten lykkes alle, eller så lykkes ingen. Bruk dbBegin() for å starte, og deretter dbCommit() for å fullføre alle endringene samlet.

library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
dbExecute(con, 'CREATE TABLE accounts (id INTEGER, balance REAL)')
dbExecute(con, "INSERT INTO accounts VALUES (1, 1000), (2, 500)")

# Transfer 200 from account 1 to account 2
dbBegin(con)
  dbExecute(con, 'UPDATE accounts SET balance = balance - ? WHERE id = ?',
            params = list(200, 1))
  dbExecute(con, 'UPDATE accounts SET balance = balance + ? WHERE id = ?',
            params = list(200, 2))
dbCommit(con)

result <- dbGetQuery(con, 'SELECT * FROM accounts')
print(result)
dbDisconnect(con)

dbRollback() – Angre en transaksjon

Hvis en setning i en transaksjon mislykkes, kaller De dbRollback() for å angre alle endringer som er gjort siden dbBegin(). Dette holder databasen i en konsistent tilstand. Koble alltid dbBegin() sammen med enten dbCommit() eller dbRollback().

library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
dbExecute(con, 'CREATE TABLE orders (id INTEGER, amount REAL)')
dbExecute(con, 'INSERT INTO orders VALUES (1, 500)')

# Simulate a failed transaction
dbBegin(con)
tryCatch({
  dbExecute(con, 'UPDATE orders SET amount = ? WHERE id = ?',
            params = list(600, 1))
  stop('Simulated error during processing')  # something goes wrong
  dbCommit(con)
}, error = function(e) {
  dbRollback(con)
  cat('Transaction rolled back:', e$message, '\n')
})

# Amount is still 500 (rollback worked)
result <- dbGetQuery(con, 'SELECT * FROM orders')
cat('Amount after rollback:', result$amount)
dbDisconnect(con)

dbWithTransaction() – Et tryggere mønster

dbWithTransaction() pakker automatisk kodeblokken inn i en transaksjon. Den gjennomfører endringene hvis blokken lykkes, og ruller tilbake hvis det oppstår en feil. Dette er ryddigere og mindre utsatt for feil enn å kalle dbBegin() / dbCommit() / dbRollback() manuelt.

library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
dbExecute(con, 'CREATE TABLE ledger (account TEXT, amount REAL)')
dbExecute(con, "INSERT INTO ledger VALUES ('Alice', 1000), ('Bob', 500)")

# Automatic transaction management:
dbWithTransaction(con, {
  dbExecute(con, "UPDATE ledger SET amount = amount - 100 WHERE account = 'Alice'",
            params = list())
  dbExecute(con, "UPDATE ledger SET amount = amount + 100 WHERE account = 'Bob'",
            params = list())
})
# Committed automatically on success

print(dbGetQuery(con, 'SELECT * FROM ledger'))
dbDisconnect(con)

Feilhåndtering i transaksjoner

Ved å kombinere tryCatch() med dbWithTransaction() får De en ryddig feilrapportering. Når den indre blokken utløser en feil, ruller dbWithTransaction() automatisk tilbake, og feilhåndteringen kan logge eller kaste feilen videre.

library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
dbExecute(con, 'CREATE TABLE inventory (item TEXT, qty INTEGER)')
dbExecute(con, "INSERT INTO inventory VALUES ('Widget', 100)")

safe_update <- function(con, item, qty_change) {
  tryCatch(
    dbWithTransaction(con, {
      dbExecute(con,
        'UPDATE inventory SET qty = qty + ? WHERE item = ?',
        params = list(qty_change, item))
      cat('Updated', item, 'by', qty_change, '\n')
    }),
    error = function(e) cat('Failed (rolled back):', e$message, '\n')
  )
}

safe_update(con, 'Widget', -30)
print(dbGetQuery(con, 'SELECT * FROM inventory'))
dbDisconnect(con)

Batchinnsetting for bedre ytelse

Det går sakte å sette inn mange rader én etter én i en løkke. To bedre fremgangsmåter er å bruke dbAppendTable() for å sette inn en hel dataramme på én gang, eller å pakke individuelle innsettinger inn i én transaksjon – databaser skriver endringene til disk én gang til slutt, slik at masseinnsetting i en transaksjon er mye raskere enn innsettinger med automatisk gjennomføring.

library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
dbExecute(con, 'CREATE TABLE events (user_id INTEGER, action TEXT)')

# Fast: insert a whole data frame at once
new_events <- data.frame(
  user_id = c(101, 102, 103, 104),
  action  = c('login', 'view', 'purchase', 'logout')
)
dbAppendTable(con, 'events', new_events)

result <- dbGetQuery(con, 'SELECT COUNT(*) AS n FROM events')
cat('Rows inserted:', result$n)

dbDisconnect(con)

dbDisconnect() – Lukk alltid tilkoblinger

Databasetilkoblinger bruker ressurser på serveren. Kall alltid dbDisconnect(con) når De er ferdig. Hvis De bruker on.exit(dbDisconnect(con)) øverst i en funksjon, sikrer De at tilkoblingen lukkes selv om det oppstår en feil.

library(DBI)

# Pattern: use on.exit to guarantee disconnection
run_query <- function(sql) {
  con <- dbConnect(RSQLite::SQLite(), ':memory:')
  on.exit(dbDisconnect(con), add = TRUE)  # always runs

  dbExecute(con, 'CREATE TABLE t (x INTEGER)')
  dbExecute(con, 'INSERT INTO t VALUES (1), (2), (3)')
  result <- dbGetQuery(con, sql)
  result  # con is closed automatically after return
}

df <- run_query('SELECT * FROM t WHERE x > 1')
cat('Rows:', nrow(df))
# on.exit fires after return — connection cleanly closed

Hurtigsjekk

De må gruppere to relaterte UPDATE-setninger slik at enten begge lykkes, eller ingen av dem endrer databasen. Hvilken fremgangsmåte er best?

Parametriserte spørringer og transaksjoner – Viktigste punkter

Trygge og pålitelige databaseoperasjoner i R med DBI:

  • Sett aldri brukerinndata sammen med SQL-strenger – bruk alltid params
  • dbGetQuery(con, sql, params = list(...)) – trygg SELECT
  • dbExecute(con, sql, params = list(...)) – trygg INSERT/UPDATE/DELETE
  • Plassholdere: ? for SQLite/MySQL, $1 / $2 for PostgreSQL
  • dbBegin() + dbCommit() + dbRollback() – manuell transaksjonsstyring
  • dbWithTransaction(con, { ... }) – automatisk gjennomføring/tilbakerulling (anbefalt)
  • dbAppendTable(con, 'tbl', df) – rask masseinnsetting
  • on.exit(dbDisconnect(con)) – lukk alltid tilkoblinger
library(DBI)

con <- dbConnect(RSQLite::SQLite(), ':memory:')
on.exit(dbDisconnect(con), add = TRUE)

dbExecute(con, 'CREATE TABLE transfers (from_id INT, to_id INT, amount REAL)')

# Safe parameterized insert inside a transaction:
dbWithTransaction(con, {
  dbExecute(con,
    'INSERT INTO transfers (from_id, to_id, amount) VALUES (?, ?, ?)',
    params = list(1, 2, 250.00)
  )
})

result <- dbGetQuery(con, 'SELECT * FROM transfers')
cat('Transfer recorded:', result$amount)
Gratis å komme i gang

Lær deg R med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
43
Leksjoner
159

Ofte stilte spørsmål

Er leksjonen «Parameteriserte spørringer og transaksjoner» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien R Academy, inkludert «Parameteriserte spørringer og transaksjoner», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i R Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Parameteriserte spørringer og transaksjoner»?

Send parametere trygt og håndter transaksjoner med flere trinn i R. Du øver på R Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med R Academy?

Ingen tidligere erfaring er nødvendig. R Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.

Hvor lang tid tar leksjonen «Parameteriserte spørringer og transaksjoner»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne R Academy-leksjonen?

Ja. Alle R Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Grunnleggende om DBI og RSQLite
  2. Koble til PostgreSQL og MySQL
  3. dbplyr: SQL via dplyr-syntaks
  4. Parameteriserte spørringer og transaksjoner
← Tilbake til R Academy