diff --git a/VERSIONS_HOTLINE/31425 b/VERSIONS_HOTLINE/31425
new file mode 100644
index 0000000000000000000000000000000000000000..ffb1afc4424d0c0f223148d1ced30009797a3bf3
--- /dev/null
+++ b/VERSIONS_HOTLINE/31425
@@ -0,0 +1 @@
+ - ticket #31425 : patch sql de suppression des pseudos notices orphelines
\ No newline at end of file
diff --git a/cosmogramme/php/_init.php b/cosmogramme/php/_init.php
index 19dae8187bafcdd1736c8d3db7db0d157f8960b5..0af923d9c11565ed8f24800127bf788277bca265 100644
--- a/cosmogramme/php/_init.php
+++ b/cosmogramme/php/_init.php
@@ -1,7 +1,7 @@
 <?php
 error_reporting(E_ERROR | E_PARSE);
 
-define("PATCH_LEVEL","275");
+define("PATCH_LEVEL","276");
 
 define("APPLI","cosmogramme");
 define("COSMOPATH", "/var/www/html/vhosts/opac2/www/htdocs");
diff --git a/cosmogramme/sql/patch/patch_276.php b/cosmogramme/sql/patch/patch_276.php
new file mode 100644
index 0000000000000000000000000000000000000000..419c9b69f50dd8d52b3d0cec3e3dd01652cf46a1
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_276.php
@@ -0,0 +1,31 @@
+<?php
+
+$adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
+$types = [Class_TypeDoc::SITE => 'getSite',
+          Class_TypeDoc::ARTICLE => 'getArticle'];
+
+foreach($types as $type => $parent_getter) {
+  $ids = $adapter->query('select id_notice from notices where type_doc=' . $type)
+                 ->fetchAll();
+
+  if (!$ids)
+    return;
+
+  $ids = array_chunk($ids, 500, true);
+
+  foreach($ids as $page) {
+    foreach($page as $data) {
+      if (!$record = Class_Notice::find((int)$data['id_notice']))
+        continue; // Record does not exists
+
+      if (call_user_func([$record, $parent_getter]))
+        continue; // Record is not an orphan
+
+      $record->delete();
+    }
+
+    Storm_Model_Abstract::unsetLoaders();
+    Storm_Model_Loader::resetCache();
+    gc_collect_cycles();
+  }
+}
\ No newline at end of file
diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php
index 77195bf2d0dd0415b32b4f4e9c845ebfb3799c14..b679b2e958113943ea5c470e263569d274d0d1c9 100644
--- a/tests/db/UpgradeDBTest.php
+++ b/tests/db/UpgradeDBTest.php
@@ -28,10 +28,9 @@ abstract class UpgradeDBTestCase extends PHPUnit_Framework_TestCase {
 
   public function setUp() {
     if (!self::$_upgrade_done) {
-
-    $this->_movePatchLevelTo(self::BACK_PATCH_LEVEL);
-    $this->_runAllPrepares();
-    self::$_upgrade_done = true;
+      $this->_movePatchLevelTo(self::BACK_PATCH_LEVEL);
+      $this->_runAllPrepares();
+      self::$_upgrade_done = true;
     }
 
     (new Class_Migration_ScriptPatchs())->runTo($this->_getPatchLevel());
@@ -43,6 +42,11 @@ abstract class UpgradeDBTestCase extends PHPUnit_Framework_TestCase {
   }
 
 
+  public function lastInsertId() {
+    return Zend_Db_Table::getDefaultAdapter()->lastInsertId();
+  }
+
+
   public function _movePatchLevelTo($patch_level) {
     $this->query('update variables set valeur=' . $patch_level . ' where clef="patch_level"');
     $this->query('delete from patch_hash');
@@ -77,7 +81,6 @@ abstract class UpgradeDBTestCase extends PHPUnit_Framework_TestCase {
 
 
 
-
 class UpgradeDB_263_Test extends UpgradeDBTestCase {
   public function prepare() {
     $this->query('delete from permission where code="CATEGORY"');
@@ -98,6 +101,31 @@ class UpgradeDB_263_Test extends UpgradeDBTestCase {
 
 
 
+class UpgradeDB_268_Test extends UpgradeDBTestCase {
+  protected static $user_backup;
+
+  public function prepare() {
+    static::$user_backup = $this->query('select id_user, pseudo from bib_admin_users order by id_user limit 1')->fetch();
+  }
+
+
+  public function tearDown() {
+    $this->query('update bib_admin_users set pseudo="' . static::$user_backup['pseudo'] . '" where id_user=' . static::$user_backup['id_user']);
+  }
+
+
+  /** @test */
+  public function userPseudoCanBeHundredCharsLong() {
+    $hundred_chars_pseudo = str_pad('pseudo', 100, 'o', STR_PAD_BOTH);
+
+    $this->query('update bib_admin_users set pseudo="' . $hundred_chars_pseudo . '" where id_user=' . static::$user_backup['id_user']);
+
+    $this->assertEquals($hundred_chars_pseudo,
+                        $this->query('select pseudo from bib_admin_users where id_user=' . static::$user_backup['id_user'])->fetch()['pseudo']);
+  }
+}
+
+
 
 class UpgradeDB_274_Test extends UpgradeDBTestCase {
   public function prepare() {
@@ -115,29 +143,34 @@ class UpgradeDB_274_Test extends UpgradeDBTestCase {
 
 
 
-
-class UpgradeDB_268_Test extends UpgradeDBTestCase {
-  protected static $user_backup;
+class UpgradeDB_276_Test extends UpgradeDBTestCase {
+  protected static $_inserted_records = [];
 
   public function prepare() {
-    static::$user_backup = $this->query('select id_user, pseudo from bib_admin_users order by id_user limit 1')->fetch();
+    // insert orphan sito and article
+    $this->_prepareRecordOfType(Class_TypeDoc::SITE)
+         ->_prepareRecordOfType(Class_TypeDoc::ARTICLE);
   }
 
 
-  public function tearDown() {
-    $this->query('update bib_admin_users set pseudo="' . static::$user_backup['pseudo'] . '" where id_user=' . static::$user_backup['id_user']);
+  protected function _prepareRecordOfType($type) {
+    $this->query('insert into notices (type_doc) values (' . $type . ')');
+    static::$_inserted_records[] = $id = $this->lastInsertId();
+
+    $this->query('insert into exemplaires '
+                 .'(id_notice, id_bib, section, emplacement, zone995, id_int_bib) values '
+                 .'(' . $id . ', 1, "", "", "", 1)');
+
+    return $this;
   }
 
 
   /** @test */
-  public function userPseudoCanBeHundredCharsLong() {
-    $hundred_chars_pseudo = str_pad('pseudo', 100, 'o', STR_PAD_BOTH);
+  public function shouldHaveDeletedOrphanedRecordsAndItems() {
+    $query = 'select * from notices where id_notice in (' . implode(', ', static::$_inserted_records) .')';
+    $this->assertEmpty($this->query($query)->fetchAll());
 
-    $this->query('update bib_admin_users set pseudo="' . $hundred_chars_pseudo . '" where id_user=' . static::$user_backup['id_user']);
-
-    $this->assertEquals($hundred_chars_pseudo,
-                        $this->query('select pseudo from bib_admin_users where id_user=' . static::$user_backup['id_user'])->fetch()['pseudo']);
+    $query = 'select * from exemplaires where id_notice in (' . implode(', ', static::$_inserted_records) .')';
+    $this->assertEmpty($this->query($query)->fetchAll());
   }
-}
-
-?>
\ No newline at end of file
+}
\ No newline at end of file