diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,8 @@
+## 2.2
+
+* Add a `RawSql` instance for `Key`. This allows selecting primary keys using functions like `rawSql`. [#407](https://github.com/yesodweb/persistent/pull/407)
+* SqlBackend support for an optimized `insertMany`
+
 ## 2.1.6
 
 Important! If persistent-template is not upgraded to 2.1.3.3
diff --git a/Database/Persist/Class/PersistStore.hs b/Database/Persist/Class/PersistStore.hs
--- a/Database/Persist/Class/PersistStore.hs
+++ b/Database/Persist/Class/PersistStore.hs
@@ -80,31 +80,38 @@
             => val -> ReaderT backend m ()
     insert_ val = insert val >> return ()
 
-    -- | Create multiple records in the database.
+    -- | Create multiple records in the database and return their 'Key's.
     --
-    -- If you don't need the inserted @Key@s, use 'insertMany_'
+    -- If you don't need the inserted 'Key's, use 'insertMany_'.
     --
-    -- Some backends use the slow default implementation of
-    -- @mapM insert@
+    -- The MongoDB and PostgreSQL backends insert all records and
+    -- retrieve their keys in one database query.
+    --
+    -- The SQLite and MySQL backends use the slow, default implementation of
+    -- @mapM insert@.
     insertMany :: (MonadIO m, backend ~ PersistEntityBackend val, PersistEntity val)
                => [val] -> ReaderT backend m [Key val]
     insertMany = mapM insert
 
-    -- | Same as 'insertMany', but doesn't return any @Key@s.
+    -- | Same as 'insertMany', but doesn't return any 'Key's.
     --
-    -- SQL backends currently use an efficient implementation for this
-    -- unlike 'insertMany'.
+    -- The MongoDB, PostgreSQL, and MySQL backends insert all records in
+    -- one database query.
+    --
+    -- The SQLite backend inserts rows one-by-one.
     insertMany_ :: (MonadIO m, backend ~ PersistEntityBackend val, PersistEntity val)
                 => [val] -> ReaderT backend m ()
     insertMany_ x = insertMany x >> return ()
 
-    -- | Same as 'insertMany_', but takes an 'Entity' instead of just a record
+    -- | Same as 'insertMany_', but takes an 'Entity' instead of just a record.
     --
     -- Useful when migrating data from one entity to another
-    -- and want to preserve ids
+    -- and want to preserve ids.
     --
-    -- Some backends use the slow default implementation of
-    -- @mapM insertKey@
+    -- The MongoDB backend inserts all the entities in one database query.
+    --
+    -- The SQL backends use the slow, default implementation of
+    -- @mapM_ insertKey@.
     insertEntityMany :: (MonadIO m, backend ~ PersistEntityBackend val, PersistEntity val)
                      => [Entity val] -> ReaderT backend m ()
     insertEntityMany = mapM_ (\(Entity k record) -> insertKey k record)
diff --git a/Database/Persist/Sql/Class.hs b/Database/Persist/Sql/Class.hs
--- a/Database/Persist/Sql/Class.hs
+++ b/Database/Persist/Sql/Class.hs
@@ -58,6 +58,13 @@
     rawSqlProcessRow [pv]  = Single <$> fromPersistValue pv
     rawSqlProcessRow _     = Left $ pack "RawSql (Single a): wrong number of columns."
 
+instance (PersistEntity a, PersistEntityBackend a ~ SqlBackend) => RawSql (Key a) where
+  rawSqlCols _ key         = (length $ keyToValues key, [])
+  rawSqlColCountReason key = "The primary key is composed of "
+                             ++ (show $ length $ keyToValues key)
+                             ++ " columns"
+  rawSqlProcessRow         = keyFromValues
+
 instance (PersistEntity a, PersistEntityBackend a ~ SqlBackend) => RawSql (Entity a) where
     rawSqlCols escape = ((+1) . length . entityFields &&& process) . entityDef . Just . entityVal
         where
diff --git a/Database/Persist/Sql/Orphan/PersistStore.hs b/Database/Persist/Sql/Orphan/PersistStore.hs
--- a/Database/Persist/Sql/Orphan/PersistStore.hs
+++ b/Database/Persist/Sql/Orphan/PersistStore.hs
@@ -187,6 +187,21 @@
         t = entityDef $ Just val
         vals = map toPersistValue $ toPersistFields val
 
+    insertMany [] = return []
+    insertMany vals = do
+        conn <- ask
+
+        case connInsertManySql conn of
+            Nothing -> mapM insert vals
+            Just insertManyFn -> do
+                case insertManyFn ent valss of
+                    ISRSingle sql -> rawSql sql (concat valss)
+                    _ -> error "ISRSingle is expected from the connInsertManySql function"
+                where
+                    ent = entityDef vals
+                    valss = map (map toPersistValue . toPersistFields) vals
+
+
     insertMany_ [] = return ()
     insertMany_ vals = do
         conn <- ask
@@ -200,7 +215,7 @@
                 , ")"
                 ]
 
-        -- SQLite support is only in later versions
+        -- SQLite only supports multi-row inserts in 3.7.11+ (see https://www.sqlite.org/releaselog/3_7_11.html).
         if connRDBMS conn == "sqlite"
             then mapM_ insert vals
             else rawExecute sql (concat valss)
diff --git a/Database/Persist/Sql/Types.hs b/Database/Persist/Sql/Types.hs
--- a/Database/Persist/Sql/Types.hs
+++ b/Database/Persist/Sql/Types.hs
@@ -43,6 +43,7 @@
     { connPrepare :: Text -> IO Statement
     -- | table name, column names, id name, either 1 or 2 statements to run
     , connInsertSql :: EntityDef -> [PersistValue] -> InsertSqlResult
+    , connInsertManySql :: Maybe (EntityDef -> [[PersistValue]] -> InsertSqlResult) -- ^ SQL for inserting many rows and returning their primary keys, for backends that support this functioanlity. If 'Nothing', rows will be inserted one-at-a-time using 'connInsertSql'.
     , connStmtMap :: IORef (Map Text Statement)
     , connClose :: IO ()
     , connMigrateSql
diff --git a/persistent.cabal b/persistent.cabal
--- a/persistent.cabal
+++ b/persistent.cabal
@@ -1,5 +1,5 @@
 name:            persistent
-version:         2.1.6
+version:         2.2
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
