如何在C#中统计SQLite读取器返回的行数?

14

我在使用微软的Visual C# 2008 Express以及SQLite。

我正在使用类似于这样的查询语句查询我的数据库:

SQLiteCommand cmd = new SQLiteCommand(conn);

cmd.CommandText = "select id from myTable where word = '" + word + "';";
cmd.CommandType = CommandType.Text;
SQLiteDataReader reader = cmd.ExecuteReader();

然后我做了这样的事情:

if (reader.HasRows == true) {
    while (reader.Read()) {
        // I do stuff here
    }
}

我想做的是在执行"reader.Read()"之前计算行数,因为返回的数字会影响我想要/需要做的事情。 我知道我可以在while循环语句中添加计数器,但我真的需要在此之前知道计数。

有什么建议吗?


还可以在SQLite邮件列表上查看查询结果中的行数 - jww
11个回答

32

DataReader 是惰性运行的,因此它在开始之前不会获取整个行集。这给您留下了两个选择:

  1. 迭代并计数
  2. 在 SQL 语句中进行计数。

因为我更擅长 SQL,所以我会在 SQL 语句中进行计数:

cmd.CommandText = "select count(id) from myTable where word = '" + word + "';";
cmd.CommandType = CommandType.Text;
int RowCount = 0;

RowCount = Convert.ToInt32(cmd.ExecuteScalar());

cmd.CommandText = "select id from myTable where word = '" + word + "';";
SQLiteDataReader reader = cmd.ExecuteReader();

//...

注意我一开始是计数了 * 而不是 id。这是因为 count(id) 会忽略id,而 count(*) 只会忽略完全为空的行。如果您没有空的id,则使用 count(id)(根据您的表大小,它会稍微快一点)。

更新:根据评论更改为 ExecuteScalar,并使用 count(id)。


是的 - 在你的回复和Jeremy的回复之间,这个方法会起作用。虽然“myTable”最终可能会非常大,但返回的行数很可能总是很少 - 少于10行,更多时只有0-2行。谢谢! - adeena
3
哦,而且"id"永远不会为空。如果是空的话,那就意味着有其他问题出现了 : ) - adeena
“VERY large”有多大?你确定SQLite是最好的选择吗? - Eric
目前,由于我在编码时正在学习,我确信SQLite是目前最好的选择。将来我是否需要改用其他数据库解决方案?很可能会,但我还有很多其他事情要学习和做,所以SQLite目前满足了我的需求。那个表在不久的将来将有几千条记录,但当我的应用程序完成并且我完全使用它时,它应该至少有60,000条记录,甚至多达100,000条。 - adeena
5
虽然这与问题并不直接相关,但我建议不要使用字符串连接构建SQL语句,而是使用参数化查询来防止可能的SQL注入攻击。 - Venr
显示剩余2条评论

4
您的要求是不可行的 -- 引用 Igor Tandetnik 的话来说,我的重点是:

SQLite 每次调用 sqlite3_step 时都会按请求逐个生成记录。 它根本不知道有多少条记录,直到在某些 sqlite3_step 调用中发现没有更多记录。

sqlite3_step 是 SQLite C API 中的函数,C# 接口在此为结果中的每一行调用它)。

您可以在“真正”的查询之前先执行 "SELECT COUNT(*) from myTable where word = '" + word + "';" -- 这将告诉您从真正的查询中会得到多少行。


2

进行第二个查询:

cmd.CommandText = "select count(id) from myTable where word = '" + word + "';";
cmd.CommandType = CommandType.Text;
SQLiteDataReader reader = cmd.ExecuteReader();

读取器将包含单个行,其中包含一个列,该列包含结果集中的行数。计数将在服务器上执行,因此应该非常快速。


在计数单个字段时要小心:如果它为空,它不会被计算,但会被返回。在使用计数之前,请确保字段具有NOT NULL约束。 - Eric
当然。一般来说,我希望名为“id”的字段不为空(NOT NULL),但你永远不能确定! - Jeremy McGee

1

