Batch Deleting

Previously we covered deleting a single model by ID. We can also perform more complex, batch deletes using a where clause and optionally joining tables.

Batch deleting is nearly identical to Batch Updating, so you should be familiar with that section. Like a batch update, a batch delete starts out with a call to DataContext.from. That returns a FromAdapter instance, which can be used to add a where clause, and/or join in additional tables.

The FromAdapter class also has a delete method. It returns a Delete instance that’s used to batch delete records.

Next up, we’ll go through some example code that deletes using a where clause and a join.

Batch Delete a Person’s Phone Numbers

In the example database we defined people and phone_numbers tables along with models for each. Now let’s say we want to delete all the PhoneNumber records associated with a Person, and we want to identify the person by firstName and lastName. We’ll delete from the phone_numbers table, join in people, and set an appropriate where clause. If you’re not familiar with where conditions or joining, take a look at those sections: Conditions; Joining.

// Used for building the delete's where clause.
const cb = new ConditionBuilder();

// Delete all PhoneNumber (pn) records for the Person (p) named
// "Rand al'Thor."
const query: Delete = dataContext
  .from(PhoneNumber, 'pn')
  .innerJoin(Person, 'p', 'pn.person')
  .where(
    cb.and(
      cb.eq('p.firstName', ':fname', 'Rand'),
      cb.eq('p.lastName', ':lname', 'al\'Thor')))
  .delete('pn');

As mentioned above, a Delete instance is returned. It’s a type of Query, so we can view the generated SQL by calling toString.

console.log(query.toString());

To run the query, we execute it.

const result: MutateResultType = await query.execute();

The result will have at least an affectedRows property, indicating the number of deleted rows.

Full Example

The full code presented above can be found in the formn-example repository under src/delete/delete-phone-numbers-for-person.ts.

import { inspect } from 'util';

import {
  MySQLDataContext, ConnectionOptions, ConditionBuilder, Delete,
  MutateResultType
} from 'formn';

import { PhoneNumber } from '../entity/phone-number.entity';
import { Person } from '../entity/person.entity';

async function main() {
  const connOpts: ConnectionOptions = require('../../connections.json');
  const dataContext = new MySQLDataContext();

  try {
    await dataContext.connect(connOpts);

    // Used for building the delete's where clause.
    const cb = new ConditionBuilder();

    // Delete all PhoneNumber (pn) records for the Person (p) named
    // "Rand al'Thor."
    const query: Delete = dataContext
      .from(PhoneNumber, 'pn')
      .innerJoin(Person, 'p', 'pn.person')
      .where(
        cb.and(
          cb.eq('p.firstName', ':fname', 'Rand'),
          cb.eq('p.lastName', ':lname', 'al\'Thor')))
      .delete('pn');

    console.log(query.toString());

    // The result will have at least an affectedRows property.
    const result: MutateResultType = await query.execute();

    console.log(inspect(result, {depth: null, compact: false}));

    await dataContext.end();
  }
  catch(err) {
    console.error(err);
  }
}

main();

Run the example with ts-node.

npx ts-node ./src/delete/delete-phone-numbers-for-person.ts

Output will look like this.

DELETE  `pn`
FROM    `phone_numbers` AS `pn`
INNER JOIN `people` AS `p` ON `pn`.`personID` = `p`.`personID`
WHERE   (`p`.`firstName` = :fname AND `p`.`lastName` = :lname)
ResultSetHeader {
  fieldCount: 0,
  affectedRows: 1,
  insertId: 0,
  info: '',
  serverStatus: 34,
  warningStatus: 0
}