Syntax

Platform.Function.DeleteDE(deName, whereFieldNames, whereFieldValues)
3 arguments

Parameters

Name Type Required Description
deName string Yes Data Extension Name (the external key / CustomerKey is not accepted — runtime-verified)
whereFieldNames string[] Yes Array of column names to match for deletion
whereFieldValues array Yes Array of values aligned to whereFieldNames that identify rows to delete
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: Parameters —
 * Platform.Function.DeleteDE(deName, whereFieldNames, whereFieldValues)
 *
 * Proves:
 *   1. The member exists and the documented 3-argument call succeeds against
 *      a real data extension that this script creates itself.
 *   2. deName resolves the data extension by its NAME. The data extension
 *      built here deliberately has a Name that DIFFERS from its CustomerKey,
 *      so the two identifiers are distinguishable; the CustomerKey form
 *      throws and removes nothing.
 *   3. whereFieldNames is an array of column names and whereFieldValues an
 *      array of values aligned to it — a single-column filter and a
 *      two-column (AND) filter are both exercised, and the AND filter is
 *      proven to spare rows that match only one of the two columns.
 *   4. Exactly three arguments are required (min_args 3 / max_args 3):
 *      arity 0, 1, 2 and 4 all throw.
 *   5. THE DELETE REALLY COMMITS: every row is read back afterwards with
 *      Platform.Function.LookupRows and the surviving / removed rows match
 *      the filters exactly. DeleteDE returns no count, so read-back is the
 *      ONLY evidence available.
 *   6. A filter naming a column that does not exist throws, and a data
 *      extension name that does not exist throws — and neither removes
 *      anything.
 *
 * QUERY-CACHING CONSTRAINT: identical data-extension queries are cached
 * within one request, so each verification query below is issued only once.
 * Setup state is asserted through InsertData's own return count rather than
 * through a read that would later have to be repeated.
 *
 * SCOPE: CloudPage only (MCDEV_Training_QA business unit). The page also
 * lists email, automation and triggered-send availability; those execution
 * contexts were NOT exercised here.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}

/* Build a throwaway data extension whose Name differs from its CustomerKey. */
function addField(de, name, len, isKey) {
    var f = Platform.Function.CreateObject("DataExtensionField");
    Platform.Function.SetObjectProperty(f, "Name", name);
    Platform.Function.SetObjectProperty(f, "FieldType", "Text");
    Platform.Function.SetObjectProperty(f, "MaxLength", len);
    Platform.Function.SetObjectProperty(f, "IsPrimaryKey", isKey);
    Platform.Function.SetObjectProperty(f, "IsRequired", isKey);
    Platform.Function.AddObjectArrayItem(de, "Fields", f);
}
function createDE(name, key) {
    var de = Platform.Function.CreateObject("DataExtension");
    Platform.Function.SetObjectProperty(de, "CustomerKey", key);
    Platform.Function.SetObjectProperty(de, "Name", name);
    addField(de, "Email", "100", "true");
    addField(de, "Status", "50", "false");
    addField(de, "Active", "10", "false");
    var st = [0, 0];
    return String(Platform.Function.InvokeCreate(de, st, null));
}
function dropDE(key) {
    var de = Platform.Function.CreateObject("DataExtension");
    Platform.Function.SetObjectProperty(de, "CustomerKey", key);
    var st = [0, 0];
    return String(Platform.Function.InvokeDelete(de, st, null));
}
function rowCount(deName, field, value) {
    var rows = Platform.Function.LookupRows(deName, field, value);
    return rows === null ? 0 : rows.length;
}

var deName = "ssjsguide_deletede_params_name";
var deKey = "ssjsguide_deletede_params_key";

assert("setup: the throwaway data extension is created", createDE(deName, deKey), "OK");
assert("control: the Name and the CustomerKey differ", deName === deKey ? "same" : "different", "different");

/* Seed four rows: a(expired,0) b(expired,1) c(active,1) d(active,1). */
assert("setup: row a inserted", Platform.Function.InsertData(deName, ["Email", "Status", "Active"], ["a@example.com", "expired", "0"]), 1);
assert("setup: row b inserted", Platform.Function.InsertData(deName, ["Email", "Status", "Active"], ["b@example.com", "expired", "1"]), 1);
assert("setup: row c inserted", Platform.Function.InsertData(deName, ["Email", "Status", "Active"], ["c@example.com", "active", "1"]), 1);
assert("setup: row d inserted", Platform.Function.InsertData(deName, ["Email", "Status", "Active"], ["d@example.com", "active", "1"]), 1);

