【问题标题】:SQLite Multiple Insert with Query Syntax Error带有查询语法错误的 SQLite 多次插入
【发布时间】:2017-01-11 17:25:19
【问题描述】:

我正在尝试使用 python 将多行插入 SQLite DB。我有三张桌子:

大学A:

  • 身份证
  • 课程名称
  • 课程代码
  • 课程前缀

大学B:

  • 身份证
  • 课程名称
  • 课程代码
  • 课程前缀

课程地图:

  • universityA_id(来自大学 A 的引用 id)
  • universityB_id(来自大学 B 的参考 id)
  • is_flagged(布尔值 0/1)

我可以使用以下语法轻松地将单个记录添加到 CourseMap 表:

cur.execute('INSERT INTO CourseMap (universityA_id, universityB_id, is_flagged) VALUES ( (SELECT id from universityA WHERE course_code=301 AND course_prefix="AERO"), (SELECT id from universityB WHERE course_code=101 AND course_prefix="ARCH"), 0)')

但是当我尝试将此语句合并到一个列表中以使用executemany() 执行多个插入时,我得到一个语法错误:

equivs = [
    ((SELECT id from universityA WHERE course_code=301 AND course_prefix="AERO"), (SELECT id from universityB WHERE course_code=101 AND course_prefix="ARCH"), 0),
    ((SELECT id from universityA WHERE course_code=301 AND course_prefix="AERO"), (SELECT id from universityB WHERE course_code=101 AND course_prefix="ARCH"), 0),
    ((SELECT id from universityA WHERE course_code=301 AND course_prefix="AERO"), (SELECT id from universityB WHERE course_code=101 AND course_prefix="ARCH"), 0)
]

# Fill the table
cur.executemany('INSERT INTO courseMap (universityA_id, universityB_id, is_flagged) VALUES (?,?,?)', equivs)

我得到的错误是“语法错误:无效语法”,插入符号位于第一次插入的布尔值 is_flagged 之前。

我在executemany() 的多重插入语法中遗漏了什么吗?我的 SQLite 3 版本是 3.10.0。

【问题讨论】:

  • equivs 声明中的选择语句是否应该是字符串?
  • 我的想法是它们不应该是,因为它们将插入的INSERT 语句是一个字符串...你的意思是equivs 中的项目应该用引号引起来吗?
  • 是的。我不确定您是如何在 that 到达 executemany 语句之前没有收到语法错误的。
  • 澄清一下,每个SELECT 之前和每个"AERO""ARCH" 之后都应该有引号(单引号,因为您在选择中有双引号)。
  • 其实我的错。看起来他们需要在括号之外。

标签: python sql sqlite


【解决方案1】:

您不能将 DDL/DML SQL 语句(即SELECT 语句)作为参数传递。事实上,这就是参数化的非常原因!回想一下Bobby Tables 注入问题。参数旨在为每个执行的调用接收单个绑定值。

根据您的需要,考虑一个联合交叉连接的执行语句,您可以通过逗号分隔的SELECT 语句或显式CROSS JOIN 来执行该语句。下面假设每个查询 SELECT 只返回一个 id 值)否则您将附加笛卡尔叉积。

INSERT INTO CourseMap (universityA_id, universityB_id, is_flagged) 

SELECT * FROM
   (SELECT id from universityA WHERE course_code=301 AND course_prefix="AERO"), 
   (SELECT id from universityB WHERE course_code=101 AND course_prefix="ARCH"), (SELECT 0)

UNION ALL 

SELECT * FROM
    (SELECT id from universityA WHERE course_code=301 AND course_prefix="AERO"), 
    (SELECT id from universityB WHERE course_code=101 AND course_prefix="ARCH"), (SELECT 0)

UNION ALL 

SELECT * FROM
    (SELECT id from universityA WHERE course_code=301 AND course_prefix="AERO"),  
    (SELECT id from universityB WHERE course_code=101 AND course_prefix="ARCH"), (SELECT 0)

除此之外 - 将此架构重新考虑为重复的结构化表,例如两个偏离最佳实践的大学表,因为两个表都可以附加到一个带有指示字段(“A”或“B”)的表中,以实现高效存储、查询, 和缩放。

【讨论】:

  • 我很困惑为什么我可以在 INSERT 中插入带有子查询的单行,但不能对多行执行相同的操作。
  • 您的第一次尝试成功运行了整个 SQL 语句,没有任何参数,如此解决方案所示。您尝试将 SQL 语句作为参数传递失败的尝试。此外,参数不会自行评估,而是绑定到准备好的语句。准备您的 SQL 语句,然后将值作为元组列表作为@CL 传递。像这里一样使用联合交叉连接一次显示或全部显示。
【解决方案2】:

在 SQL 中,可以在 VALUES 子句中使用多个元组;这需要多次编写子查询:

INSERT INTO CourseMap (universityA_id, universityB_id, is_flagged)
VALUES ( (SELECT ...), (SELECT ...), 0),
       ( (SELECT ...), (SELECT ...), 0),
       ( (SELECT ...), (SELECT ...), 0);

Python 的executemany() 使用不同的参数值多次执行same 语句。因此,您唯一可以更改的是子查询中使用的实际值:

args = [ (301, "AERO", 101, "ARCH"), (...) ]
db.executemany("""INSERT INTO CourseMap (universityA_id, universityB_id, is_flagged)
                  VALUES ( (SELECT id from universityA WHERE course_code=? AND course_prefix=?),
                           (SELECT id from universityB WHERE course_code=? AND course_prefix=?),
                          0)""", args)

如果这不是您想要更改的,您必须单独执行子查询,并将其结果提供给executemany

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多