Java SQL从一个表中删除+;可接合的?

Java SQL从一个表中删除+;可接合的?,java,android,sql,sqlite,Java,Android,Sql,Sqlite,我有三个表:log、activity和jointable(许多)log\u activity(带有log\u id和activity\u id+附加信息数据作为列) 我想从日志和日志活动中删除 我想保留特定用户的所有日志,而只保留其他用户的100行。 这意味着我要删除与WHERE log.user_id!=1,但最后100(按log.timestamp DESC排序) 我还想从jointablelog\u活动中删除与被删除的日志相关的所有条目。不应触摸活动表 我认为db.delete(TABLE

我有三个表:
log
activity
和jointable(许多)
log\u activity
(带有
log\u id
activity\u id
+附加信息数据作为列)

我想从
日志
日志活动
中删除

我想保留特定用户的所有日志,而只保留其他用户的100行。 这意味着我要删除与
WHERE log.user_id!=1
,但最后100(
按log.timestamp DESC排序

我还想从jointable
log\u活动中删除与被删除的日志相关的所有条目。不应触摸
活动

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

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


更新


受雅各布·艾格斯和普拉芬德的回答以及进一步研究的启发,我现在尝试这样做,但还没有成功:

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
);
现在来看android部分:

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

这意味着删除操作本身是正常的(我还检查了是否删除了正确的行,这解决了第一个问题!!),但是删除级联的不起作用。。。为什么?

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

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
);
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);

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

取决于日志id和活动id等关系列的设置

如果它们不为null,则需要根据where子句删除join表中的行,然后删除日志表和活动表中的行


如果可以为null,则首先在联接表中将log_id和activity_id值设置为null。然后删除日志和活动表。

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

ALTER TABLE log DROP CONSTRAINT aa

更改表日志添加约束
(外键(日志id)引用日志活动
在删除级联约束时(ab)


然后,只需使用所需的任何子句运行正常的JDBC delete语句。

通过在log\U活动表上使用外键解决了此问题,如下所示:

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
);
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);
下面是一个delete语句:

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
);
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;");
我的问题是模拟器没有级联日志活动。。但在一台设备上,它可以工作。感谢其他回答我的人给了我一些提示


有关详细信息,请再次查看我的问题。

将它们设置为null将导致日志活动中出现垃圾。另外。。我说过活动表不应该改变。。。请复习问题和你的答案。log_id和activity_id都不能为null,因为它是一个联接表。谢谢。我会试试的。。但是我想知道这是否有效,删除查询保持100行是什么样子?它既不适用于旧的。_id,也不适用于_id(id字段为
\u id
):错误/数据库(312):在[log]上删除之前准备“创建触发器[delete_log\u activity\u join]”时0x1350d8上出现故障1(靠近“_id”:语法错误)对于每一行,开始从log_activity.log_id=old的log_activity.log_id=old._id.尝试old.id而不是old._id.正在删除的log_activity中的old ROW:;-)@Jacob Eggers:字段名是_id。但为了确保我尝试了old.id:ERROR/AndroidRuntime(462):原因:android.database.sqlite.SQLiteException:near“id”:语法错误:在[log]上删除之前为每一行创建触发器[delete_log_activity_join],开始从log_活动中删除,其中log_activity.log_id=old.id好主意。。我现在想用约束来做。我在互联网上搜索了一下,尝试了几种级联方法,但都不起作用。请查看我的问题更新。不确定为什么会出现最新问题(手机/模拟器之间的行为不同)。但要注意最后一个限制:“外键(activity_id)引用了DELETE CASCADE上的activity(_id)”——我以为您希望不要删除活动记录?!如果日志或日志活动被删除,则不会删除活动。该约束表示,如果某个活动被删除,它将删除引用该活动的所有日志活动行。您需要启用外键约束。我不是用db.execSQL(“PRAGMA foreign_keys=ON;”)做的吗??是 啊对不起,我错过了。你能检查一下吗?你能做些什么吗?(还有,您是否将记录添加到某个活动中?那里的外键可能会阻止插入。)是的,我将其添加到了之前插入的活动中。使用RESTRICT,我得到一个错误/数据库(6958):当执行“从用户id所在的日志中删除”时,0x24f408出现故障19(外键约束失败)=1和日志。_id不在(…好的,不知道为什么,但在我的手机设备上对其进行除错会输出正确的值。而模拟器不会。。。