这是我的完整实现,作为一个静态方法。 你应该能够将其插入到你的类中(用你的数据库文件名和表名替换_STR_DB_FILENAME & STR_TABLE_NAME)。

    /// <summary>
    /// Returns a count of the number of items in the database.
    /// </summary>
    /// <returns></returns>
    public static int GetNumberOfItemsInDB()
    {
        // Initialize the count variable to be returned
        int count = 0;            

        // Start connection to db
        using (SqliteConnection db =
            new SqliteConnection("Filename=" + _STR_DB_FILENAME))
        {
            // open connection
            db.Open();

            SqliteCommand queryCommand = new SqliteCommand();
            queryCommand.Connection = db;

            // Use parameterized query to prevent SQL injection attacks
            queryCommand.CommandText = "SELECT COUNT(*) FROM " + _STR_TABLE_NAME;
            // Execute command and convert response to Int
            count = Convert.ToInt32(queryCommand.ExecuteScalar());
            // Close connection
            db.Close();
        }
        // return result(count)
        return count;
    }

注意:为了提高性能,您可以将“”替换为表主键列的列名称,以在更大的数据集上实现更快的性能。

1

通常我会这样做

select count(1) from myTable where word = '" + word + "';";

为了尽快获得结果。如果id是int类型,则不会有太大区别。如果它是一些更大的类型,比如字符串类型,那么在大型数据集上你会注意到差异。

对此进行推理,count(1)将包括空行。但如果我对此错误,请纠正我。


1
尝试这个,
SQLiteCommand cmd = new SQLiteCommand(conn);

cmd.CommandText = "select id from myTable where word = '" + word + "';";

SQLiteDataReader reader = cmd.ExecuteReader();

while (reader.HasRows)

     reader.Read();

int total_rows_in_resultset = reader.StepCount;

total_rows_in_resultset可以在处理查询后给出结果集中行的数量。

请记住,如果您想使用同一个reader,则需要关闭该reader并重新启动它。


1

如果你只是从数据库中加载一个id列,那么直接加载到一个List<string>中,然后在内存中处理不是更容易吗?


如果你考虑到表中有几十万行的情况,这可能不是你想要做的。最好提前运行另一个查询来查找行集的计数。 - Jeremy McGee
这取决于“我在这里做的事情”是什么... 根据算法,大部分/全部数据最终可能会存储在内存中。 - jerryjvl
还要注意的是,在另一个评论中,提交者指出通常预期从查询中返回大约10行数据,因此使用列表可能比两次往返数据库更快。 - jerryjvl

0
SQLiteCommand cmd = new SQLiteCommand(conn);
cmd.CommandText = "select id from myTable where word = '" + word + "';";
SQLiteDataReader reader = cmd.ExecuteReader();

while (reader.Read())
       {
          total_rows_in_resultset++;
       }

2
请添加一些解释,说明这段代码为什么能够帮助提问者。这将有助于为未来的观众提供一个可以学习的答案。请参见[答案]以获取更多信息。当回答像这样的旧问题时,特别重要的是要区分您的答案和许多其他现有答案。 - Heretic Monkey

0
但是我确实需要在之前知道计数。 为什么呢?如果您使用适当的内存数据结构(如数据集、列表等),通常是不必要的。可能有一种方法可以实现您想要的结果,而不需要事先计算行数。

我正在尝试实现这里定义的“图形大师”版本:http://www.alicebot.org/documentation/matching.html。基本上,如果有0或1行,我有一个唯一的答案,没有问题。但是,如果有2行或更多行,则必须执行其他复杂操作以确定哪个是正确的答案...这取决于我的“图形”中的下一个节点。这大概就是我能在注释中解释的最好的了! :) - adeena
您可以在列表或DataSet中检索所有行,然后根据行数对内存数据执行必要的操作。除非查询可能会获取数百或数千行,否则这不应该是一个问题。 - Thomas Levesque

0

你必须考虑到 select count... from...

这会使你的应用程序变慢。然而,有一种简单的方法可以使你的应用程序更快,那就是使用参数化查询。

请参见此处:如何解决sqlite和c#中的“'”问题?

(因此,除了速度之外,参数化查询还有其他两个优点。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接