这个问题脱离了前一个问题here,其中创建了一个数据库。但是,当涉及向该数据集添加信息时,我可以手动添加信息或通过编程方式进行。后者是我选择的教学理由。
我在python中尝试做的相当于:
for x in cursor.execute(sql):
lastid = x[0]
# Insert data into the instructions table
sql = 'INSERT INTO Instructions (recipeID,instructions) VALUES( %s,"Brown hamburger. Stir in all other ingredients. Bring to a boil. Stir. Lower to simmer. Cover and cook for 20 minutes or until all liquid is absorbed.")' % lastid
cursor.execute(sql)
我的方式是:
//Insert the rest of instructions
var last_id = db.last_insert_rowid()
for var x in last_id
query = """INSERT INTO Instructions (recipeID,instructions) VALUES(
%s,
"Brown hamburger. Stir in all other ingredients. Bring to a boil. Stir. Lower to simmer. Cover and cook for 20 minutes or until all liquid is absorbed."
), x
"""
但是,似乎last_id是一个int64类型,不能是迭代器,因为我得到的错误:
valac --pkg sqlite3 cookcreate.gs cookcreate.gs:55.18-55.24:错误:
int64' does not have an
迭代'方法 对于last_id中的var x ^^^^^^^编译失败:1个错误,0个警告
如何用Genie中的代码解决这个问题?我应该将它转换为另一种类型,它接受被用作迭代器吗?此外,语法(%s), x
是否正确?
由于
答案 0 :(得分:1)
问题的关键在于如何获取最后一个插入值(Recipes表的主键)并将其放入下一个语句中。
要使插入完全安全(证明SQL injection),您应该使用prepared statement。
我还添加了更多的错误检查。
[indent=4]
def check_ok (db : Sqlite.Database, ec : int)
if (ec != Sqlite.OK)
stderr.printf ("Error: %d: %s\n", db.errcode (), db.errmsg ())
Process.exit (-1)
def checked_exec (db : Sqlite.Database, sql : string)
check_ok (db, db.exec (sql))
init
// Opening/creating database. Database name is cookbook.db3
db : Sqlite.Database? = null
if (Sqlite.Database.open ("cookbook.db3", out db) != Sqlite.OK)
stderr.printf ("Error: %d: %s\n", db.errcode (), db.errmsg ())
Process.exit (-1)
checked_exec (db, "CREATE TABLE Recipes (pkiD INTEGER PRIMARY KEY, name TEXT, servings TEXT, source TEXT)")
checked_exec (db, "CREATE TABLE Instructions (pkID INTEGER PRIMARY KEY, instructions TEXT, recipeID NUMERIC)")
checked_exec (db, "CREATE TABLE Ingredients (pkID INTEGER PRIMARY KEY, ingredients TEXT, recipeID NUMERIC)")
// Insert data into Recipe table
checked_exec (db, """INSERT INTO Recipes (name, servings, source) VALUES ("Spanish Rice", 4, "Greg")""")
lastid : int64 = db.last_insert_rowid ()
// Insert data into Inctructions table
instr_sql : string = """INSERT INTO Instructions (recipeID, instructions) VALUES($recipeID, "Brown hamburger. Stir in all other ingredients. Bring to a boil. Stir. Lower to simmer. Cover and cook for 20 minutes or until all liquid is absorbed.")"""
instr_stmt : Sqlite.Statement = null
check_ok (db, db.prepare_v2 (instr_sql, instr_sql.length, out instr_stmt))
param_position : int = instr_stmt.bind_parameter_index ("$recipeID")
assert (param_position > 0)
check_ok (db, instr_stmt.bind_int64 (param_position, lastid))
// Warning: Statment.step uses a different return value mechanism
// check_ok can't be used here
if (instr_stmt.step () != Sqlite.DONE)
stderr.printf ("Error: %d: %s\n", db.errcode (), db.errmsg ())
Process.exit (-1)
PS:如果我要写一个真正的程序,我可能首先用错误域编写更高级别的SQLite抽象。使用这种抽象,代码会短得多。
答案 1 :(得分:1)
您似乎遇到的问题是使用last_insert_rowid()
来创建外键。 last_insert_rowid()
是单个值,而不是值集合。因此,无需在for
循环中循环它。
以下示例使用预准备语句将值插入两个表中。第一个表包含用户名,第二个表包含用户表的外键和随机生成的引用ID。
您正在查看的问题区域是数据加载。因此,该程序可以构成利用Genie性能的数据加载程序的基础。例如,如果你想在加载之前以某种方式整理数据,那么Genie可能对此有好处。稍后有关性能的更多细节。
[indent=4]
uses Sqlite
exception DatabaseError
FAILED_TO_CREATE_DATABASE
FAILED_TO_CREATE_TABLES
FAILED_TO_LOAD_DATA
init
try
database:Database = create_database( "example.sqlite" )
create_tables( database )
load_data( database )
except error:DatabaseError
print error.message
Process.exit( -1 )
def load_data( db:Database ) raises DatabaseError
user_insert_stmnt:Statement = prepare_user_insert_stmnt( db )
posts_insert_stmnt:Statement = prepare_posts_insert_stmnt( db )
var data = new DataGenerator()
user_id:int64 = 0
db.exec( "BEGIN TRANSACTION" )
while data.read()
user_insert_stmnt.bind_text(
user_insert_stmnt.bind_parameter_index( "@name" ),
data.user_name
)
user_insert_stmnt.step()
user_insert_stmnt.reset()
user_id = db.last_insert_rowid()
for var reference_id in data.reference_ids
posts_insert_stmnt.bind_int64(
posts_insert_stmnt.bind_parameter_index( "@user_id" ),
user_id
)
posts_insert_stmnt.bind_int64(
posts_insert_stmnt.bind_parameter_index( "@reference_id" ),
reference_id
)
posts_insert_stmnt.step()
posts_insert_stmnt.reset()
db.exec( "END TRANSACTION" )
def prepare_user_insert_stmnt( db:Database ):Statement
statement:Statement
db.prepare_v2( """
insert into users(
name
)
values( @name )
""", -1, out statement )
return statement
def prepare_posts_insert_stmnt( db:Database ):Statement
statement:Statement
db.prepare_v2( """
insert into posts(
user_id,
reference_id
)
values( @user_id, @reference_id )
""", -1, out statement )
return statement
class DataGenerator
user_name:string = ""
reference_ids:array of uint = new array of uint[ 2 ]
_iteration:int = 0
_max_iterations:int = 10000
def read():bool
user_name = "User%06d".printf( _iteration )
_iteration++
for a:int = 0 to (reference_ids.length -1)
reference_ids[ a ] = Random.next_int()
more:bool = true
if _iteration > _max_iterations
more = false
return more
def create_database( db_name:string ):Database raises DatabaseError
db:Database
result:int = Database.open( db_name, out db )
if result != OK
raise new DatabaseError.FAILED_TO_CREATE_DATABASE(
"Can't create %s SQLite error %d, \"%s\"",
db_name,
db.errcode(),
db.errmsg()
)
return db
def create_tables( db:Database ) raises DatabaseError
sql:string = """
create table users ( id integer primary key,
name varchar not null
);
create table posts ( id integer primary key,
user_id integer not null,
reference_id integer not null
);
"""
if db.exec( sql ) != OK
raise new DatabaseError.FAILED_TO_CREATE_TABLES(
"Can't create tables. SQLite error %d, \"%s\"",
db.errcode(),
db.errmsg()
)
需要注意的一些要点:
try...except
允许程序在try
块结束时取消引用任何对象时发生错误时停止,然后except
块可以安全地使用Process.exit( -1 )
。通过返回-1,程序可以向任何调用脚本发出信号,表明加载失败DataGenerator
类还提供了封装示例,它跟踪已生成的示例数,然后在超出_max_iterations
限制时停止DataGenerator
类可以很容易地用于从文件中读取。希望你能开始看到Genie和任何其他面向对象编程语言一样,如何帮助模块化你的代码last_insert_rowid()
,否则当last_insert_rowid()
更改插入的第一个帖子的ID时,数据将会损坏DataGenerator
创建了一万个示例,这些示例在我的计算机上加载了大约四分之一秒。注释掉BEGIN TRANSACTION
和END TRANSACTION
行,程序大约需要一百六十秒!因此,对于SQLite中的数据加载,事务是一个巨大的性能提升sqlite3 example.sqlite .dump > backup.sql time cat backup.sql | sqlite3 test.sqlite需要大约0.8秒更快,而程序大约需要0.25秒