【问题标题】:How to get the last message by distinct userID using SQlite and iOS?如何使用 SQlite 和 iOS 通过不同的用户 ID 获取最后一条消息?
【发布时间】:2014-04-28 13:32:44
【问题描述】:

我有一个 "messages table",我只想用他的最后一条消息检索 "user ID"

我尝试在彼此之间添加“2条sql语句”,但它一直在不停地循环,

   sqlite3_stmt *statement;
NSMutableArray * messages = [[NSMutableArray alloc]init];

const char *dbpath = [_databasePath UTF8String];
if (sqlite3_open(dbpath, &_chatDB) == SQLITE_OK)
{
    NSString *querySQL = [NSString stringWithFormat:
                          @"SELECT DISTINCT FROMID , USERNAME from CHATCOMPLETE"];
    const char *query_stmt = [querySQL UTF8String];
    if (sqlite3_prepare_v2(_chatDB,
                           query_stmt, -1, &statement, NULL) == SQLITE_OK)
    {
        while (sqlite3_step(statement) == SQLITE_ROW)
        {
            int userID = [[[NSString alloc] initWithUTF8String:
                          (const char *) sqlite3_column_text(statement, 0)] integerValue];

            NSString *querySQL2 = [NSString stringWithFormat:
                                  @"SELECT MESSAGE , USERNAME from CHATCOMPLETE where FROMID=\"%d\"",userID];
            const char *query_stmt2 = [querySQL2 UTF8String];
            if (sqlite3_prepare_v2(_chatDB,
                                   query_stmt2, -1, &statement, NULL) == SQLITE_OK)
            {
                while (sqlite3_step(statement) == SQLITE_ROW)
                {

                    NSLog(@"LAST MESSAGE %@",[[NSString alloc] initWithUTF8String:
                                               (const char *) sqlite3_column_text(statement, 0)]);

                    sqlite3_reset(statement);

                }
            }
        }
                    sqlite3_reset(statement);
    }
}
return messages;

更新: 这是插入消息

    -(void)saveData:(NSString *)message toID:(int)toID fromID:(int)fromID isRead:(BOOL)read date:(NSDate *)date messageID:(int)messageID userName:(NSString*)userName
{
    sqlite3_stmt    *statement;

    const char *dbpath = [_databasePath UTF8String];

    if (sqlite3_open(dbpath, &_chatDB) == SQLITE_OK)
    {
        NSString *insertSQL = [NSString stringWithFormat: @"INSERT INTO CHATCOMPLETE (MESSAGE, TOID, FROMID, READ, date, MESSAGEID, USERNAME) VALUES (\"%@\", \"%d\", \"%d\", \"%c\", \"%@\", \"%d\", \"%@\")", message, toID, fromID, read, date,messageID,userName];

        const char *insert_stmt = [insertSQL UTF8String];

        sqlite3_prepare_v2(_chatDB, insert_stmt, -1, &statement, NULL);
        if (sqlite3_step(statement) == SQLITE_DONE)
        {
            NSLog(@"DONE");
          /*  status.text = @"Contact added";
            name.text = @"";
            address.text = @"";
            phone.text = @"";*/
        } else {
         //   status.text = @"Failed to add contact";
        }
        sqlite3_finalize(statement);
        sqlite3_close(_chatDB);
    }

}

【问题讨论】:

  • 我有一个聊天表,它有“ID 作为主键”、fromID、TOID、Message,我想获取 fromID 发送的最后一条消息。
  • 任何日期/时间列?如果没有,如何找到“最后一条消息”?
  • 只需在 while 循环外打印 NSLog(@"LAST MESSAGE %@",[[NSString alloc] initWithUTF8String: (const char *) sqlite3_column_text(statement, 0)])
  • 总是在循环之外只显示最后一个值\
  • @trojanfoe 我不能用 messageID 降序排列它们吗?

标签: ios sql sqlite xcode5


【解决方案1】:

这是获取给定fromID 的最后一条消息的查询:

SELECT * FROM chatting WHERE fromID=9999 ORDER BY id DESC LIMIT 1

【讨论】:

  • 没用,这只返回所有用户发送的最后一条消息,但我想要每个用户ID的最后一条消息
【解决方案2】:

在 SQLite 3.7.11 或更高版本中,以下查询将返回每个发件人日期最长的消息:

SELECT *, MAX(date)
FROM ChatComplete
GROUP BY FromID

