【问题标题】:SQL delete from one table + a jointable?SQL 从一个表中删除 + 一个可连接的?
【发布时间】:2011-06-15 22:53:43
【问题描述】:

我有三个表:logactivity 和可连接(many2many)log_activity(以 log_idactivity_id + 附加信息数据作为列)。

我想从loglog_activity 中删除。

我想保留来自特定用户的所有日志,并且只保留来自其他用户的 100 行。 这意味着我想删除所有匹配 WHERE log.user_id != 1 的行,但最后 100 行 (ORDER BY log.timestamp DESC)。

我还想从可连接的log_activity 中删除与被删除的日志相关的所有条目。 activity 表应该被触摸。

我认为db.delete(TABLE_NAME, whereClause , whereArgs); 在这种情况下没有帮助..

那么有人能想出一个高效的解决方案吗?


更新更新更新更新更新更新更新更新更新更新


受到 Jacob Eggers 和 plafond 的回答以及进一步研究的启发,我现在正在尝试这样,但它还不起作用:

CREATE TABLE IF NOT EXISTS log ( 
    _id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    timestamp LONG NOT NULL
);

CREATE TABLE IF NOT EXISTS log_activity ( 
    _id INTEGER PRIMARY KEY AUTOINCREMENT,
    log_id INTEGER NOT NULL,
    activity_id INTEGER NOT NULL,
    points INTEGER NOT NULL,
    FOREIGN KEY(log_id) REFERENCES log(_id) ON DELETE CASCADE,
    FOREIGN KEY(activity_id) REFERENCES activity(_id) ON DELETE CASCADE
);

现在是安卓部分:

SQLiteDatabase db = openHelper.getWritableDatabase();
db.execSQL("PRAGMA foreign_keys = ON;");
db.execSQL(CREATE_LOG); // see sql above
db.execSQL(CREATE_ACTIVITY); // not shown here, but like the sql-creates above
db.execSQL(CREATE_LOG_ACTIVITY); // see sql above

// ... insert some data ...
INSERT INTO "log" VALUES(1,1,1307797289000);
INSERT INTO "log" VALUES(2,1,1307710289000);
INSERT INTO "log" VALUES(3,2,1308089465000);
INSERT INTO "log" VALUES(4,2,1308079465000);

INSERT INTO "log_activity" VALUES(1,1,1,1);
INSERT INTO "log_activity" VALUES(2,1,2,2);
INSERT INTO "log_activity" VALUES(3,2,1,1);
INSERT INTO "log_activity" VALUES(4,2,2,2);
INSERT INTO "log_activity" VALUES(5,3,1,1);
INSERT INTO "log_activity" VALUES(6,3,2,2);
INSERT INTO "log_activity" VALUES(7,4,1,1);
INSERT INTO "log_activity" VALUES(8,4,2,2);

// check count of logs
Cursor c = db.query(false, "log", null, null, null, null, null, "_id asc", null);
android.util.Log.d("TEST", "log count before: "+c.getCount());

// check count of log_activities
Cursor c2 = db.query(false, "log_activity", null, null, null, null, null, "_id asc", null);
android.util.Log.d("TEST", "la count before: "+c2.getCount());

// delete some log-rows
long userId = 1;
int keepXLogsOfOthers = 1;
String del = "DELETE FROM log" +
                " WHERE user_id != " + userId +
                "  AND log._id NOT IN (" +
                "    SELECT _id" +
                "    FROM (" +
                "      SELECT _id" +
                "      FROM log" +
                "      WHERE user_id != " + userId +
                "      ORDER BY timestamp DESC" +
                "      LIMIT " + keepXLogsOfOthers +
                "    ) logs_of_others_to_keep" +
                ");";
db.execSql(del);

// check count of logs
Cursor c3 = db.query(false, "log", null, null, null, null, null, "_id asc", null);
android.util.Log.d("TEST", "log count after: "+c3.getCount());

// check count of log_activities
Cursor c4 = db.query(false, "log_activity", null, null, null, null, null, "_id asc", null);
android.util.Log.d("TEST", "la count after: "+c4.getCount());

输出:

06-16 10:40:01.748: DEBUG/TEST(451): log count before: 4
06-16 10:40:01.748: DEBUG/TEST(451): la count before: 8
06-16 10:40:01.828: DEBUG/TEST(451): log count after: 3
06-16 10:40:01.838: DEBUG/TEST(451): la count after: 8

这意味着它本身的 DELETE 操作很好(我还检查了是否删除了正确的行,这解决了第一个问题!!),但是 ON DELETE CASCADE 不起作用...为什么?

