diff --git a/includes/menu.inc b/includes/menu.inc
index d23edd0..6ac26c1 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -2359,8 +2359,9 @@ function _menu_router_build($callbacks) {
     watchdog('php', 'Menu router rebuild failed - some paths may not work correctly.', array(), WATCHDOG_ERROR);
     return array();
   }
-  // Delete the existing router since we have some data to replace it.
-  db_query('DELETE FROM {menu_router}');
+
+  $rows_new = array();
+
   // Apply inheritance rules.
   foreach ($menu as $path => $v) {
     $item = &$menu[$path];
@@ -2443,23 +2444,33 @@ function _menu_router_build($callbacks) {
     }
 
     $title_arguments = $item['title arguments'] ? serialize($item['title arguments']) : '';
-    db_query("INSERT INTO {menu_router}
-      (path, load_functions, to_arg_functions, access_callback,
-      access_arguments, page_callback, page_arguments, fit,
-      number_parts, tab_parent, tab_root,
-      title, title_callback, title_arguments,
-      type, block_callback, description, position, weight, file)
-      VALUES ('%s', '%s', '%s', '%s',
-      '%s', '%s', '%s', %d,
-      %d, '%s', '%s',
-      '%s', '%s', '%s',
-      %d, '%s', '%s', '%s', %d, '%s')",
-      $path, $item['load_functions'], $item['to_arg_functions'], $item['access callback'],
-      serialize($item['access arguments']), $item['page callback'], serialize($item['page arguments']), $item['_fit'],
-      $item['_number_parts'], $item['tab_parent'], $item['tab_root'],
-      $item['title'], $item['title callback'], $title_arguments,
-      $item['type'], $item['block callback'], $item['description'], $item['position'], $item['weight'], $item['include file']);
+
+    $rows_new[$path] = array(
+      'path'             => $path,
+      'load_functions'   => $item['load_functions'],
+      'to_arg_functions' => $item['to_arg_functions'],
+      'access_callback'  => $item['access callback'],
+      'access_arguments' => serialize($item['access arguments']),
+      'page_callback'    => $item['page callback'],
+      'page_arguments'   => serialize($item['page arguments']),
+      'fit'              => $item['_fit'],
+      'number_parts'     => $item['_number_parts'],
+      'tab_parent'       => $item['tab_parent'],
+      'tab_root'         => $item['tab_root'],
+      'title'            => $item['title'],
+      'title_callback'   => $item['title callback'],
+      'title_arguments'  => $title_arguments,
+      'type'             => $item['type'],
+      'block_callback'   => $item['block callback'],
+      'description'      => $item['description'],
+      'position'         => $item['position'],
+      'weight'           => $item['weight'],
+      'file'             => $item['include file'],
+    );
   }
+
+  _menu_router_save_table_contents($rows_new);
+
   // Sort the masks so they are in order of descending fit, and store them.
   $masks = array_keys($masks);
   rsort($masks);
@@ -2468,6 +2479,102 @@ function _menu_router_build($callbacks) {
   return $menu;
 }
 
+
+function _menu_router_save_table_contents($rows_new) {
+
+  // These placeholders will be used in the SQL queries.
+  $field_placeholders = array(
+    'path'              => "'%s'",
+    'load_functions'    => "'%s'",
+    'to_arg_functions'  => "'%s'",
+    'access_callback'   => "'%s'",
+    'access_arguments'  => "'%s'",
+    'page_callback'     => "'%s'",
+    'page_arguments'    => "'%s'",
+    'fit'               => '%d',
+    'number_parts'      => '%d',
+    'tab_parent'        => "'%s'",
+    'tab_root'          => "'%s'",
+    'title'             => "'%s'",
+    'title_callback'    => "'%s'",
+    'title_arguments'   => "'%s'",
+    'type'              => '%d',
+    'block_callback'    => "'%s'",
+    'description'       => "'%s'",
+    'position'          => "'%s'",
+    'weight'            => '%d',
+    'file'              => "'%s'",
+  );
+
+  // Determine the diff of new and old table contents.
+  $delete = array();
+  $update = array();
+  $insert = $rows_new;
+  $q = db_query("SELECT * FROM {menu_router}");
+  while ($row_old = db_fetch_array($q)) {
+    $path = $row_old['path'];
+    if (!isset($rows_new[$path])) {
+      $delete[$path] = TRUE;
+    }
+    else {
+      // Compare the old row with the new row.
+      $row_changes = array();
+      foreach ($row_old as $key => $value_old) {
+        if ($rows_new[$path][$key] != $value_old) {
+          $row_changes[$key] = $rows_new[$path][$key];
+        }
+      }
+      if (count($row_changes)) {
+        $update[$path] = $row_changes;
+      }
+    }
+    unset($insert[$path]);
+  }
+
+  // Save the changes.
+
+  // Delete rows that no longer exist.
+  if ($n_delete = count($delete)) {
+    $placeholders_sql = implode(', ', array_fill(0, $n_delete, "'%s'"));
+    db_query("DELETE FROM {menu_router} WHERE path IN ($placeholders_sql)", array_keys($delete));
+  }
+
+  // Update rows that have changed.
+  if ($n_update = count($update)) {
+    foreach ($update as $path => $row_changes) {
+      $set_fragments = array();
+      $args = array();
+      foreach ($row_changes as $key => $value) {
+        $placeholder = $field_placeholders[$key];
+        $set_fragments[] = "$key = $placeholder";
+        $args[] = $value;
+      }
+      $set_sql = implode(', ', $set_fragments);
+      $args[] = $path;
+      db_query("UPDATE {menu_router} SET $set_sql WHERE path = '%s'", $args);
+    }
+  }
+
+  // Insert new rows, in chunks of 100.
+  if ($n_insert = count($insert)) {
+    $insert = array_values($insert);
+    $fieldnames_sql = implode(', ', array_keys($field_placeholders));
+    $row_sql = '(' . implode(', ', $field_placeholders) . ')';
+    $rows_sql = implode(', ', array_fill(0, 100, $row_sql));
+    for ($i = 0; $i < $n_insert; $i += 100) {
+      $args = array();
+      for ($j = 0; $i + $j < $n_insert && $j < 100; ++$j) {
+        $args = array_merge($args, array_values($insert[$i + $j]));
+      }
+      if ($j < 100) {
+        $rows_sql = implode(', ', array_fill(0, $j, $row_sql));
+      }
+      db_query("INSERT INTO {menu_router} ($fieldnames_sql) VALUES $rows_sql", $args);
+    }
+  }
+}
+
+
 /**
  * Returns TRUE if a path is external (e.g. http://example.com).
  */
