Как вставить информацию в базу данных Sqlite с помощью языка программирования Genie?

Этот вопрос связан с предыдущим вопросом, в котором была создана база данных. Однако, когда дело доходит до добавления информации к этому набору данных, я могу вручную добавить информацию или перейти через программный путь. Последний мой выбор по дидактической причине.

Эквивалентом того, что я пытаюсь сделать в 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 метода итератора для var x в last_id ^^^^^^^ Ошибка компиляции: 1 ошибка (-а), предупреждение 0 (ы)

Как решить эту проблему с помощью кода в Genie? Должен ли я преобразовать его в другой тип, который принимает использование в качестве итератора? Кроме того, этот синтаксис (%s), x правильный?

благодаря

2 Solutions collect form web for “Как вставить информацию в базу данных Sqlite с помощью языка программирования Genie?”

Ключевой момент вашей проблемы – получить последнее значение вставки (первичный ключ для таблицы рецептов) и поместить его в следующий оператор.

Чтобы сделать вставку полностью безопасной (доказательство против SQL-инъекции ), вы должны использовать подготовленный оператор .

Я также добавил намного больше ошибок.

 [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() для создания внешнего ключа. last_insert_rowid() – это одно значение, а не набор значений. Поэтому нет необходимости перебирать его в цикле for .

В следующем примере используются подготовленные операторы для вставки значений в две таблицы. Первая таблица содержит имя пользователя, а вторая таблица содержит внешний ключ для таблицы пользователя и случайно сгенерированный идентификатор ссылки.

Проблемная область, на которую вы смотрите, – это загрузка данных. Таким образом, эта программа может стать основой программы загрузки данных, которая использует преимущества производительности 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 позволяет программе останавливаться при возникновении ошибки путем Process.exit( -1 ) ссылки на любой объект, когда блок try заканчивается, а затем блок except может использовать Process.exit( -1 ) безопасно. При возврате -1 программа может сигнализировать любому вызывающему скрипту, что ошибка загрузки
  • Программа была разделена на отдельные функции и классы, обратите внимание, что соединение с базой данных передается как аргумент каждой функции, это принцип инкапсуляции в программировании
  • Класс DataGenerator также предоставляет пример инкапсуляции, он отслеживает, сколько примеров он сгенерировал, а затем останавливается при _max_iterations лимита _max_iterations
  • Класс DataGenerator можно было бы легко использовать для чтения из файла. Надеюсь, вы можете начать видеть, как Genie, как и любой другой объектно-ориентированный язык программирования, может помочь модулизовать ваш код
  • Каждый пользователь имеет два сообщения, поэтому программе необходимо сохранить last_insert_rowid() иначе данные будут повреждены, когда last_insert_rowid() изменится на ID первого вставленного сообщения
  • DataGenerator создает десять тысяч примеров, и они загружаются примерно на четверть секунды на моем компьютере. Комментируйте строки BEGIN TRANSACTION и END TRANSACTION и программа занимает около ста шестидесяти секунд! Таким образом, для загрузки данных в SQLite транзакция представляет собой огромный прирост производительности
  • В этом примере подготовленные операторы транзакции быстрее, чем загрузка дампа базы данных
      sqlite3 example.sqlite .dump> backup.sql
     время cat backup.sql |  sqlite3 test.sqlite 

    занимает около 0,8 с на моей машине, тогда как программа занимает около 0,25 с

Python - лучший язык программирования в мире.