2014-01-27 39 views
0

我试图在sqlite表中存储播放列表。为了节省存储空间,我想将歌曲艺术家和标题保存在表格中,并使用表格的rowid来存储剧情。Python&Sqlite3 - 如何从两条select语句插入ID

数据库的结构是这样的:

conn = sqlite3.connect('playlist.db') 
c = conn.cursor() 
sqlcommand= 'CREATE TABLE if not EXISTS artists(artist text PRIMARY KEY)' 
c.execute(sqlcommand) 
sqlcommand= 'CREATE TABLE if not EXISTS titles(title text PRIMARY KEY)' 
c.execute(sqlcommand) 
sqlcommand= 'CREATE TABLE if not EXISTS plays(PlayDate date, PlayTime datetime, ArtistID integer, TitleID integer)' 
c.execute(sqlcommand) 

每首歌曲的表演事件存储这样

sqlcommand= 'INSERT or IGNORE INTO artists VALUES ("' + FoundArtist + '")' 
c.execute(sqlcommand) 
sqlcommand= 'INSERT or IGNORE INTO titles VALUES ("' + FoundTitle + '")' 
c.execute(sqlcommand) 

sqlcommand= 'SELECT ROWID from artists where artist = "'+FoundArtist+'"' 
c.execute(sqlcommand) 
ArtistID = str(c.fetchone()[0]) 

sqlcommand= 'SELECT ROWID from titles where title = "'+FoundTitle+'"' 
c.execute(sqlcommand) 
TitleID = str(c.fetchone()[0]) 

sqlcommand= 'INSERT or IGNORE INTO plays VALUES ("' + FoundPlayDate + '",' + \ 
              '"'+ FoundPlayTime + '",' + \ 
                ArtistID + ',' + \ 
                TitleID + ')' 

c.execute(sqlcommand) 

这工作,但相当缓慢。

如何组合两个select和last insert命令,以便它们同时运行?

我对树莓PI运行的Python 2.7.6(与Raspian/Debian的喘息)

回答

1

你不能依靠rowid值时,该列未在表定义出现;任何VACUUM都可能改变它们。 所以,你的表应该被定义是这样的:

CREATE TABLE artists(ID INTEGER PRIMARY KEY, name TEXT UNIQUE); 
CREATE TABLE titles(ID INTEGER PRIMARY KEY, name TEXT UNIQUE); 
CREATE TABLE plays(
    PlayDate DATE, 
    PlayTime DATETIME, 
    /* ... */ 
    ArtistID INTEGER REFERENCES artists(ID), 
    TitleID INTEGER REFERENCES titles(ID) 
); 

从理论上讲,ID查询可以用子查询来完成,像这样:

INSERT INTO plays VALUES('...', '...', 
         (SELECT ID FROM artists WHERE name = '...'), 
         (SELECT ID FROM titles WHERE name = '...')); 

然而,该数据库已经必须做一个查询当您尝试插入它们时,名称的名称。 对于已经存在,就可以避免这些查找之一,如果你尝试读取ID(并在同一时间检查其存在),将其插入名:

c.execute("SELECT ID FROM artists WHERE name = ?", (FoundArtist,)) 
row = c.fetchone() 
if row is None: 
    c.execute("INSERT INTO artists(name) VALUES(?)", (FoundArtist,)) 
    artistID = c.lastrowid 
else: 
    artistID = row[0] 

c.execute("SELECT ID FROM titles WHERE name = ?", (FoundTitle,)) 
row = c.fetchone() 
if row is None: 
    c.execute("INSERT INTO titles(name) VALUES(?)", (FoundTitle,)) 
    titleID = c.lastrowid 
else: 
    titleID = row[0] 

c.execute("INSERT INTO plays VALUES(?,?,?,?)", 
      (FoundPlayDate, FoundPlayTime, artistID, titleID)) 
+0

VACUUM评论的好处。我将在我的空闲时间稍后测试它,并在这适用于我时标记它解决。谢谢。 – 576i

+0

我遇到了'c.execute(“SELECT ID FROM titles WHERE name =?”,(FoundTitle,))''''我在哪里出错** sqlite3。ProgrammingError:除非使用可解释8位字节串的text_factory,否则不得使用8位字节串**,以便我将它们组装为字符串。其余的工作正常,所以我会标记这个解决。 – 576i

0

你并不需要从artiststitlesrowid秒。您已将艺术家和标题名称设置为主键。他们的ID,你不需要查询它们,因为你已经拥有了它们。

您真正需要做的是改变你的plays模式是这样的:

CREATE TABLE plays (
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    founddatetime TEXT DEFAULT CURRENT_TIMESTAMP, 
    artist TEXT, 
    title TEXT, 
    FOREIGN KEY(artist) REFERENCES artists(artist), 
    FOREIGN KEY(title) REFERENCES titles(title) 
); 

FOREIGN KEY指令确保每个artisttitleplays相当于分别为artiststitles,一个条目。

然后添加记录看起来有点像这样:

c.execute('INSERT OR IGNORE INTO artists(artist) VALUES ?', [FoundArtist]) 
c.execute('INSERT OR IGNORE INTO titles(title) VALUES ?', [FoundTitle]) 
c.execute('INSERT INTO plays(founddatetime, artist, title) VALUES (?, ?, ?)', 
      [FoundPlayDateTime, FoundArtist, FoundTitle]) 

c.commit() 
+0

感谢您的答复。我将艺术家和标题设置为主**,以避免插入重复项**。 (这样做肯定有更好的方法,但是我还不知道那种语法)我把rowid当作**真正的主键**,因为如果我在游戏中只使用id,数据库大小会缩小50%表。这也使得查询速度更快。 – 576i