如何检查结果集是否为空?

80

我有一个查询语句没有返回结果,例如:'select * from TAB where 1 = 2'

我想检查有多少行被返回,

cursor.execute(query_sql)

rs = cursor.fetchall()

我已经遇到了异常:"(0, 'No result set')"

如何避免这个异常,并检查结果集是否为空?


4
这是什么数据库?我不希望在这里出现异常,相反,我期望.fetchall()返回一个空列表。 - Martijn Pieters
对于 Django 2.1SQLite,返回空列表。 - Tunn
9个回答

84

cursor.rowcount 通常会被设置为0。

然而,如果您正在运行一个永远不会返回结果集的语句(比如没有RETURNINGINSERT,或者SELECT ... INTO),那么您不需要调用.fetchall();对于这种语句,没有结果集。仅调用.execute()就足以运行该语句。


请注意,如果数据库适配器无法确定确切的受影响行数,也可以将rowcount设置为-1。请参阅PEP 249 Cursor.rowcount规范

如果游标上没有执行过.execute*(),或者最后一个操作的行数无法通过接口确定,则该属性为-1

sqlite3很容易出现这种情况。在所有这些情况下,如果您必须事先知道受影响的行数,请先在同一事务中执行COUNT()选择操作。


2
在我的脚本中,我执行一系列SQL语句并测量它们的运行时间和命中次数。我无法预先知道一个语句是SELECT还是INSERT,是否有一种方法让我在每个查询执行后知道是否有结果集?非常感谢。 - Tao Venzke
2
@TaoVenzke:使用 try: results = cursor.fetchall(),然后用 except 处理程序捕获异常?并非所有数据库在这种情况下都会抛出异常,请问您在这里使用的是哪个数据库? - Martijn Pieters
1
我正在使用SAP HANA数据库。我用TRY实现了对异常的捕获。但是在这种情况下,我想避免异常。cursor.rowcount相当令人困惑:它对于select语句返回-1;对于insert语句,它返回非0值,它返回的数字似乎是插入记录的数量,但我真的能相信它吗? - Tao Venzke
2
当您使用数据查询语句(通常是SELECT,但也适用于执行存储过程)并且获得空结果集时,cursor.rowcount将为0。对于没有RETURNING子句的数据操作语句(UPDATEINSERT),您可以期望行计数为-1,但这实际上取决于特定的数据库适配器。 - Martijn Pieters
1
@xtian:只需坚持使用COUNT(),就可以得到一个确切的数字,保证无误。 - Martijn Pieters
显示剩余6条评论

35

无论我尝试什么解决方案,都会出现rowcount始终返回-1的问题。

我发现以下代码可作为检查空结果的良好替代方法。

c.execute("SELECT * FROM users WHERE id=?", (id_num,))
row = c.fetchone()
if row == None:
   print("There are no results for this query")

5
使用 is None 要比使用 == None 更好,是吗? - Героям слава
如果我们没有注意到,就会失去一行的追踪。 - Bablu

27

如果结果集为空,MySQLdb不会引发异常。此外,cursor.execute()函数将返回一个长整型值,该值为获取的结果集中行的数量。因此,如果您想检查空结果,可以重写您的代码:

rows_count = cursor.execute(query_sql)
if rows_count > 0:
     rs = cursor.fetchall()
else:
     // handle empty result set

与Oracle完美兼容! - Julien

7

注意:这是针对Python中的MySQLdb模块。

对于SELECT语句,如果记录集为空,则不应该出现异常。只需要使用空列表([])表示cursor.fetchall()None表示cursor.fetchone()

对于其他任何没有返回记录集的语句,例如INSERTUPDATE,您不能在游标上调用fetchall()fetchone()。否则,将引发异常。

区分上述两种类型游标的一种方法是:

def yield_data(cursor):
    while True:
        if cursor.description is None:
            # No recordset for INSERT, UPDATE, CREATE, etc
            pass
        else:
            # Recordset for SELECT, yield data
            yield cursor.fetchall()
            # Or yield column names with
            # yield [col[0] for col in cursor.description]

        # Go to the next recordset
        if not cursor.nextset():
            # End of recordsets
            return

