Pandas to_sql使索引唯一

时间:2017-09-02 18:11:20

标签: pandas unique primary-key flask-sqlalchemy pandas-to-sql

我已经阅读了有关pandas to_sql解决方案的信息,以便不向数据库添加重复记录。我正在处理日志的csv文件,每次我上传一个新的日志文件,然后读取数据并使用pandas进行一些更改,创建一个新的数据帧。 然后我执行to_sql('Logs',con = db.engine,if_exists ='append',index = True)。使用if_exists arg,我确保每次将新文件中新创建的数据帧附加到现有数据库。问题是它不断添加重复值。我想确保如果已经上传的文件被错误地再次上传,则不会将其附加到数据库中。我想在创建数据库时直接尝试这样做,而不是找到一种解决方法,例如只检查文件名是否曾被使用过。

我正在使用flask-sqlalchemy。

谢谢。

1 个答案:

答案 0 :(得分:1)

最好的办法是通过将索引设置为主键来捕获重复项,然后使用try / except来捕获唯一性违规。您提到另一篇建议观看IntegrityError例外的帖子,我同意这是最好的方法。您可以将其与去除重复功能结合使用,以确保表更新顺利运行。

证明问题

这是一个玩具示例:

from sqlalchemy import *
import sqlite3

# make a database, 'test', and a table, 'foo'.
conn = sqlite3.connect("test.db")
c = conn.cursor()
# id is a primary key.  this will be the index column imported from to_sql().
c.execute('CREATE TABLE foo (id integer PRIMARY KEY, foo integer NOT NULL);')
# use the sqlalchemy engine.
engine = create_engine('sqlite:///test.db')

pd.read_sql("pragma table_info(foo)", con=engine)

   cid name     type  notnull dflt_value  pk
0    0   id  integer        0       None   1
1    1  foo  integer        1       None   0

现在,有两个示例数据框dfdf2

data = {'foo':[1,2,3]}
df = pd.DataFrame(data)
df
   foo
0    1
1    2
2    3

data2 = {'foo':[3,4,5]}
df2 = pd.DataFrame(data2, index=[2,3,4])
df2
   foo
2    3       # this row is a duplicate of df.iloc[2,:]
3    4
4    5

df移至表格foo

df.to_sql('foo', con=engine, index=True, index_label='id', if_exists='append')

pd.read_sql('foo', con=engine)
   id  foo
0   0    1
1   1    2
2   2    3

现在,当我们尝试追加df2时,我们会抓住IntegrityError

try:
    df2.to_sql('foo', con=engine, index=True, index_label='id', if_exists='append')
# use the generic Exception, both IntegrityError and sqlite3.IntegrityError caused trouble.
except Exception as e: 
    print("FAILURE TO APPEND: {}".format(e))

输出:

FAILURE TO APPEND: (sqlite3.IntegrityError) UNIQUE constraint failed: foo.id [SQL: 'INSERT INTO foo (id, foo) VALUES (?, ?)'] [parameters: ((2, 3), (3, 4), (4, 5))]

建议的解决方案

IntegrityError上,您可以提取现有表数据,删除新数据的重复条目,然后重试append语句。请使用apply()

def append_db(data):
    try:
        data.to_sql('foo', con=engine, index=True, index_label='id', if_exists='append')
        return 'Success'
    except Exception as e:
        print("Initial failure to append: {}\n".format(e))
        print("Attempting to rectify...")
        existing = pd.read_sql('foo', con=engine)
        to_insert = data.reset_index().rename(columns={'index':'id'})
        mask = ~to_insert.id.isin(existing.id)
        try:
            to_insert.loc[mask].to_sql('foo', con=engine, index=False, if_exists='append')
            print("Successful deduplication.")
        except Exception as e2:
            "Could not rectify duplicate entries. \n{}".format(e2)
        return 'Success after dedupe'

df2.apply(append_db)

输出:

Initial failure to append: (sqlite3.IntegrityError) UNIQUE constraint failed: foo.id [SQL: 'INSERT INTO foo (id, foo) VALUES (?, ?)'] [parameters: ((2, 3), (3, 4), (4, 5))]

Attempting to rectify...
Successful deduplication.

foo    Success after dedupe
dtype: object