如何使用 Genie 编程语言将信息插入到 Sqlite 数据库中?
How to INSERT information into a Sqlite database using Genie Programming Language?
这个问题是上一个创建数据库的问题 的衍生问题。但是,在向该数据集添加信息时,我可以手动添加信息或通过编程方式进行。出于教学原因,我选择后者。
相当于我在 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: error:
int64' does not have an
iterator' method
for var x in last_id
^^^^^^^ Compilation failed: 1 error(s), 0 warning(s)
如何在精灵中用代码解决这个问题?我应该将它转换为另一种接受用作迭代器的类型吗?另外,语法 (%s), x
正确吗?
谢谢
你的问题的关键在于如何获取最后一次插入的值(Recipestable的主键)并放入下一条语句
使插入物完全安全(防止SQL injection) you should be using a 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 抽象。使用这种抽象,代码会短很多。
您似乎遇到的问题是使用 last_insert_rowid()
制作外键。 last_insert_rowid()
是单个值,而不是值的集合。所以没有必要在 for
循环中循环它。
以下示例使用准备好的语句将值插入到两个 table 中。第一个 table 包含用户名,第二个 table 包含用户的外键 table 和随机生成的参考 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()
)
注意事项:
- 创建数据库的函数和 tables 在程序的末尾,因为它们只是为了让示例起作用
- 使用
try...except
允许程序在try
块结束时通过取消引用任何对象而在发生错误时停止,然后except
块可以使用[=17= 】 安全。通过返回 -1,程序可以向任何调用脚本发出加载失败的信号
- 程序已经拆分成单独的函数和classes,注意数据库连接作为参数传递给每个函数,这就是编程中的封装原则
-
DataGenerator
class 也提供了一个封装的例子,它跟踪它产生了多少例子然后在超过 _max_iterations
限制时停止
-
DataGenerator
class 可以很容易地用于读取文件。希望您可以开始了解 Genie 与任何其他面向对象的编程语言一样,如何帮助模块化您的代码
- 每个用户有两个post,所以程序必须存储
last_insert_rowid()
,否则当last_insert_rowid()
更改为第一个[=55]的ID时,数据将被破坏=] 已插入
DataGenerator
创建了一万个示例,这些示例在大约四分之一秒内加载到我的机器上。注释掉 BEGIN TRANSACTION
和 END TRANSACTION
行,程序大约需要一百六十秒!因此对于 SQLite 中的数据加载,一个事务是一个巨大的性能提升
- 在此示例中,事务中的准备语句比加载数据库转储更快
sqlite3 example.sqlite .dump > backup.sql
time cat backup.sql | sqlite3 test.sqlite
在我的机器上大约需要 0.8 秒,而程序大约需要 0.25 秒
这个问题是上一个创建数据库的问题
相当于我在 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: error:
int64' does not have an
iterator' method for var x in last_id ^^^^^^^ Compilation failed: 1 error(s), 0 warning(s)
如何在精灵中用代码解决这个问题?我应该将它转换为另一种接受用作迭代器的类型吗?另外,语法 (%s), x
正确吗?
谢谢
你的问题的关键在于如何获取最后一次插入的值(Recipestable的主键)并放入下一条语句
使插入物完全安全(防止SQL injection) you should be using a 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 抽象。使用这种抽象,代码会短很多。
您似乎遇到的问题是使用 last_insert_rowid()
制作外键。 last_insert_rowid()
是单个值,而不是值的集合。所以没有必要在 for
循环中循环它。
以下示例使用准备好的语句将值插入到两个 table 中。第一个 table 包含用户名,第二个 table 包含用户的外键 table 和随机生成的参考 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()
)
注意事项:
- 创建数据库的函数和 tables 在程序的末尾,因为它们只是为了让示例起作用
- 使用
try...except
允许程序在try
块结束时通过取消引用任何对象而在发生错误时停止,然后except
块可以使用[=17= 】 安全。通过返回 -1,程序可以向任何调用脚本发出加载失败的信号 - 程序已经拆分成单独的函数和classes,注意数据库连接作为参数传递给每个函数,这就是编程中的封装原则
-
DataGenerator
class 也提供了一个封装的例子,它跟踪它产生了多少例子然后在超过_max_iterations
限制时停止 -
DataGenerator
class 可以很容易地用于读取文件。希望您可以开始了解 Genie 与任何其他面向对象的编程语言一样,如何帮助模块化您的代码 - 每个用户有两个post,所以程序必须存储
last_insert_rowid()
,否则当last_insert_rowid()
更改为第一个[=55]的ID时,数据将被破坏=] 已插入 DataGenerator
创建了一万个示例,这些示例在大约四分之一秒内加载到我的机器上。注释掉BEGIN TRANSACTION
和END TRANSACTION
行,程序大约需要一百六十秒!因此对于 SQLite 中的数据加载,一个事务是一个巨大的性能提升- 在此示例中,事务中的准备语句比加载数据库转储更快
sqlite3 example.sqlite .dump > backup.sql time cat backup.sql | sqlite3 test.sqlite
在我的机器上大约需要 0.8 秒,而程序大约需要 0.25 秒