【问题讨论】:

  • 您需要启用外键约束。 See this
  • 我不是用 db.execSQL("PRAGMA foreign_keys = ON;"); ??
  • 是的。抱歉错过了。你能检查一下,ON DELETE RESTRICT 有什么作用吗? (另外,您是否将记录添加到某处的活动中?那里的外键可能会阻止插入。)
  • 是的,我将它添加到之前插入的活动中。使用 RESTRICT,我在执行 'DELETE FROM WHERE user_id != 1 AND log._id NOT IN (...
  • OK 不知道为什么,但是在我的手机设备上调试它确实会输出正确的值。而模拟器没有...

标签: java android sql sqlite


【解决方案1】:

您可以创建一个触发器来自动执行此操作。

CREATE TRIGGER [delete_log_joins]
BEFORE DELETE
ON [log]
FOR EACH ROW
BEGIN
DELETE FROM log_activity WHERE log_activity.log_id = old.id;
END

要选择删除除最新的 100 条日志之外的所有日志,您可以执行以下操作:

delete * from log where log.id not in (
  select id
  from (
    select l.id
    from log l
    where l.id in (
      select top 100 l2.id
      from log l2
      where l2.user_id = l.user_id
      order by log.timestamp desc
    )
  ) the_tops
);

我不确定它的性能如何,也许有人可以改进它。

【讨论】:

  • 我会尝试的.. 但我想知道这是否有效以及删除查询如何保持 100 行。
  • 什么是 old.id?它既不适用于 old._id 也不适用于 _id(id 字段为_id):错误/数据库(312):在准备“CREATE TRIGGER [delete_log_activity_join] BEFORE 时,0x1350d8 上的失败 1(靠近“_id”:语法错误) DELETE ON [log] FOR EACH ROW BEGIN DELETE FROM log_activity WHERE log_activity.log_id = old._id'。
  • 尝试 old.id 而不是 old._id。 正在删除的日志中的旧行
  • @Jacob Eggers:字段名称是_id。但可以肯定的是,我尝试使用 old.id: ERROR/AndroidRuntime(462): Caused by: android.database.sqlite.SQLiteException: near "id": syntax error: CREATE TRIGGER [delete_log_activity_join] BEFORE DELETE ON [log] FOR EACH ROW BEGIN DELETE FROM log_activity WHERE log_activity.log_id = old.id
【解决方案2】:

如果您有权访问数据库(创建触发器),那么 SQL DELETE CASCADE 不是一个选项吗?

ALTER TABLE log DROP CONSTRAINT aa
ALTER TABLE log ADD CONSTRAIN (FOREIGN KEY (log_id) REFERENCES log_activity ON DELETE CASCADE CONSTRAINT ab)

然后,只需使用您想要的任何子句运行您的正常 JDBC 删除语句。

【讨论】:

  • 好主意.. 我现在想用约束来做。我在互联网上搜索了一下,并尝试了几件事与级联,但它不工作。请查看我对问题的更新。
  • 不确定为什么您会收到最新问题(手机/模拟器之间的行为不同)。但请注意您的最后一个约束:“FOREIGN KEY(activity_id) REFERENCES activity(_id) ON DELETE CASCADE” - 我以为您不希望删除活动记录?!
  • 如果日志或 log_activity 被删除,则不会删除活动。约束表明,如果一个活动被删除,它将删除所有引用该活动的 log_activity 行。
【解决方案3】:

根据关系列设置log_id和activity_id。

如果它们不是_null,那么你需要根据你的where子句删除连接表中的行,然后删除日志和活动表中的行。

如果它们可以为空,您将首先在连接表中将 log_id 和 activity_id 值设置为空。然后删除日志和活动表。

【讨论】:

  • 将它们设置为 null 会导致 log_activity 中的垃圾。另外..我告诉过不应更改活动表...请查看问题和您的答案。 log_id 和 activity_id 都不能为空,因为它是一个连接表。谢谢。
【解决方案4】:

通过在 log_activity 表上使用外键解决了这个问题,如下所示:

FOREIGN KEY(log_id) REFERENCES log(_id) ON DELETE CASCADE

还有这样的删除语句:

long userId = 1;
int keepXLogsOfOthers = 1;
String del = "DELETE FROM log" +
                " WHERE user_id != " + userId +
                "  AND log._id NOT IN (" +
                "    SELECT _id" +
                "    FROM (" +
                "      SELECT _id" +
                "      FROM log" +
                "      WHERE user_id != " + userId +
                "      ORDER BY timestamp DESC" +
                "      LIMIT " + keepXLogsOfOthers +
                "    ) logs_of_others_to_keep" +
                ");";
db.execSql(del);

别忘了启用外键:

db.execSQL("PRAGMA foreign_keys = ON;");

我遇到的问题是模拟器没有级联 log_activities.. 但在设备上它可以工作。感谢其他回答者给了我一些提示。

请再次查看我的问题以获取更多详细信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 2021-05-05
    • 1970-01-01
    相关资源
    最近更新 更多