这是一个简短的程序,它创建一个SQLite数据库来保存一些基本的音乐元数据。数据库中有三个表用于三个元数据字段;歌曲名称,歌曲来自的专辑和制作专辑的艺术家。由于数据的性质,元数据中的重复信息是确定的(如代码中所示)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sqlite3.h>
// Error checks replaced with assert for brevity
#define STRING_MAX 32
// metadata for a audio track
typedef struct {
char title[ STRING_MAX ];
char artist[ STRING_MAX ];
char album[ STRING_MAX ];
} metadata_t;
// some metadata for testing
static metadata_t tracks[] = {
{ "Mr Self Destruct", "Nine Inch Nails", "The Downward Spiral" },
{ "Sit Down, Stand Up", "Radiohead", "Hail to the Thief" },
{ "March of the Pigs", "Nine Inch Nails", "The Downward Spiral" },
};
// number of test tracks in the above array
#define TRACK_COUNT ( sizeof( tracks ) / sizeof( tracks[ 0 ] ) )
int main( int argc, char **argv ) {
sqlite3 *db;
int result = sqlite3_open_v2( "database.db3", &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, "unix-none" );
assert( result == SQLITE_OK );
// create the three tables
// artists have a name
result = sqlite3_exec( db, "CREATE TABLE IF NOT EXISTS artists(\
artist_id INTEGER PRIMARY KEY,\
name TEXT UNIQUE\
);",
NULL, NULL, NULL );
assert( result == SQLITE_OK );
// albums have a name and an artist
result = sqlite3_exec( db, "CREATE TABLE IF NOT EXISTS albums(\
album_id INTEGER PRIMARY KEY,\
name TEXT UNIQUE,\
artist_id INTEGER,\
UNIQUE( name, artist_id )\
);",
NULL, NULL, NULL );
assert( result == SQLITE_OK );
// titles have a name and belong to an album
result = sqlite3_exec( db, "CREATE TABLE IF NOT EXISTS titles(\
title_id INTEGER PRIMARY KEY,\
name TEXT,\
album_id INTEGER\
);",
NULL, NULL, NULL );
assert( result == SQLITE_OK );
// insert the metadata into the databse, one track at a time
int i;
for ( i = 0; i < TRACK_COUNT; ++i ) {
char command[ 1024 ]; // The SQL to execute
int result;
sqlite3_stmt *stmt;
// Ignore the UNIQUE error if the artist is already in the table.
// This makes sqlite3_last_insert_rowid() not work.
(void)sqlite3_snprintf( 1024, command,
"INSERT OR IGNORE INTO artists( name )\
VALUES( '%q' );",
tracks[ i ].artist );
result = sqlite3_exec( db, command, NULL, NULL, NULL );
assert( result == SQLITE_OK );
// Get the rowid for the newly inserted artist
(void)sqlite3_snprintf( 1024, command,
"SELECT artist_id FROM artists WHERE name='%q';",
tracks[ i ].artist );
sqlite3_prepare( db, command, strlen( command ), &stmt, NULL );
assert( sqlite3_column_count( stmt ) == 1 );
sqlite3_step( stmt );
int artist_id = sqlite3_column_int( stmt, 0 );
assert( sqlite3_step( stmt ) == SQLITE_DONE );
sqlite3_finalize( stmt );
// Ignore the UNIQUE error if the album/artist_id combo is
// already in the table
(void)sqlite3_snprintf( 1024, command,
"INSERT OR IGNORE INTO albums( name, artist_id )\
VALUES( '%q', %d );",
tracks[ i ].album, artist_id );
result = sqlite3_exec( db, command, NULL, NULL, NULL );
assert( result == SQLITE_OK );
// Get the rowid for the newly inserted album
(void)sqlite3_snprintf( 1024, command,
"SELECT album_id FROM albums WHERE name='%q';",
tracks[ i ].album );
sqlite3_prepare( db, command, strlen( command ), &stmt, NULL );
assert( sqlite3_column_count( stmt ) == 1 );
sqlite3_step( stmt );
int album_id = sqlite3_column_int( stmt, 0 );
assert( sqlite3_step( stmt ) == SQLITE_DONE );
sqlite3_finalize( stmt );
// Finally, insert the track title and the album it came from
(void)sqlite3_snprintf( 1024, command,
"INSERT INTO titles( name, album_id )\
VALUES( '%q', %d );",
tracks[ i ].title, album_id );
result = sqlite3_exec( db, command, NULL, NULL, NULL );
assert( result == SQLITE_OK );
}
sqlite3_close( db );
return ( 0 );
}
编译和测试:
$ gcc -Wall database.c -o database -lsqlite3&amp;&amp; ./database&amp;&amp; sqlite3 database.db
SQLite版本3.6.23.1
输入“.help”获取说明
输入以“;”结尾的SQL语句
源码&GT; 。上的
源码&GT; .mode csv
源码&GT; SELECT
...&GT; titles.name AS标题,
...&GT; albums.name AS专辑,
...&GT; artists.name AS艺术家
...&GT;来自标题
...&GT; INNER JOIN专辑USING(album_id)
...&GT; INNER JOIN艺术家使用(artist_id);
标题,专辑,艺术家
“自毁先生”,“向下螺旋”,“九寸钉”
“坐下来,站起来”,“向小偷致敬”,Radiohead
“猪的三月”,“向下的螺旋”,“九英寸的钉子”
因为每个INSERT都可以处理数据库中已有的数据,所以我使用的是INSERT或IGNORE,这意味着我不能再依赖sqlite_last_insert_rowid()为artist_id和album_id提供ROWID,这就是为什么我然后搜索数据库以获取ROWID。任何有关更好设计的建议都会很棒!
答案 0 :(得分:1)
回答我自己的问题感觉有点不对劲;我仍然愿意接受更好的建议!如果数据不存在,则此通用插入函数将插入数据,并返回ROWID。
sqlite3_int64 _insert_generic(
sqlite3 *db,
const char *table,
const char *key,
const char *value
) {
char sql[ SQL_MAX ];
int result;
sqlite3_stmt *stmt;
sqlite3_int64 id;
/* check if this key/value is already in table */
(void)sqlite3_snprintf( SQL_MAX, sql,
"SELECT %s FROM %s WHERE name='%q';",
key,
table,
value );
result = sqlite3_prepare_v2( db, sql, strlen( sql ), &stmt, NULL );
assert( result == SQLITE_OK );
if ( sqlite3_step( stmt ) == SQLITE_ROW ) {
/* a key/value was found in table, get the ROWID */
id = sqlite3_column_int( stmt, 0 );
assert( sqlite3_step( stmt ) == SQLITE_DONE );
sqlite3_finalize( stmt );
} else {
/* key/value is not in table, so insert it */
sqlite3_finalize( stmt );
(void)sqlite3_snprintf( SQL_MAX, sql,
"INSERT INTO %s( name )\
VALUES( '%q' );",
table,
value );
result = sqlite3_exec( db, sql, NULL, NULL, NULL );
assert( result == SQLITE_OK );
id = sqlite3_last_insert_rowid( db );
}
assert( id > 0 );
return ( id );
}
答案 1 :(得分:0)
如果使用INSERT OR REPLACE怎么办?插入内容应占用相同的时间。重复不应影响任何事情(可能不会比整体进行二次搜索花费更长的时间)。文档说它被视为成功,最后的rowid将被更新。但是,我不知道替换是否会实际更改表中现有的rowid,丢掉主键。