我使用pandas df.to_sql创建了一个sqlite数据库,但访问它似乎比只读取500mb csv文件慢得多。
我需要:
代码....(格式代码按钮不起作用)
if ext == ".csv":
df = pd.read_csv("/Users/data/" +filename)
columns = df.columns columns = [i.replace(' ', '_') for i in columns]
df.columns = columns
df.to_sql(name,con,flavor='sqlite',schema=None,if_exists='replace',index=True,index_label=None, chunksize=None, dtype=None)
答案 0 :(得分:10)
不幸的是,现在无法在pandas df.to_sql()方法中设置主键。另外,为了让事情变得更加困难,在创建表之后,无法在sqlite中的列上设置主键。
但是,目前的解决方法是使用pandas df.to_sql()方法在sqlite中创建表。然后,您可以创建一个重复的表并设置主键,然后复制数据。然后放下旧桌子进行清理。
这将是与此类似的事情。
import pandas as pd
import sqlite3
df = pd.read_csv("/Users/data/" +filename)
columns = df.columns columns = [i.replace(' ', '_') for i in columns]
#write the pandas dataframe to a sqlite table
df.columns = columns
df.to_sql(name,con,flavor='sqlite',schema=None,if_exists='replace',index=True,index_label=None, chunksize=None, dtype=None)
#connect to the database
conn = sqlite3.connect('database')
c = conn.curser()
c.executescript('''
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE table RENAME TO old_table;
/*create a new table with the same column names and types while
defining a primary key for the desired column*/
CREATE TABLE new_table (col_1 TEXT PRIMARY KEY NOT NULL,
col_2 TEXT);
INSERT INTO new_table SELECT * FROM old_table;
DROP TABLE old_table;
COMMIT TRANSACTION;
PRAGMA foreign_keys=on;''')
#close out the connection
c.close()
conn.close()
过去我这样做是因为我遇到了这个问题。只是将整个东西包裹起来以使其更方便......
在我对sqlite的有限经验中,我发现在创建表后无法添加主键,无法执行更新插入或UPSERTS,UPDATE JOIN引起了很多挫折和一些非常规的解决方法。
最后,在pandas df.to_sql()方法中,有一个dtype关键字参数,可以获取列名称的字典:types。 IE:dtype = {col_1:TEXT}
答案 1 :(得分:5)
基于Chris Guarino的回答,这里提供了一些更通用的解决方案。请参阅底部的示例,了解如何使用它们。
import re
def get_create_table_string(tablename, connection):
sql = """
select * from sqlite_master where name = "{}" and type = "table"
""".format(tablename)
result = connection.execute(sql)
create_table_string = result.fetchmany()[0][4]
return create_table_string
def add_pk_to_create_table_string(create_table_string, colname):
regex = "(\n.+{}[^,]+)(,)".format(colname)
return re.sub(regex, "\\1 PRIMARY KEY,", create_table_string, count=1)
def add_pk_to_sqlite_table(tablename, index_column, connection):
cts = get_create_table_string(tablename, connection)
cts = add_pk_to_create_table_string(cts, index_column)
template = """
BEGIN TRANSACTION;
ALTER TABLE {tablename} RENAME TO {tablename}_old_;
{cts};
INSERT INTO {tablename} SELECT * FROM {tablename}_old_;
DROP TABLE {tablename}_old_;
COMMIT TRANSACTION;
"""
create_and_drop_sql = template.format(tablename = tablename, cts = cts)
connection.executescript(create_and_drop_sql)
# Example:
# import pandas as pd
# import sqlite3
# df = pd.DataFrame({"a": [1,2,3], "b": [2,3,4]})
# con = sqlite3.connect("deleteme.db")
# df.to_sql("df", con, if_exists="replace")
# add_pk_to_sqlite_table("df", "index", con)
# r = con.execute("select sql from sqlite_master where name = 'df' and type = 'table'")
# print(r.fetchone()[0])
此代码here
有一个要点答案 2 :(得分:1)
在Sqlite中,具有普通的rowid表,除非主键是单个INTEGER
列(请参见文档中的ROWIDs and the INTEGER PRIMARY KEY),否则它等效于UNIQUE
索引(因为标准表的实际PK是rowid
)。
来自the documentation for rowid tables的笔记:
rowid表的PRIMARY KEY(如果有)通常不是该表的真正主键,从某种意义上说,它不是基础B树存储引擎使用的唯一键。当rowid表声明INTEGER PRIMARY KEY时,此规则例外。例外情况是,INTEGER PRIMARY KEY成为该rowid的别名。
rowid表的真正主键(用作在基础B树存储引擎中查找行的键的值)是rowid。
rowid表的PRIMARY KEY约束(只要它不是真正的主键或INTEGER PRIMARY KEY)与UNIQUE约束实际上是同一回事。因为它不是真正的主键,所以允许PRIMARY KEY的列为NULL,这违反了所有SQL标准。
因此,您可以使用以下命令创建表后轻松伪造主键:
CREATE UNIQUE INDEX mytable_fake_pk ON mytable(pk_column)
除了NULL
之外,如果您的列应该容纳整数,那么您将无法获得INTEGER PRIMARY KEY
的好处,例如占用更少的空间并在插入时自动生成插入值(如果忽略的话) ,但它在大多数情况下仍然可以使用。
答案 3 :(得分:1)
基于Chris Guarino的答案,几乎不可能使用df.to_sql()方法将主键分配给已经存在的列。同样,在500mb的csv文件中,您无法创建具有大量列的重复表。
但是,在为SQL创建数据帧时添加新列作为主键的一个小解决方法。可以遍历Pandas的 dataframe.columns 函数来创建新数据库,并且在创建过程中可以添加主键。有了这个重复的表就不需要了。
我正在为其添加一个小的代码段。
import pandas as pd
import sqlite3
import sqlalchemy
from sqlalchemy import create_engine
df= pd.read_excel(r'C:\XXX\XXX\XXXX\XXX.xlsx',sep=';')
X1 = df1.iloc[0:,0:]
dataset = X1.astype('float32')
dataset['date'] = pd.date_range(start='1/1/2020', periods=len(dataset), freq='D')
dataset=dataset.set_index('date')
engine = create_engine('sqlite:///measurement.db')
sqlite_connection = engine.connect()
sqlite_table = "table1"
sqlite_connection.execute("CREATE TABLE table1 (id INTEGER PRIMARY KEY AUTOINCREMENT, date TIMESTAMP, " +
",".join(["%s REAL" % x for x in dataset.columns]) + ")" )
dataset.to_sql(sqlite_table, sqlite_connection, if_exists='append')
Output database table:
[(0, 'id', 'INTEGER', 0, None, 1),
(1, 'date', 'TIMESTAMP', 0, None, 0),
(2, 'time_stamp', 'REAL', 0, None, 0),
(3, 'column_1', 'REAL', 0, None, 0),
(4, 'column_2', 'REAL', 0, None, 0)]
仅当数据框具有索引时,此方法才有效。此外,要使索引作为表中的列,还应在编写查询时明确定义。
希望这有助于创建大量数据库。
答案 4 :(得分:0)
还有另一种选择,让熊猫使用来自熊猫内部的一些未记录的方法在创建表时创建主键(后果自负)。您可以仔细阅读代码here。密钥是keys
API中未公开的SQLTable
的{{1}}参数。
请注意,我to_sql
并在对reset_index
的调用中设置了index=False
,以防止除了主键约束之外还创建duplicate/unnecessary索引。
SQLTable
该文件中还有一个from pandas.io.sql import SQLTable, pandasSQL_builder
df = <your dataframe>
engine = <sqlalchemy engine>
table = SQLTable(
"my_table",
pandasSQL_builder(engine, schema="my_schema"),
frame=df.reset_index(),
index=False,
keys=df.index.names,
if_exists=if_exists,
schema="my_schema",
)
table.create() # Will honor your if_exists settings
table.insert(chunksize, method="multi") # This hits limits in allowed sqlite params if chunks are too large
函数,如果您想手动执行某项操作,可以使您获得一条create table语句。
答案 5 :(得分:0)
没有办法做到。移动数据后,只能直接在数据库中设置主键。