/* 1 + 3. The documented 3-argument call, single-column filter. */
var res = Platform.Function.DeleteDE(deName, ["Email"], ["a@example.com"]);
assert("the documented 3-argument call does not throw and yields null", res === null ? "null" : "not null", "null");

/* 3. Two-column (AND) filter — arrays aligned by position. */
var res2 = Platform.Function.DeleteDE(deName, ["Status", "Active"], ["active", "1"]);
assert("the two-column AND filter call also yields null", res2 === null ? "null" : "not null", "null");

/* 2. The CustomerKey is not accepted — it throws instead of deleting. */
assertThrows("DEV the external key / CustomerKey is rejected (docs do not restrict deName to the Name)", function () {
    return Platform.Function.DeleteDE(deKey, ["Email"], ["b@example.com"]);
});

/* 4. Exactly three arguments. */
assertThrows("arity 0 throws", function () { return Platform.Function.DeleteDE(); });
assertThrows("arity 1 throws", function () { return Platform.Function.DeleteDE(deName); });
assertThrows("arity 2 throws", function () { return Platform.Function.DeleteDE(deName, ["Email"]); });
assertThrows("arity 4 throws", function () { return Platform.Function.DeleteDE(deName, ["Email"], ["b@example.com"], "extra"); });

/* 6. Bad filter column and bad data extension name both throw. */
assertThrows("a filter column that does not exist throws", function () {
    return Platform.Function.DeleteDE(deName, ["NoSuchColumn"], ["x"]);
});
assertThrows("a data extension name that does not exist throws", function () {
    return Platform.Function.DeleteDE("ssjsguide_deletede_no_such_de", ["Email"], ["x"]);
});

/* 5. Read every row back — each query issued exactly once. */
assert("the single-filter delete really committed: row a is gone", rowCount(deName, "Email", "a@example.com"), 0);
assert("the AND filter spared the 'expired' row: row b survived", rowCount(deName, "Email", "b@example.com"), 1);
assert("the AND filter removed row c", rowCount(deName, "Email", "c@example.com"), 0);
assert("the AND filter removed row d", rowCount(deName, "Email", "d@example.com"), 0);
assert("no 'active' row is left", rowCount(deName, "Status", "active"), 0);
assert("exactly 1 'expired' row is left", rowCount(deName, "Status", "expired"), 1);
assert("the rejected CustomerKey call and the failed negative cases removed nothing", rowCount(deName, "Active", "1"), 1);

/* Cleanup. */
assert("cleanup: the throwaway data extension is deleted", dropDE(deKey), "OK");
</script>

Description

DeleteDE removes rows from a Data Extension matching the filter criteria. It performs the same delete as DeleteData, but returns null (no row count).

Show test script — DeleteDE runs and commits on a CloudPage, returns null, and resolves by Name only
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Differs-from-docs claim, all three halves of it:
 *
 *   a) The official docs restrict DeleteDE to EMAIL contexts. At runtime it
 *      nevertheless EXECUTES and COMMITS its delete on a CloudPage — this
 *      whole script runs on a CloudPage, and the targeted row is proven
 *      absent afterwards.
 *   b) It returns null, NOT the affected-row count that DeleteData returns
 *      for the identical operation. Both are called side by side here.
 *   c) It resolves the data extension by NAME only, not by the external key
 *      / CustomerKey.
 *
 * THE CONTROL THAT MAKES (c) DISCRIMINATING: the throwaway data extension
 * built here has a Name that is DIFFERENT from its CustomerKey. If the
 * engine accepted either identifier, the CustomerKey call would delete the
 * row. It does not — and because DeleteDE itself reports nothing, the proof
 * that the row survived that call is the FOLLOWING DeleteData(<Name>, …)
 * returning 1 rather than 0.
 *
 * QUERY-CACHING CONSTRAINT: identical data-extension queries are cached
 * within one request, so every verification query below is issued only once.
 *
 * SCOPE: CloudPage only (MCDEV_Training_QA business unit). The email context
 * in which the official docs place DeleteDE was NOT exercised — the claim
 * proven here is that the CloudPage context works too, not that the email
 * context differs.
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}