3

我在需要进行多个SQL查询时遇到了类似的问题。问题在于有些查询没有返回结果,但我想要打印出这个结果。然而存在一个错误。正如之前所述,有几种解决方案。

if cursor.description is None:
    # No recordset for INSERT, UPDATE, CREATE, etc
    pass
else:
    # Recordset for SELECT

除此之外:

exist = cursor.fetchone()
if exist is None:
  ... # does not exist
else:
  ... # exists

其中一种解决方案是:

tryexcept 块允许您处理 error/exceptions。而 finally 块则无论 tryexcept 块的结果如何,都允许您执行代码。因此,可以通过使用它来解决所提出的问题。

s = """ set current query acceleration = enable;
        set current GET_ACCEL_ARCHIVE = yes;
        SELECT * FROM TABLE_NAME;"""

query_sqls = [i.strip() + ";" for i in filter(None, s.split(';'))]
for sql in query_sqls:
    print(f"Executing SQL statements ====> {sql} <=====")
    cursor.execute(sql)
    print(f"SQL ====> {sql} <===== was executed successfully")
    try:
        print("\n****************** RESULT ***********************")
        for result in cursor.fetchall():
            print(result)
        print("****************** END RESULT ***********************\n")
    except Exception as e:
        print(f"SQL: ====> {sql} <==== doesn't have output!\n")
        # print(str(e))

输出:

Executing SQL statements ====> set current query acceleration = enable; <=====
SQL: ====> set current query acceleration = enable; <==== doesn't have output!

Executing SQL statements ====> set current GET_ACCEL_ARCHIVE = yes; <=====
SQL: ====> set current GET_ACCEL_ARCHIVE = yes; <==== doesn't have output!

Executing SQL statements ====> SELECT * FROM TABLE_NAME; <=====

****************** RESULT ***********************

       ----------   DATA   ----------

****************** END RESULT ***********************

以上示例只是呈现了一个简单的使用想法,可以帮助您解决问题。当然,您还应该注意其他错误,如查询语句的正确性等。


2
如果您正在连接到一个PostgreSQL数据库,以下内容适用:
result = cursor.execute(query)

if result.returns_rows:
    # we got rows!
    return [{k:v for k,v in zip(result.keys(), r)} for r in result.rows]
else:
    return None

1
你可以这样做:

count = 0
cnxn = pyodbc.connect("Driver={SQL Server Native Client 11.0};"
                      "Server=serverName;"
                      "Trusted_Connection=yes;")
cursor = cnxn.cursor()
cursor.execute(SQL query)
for row in cursor:
    count = 1
    if true condition:
        print("True")
    else:
        print("False")
if count == 0:
    print("No Result")

感谢 :)

3
在回答问题时,请尽量解释为什么这个答案可以解决问题,并附上代码本身。 - David

0

我的函数对我来说运行良好

option = cursor.execute("SELECT value FROM options WHERE key = '{}'".format(key))
    if option.fetchone() is not None:
        return cursor.execute("SELECT value FROM options WHERE key = '{}'".format(key)).fetchone()

2
  1. 使用 .format() 或其他通用的字符串替换方式将参数传递给 SQL 查询是一种非常糟糕的做法(SQL 注入!)。请使用参数化的 SQL 查询,例如 Piney 在这里的答案中所示:https://dev59.com/C2Qn5IYBdhLWcg3wzZxU#49580560
  2. 检查是否运行不同的查询而不是第一个查询可能更有效。例如,我可以想象使用 COUNT()FIRST_VALUE()
- pabouk - Ukraine stay strong
当我使用这段代码时,出现了以下错误:sqlite3.ProgrammingError: 绑定的数量不正确。当前语句使用了1个绑定,但提供了9个。 - Mohamed Slimane

0

参考资料,cursor.rowcount 仅在 CREATEUPDATEDELETE 语句中返回:

 |  rowcount
 |      This read-only attribute specifies the number of rows the last DML statement
 |      (INSERT, UPDATE, DELETE) affected.  This is set to -1 for SELECT statements.

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