【问题标题】:Using UPDATE in SQLite for Many Rows with a Python List在 SQLite 中对带有 Python 列表的多行使用 UPDATE
【发布时间】:2017-01-28 01:20:05
【问题描述】:

我使用与 Python 连接的 SQLite (sqlite3),将参数保存在用于处理大量数据的表中。假设我最初已经填充了表格,但随后更改了参数,并且我想更新我的表格。如果我创建一个包含更新参数的 Python 列表,对于表中的每一行和每一列,我如何更新表?

我看过 herehere(尽管后者指的是 C++ 而不是 Python),但这些并不能真正回答我的问题。

为了具体说明,我在下面展示了我的一些代码:

import sqlite3 as sql
import numpy as np

db = sql.connect('./db.sq3')
cur = db.cursor()

#... Irrelevant Processing Code ...#

cur.execute("""CREATE TABLE IF NOT EXISTS process_parameters (
                 parameter_id            INTEGER PRIMARY KEY,
                 exciton_bind_energy     REAL,
                 exciton_bohr_radius     REAL,
                 exciton_mass            REAL,
                 exciton_density_per_QW  REAL,
                 box_trap_side_length    REAL,
                 electron_hole_overlap   REAL,
                 dipole_matrix_element   REAL,
                 k_cutoff                REAL)""")

#Parameter list
process_params = [(E_X/1.6e-19, a_B/1e-9, m_exc/9.11e-31, 1./(np.sqrt(rho_0)*a_B), D/1e-6, phi0/1e8, d/1e-28, k_cut/(1./a_B)) for i in range(0,14641)]

#Check to see if table is populated or not
count = cur.execute("""SELECT COUNT (*) FROM process_parameters""").fetchone()[0]

#If it's not, fill it up
if count == 0:
    cur.executemany("""INSERT INTO process_parameters VALUES(NULL, ?, ?, ?, ?, ?, ?, ?, ?);""", process_params)
    db.commit()

现在,假设在后续处理运行中,我更改了process_params 中的一个或多个参数。我想要的是在任何后续运行中 Python 将使用最新版本的参数更新数据库。所以我愿意

else:
    cur.executemany("""UPDATE process_parameters SET exciton_bind_energy=?, exciton_bohr_radius=?, exciton_mass=?, exciton_density_per_QW=?, box_trap_side_length=?, electron_hole_overlap=?, dipole_matrix_element=?, k_cutoff=?;""", process_params)
    db.commit()
db.close()

但是当我这样做时,脚本似乎挂起(或运行非常缓慢),因此 Ctrl+C 甚至不会退出脚本(通过 ipython 运行)。

我知道在这种情况下,使用巨大的 Python 列表进行更新可能无关紧要,但这是我想澄清的原则,因为在其他时候,我可能不会用相同的值更新每一行。如果有人可以帮助我了解正在发生的事情和/或如何解决此问题,我将不胜感激。谢谢。

【问题讨论】:

    标签: python sqlite


    【解决方案1】:
    cur.executemany("""
        UPDATE process_parameters SET 
            exciton_bind_energy=?, 
            exciton_bohr_radius=?, 
            exciton_mass=?, 
            exciton_density_per_QW=?, 
            box_trap_side_length=?, 
            electron_hole_overlap=?, 
            dipole_matrix_element=?,
            k_cutoff=?
       ;
    """, process_params)
    

    您在更新时忘记了 WHERE 子句。如果没有 WHERE 子句,UPDATE 语句将更新表中的每一行。由于您提供了 14641 组参数,SQLite 驱动程序将更新行数为 14641(输入)×14641(表中的行数)= 2.14 亿次,这说明了为什么它很慢。

    正确的方法是每次只更新相关行:

    cur.executemany("""
        UPDATE process_parameters SET 
            exciton_bind_energy=?, 
            exciton_bohr_radius=?, 
            exciton_mass=?, 
            exciton_density_per_QW=?, 
            box_trap_side_length=?, 
            electron_hole_overlap=?, 
            dipole_matrix_element=?,
            k_cutoff=?
       WHERE parameter_id=?        
    -- ^~~~~~~~~~~~~~~~~~~~ don't forget this
       ;
    """, process_params)
    

    当然,这意味着process_params 必须包含参数ID,并且您需要修改INSERT 语句以插入参数ID。

    【讨论】:

    • 谢谢,这似乎有效。不过,与仅创建和填充表相比,逐行更新确实需要一些时间,这并不奇怪。一个后续问题:在 INSERT 语句中,我将 parameter_id 从 INTEGER PRIMARY KEY 更改为 INTEGER 并将其作为表中的最后一列。在这种情况下,使用和 INTEGER 而不是 INTEGER PRIMARY KEY 有什么“不好”的地方吗?将 id 列放在表的末尾是不好的做法吗?谢谢!
    • @pvasudev 保留 PRIMARY KEY,否则您将需要 O(n) 来查找行,而不是 O(log n) 或 O(1)。您可以使用语法INSERT INTO process_parameters (exciton_bind_energy, <snip>, k_cutoff, parameter_id) VALUES (?, <snip>, ?, ?) 来指定插入时的列顺序。
    • @pvasudev 我建议你先找一本关于 SQL 的教程或书籍。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-10
    • 2017-08-02
    • 1970-01-01
    • 2020-01-23
    • 2016-04-24
    • 2021-01-19
    相关资源
    最近更新 更多