SQLAlchemy

基于SQLAlchemy连接mysql库

pymsql插件

# -*- coding:UTF -8-*-
import os
import pandas as pd
# 检查是否有pymysql这个包存在
import  pymysql
# SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件。
from sqlalchemy import create_engine,Sequence,text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
from sqlalchemy.orm import sessionmaker, relationship

# 设置工作路径
os.chdir('E: /data')
# 读取文件
sale = pd.read_csv('sale.csv',encoding = 'GB18030')
'''
sale.csv:
year   market sale   profit
2010   东      33912  2641
2010   南      32246  2699
2010   西      34792  2574
2010   北      31884  2673
2011   东      31651  2437
2011   南      30572  2853
2011   西      34175  2877
2011   北      30555  2749
2012   东      31619  2106
2012   南      32443  3124
2012   西      32103  2593
2012   北      31744  2962
'''
# 初始化数据连接数据库的信息
host = yourhost
user = yourid
password = yourpassword
db = yourdatbasename
port = 3306
charset = 'utf8'
# 20210622 针对1.4版本后,如果密码中含有特殊符号,如@,:等,需要先将密码编译才可以解析正确
import urllib.parse
password = urllib.parse.quote_plus(password)
# 连接数据库

# pool_recycle控制任何连接的最大连接时长(秒);pool_size连接池的最大连接数,默认5,设置0时为无限制连接数连接;

# 20210728 pool_pre_ping预连接,相当于发送“SELECT 1”到sql中在每次连接中检查连接池。尝试3次在放弃之前。

# 20210728 pool_recycle=-1,此设置会导致池在给定的秒数过后回收连接。它默认为 -1,或没有超时。例如,设置为 3600 表示连接将在 1 小时后回收。请注意,如果在八小时内未在连接上检测到任何活动,MySQL 特别会自动断开连接。
# 请注意,失效仅在检查期间发生(only occurs during checkout)。不会发生在任何处于检查状态的连接上(not on any connections that are held in a checked out state)。 pool_recycle是Pool本身的函数,与anEngine是否在使用中无关。

# 使用 FIFO 与 LIFO, pool_use_lifo=True
# 所述QueuePool类设有称为标志 QueuePool.use_lifo,其也可以从访问 create_engine()经由所述标志create_engine.pool_use_lifo。将此标志设置为True会导致池的“队列”行为改为“堆栈”行为,例如,返回到池的最后一个连接是下一个请求中使用的第一个连接。与池长期存在的先进先出行为相反,先进先出会产生串联使用池中每个连接的循环效应,lifo 模式允许池中多余的连接保持空闲,从而允许服务器-关闭这些连接的侧超时方案。FIFO 和 LIFO 之间的区别基本上在于池是否需要即使在空闲期间也能保持完整的连接集:

# 将连接池与 Multiprocessing 或 os.fork() 一起使用
# 使用NullPool. 这是最简单的一次性系统,可防止Engine多次使用任何连接:
# from sqlalchemy.pool import NullPool
# engine = create_engine("mysql://user:pass@host/dbname", poolclass=NullPool)

# 一旦有人在新流程中,就呼叫Engine.dispose()任何给定的Engine人。在 Python 多处理中,诸如multiprocessing.Pool包含“初始化程序”钩子之类的构造是可以执行此操作的地方;否则在对象开始子叉的位置os.fork()或位置的顶部 Process,单个调用Engine.dispose()将确保刷新任何剩余的连接。这是推荐的方法:
# engine = create_engine("mysql://user:pass@host/dbname")
#def run_in_process():
    # process starts.  ensure engine.dispose() is called just once
    # at the beginning
    #engine.dispose()

    #with engine.connect() as conn:
        #conn.execute(text("..."))

#p = Process(target=run_in_process)
#p.start()

# 20210808 connect_args – 一个选项字典,将connect()作为附加关键字参数直接传递给 DBAPI 的方法。
# https://docs.sqlalchemy.org/en/14/core/engines.html#custom-dbapi-args

