diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,10 @@
 # Revision history for mysql-haskell
 
+## 0.8.4.0  -- 2018-10-23
+
+* Add `executeMany_` to execute batch SQLs, [#26](https://github.com/winterland1989/mysql-haskell/issues/26).
+* Optimize connection closing sequence, [#20](https://github.com/winterland1989/mysql-haskell/pull/20), [#25](https://github.com/winterland1989/mysql-haskell/pull/25).
+
 ## 0.8.3.0  -- 2017-10-09
 
 * Remove unnecessary exports from `Database.MySQL.Base`.
diff --git a/Database/MySQL/Base.hs b/Database/MySQL/Base.hs
--- a/Database/MySQL/Base.hs
+++ b/Database/MySQL/Base.hs
@@ -37,6 +37,7 @@
       -- * Direct query
     , execute
     , executeMany
+    , executeMany_
     , execute_
     , query_
     , queryVector_
@@ -121,6 +122,19 @@
 
 {-# SPECIALIZE executeMany :: MySQLConn -> Query -> [[MySQLValue]] -> IO [OK] #-}
 {-# SPECIALIZE executeMany :: MySQLConn -> Query -> [[Param]]      -> IO [OK] #-}
+
+-- | Execute multiple querys (without param) which don't return result-set.
+--
+-- This's useful when your want to execute multiple SQLs without params, e.g. from a
+-- SQL dump, or a table migration plan.
+--
+-- @since 0.8.4.0
+--
+executeMany_ :: MySQLConn -> Query -> IO [OK]
+executeMany_ conn@(MySQLConn is os _ _) qry = do
+    guardUnconsumed conn
+    writeCommand (COM_QUERY (fromQuery qry)) os
+    waitCommandReplys is
 
 -- | Execute a MySQL query which don't return a result-set.
 --
diff --git a/Database/MySQL/Connection.hs b/Database/MySQL/Connection.hs
--- a/Database/MySQL/Connection.hs
+++ b/Database/MySQL/Connection.hs
@@ -13,8 +13,9 @@
 
 module Database.MySQL.Connection where
 
+import           Control.Applicative
 import           Control.Exception               (Exception, bracketOnError,
-                                                  throwIO)
+                                                  throwIO, catch, SomeException)
 import           Control.Monad
 import qualified Crypto.Hash                     as Crypto
 import qualified Data.Binary                     as Binary
@@ -106,23 +107,29 @@
 --
 connectDetail :: ConnectInfo -> IO (Greeting, MySQLConn)
 connectDetail (ConnectInfo host port db user pass charset)
-  = bracketOnError open TCP.close go
+    = bracketOnError open TCP.close go
   where
     open  = connectWithBufferSize host port bUFSIZE
     go c  = do
-      let is = TCP.source c
-      is' <- decodeInputStream is
-      p <- readPacket is'
-      greet <- decodeFromPacket p
-      let auth = mkAuth db user pass charset greet
-      write c $ encodeToPacket 1 auth
-      q <- readPacket is'
-      if isOK q
-      then do
-          consumed <- newIORef True
-          let conn = MySQLConn is' (write c) (TCP.close c) consumed
-          return (greet, conn)
-      else TCP.close c >> decodeFromPacket q >>= throwIO . ERRException
+        let is = TCP.source c
+        is' <- decodeInputStream is
+        p <- readPacket is'
+        greet <- decodeFromPacket p
+        let auth = mkAuth db user pass charset greet
+        write c $ encodeToPacket 1 auth
+        q <- readPacket is'
+        if isOK q
+        then do
+            consumed <- newIORef True
+            let waitNotMandatoryOK = catch
+                    (void (waitCommandReply is'))           -- server will either reply an OK packet
+                    ((\ _ -> return ()) :: SomeException -> IO ())   -- or directy close the connection
+                conn = MySQLConn is'
+                    (write c)
+                    (writeCommand COM_QUIT (write c) >> waitNotMandatoryOK >> TCP.close c)
+                    consumed
+            return (greet, conn)
+        else TCP.close c >> decodeFromPacket q >>= throwIO . ERRException
 
     connectWithBufferSize h p bs = TCP.connectSocket h p >>= TCP.socketToConnection bs
     write c a = TCP.send c $ Binary.runPut . Binary.put $ a
@@ -197,6 +204,17 @@
         | isOK  p -> decodeFromPacket p
         | otherwise -> throwIO (UnexpectedPacket p)
 {-# INLINE waitCommandReply #-}
+
+waitCommandReplys :: InputStream Packet -> IO [OK]
+waitCommandReplys is = do
+    p <- readPacket is
+    if  | isERR p -> decodeFromPacket p >>= throwIO . ERRException
+        | isOK  p -> do ok <- decodeFromPacket p
+                        if isThereMore ok
+                        then (ok :) <$> waitCommandReplys is
+                        else return [ok]
+        | otherwise -> throwIO (UnexpectedPacket p)
+{-# INLINE waitCommandReplys #-}
 
 readPacket :: InputStream Packet -> IO Packet
 readPacket is = Stream.read is >>= maybe
diff --git a/Database/MySQL/Protocol/Packet.hs b/Database/MySQL/Protocol/Packet.hs
--- a/Database/MySQL/Protocol/Packet.hs
+++ b/Database/MySQL/Protocol/Packet.hs
@@ -72,6 +72,13 @@
 isEOF p = L.index (pBody p) 0 == 0xFE
 {-# INLINE isEOF #-}
 
+-- | Is there more packet to be read?
+--
+--  https://dev.mysql.com/doc/internals/en/status-flags.html
+isThereMore :: OK -> Bool
+isThereMore p  = okStatus p .&. 0x08 /= 0
+{-# INLINE isThereMore #-}
+
 -- | Decoding packet inside IO, throw 'DecodePacketException' on fail parsing,
 -- here we choose stability over correctness by omit incomplete consumed case:
 -- if we successful parse a packet, then we don't care if there're bytes left.
diff --git a/mysql-haskell.cabal b/mysql-haskell.cabal
--- a/mysql-haskell.cabal
+++ b/mysql-haskell.cabal
@@ -1,5 +1,5 @@
 name:                mysql-haskell
-version:             0.8.3.0
+version:             0.8.4.0
 synopsis:            pure haskell MySQL driver
 description:         pure haskell MySQL driver
 license:             BSD3
