hmssqlguide

Writing data

Many statements in one round trip

executeStatements sends a list of statements in one request and returns each one’s row count. executeMany runs one statement for each row in a list of parameter rows:

counts <- executeMany conn "INSERT INTO dbo.item (id, name) VALUES (@p1, @p2)"
            [(1 :: Int, "one" :: Text), (2, "two"), (3, "three")]

When a statement fails#

The server runs the statements in order, and statements after a failed one may still run. The first failure is thrown once the whole response has been read. For all-or-nothing behavior, wrap the call in withTransaction.

To see each statement’s outcome instead of an exception, use executeStatementsOutcomes or executeManyOutcomes from Hmssql.Query.

Statements with results#

A BatchQuery pairs each statement with what to do with its rows, and runBatchQuery sends the statements together and reads the results in order. Both are in Hmssql.BatchQuery:

(updated, Only total, items) <- runBatchQuery conn $ (,,)
  <$> batchExecute ("UPDATE dbo.item SET price = price * 1.1 WHERE category = " <> param cat)
  <*> batchQuery1 ("SELECT SUM(price) FROM dbo.item WHERE category = " <> param cat)
  <*> batchQuery ("SELECT id, name FROM dbo.item WHERE category = " <> param cat)
-- updated :: Int64, total :: Double, items :: [(Int32, Text)]

The consumers mirror the query functions: batchExecute, batchQuery, batchQuery1, batchQueryMay, batchQueryFold, batchQueryMulti, batchQueryResultSets and batchQueryS. Each has a With variant that takes a row decoder instead of using the row type’s instance.

batchQueryS streams the last statement’s rows after runBatchQuery returns. Only the last statement can stream.

Errors behave as they do for executeStatements.

Scope#

Each statement is its own sp_executesql call, so a SET option or a #temp table it creates lasts only until it returns. Statements that must share such state go in one Query to queryResultSets instead, as Several result sets shows.

How it works#

All the statements travel in one message. The server answers each one between markers of its own, so a consumer only ever sees its own rows.

A very long list goes out in messages of about 1 MiB each. The list is consumed as it is sent, so memory stays bounded.