# max_overflow溢出连接池的最大额外连接数,超过pool_size所设置的连接数的溢出连接数。实际上总的并行连接总数为pool_size
# +max_overflow,设置-1为不设置溢出最大限制,默认为10;pool_timeout等待返回信息时间,默认为30(秒)
# echo类似于python的logging(日志)功能,默认为False
# 'mysql+pymysql',mysql为数据库类型,pymysql为连接方式
engine = create_engine('mysql+pymysql://{user}:{password}@{host}:{port}/{db}?charset={charset}'
                .format(user = user,
                        host = host,
                        password = password,
                        db = db,
                        port = port,
                        charset = charset),
                pool_size = 30,max_overflow = 0
                pool_pre_ping=True , pool_recycle=-1, isolation_level="AUTOCOMMIT",
                connect_args={
   
   'connect_timeout': 30)
# 创建直接调用数据库属性
conn = engine.connect()
# 查询数据库版本号
# execute里面为原始sql语句;
# fetchone为返回第一行结果;fetchall()为返回所有结果;fetchmany(n)为返回第n行结果
databaseVersion = conn.execute("SELECT VERSION()").fetchone()


# ##################使用原始sql语句运行#####################
# 创建一个数据表
conn.execute('CREATE TABLE sale ('
             'year CHAR(20) NOT NULL,'
             'market CHAR(20),'
             'sale INT ,'
             'profit_from_sale INT )')
# 删除表
conn.execute('DROP TABLE sale')
# 将df存进数据表中且如果有同名数据表就把之前的删除后重新添加
sale.to_sql('sale',conn,if_exists='replace',index = False)
# 查询数据表语句
conn.execute('select * from sale').fetchall()
conn.execute('select DISTINCT year,market from sale').fetchall()
conn.execute("select * from sale where year=2012 and market=%s",'东').fetchall()
conn.execute('select year,market,sale,profit from sale order by sale').fetchall()
# 添加数据记录
conn.execute("insert into sale (year,market,sale) value (2010,'东',33912)")
# 删除数据记录
conn.execute("delete from sale")


# #########################ORM模式############################
##创建表
# 创建对象的基类
Base = declarative_base()


# 在本地内存创建单表(建立本地与sql的连接)
class Sale(Base):
    __tablename__ = 'sale'
    year = Column(String(20), primary_key=True, nullable=False)
    market = Column(String(10))
    sale = Column(Integer)
    profit_from_sale = Column(Integer)
    # 测试用
    def __repr__(self):
        return '%s(%s,%s,%s,%s)'%\
               (self.__tablename__,self.year,self.market,self.sale,self.profit_from_sale)
    # 创建属性,UniqueConstraint为unique key,Index为index
    # __table_args__ = (
    #     UniqueConstraint('id', 'name', name='uix_id_name'),
    #     Index('ix_id_name', 'name', 'extra'),)
'''
sale表在sql的DDL
CREATE TABLE `sale` (
  `year` varchar(20) NOT NULL,
  `market` varchar(10) DEFAULT NULL,
  `sale` int(11) DEFAULT NULL,
  `profit_from_sale` int(11) DEFAULT NULL,
  PRIMARY KEY (`year`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
'''
# 查询本地表的结构
Sale.__table__


# 创建一对多表(建立本地与sql的连接)
class Favor(Base):
    __tablename__ = 'favor'
    nid = Column(Integer, primary_key=True, autoincrement=True)
    caption = Column(String(50), default='red', unique=True)
    sale_year = Column(String(10), ForeignKey("sale.year"))
'''
favor表在sql的DDL
CREATE TABLE `favor` (
  `nid` int(11) NOT NULL AUTO_INCREMENT,
  `caption` varchar(50) DEFAULT NULL,
  `sale_year` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`nid`),
  UNIQUE KEY `caption` (`caption`),
  KEY `sale_year` (`sale_year`),
  CONSTRAINT `favor_ibfk_1` FOREIGN KEY (`sale_year`) REFERENCES `sale` (`year`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
'''
# 初始化数据库函数(全部)
Base.metadata.create_all(conn)
# 删除数据库(全部)
Base.metadata.drop_all(conn)

##操作表
# 创建session********************
Session = sessionmaker(bind=conn)
session = Session()


# 新增单条数据
new = Sale(year=2010, market='东', sale=33912)
session.add(new)
# 新增多条数据
session.add_all([
    Sale(year=2011, market='东', sale=31651, profit_from_sale=2437),
    Sale(year=2012, market='东', sale=31619),
                ])
try:
    # 提交即保存到数据库
    session.commit()
except:
    # 回滚,返回session执行之前的状态
    session.rollback()


# 查询表(Querying)
    # 返回的是list格式
session.query(Sale.year, Sale.sale).all()
    # order_by以什么为顺序(默认升序)
for i in session.query(Sale).order_by(Sale.sale):
    print(i.year,i.sale)
    #first()为第一行,all()为全部
for i in session.query(Sale,Sale.year).all():
    print(i.Sale,i.year)# 测试用
    # label为设置替换名
for i in session.query(Sale.year.label('test')).all():
    print(i.test)
from sqlalchemy.orm import aliased# 替换功能
Sale_test = aliased(Sale, name='sale_test' )# name为table的名字
for i in session.query(Sale_test).all()[1:3]:
    print(i)
    # filter_by为匹配;filter也一样,用法为filter(Sale.year = 2012),
    # filter与filter之间用.连接,代表and的意思
for i in session.query(Sale).filter_by(year = 2012).all():
    print(i)

# 使用text来混合原始sql语句使用
session.query(Sale).filter(text('year>2010')).order_by(text('sale')).all()
session.query(Sale.sale).filter(text('year>2010')).group_by(Sale.sale).all()
session.query(Sale).filter(text('year=:year and market=:market'))\
    .params(year = 2011,market ='东').order_by(Sale.sale).one()
session.query(Sale).from_statement(text('select * from sale where year={}'.format(2012))).all()

# Counting统计
session.query(Sale.profit_from_sale).count() # 统计行数,返回int
from sqlalchemy import func# func 后可以跟任意函数名,只要该数据库支持
session.query(func.count(Sale.profit_from_sale)).scalar()# 统计非nan值个数,返回int
session.query(func.count()).select_from(Sale).scalar()
session.query(Sale.profit_from_sale).distinct().count()# 统计不同的个数

# 删除数据
session.query(Sale).filter(Sale.year == 2012).delete()
session.commit()
# 修改数据(如果之前删除了数据,由于数据表发生变化,需要重新连接sql)
session.query(Sale).filter(Sale.year == 2010).update({
   
   "profit_from_sale":666})
session.query(Sale).filter(Sale.year == 2010).update({
   
   Sale.profit_from_sale:Sale.profit_from_sale+333})
session.commit()


# 关闭session****************
session.close()

# ###################pandas模式###################
# 将DataFrame/Series写入sql里面
# sale1为写入表的名称;conn为SQLAlchemy/DBAPI2连接sql的引擎;schema为设定模式默认为默认模式;
# if_exists:{'fail','replace','append'}为如果存在相同的表,fail不操作不添加,
# replace删除原表重新添加,append插入数据(列名要一致,尾端插入)。
# 三者在没有原表时都会创建新表,default ‘fail’;
# index:default True,将DataFrame的index作为一列写入sql里;
# index_label:{string or sequence}, default None,设置sql的索引所在的列(名);
# chunksize:{int}, default None,设置每次写入sql里的DF行数,若为空,则一次心写入全部;
# dtype:{dict of column name to SQL type}, default None,写入sql里面的列解释,必须是在
# SQLAlchemy模式下
sale.to_sql('sale1',conn,if_exists='append',index = 0,index_label='year',chunksize = None)

# 将sql里面的数据读取到DF
# schema为sql的模式;index_col为指定sql里的一列为DF的index;
# coerce_float将非数字非字符串自动转化为浮点数,默认为True;
# parse_dates解析列属性,输入List of column names将对应的列内容转化为时间日期格式。
# 输入Dict{column_name: format string}将列内容转化为对应的字符串格式.
# 输入Dict{column_name: arg dict}将列内容转化为pandas.to_datetime()格式,
# arg dict为pandas.to_datetime()参数;
# columns为需要查询sql的table的列内容所对应的列名,默认None为全查
readSqlTable1 = pd.read_sql_table('sale1',conn,parse_dates=('year'))
type(readSqlTable1['year'][0])#pandas._libs.tslib.Timestamp
readSqlTable2 = pd.read_sql_table('sale1',conn,parse_dates={
   
   'year':'%Y-%M-%D'},columns=['year','sale'])
type(readSqlTable2['year'][0])#datetime.date
readSqlTable3 = pd.read_sql_table('sale1',conn,parse_dates={
   
   'year':{
   
   'format':'%Y-%M-%D'}})
type(readSqlTable3['year'][0])#datetime.date
# 参数同上
readSqlQuery = pd.read_sql_query('select * from sale1',conn)
readSql1 = pd.read_sql('select * from sale1',conn)
readSql2 = pd.read_sql('sale1',conn)

# 关闭sql连接
conn.close()

# 20210511添加游标全表扫描千万级别数据方法
# https://docs.sqlalchemy.org/en/14/core/connections.html
# 流处理
with sql_engine.connect() as conn:
    source = conn.execution_options(stream_results=True, max_row_buffer=100).execute(sql_str)
    for row in source:
        tmp = pd.DataFrame([row],colum
### SQLAlchemy 是什么? SQLAlchemy 是一个功能强大的 Python SQL 工具包和对象关系映射(Object-Relational Mapping, ORM)框架,它提供了全功能的数据库抽象层和透明的数据库转换,使得开发者能够以面向对象的方式操作数据库,同时支持多种数据库系统,极大地简化了数据库编程的复杂性[^3]。 ### SQLAlchemy 的核心特性 1. **ORM 支持**:SQLAlchemy 提供了灵活的 ORM 系统,允许开发者将数据库表映射到 Python 类,从而以面向对象的方式操作数据库数据。 2. **SQL 表达式语言**:除了 ORM,SQLAlchmey 还提供了强大的 SQL 表达式语言,支持构建复杂的 SQL 查询。 3. **多数据库支持**:SQLAlchemy 支持包括 MySQL、PostgreSQL、SQLite、Oracle 等在内的多种数据库系统。 4. **事务管理**:SQLAlchemy 提供了完整的事务管理功能,确保数据的一致性和完整性。 5. **连接池**:内置的连接池机制可以提高数据库连接的效率和性能。 ### 安装 SQLAlchemy 要安装 SQLAlchemy,可以使用 pip: ```bash pip install sqlalchemy ``` 如果需要连接特定的数据库(如 MySQL),还需要安装相应的驱动程序,例如 `pymysql`: ```bash pip install pymysql ``` ### 连接数据库 以下是一个使用 SQLAlchemy 连接 MySQL 数据库的示例: ```python from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker # 数据库连接字符串 DATABASE_URI = 'mysql+pymysql://root:[email protected]:3306/test' # 创建引擎 engine = create_engine(DATABASE_URI, echo=True) # 创建会话 Session = sessionmaker(bind=engine) session = Session() # 声明基类 Base = declarative_base() ``` ### 定义模型 在 SQLAlchemy 中,可以通过定义类来表示数据库表。以下是一个简单的模型定义示例: ```python from sqlalchemy import Column, Integer, String class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50)) email = Column(String(50)) # 创建表 Base.metadata.create_all(engine) ``` ### 数据库操作 #### 插入数据 ```python new_user = User(name='Alice', email='[email protected]') session.add(new_user) session.commit() ``` #### 查询数据 ```python # 查询所有用户 users = session.query(User).all() for user in users: print(user.name, user.email) # 条件查询 user = session.query(User).filter_by(name='Alice').first() print(user.email) ``` #### 更新数据 ```python user = session.query(User).filter_by(name='Alice').first() user.email = '[email protected]' session.commit() ``` #### 删除数据 ```python user = session.query(User).filter_by(name='Alice').first() session.delete(user) session.commit() ``` ### 高级查询 SQLAlchemy 提供了丰富的查询 API,支持复杂的查询操作。例如,可以使用 `filter`、`order_by`、`group_by` 等方法来构建复杂的查询: ```python # 查询并排序 users = session.query(User).order_by(User.name).all() # 聚合查询 from sqlalchemy import func user_count = session.query(func.count(User.id)).scalar() print(f"Total users: {user_count}") ``` ### 在 Flask 中使用 SQLAlchemy 在 Flask 应用中,可以通过 `Flask-SQLAlchemy` 扩展来简化 SQLAlchemy 的使用: ```python from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:[email protected]:3306/test' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50)) email = db.Column(db.String(50)) @app.route('/') def index(): users = User.query.all() return str(users) if __name__ == '__main__': app.run(debug=True) ``` ### 嵌入使用 SQL 语句 虽然 SQLAlchemy 提供了强大的 ORM 功能,但在某些情况下,直接使用原始 SQL 语句可能更方便。可以通过 `engine.execute()` 方法执行原始 SQL: ```python result = engine.execute("SELECT * FROM users") for row in result: print(row) ``` ### 总结 SQLAlchemy 是一个功能强大且灵活的 Python ORM 工具,适用于各种规模的应用程序开发。通过其丰富的功能,开发者可以更轻松地与数据库进行交互,同时保持代码的可维护性和可扩展性。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值