I have more or less completed this project, so I thought I would post the final script. The script is in ruby and accepts the name of an input file and the name of the database,
ruby loader.rb inputFilename databaseName The input file uses metadata rows to identify the database table that the column will go to, the data type, and the column name. The unique fields iK1, iK2, iK3, iK4 are how the data for the primary table is registered for each input record. If a record is inserted and the database already contains a record with the same value for these 4 ints, there will be an exception. The script will create the database if it doesn't exist, but can also be used to insert records to an existing database. The "structure" table is required with all 9 fields not null. The other satellite tables are optional. Additional tables can be added by adding new columns with the table name in the first row, the data type for the column in the second row, and the column name in the third row. All fields in the satellite tables are not null by default. The script is below, but I have also attached it with a sample input file. sample_files_sqlite_ruby_loader.zip <http://sqlite.1065341.n5.nabble.com/file/n69543/sample_files_sqlite_ruby_loader.zip> Thanks for the assistance. I am now working on a script to add/change data in the existing records in a database, as well as a simple query script to export data to a delimited text file. *LMHmedchem* # script loader.rb require 'rubygems' require 'sqlite3' require 'logger' class DataLoader attr_accessor :db_name, :current_row, :table_names, :field_names, :data_types, :line_number, :db, :ready, :structure_id UNIQUE_FIELDS = %w(iK1 iK2 iK3 iK4) def initialize(db_path) @db_name = db_path @table_names = [] @field_names = {} @data_types = {} @ready = false @log = Logger.new('sqlite_data_loader.log', 'weekly') db_connect end def db_connect @db = SQLite3::Database.open @db_name # Enabling foreign_keys features @db.execute('PRAGMA foreign_keys = ON') @ready = true end def load_data(tsv_file) @line_number = 1 File.open(tsv_file, 'r') do |f1| while line = f1.gets @current_row = line.strip.split("\t") next if @current_row.empty? if @line_number <= 3 collect_table_info # After processing third line, we can create tables with the information collected. create_tables if @line_number == 3 else collect_table_data end @line_number += 1 end end end def collect_table_info if @line_number == 1 @current_row.uniq.each do |name| @table_names << [name, @current_row.count(name)] end else offset = 0 @table_names.each do |table_name, fields_count| @data_types[table_name] = @current_row[offset..(offset + fields_count - 1)] if @line_number == 2 @field_names[table_name] = @current_row[offset..(offset + fields_count - 1)] if @line_number == 3 offset += fields_count end end end def create_tables @table_names.each do |table_name, arity| create_table table_name end end def create_table(table_name) query = "CREATE TABLE IF NOT EXISTS #{table_name}(#{fields_for table_name})" @log.debug query @db.execute query unless 'structure' == table_name @log.debug "Creating index on #{table_name}" @db.execute "CREATE INDEX IF NOT EXISTS idx_#{table_name}_structure_id ON #{table_name}(structure_id)" end rescue SQLite3::Exception => e @log.debug "Exception occured #{e.class}: #{e.message}" end def fields_for(table_name) list = [] if 'structure' == table_name list << 'id INTEGER PRIMARY KEY AUTOINCREMENT' else list << 'structure_id INTEGER NOT NULL' end fields = [] @field_names[table_name].each_with_index do |field, index| next if 'primary_key' == field next if fields.member?(field) list << "'#{field}' #{field_data_type(table_name, index)}" fields << field end list << add_unique_fields(table_name) list << add_foreign_key_constraint(table_name) list.compact.join(', ') end def field_data_type(table_name, index) kind = @data_types[table_name][index].upcase kind = 'TEXT' if kind.start_with?('STR') "#{kind}#{' NOT NULL' if 'structure' == table_name}" end def add_unique_fields(table_name) available_fields = (@field_names[table_name] & UNIQUE_FIELDS) unless available_fields.empty? "UNIQUE(#{available_fields.join(', ')}) ON CONFLICT ROLLBACK" end end def add_foreign_key_constraint(table_name) unless 'structure' == table_name 'FOREIGN KEY(structure_id) REFERENCES Structure(id)' end end def insert_into_fields(table_name, values) field_names = @field_names[table_name] values_list, attr_names = [], [] if 'structure' == table_name attr_names << 'id' values_list << nil elsif @structure_id && values attr_names << 'structure_id' values_list << @structure_id end values.each_with_index do |value, indx| field_name = field_names[indx] next if 'primary_key' == field_name next if attr_names.member?(field_name) attr_names << field_name if 'NULL' == value values_list << nil else values_list << value end end [attr_names, values_list] end def collect_table_data offset = 0 @structure_id = nil @table_names.each do |table_name, fields_count| values_array = @current_row[offset..(offset + fields_count - 1)] begin fields, values = insert_into_fields(table_name, values_array) next if fields.empty? if values_array && !values_array.empty? && ('structure' == table_name or @structure_id) @log.debug "Populating #{table_name}" @db.execute "INSERT INTO #{table_name} (#{fields.collect {|name| %{'#{name}'}}.join ','}) VALUES(#{(['?'] * values.size).join ','})", values @structure_id = @db.last_insert_row_id if 'structure' == table_name else @log.debug "Skipping population of `#{table_name}` with #{values.inspect} due to structure data existance." end rescue SQLite3::Exception => e @log.debug "Exception occured while importing data into `#{table_name}`: #{e.message}" return rescue Exception => e @log.debug "Exception occured while importing data into `#{table_name}`: #{e.class} #{e.message}" fail end offset += fields_count end end end if ARGV.empty? || ARGV.length < 2 puts "Usage: #{File.basename __FILE__} data.tsv database.sqlite3" else loader = DataLoader.new(ARGV[1]) if loader.ready loader.load_data ARGV[0] end end -- View this message in context: http://sqlite.1065341.n5.nabble.com/inserting-record-data-into-mutliple-tables-with-a-composite-primary-key-tp64874p69543.html Sent from the SQLite mailing list archive at Nabble.com. _______________________________________________ sqlite-users mailing list sqlite-users@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users