function addField(de, name, len, isKey) {
    var f = Platform.Function.CreateObject("DataExtensionField");
    Platform.Function.SetObjectProperty(f, "Name", name);
    Platform.Function.SetObjectProperty(f, "FieldType", "Text");
    Platform.Function.SetObjectProperty(f, "MaxLength", len);
    Platform.Function.SetObjectProperty(f, "IsPrimaryKey", isKey);
    Platform.Function.SetObjectProperty(f, "IsRequired", isKey);
    Platform.Function.AddObjectArrayItem(de, "Fields", f);
}
function createDE(name, key) {
    var de = Platform.Function.CreateObject("DataExtension");
    Platform.Function.SetObjectProperty(de, "CustomerKey", key);
    Platform.Function.SetObjectProperty(de, "Name", name);
    addField(de, "Email", "100", "true");
    addField(de, "Status", "50", "false");
    var st = [0, 0];
    return String(Platform.Function.InvokeCreate(de, st, null));
}
function dropDE(key) {
    var de = Platform.Function.CreateObject("DataExtension");
    Platform.Function.SetObjectProperty(de, "CustomerKey", key);
    var st = [0, 0];
    return String(Platform.Function.InvokeDelete(de, st, null));
}
function rowCount(deName, field, value) {
    var rows = Platform.Function.LookupRows(deName, field, value);
    return rows === null ? 0 : rows.length;
}

var deName = "ssjsguide_deletede_cp_name";
var deKey = "ssjsguide_deletede_cp_key";

/* c) The control: Name and CustomerKey are different strings. */
assert("control: the Name and the CustomerKey differ", deName === deKey ? "same" : "different", "different");

assert("setup: the throwaway data extension is created", createDE(deName, deKey), "OK");
assert("setup: the key-test row is inserted", Platform.Function.InsertData(deName, ["Email", "Status"], ["keytest@example.com", "keep"]), 1);
assert("setup: the cloudpage-test row is inserted", Platform.Function.InsertData(deName, ["Email", "Status"], ["cptest@example.com", "keep"]), 1);

/* c) The CustomerKey form throws instead of deleting. */
assertThrows("DEV DeleteDE(<CustomerKey>, ...) throws (docs: deName is not restricted to the Name)", function () {
    return Platform.Function.DeleteDE(deKey, ["Email"], ["keytest@example.com"]);
});

/* c) Proof that the rejected call removed nothing: DeleteData with the Name
   still finds the row and reports 1. */
var stillThere = Platform.Function.DeleteData(deName, ["Email"], ["keytest@example.com"]);
assert("DEV the row the CustomerKey call could not touch is still there (DeleteData by Name returns 1)", stillThere, 1);

/* a) + b) DeleteDE executes on a CloudPage and returns null, not a count. */
var res = Platform.Function.DeleteDE(deName, ["Email"], ["cptest@example.com"]);
assert("DEV DeleteDE runs on a CloudPage without throwing (docs: email contexts only)", res === null ? "null" : "not null", "null");
assert("DEV DeleteDE returns null, not the affected-row count DeleteData gives", String(typeof res) === "number" ? "number" : "not a number", "not a number");
assert("DEV DeleteDE's return value is NOT undefined either", res === undefined ? "undefined" : "defined", "defined");

/* a) The delete really COMMITTED on the CloudPage — read-back is the only
   evidence DeleteDE leaves behind. Query issued exactly once. */
assert("DEV DeleteDE COMMITS its delete on a CloudPage (docs: email contexts only)", rowCount(deName, "Email", "cptest@example.com"), 0);
assert("nothing else was removed: the data extension is empty only because both rows were targeted", rowCount(deName, "Status", "keep"), 0);

assert("cleanup: the throwaway data extension is deleted", dropDE(deKey), "OK");
</script>

DeleteData is still preferred outside email because it returns the number of affected rows.

Irreversible — SFMC DEs have no built-in undo. Always verify the filter before deleting.

Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: Description
 *
 * Proves every claim the Description chapter makes:
 *   1. DeleteDE removes the rows matching the filter criteria — proven by
 *      reading the rows back, because the call itself reports nothing.
 *   2. It performs the SAME delete as DeleteData: the identical filter,
 *      applied to two identical rows, removes each of them. The only
 *      difference is the return value.
 *   3. DEV — it returns a genuine JavaScript null (=== null is true), NOT a
 *      row count and NOT undefined. typeof that null is "object", which is
 *      the ordinary JavaScript typeof-null result, so typeof alone cannot
 *      distinguish it from an object — the === null check is the real
 *      evidence.
 *   4. A filter that matches nothing also returns null and deletes nothing —
 *      DeleteDE cannot report "no rows matched", which is exactly why the
 *      page recommends DeleteData outside email.
 *   5. WORKAROUND — DeleteData is preferred outside email because it returns
 *      a real number for the very same operation. Asserted side by side in
 *      this script.
 *   6. IRREVERSIBLE — after a DeleteDE the row does not come back:
 *      Platform.Function.Lookup returns a genuine null for it while a
 *      surviving row is still readable.
 *
 * QUERY-CACHING CONSTRAINT: identical data-extension queries are cached
 * within one request, so every verification query below is issued only once.
 *
 * SCOPE: CloudPage only (MCDEV_Training_QA business unit). The email,
 * automation and triggered-send contexts listed on the page were NOT
 * exercised.
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}

