兩種鏈接方式
用DBAPI構建數據庫鏈接
import pymysql import pandas as pd con = pymysql.connect(host="127.0.0.1",user="root",password="password",db="world") # 讀取sql data_sql=pd.read_sql("SQL查詢語句",con) # 存儲 data_sql.to_csv("test.csv")
用sqlalchemy構建數據庫鏈接
import pandas as pd import sqlalchemy from sqlalchemy import create_engine # 用sqlalchemy構建數據庫鏈接engine db_info = {'user':'user', 'password':'pwd', 'host':'localhost', 'database':'xx_db' # 這里我們事先指定了數據庫,后續操作只需要表即可 } engine = create_engine('mysql+pymysql://%(user)s:%(password)s@%(host)s/%(database)s?charset=utf8' % db_info,encoding='utf-8') #這里直接使用pymysql連接,echo=True,會顯示在加載數據庫所執行的SQL語句。 # sql 命令 sql_cmd = "SELECT * FROM table" df = pd.read_sql(sql=sql_cmd, con=engine)
從Mysql讀取數據,返回DataFrame格式的數據
read_sql
pandas.read_sql(sql, con, index_col=None, coerce_float=True, params=None, parse_dates=None, columns=None, chunksize=None)
效果:將SQL查詢或數據庫表讀入DataFrame。
官方關於參數的詳細介紹:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_sql.html
如何使用read_sql
來源:https://www.cnblogs.com/cymwill/p/8289367.html
下面兩個的作用又是相同的:
將DataFrame格式的數據存儲到Mysql
to_sql
DataFrame.to_sql(name, con, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None,method=None)
官方關於參數的詳細介紹:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_sql.html
如何使用to_sql下部分來源:https://www.jianshu.com/p/d615699ff254
我們從一個簡單的例子開始。在 mysql 數據庫中有一個 emp_data
表,假設我們使用 pandas DataFrame ,將數據拷貝到另外一個新表 emp_backup
。
import pandas as pd from sqlalchemy import create_engine import sqlalchemy engine = create_engine('mysql+pymysql://user:password@localhost/stonetest?charset=utf8') df = pd.read_sql('emp_master', engine) df.to_sql('emp_backup', engine)
使用 mysql 的 describe
命令比較 emp_master
表和 emp_backup
表結構:
mysql> describe emp_master; +----------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------------+-------------+------+-----+---------+-------+ | EMP_ID | int(11) | NO | PRI | NULL | | | GENDER | varchar(10) | YES | | NULL | | | AGE | int(11) | YES | | NULL | | | EMAIL | varchar(50) | YES | | NULL | | | PHONE_NR | varchar(20) | YES | | NULL | | | EDUCATION | varchar(20) | YES | | NULL | | | MARITAL_STAT | varchar(20) | YES | | NULL | | | NR_OF_CHILDREN | int(11) | YES | | NULL | | +----------------+-------------+------+-----+---------+-------+ 8 rows in set (0.00 sec)
emp_backup 表結構:
mysql> describe emp_backup; +----------------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------------+------------+------+-----+---------+-------+ | index | bigint(20) | YES | MUL | NULL | | | EMP_ID | bigint(20) | YES | | NULL | | | GENDER | text | YES | | NULL | | | AGE | bigint(20) | YES | | NULL | | | EMAIL | text | YES | | NULL | | | PHONE_NR | text | YES | | NULL | | | EDUCATION | text | YES | | NULL | | | MARITAL_STAT | text | YES | | NULL | | | NR_OF_CHILDREN | bigint(20) | YES | | NULL | | +----------------+------------+------+-----+---------+-------+ 9 rows in set (0.00 sec)
我們發現,to_sql()
並沒有考慮將 emp_master
表字段的數據類型同步到目標表,而是簡單的區分數字型和字符型,這是第一個問題,第二個問題呢,目標表沒有 primary key。因為 pandas 定位是數據分析工具,數據源可以來自 CSV 這種文本型文件,本身是沒有嚴格數據類型的。而且,pandas 數據 to_excel()
或者to_sql()
只是方便數據存放到不同的目的地,本身也不是一個數據庫升遷工具。
但如果我們需要嚴謹地保留原表字段的數據類型,以及保留 primary key,該怎么做呢?
使用 SQL 語句來創建表結構
如果數據源本身是來自數據庫,通過腳本操作是比較方便的。如果數據源是來自 CSV 之類的文本文件,可以手寫 SQL 語句或者利用 pandas get_schema()
方法,如下例:
import sqlalchemy print(pd.io.sql.get_schema(df, 'emp_backup', keys='EMP_ID', dtype={'EMP_ID': sqlalchemy.types.BigInteger(), 'GENDER': sqlalchemy.types.String(length=20), 'AGE': sqlalchemy.types.BigInteger(), 'EMAIL': sqlalchemy.types.String(length=50), 'PHONE_NR': sqlalchemy.types.String(length=50), 'EDUCATION': sqlalchemy.types.String(length=50), 'MARITAL_STAT': sqlalchemy.types.String(length=50), 'NR_OF_CHILDREN': sqlalchemy.types.BigInteger() }, con=engine))
get_schema()
並不是一個公開的方法,沒有文檔可以查看。生成的 SQL 語句如下:
CREATE TABLE emp_backup ( `EMP_ID` BIGINT NOT NULL AUTO_INCREMENT, `GENDER` VARCHAR(20), `AGE` BIGINT, `EMAIL` VARCHAR(50), `PHONE_NR` VARCHAR(50), `EDUCATION` VARCHAR(50), `MARITAL_STAT` VARCHAR(50), `NR_OF_CHILDREN` BIGINT, CONSTRAINT emp_pk PRIMARY KEY (`EMP_ID`) )
to_sql()
方法使用 append 方式插入數據
to_sql()
方法的 if_exists
參數用於當目標表已經存在時的處理方式,默認是 fail
,即目標表存在就失敗,另外兩個選項是 replace
表示替代原表,即刪除再創建,append
選項僅添加數據。使用 append
可以達到要求。
import pandas as pd from sqlalchemy import create_engine import sqlalchemy engine = create_engine('mysql+pymysql://user:password@localhost/stonetest?charset=utf8') df = pd.read_sql('emp_master', engine) # make sure emp_master_backup table has been created # so the table schema is what we want df.to_sql('emp_backup', engine, index=False, if_exists='append')
也可以在 to_sql()
方法中,通過 dtype
參數指定字段的類型,然后在 mysql 中 通過 alter table
命令將字段 EMP_ID
變成 primary key。
df.to_sql('emp_backup', engine, if_exists='replace', index=False, dtype={'EMP_ID': sqlalchemy.types.BigInteger(), 'GENDER': sqlalchemy.types.String(length=20), 'AGE': sqlalchemy.types.BigInteger(), 'EMAIL': sqlalchemy.types.String(length=50), 'PHONE_NR': sqlalchemy.types.String(length=50), 'EDUCATION': sqlalchemy.types.String(length=50), 'MARITAL_STAT': sqlalchemy.types.String(length=50), 'NR_OF_CHILDREN': sqlalchemy.types.BigInteger() }) with engine.connect() as con: con.execute('ALTER TABLE emp_backup ADD PRIMARY KEY (`EMP_ID`);')
當然,如果數據源本身就是 mysql,當然不用大費周章來創建數據表的結構,直接使用 create table like xxx
就行。以下代碼展示了這種用法:
import pandas as pd from sqlalchemy import create_engine engine = create_engine('mysql+pymysql://user:password@localhost/stonetest?charset=utf8') df = pd.read_sql('emp_master', engine) # Copy table structure with engine.connect() as con: con.execute('DROP TABLE if exists emp_backup') con.execute('CREATE TABLE emp_backup LIKE emp_master;') df.to_sql('emp_backup', engine, index=False, if_exists='append')