【讨论】:

    【解决方案3】:

    有几个问题:

    1. 您的两个嵌套查询只有一个 sqlite3_stmt 变量。您需要为每个单独的sqlite3_stmt

    2. 您正在致电sqlite3_reset。这仅在您准备好的语句中将新值绑定到 ? 占位符时使用,此处不适用。更糟糕的是,您在循环中调用它。

    3. 与手头的问题无关,但是对于每个准备好的语句,在完成循环结果时不要忘记调用sqlite3_finalize,以便释放准备语句时使用的内存。


    因此,您可能想要这样的东西:

    sqlite3_stmt *userStatement;
    sqlite3_stmt *messageStatement;
    int rc;                           // the return code
    NSMutableArray * messages = [[NSMutableArray alloc]init];
    
    const char *dbpath = [_databasePath UTF8String];
    if (sqlite3_open(dbpath, &_chatDB) == SQLITE_OK)
    {
        const char *query_stmt = "SELECT DISTINCT FROMID , USERNAME from CHATCOMPLETE";
        if (sqlite3_prepare_v2(_chatDB, query_stmt, -1, &userStatement, NULL) != SQLITE_OK)
        {
            NSLog(@"%s: prepare userStatement failed: %s", __PRETTY_FUNCTION__, sqlite3_errmsg(_chatDB));
        }
        else 
        {
            while ((rc = sqlite3_step(userStatement)) == SQLITE_ROW)
            {
                int userID = [[[NSString alloc] initWithUTF8String:
                              (const char *) sqlite3_column_text(statement, 0)] integerValue];
    
                const char *query_stmt2 = "SELECT MESSAGE , USERNAME from CHATCOMPLETE where FROMID=? ORDER BY timestamp DESC LIMIT 1"; // change the `ORDER BY` to use whatever field you want to sort by
    
                if (sqlite3_prepare_v2(_chatDB, query_stmt2, -1, &messageStatement, NULL) != SQLITE_OK)
                {
                    NSLog(@"%s: prepare messageStatement failed: %s", __PRETTY_FUNCTION__, sqlite3_errmsg(_chatDB));
                }
                else 
                {
                    if (sqlite3_bind_int(messageStatement, 1, userID) != SQLITE_OK) 
                    {
                        NSLog(@"%s: bind userID %d failed: %s", __PRETTY_FUNCTION__, userID, sqlite3_errmsg(_chatDB));
                    }
    
                    while ((rc = sqlite3_step(messageStatement)) == SQLITE_ROW)
                    {
                        NSLog(@"LAST MESSAGE %@",[[NSString alloc] initWithUTF8String:
                                                   (const char *) sqlite3_column_text(statement, 0)]);
                    }
                    if (rc != SQLITE_DONE)
                    {
                        NSLog(@"%s: step messageStatement failed: %s", __PRETTY_FUNCTION__, sqlite3_errmsg(_chatDB));
                    }
    
                    sqlite3_finalize(messageStatement);
                }
            }
            if (rc != SQLITE_DONE) 
            {
                NSLog(@"%s: step userStatement failed: %s", __PRETTY_FUNCTION__, sqlite3_errmsg(_chatDB));
            }
    
            sqlite3_finalize(userStatement);
        }
    }
    else
    {
        NSLog(@"%s: open %@ failed", __PRETTY_FUNCTION__, _databasePath);
    }
    return messages;
    

    注意,这个代码示例,除了我上面的三点之外,还有:

    1. 如果sqlite3_prepare_v2 失败,则使用sqlite3_errmsg 记录错误。

    2. 还添加了对来自 sqlite3_step 的返回码的检查,如果失败,则再次记录 sqlite3_errmsg

    3. 如果sqlite3_open 失败,则添加日志。

    4. 使用sqlite3_bind_int() 而不是使用stringWithFormat 构建SQL。在这种情况下,因为userID 是数字,所以这并不重要,但如果在WHERE 子句中使用字符串值,使用sqlite3_bind_text() 函数就变得很重要,所以我只想展示模式。

      例如,查看您的保存例程并尝试保存其中恰好有双引号的消息(例如I spoke with Bob and he says "hello" to you.)。您的 stringWithFormat 构造将失败。如果你使用sqlite3_bind_text,它会解决这个问题。

    顺便说一句,如您所见,当您添加所有正确的结果验证、值绑定等时,代码变得有点笨拙。您可以考虑使用FMDB,它可以大大简化您的 SQLite Objective-C 代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-03
      • 1970-01-01
      • 1970-01-01
      • 2022-07-10
      • 2018-01-15
      • 1970-01-01
      • 2022-08-18
      • 2021-05-04
      相关资源
      最近更新 更多