function addField(de, name, len, isKey) {
    var f = Platform.Function.CreateObject("DataExtensionField");
    Platform.Function.SetObjectProperty(f, "Name", name);
    Platform.Function.SetObjectProperty(f, "FieldType", "Text");
    Platform.Function.SetObjectProperty(f, "MaxLength", len);
    Platform.Function.SetObjectProperty(f, "IsPrimaryKey", isKey);
    Platform.Function.SetObjectProperty(f, "IsRequired", isKey);
    Platform.Function.AddObjectArrayItem(de, "Fields", f);
}
function createDE(name, key) {
    var de = Platform.Function.CreateObject("DataExtension");
    Platform.Function.SetObjectProperty(de, "CustomerKey", key);
    Platform.Function.SetObjectProperty(de, "Name", name);
    addField(de, "Email", "100", "true");
    addField(de, "Status", "50", "false");
    var st = [0, 0];
    return String(Platform.Function.InvokeCreate(de, st, null));
}
function dropDE(key) {
    var de = Platform.Function.CreateObject("DataExtension");
    Platform.Function.SetObjectProperty(de, "CustomerKey", key);
    var st = [0, 0];
    return String(Platform.Function.InvokeDelete(de, st, null));
}
function rowCount(deName, field, value) {
    var rows = Platform.Function.LookupRows(deName, field, value);
    return rows === null ? 0 : rows.length;
}

var deName = "ssjsguide_deletede_desc_name";
var deKey = "ssjsguide_deletede_desc_key";

assert("setup: the throwaway data extension is created", createDE(deName, deKey), "OK");
assert("setup: row d1 inserted", Platform.Function.InsertData(deName, ["Email", "Status"], ["d1@example.com", "twin"]), 1);
assert("setup: row d2 inserted", Platform.Function.InsertData(deName, ["Email", "Status"], ["d2@example.com", "twin"]), 1);
assert("setup: row d3 inserted", Platform.Function.InsertData(deName, ["Email", "Status"], ["d3@example.com", "keep"]), 1);

/* 1 + 3. The delete itself and its return value. */
var res = Platform.Function.DeleteDE(deName, ["Email"], ["d1@example.com"]);
assert("DEV DeleteDE returns a genuine JavaScript null, not a row count (docs: no return value documented for CloudPages)", res === null ? "null" : "not null", "null");
assert("DEV DeleteDE's return value is NOT undefined", res === undefined ? "undefined" : "defined", "defined");
assert("DeleteDE's null is not a number", String(typeof res) === "number" ? "number" : "not a number", "not a number");
assert("typeof DeleteDE(...) is object — the ordinary JavaScript typeof of null", String(typeof res), "object");

/* 4. A filter matching nothing also returns null. */
var none = Platform.Function.DeleteDE(deName, ["Email"], ["nobody@example.com"]);
assert("a filter that matches nothing also returns null", none === null ? "null" : "not null", "null");

/* 2 + 5. The same delete via DeleteData returns a number instead. */
var deleted = Platform.Function.DeleteData(deName, ["Email"], ["d2@example.com"]);
assert("WORKAROUND DeleteData performs the same delete but returns a number", String(typeof deleted), "number");
assert("WORKAROUND DeleteData reports the affected-row count DeleteDE withholds", deleted, 1);

/* 1 + 2. Read the rows back — each query issued exactly once. */
assert("the DeleteDE row d1 is really gone", rowCount(deName, "Email", "d1@example.com"), 0);
assert("the DeleteData row d2 is really gone", rowCount(deName, "Email", "d2@example.com"), 0);
assert("the no-match DeleteDE deleted nothing: row d3 is untouched", rowCount(deName, "Status", "keep"), 1);

/* 6. Irreversible — the deleted row does not come back. */
assert("Lookup returns a genuine null for the DeleteDE-removed row d1", Platform.Function.Lookup(deName, "Status", "Email", "d1@example.com") === null ? "null" : "not null", "null");
assert("the surviving row d3 is still readable", String(Platform.Function.Lookup(deName, "Status", "Email", "d3@example.com")), "keep");

assert("cleanup: the throwaway data extension is deleted", dropDE(deKey), "OK");
</script>

See Also

See Also