Python 操作 SQLite:Peewee ORM 与传统 sqlite3.connect 的全方位对比

在 Python 生态中,SQLite 作为轻量级嵌入式数据库,凭借无需服务端、零配置的优势,广泛应用于桌面应用、小型 Web 项目和数据原型开发。而操作 SQLite 的方式主要分为两类:一类是基于 Python 内置sqlite3模块的原生 SQL 操作,核心是sqlite3.connect(db_path);另一类是通过 ORM(对象关系映射)库简化开发,其中 Peewee 以轻量、直观的特性成为热门选择。​

本文将从实际开发场景出发,对比两种方式的使用逻辑、代码效率和适用场景,帮助你根据项目需求选择更合适的数据库操作方案。​

一、基础认知:两种方式的核心定位​

在深入对比前,我们先明确两者的本质区别 ------ 这是 "原生 SQL 操作" 与 "面向对象封装" 的差异,而非 "优劣对立"。​

  1. 传统方式:sqlite3.connect(db_path)​

sqlite3是 Python 标准库内置模块,无需额外安装,通过sqlite3.connect(db_path)建立数据库连接后,需手动编写 SQL 语句、处理游标(Cursor)和结果集转换。其核心逻辑是 "直接与数据库交互",开发者需熟悉 SQL 语法,且需手动管理数据类型映射(如 Python 字符串与 SQLite 文本类型、Python 整数与 SQLite 整型的转换)。​

例如,连接数据库并查询用户表的基础代码:

python 复制代码
import sqlite3
from pathlib import Path

# 1. 构建数据库路径(绝对路径避免工作目录问题)
db_path = Path(__file__).parent.resolve() / "app.db"
# 2. 建立连接
conn = None
try:
    conn = sqlite3.connect(db_path)
    # 3. 创建游标(执行SQL的载体)
    cursor = conn.cursor()
    # 4. 编写并执行SQL
    cursor.execute("SELECT username, email FROM user WHERE age > ?", (25,))
    # 5. 手动处理结果集(元组转字典,需手动映射字段)
    users = []
    for row in cursor.fetchall():
        users.append({
            "username": row[0],
            "email": row[1]
        })
    print("查询结果:", users)
except sqlite3.Error as e:
    print(f"数据库错误:{e}")
finally:
    # 6. 手动关闭连接(避免资源泄漏)
    if conn:
        conn.close()

可以看到,整个流程需手动完成 "连接 - 游标 - 执行 SQL - 结果转换 - 关闭连接",每一步都依赖开发者对 SQLite 底层逻辑的理解。​

  1. Peewee ORM:面向对象的封装​

Peewee 是轻量级 ORM 库(需通过pip install peewee安装),核心是将数据库表映射为 Python 类、表字段映射为类属性、SQL 操作映射为类方法。开发者无需编写 SQL,只需通过面向对象的语法操作数据,剩下的 "SQL 生成、结果转换、连接管理" 均由 Peewee 自动完成。​

同样是 "查询年龄大于 25 的用户",Peewee 的实现如下:

python 复制代码
from peewee import SqliteDatabase, Model, CharField, IntegerField
from pathlib import Path

# 1. 构建数据库路径并建立连接(Peewee自动管理连接)
db_path = Path(__file__).parent.resolve() / "app.db"
db = SqliteDatabase(db_path)

# 2. 定义数据模型(映射数据库表,无需手动建表SQL)
class User(Model):
    username = CharField(unique=True)  # 对应SQLite的TEXT类型,唯一约束
    email = CharField()                # 对应TEXT类型
    age = IntegerField()               # 对应INTEGER类型

    class Meta:
        database = db  # 指定模型关联的数据库

# 3. 自动创建表(若不存在,无需手动执行CREATE TABLE)
db.create_tables([User])

# 4. 面向对象查询(无需SQL,结果自动转为User对象)
users = User.select(User.username, User.email).where(User.age > 25)
# 5. 直接操作对象属性(无需手动映射字段)
print("查询结果:")
for user in users:
    print(f"用户名:{user.username},邮箱:{user.email}")

对比可见,Peewee 通过 "模型定义" 替代了 SQL 建表语句,通过 "类方法查询" 替代了手写 SQL,通过 "对象属性" 替代了结果集手动映射,大幅简化了代码逻辑。​

二、核心差异:从开发效率到维护成本​

两种方式的差异并非 "代码长短",而是从开发流程到长期维护的全方位影响,我们从 6 个关键维度展开对比。​

  1. 数据定义:硬编码 SQL vs 类属性映射​
  • sqlite3方式:需手动编写CREATE TABLE语句,字段类型、约束(如UNIQUE、NOT NULL)需通过 SQL 语法指定,且若表结构变更(如新增字段),需手动编写ALTER TABLE语句,容易因 SQL 语法错误导致问题。

例如,创建包含约束的用户表:​

python 复制代码
cursor.execute("""​

CREATE TABLE IF NOT EXISTS user (​

id INTEGER PRIMARY KEY AUTOINCREMENT,​

username TEXT NOT NULL UNIQUE,​

email TEXT NOT NULL,​

age INTEGER CHECK (age > 0)​

)​

""")​

conn.commit() # 需手动提交事务​
  • Peewee 方式:通过类属性定义字段,约束通过 Peewee 提供的参数(如unique=True、null=False、constraints=[Check('age > 0')])直接声明,表结构变更时只需修改类属性,Peewee 自动处理底层 SQL 逻辑。

对应上述表结构的 Peewee 模型:​

python 复制代码
class User(Model):​

username = CharField(null=False, unique=True)​

email = CharField(null=False)​

age = IntegerField(constraints=[Check('age > 0')])​

​

class Meta:​

database = db​

​

优势:避免 SQL 语法依赖,表结构与代码逻辑强绑定,便于版本控制(如 Git 跟踪模型类变更)。​

  1. 数据操作:手写 SQL vs 链式 API​

数据库操作的核心是 CRUD(增删改查),两种方式在这一环节的效率差异最为明显。​

(1)新增数据​

  • sqlite3:需手动编写INSERT语句,通过?占位符传递参数(避免 SQL 注入),且需手动提交事务:
java 复制代码
try:​

cursor.execute(​

"INSERT INTO user (username, email, age) VALUES (?, ?, ?)",​

("bob", "bob@example.com", 28)​

)​

conn.commit() # 必须手动提交,否则数据不写入​

except sqlite3.IntegrityError:​

print("用户名已存在(违反UNIQUE约束)")​
  • Peewee:通过模型实例.save()方法新增数据,自动处理参数占位符和事务提交,约束冲突直接抛出 Python 异常(如IntegrityError):
python 复制代码
try:​

User(username="bob", email="bob@example.com", age=28).save()​

except IntegrityError:​

print("用户名已存在")​

(2)查询数据​

  • sqlite3:需手动编写SELECT语句,结果集为元组列表,需手动映射为字典或自定义对象,复杂查询(如排序、分页、关联查询)需熟练掌握 SQL 语法:
python 复制代码
# 复杂查询:查询年龄>25、按年龄降序、取前10条​

cursor.execute("""​

SELECT username, age FROM user ​

WHERE age > ? ​

ORDER BY age DESC ​

LIMIT ? OFFSET ?​

""", (25, 10, 0))​

# 手动映射结果​

results = [{"username": row[0], "age": row[1]} for row in cursor.fetchall()]​

  • Peewee:通过链式 API 组合查询条件,支持where()(筛选)、order_by()(排序)、limit()(分页),结果自动转为模型对象,关联查询(如多表 JOIN)通过join()方法轻松实现:

python 复制代码
# 对应上述复杂查询​

results = (​

User.select(User.username, User.age)​

.where(User.age > 25)​

.order_by(User.age.desc())​

.limit(10)​

.offset(0)​

)​

# 直接访问对象属性​

for res in results:​

print(f"用户名:{res.username},年龄:{res.age}")​

(3)修改与删除​

  • sqlite3:需手动编写UPDATE/DELETE语句,同样需处理参数和事务:
python 复制代码
# 修改用户年龄​

cursor.execute("UPDATE user SET age = ? WHERE username = ?", (29, "bob"))​

conn.commit()​

# 删除用户​

cursor.execute("DELETE FROM user WHERE username = ?", ("bob",))​

conn.commit()​
  • Peewee:通过模型.update().where()修改,模型.delete().where()删除,无需手动提交:
python 复制代码
# 修改用户年龄​

User.update(age=29).where(User.username == "bob").execute()​

# 删除用户​

User.delete().where(User.username == "bob").execute()​

  1. 连接与事务管理:手动控制 vs 自动封装​
  • sqlite3:需手动管理连接生命周期 ------ 创建连接后必须在finally中关闭,否则会导致资源泄漏;事务需手动调用conn.commit()提交,若发生异常需调用conn.rollback()回滚,代码冗余且易出错:
python 复制代码
conn = None​

try:​

conn = sqlite3.connect(db_path)​

cursor = conn.cursor()​

cursor.execute("INSERT INTO user (...) VALUES (...)")​

conn.commit() # 手动提交​

except sqlite3.Error as e:​

if conn:​

conn.rollback() # 手动回滚​

print(f"错误:{e}")​

finally:​

if conn:​

conn.close() # 手动关闭​

  • Peewee:自动管理连接 ------SqliteDatabase会在需要时创建连接,操作完成后自动回收;事务可通过with db.atomic()上下文管理器简化,发生异常时自动回滚,无需手动处理:
python 复制代码
try:​

with db.atomic(): # 自动事务管理​

User(username="alice", email="alice@example.com", age=30).save()​

# 若此处抛出异常,事务自动回滚​

except IntegrityError as e:​

print(f"错误:{e}")​

此外,Peewee 还支持连接池(需配合playhouse.pool模块),适合高并发场景,而sqlite3需手动实现连接池逻辑。​

  1. 跨数据库兼容性:硬编码适配 vs 无缝切换​
  • sqlite3:代码与 SQLite 强绑定,若需迁移到 MySQL 或 PostgreSQL,需修改所有 SQL 语句(如 SQLite 的AUTOINCREMENT在 MySQL 中为AUTO_INCREMENT,SQLite 的TEXT在 PostgreSQL 中为VARCHAR),迁移成本极高。
  • Peewee:通过不同的数据库类(SqliteDatabase、MySQLDatabase、PostgresqlDatabase)实现跨数据库兼容,只需修改连接配置,业务逻辑代码无需变更。

例如,从 SQLite 迁移到 MySQL:​

python 复制代码
# SQLite配置​

db = SqliteDatabase("app.db")​

​

# 改为MySQL配置(只需修改这部分)​

db = MySQLDatabase(​

database="app",​

user="root",​

password="password",​

host="127.0.0.1",​

port=3306​

)​

​

# 业务逻辑(如User模型、查询代码)完全不变​

User.select().where(User.age > 25)​

这一特性对未来可能扩展数据库的项目至关重要。​

  1. 调试与错误处理:SQL 黑盒 vs 透明化​
  • sqlite3:错误通常是 SQL 语法错误(如字段名拼写错误、缺少逗号),但错误信息仅提示 "SQL syntax error",需手动核对 SQL 语句,调试效率低。

例如,字段名拼写错误导致的错误:​

java 复制代码
# 错误:将username拼为usernmae​

cursor.execute("SELECT usernmae FROM user")​

# 错误信息:sqlite3.OperationalError: no such column: usernmae​

虽能定位问题,但需逐个检查 SQL 语句。​

  • Peewee:错误分为两类 ------Python 语法错误(如模型属性拼写错误)和数据库约束错误(如违反 UNIQUE),错误信息更直观,且可通过print(query)查看 Peewee 生成的 SQL 语句,便于调试:

python 复制代码
# 错误:将User.username拼为User.usernmae​

query = User.select(User.usernmae)​

# 错误信息:AttributeError: type object 'User' has no attribute 'usernmae'​

​

# 查看生成的SQL​

query = User.select().where(User.age > 25)​

print(query) # 输出:SELECT "t1"."id", "t1"."username", "t1"."email", "t1"."age" FROM "user" AS "t1" WHERE ("t1"."age" > 25)​

优势:调试时可快速定位 "代码错误" 还是 "SQL 逻辑错误",降低排查成本。​

  1. 学习成本:SQL 依赖 vs Python 语法​
  • sqlite3:需掌握 SQL 基础语法(CREATE TABLE、INSERT、SELECT、JOIN等),且需理解 SQLite 的特殊规则(如字段类型灵活性、事务隔离级别),对不熟悉 SQL 的开发者不友好。
  • Peewee:只需掌握 Python 面向对象语法,Peewee 的 API 设计贴近自然语言(如where(User.age > 25)、order_by(User.age.desc())),学习曲线平缓,即使不熟悉 SQL 也能快速上手。

三、适用场景:没有最优,只有最合适​

两种方式各有优势,选择时需结合项目规模、团队技术栈和长期维护需求,而非盲目追求 "更先进" 的 ORM。​

  1. 选择sqlite3的场景​
  • 小型脚本或工具:如单文件数据处理脚本、临时数据存储,无需复杂的 CRUD 操作,sqlite3无需额外安装,开箱即用。
  • SQL 熟练且追求极致性能:sqlite3直接操作 SQL,减少 ORM 的封装开销,适合对性能要求极高的场景(如高频读写的嵌入式设备)。
  • 简单查询场景:如仅需执行 1-2 条 SQL 语句,使用sqlite3比引入 Peewee 更轻量。

示例场景:一个定期读取 CSV 文件并写入 SQLite 的脚本,仅需INSERT和简单SELECT,用sqlite3更高效。​

  1. 选择 Peewee 的场景​
  • 中大型项目或 Web 应用:如 Flask/Django 小型 Web 项目,需频繁进行复杂 CRUD 操作(如用户管理、订单查询),Peewee 可降低代码冗余,提高维护性。
  • 团队中有非 SQL 熟练开发者:通过 Python 语法统一开发语言,避免因 SQL 能力差异导致的代码质量参差不齐。
  • 未来可能迁移数据库:若项目后期可能从 SQLite 迁移到 MySQL/PostgreSQL,Peewee 的跨数据库特性可大幅降低迁移成本。
  • 需要模型化数据管理:如数据结构复杂(多表关联、约束较多),Peewee 的模型定义可使数据结构更清晰,便于团队协作。

示例场景:一个小型电商 Web 应用,涉及用户表、商品表、订单表的关联查询,用 Peewee 可简化多表 JOIN 逻辑,且便于后续扩展。​

四、总结:工具选择的核心是 "匹配需求"​

sqlite3.connect(db_path)代表了 "原生、灵活、轻量",适合简单场景和 SQL 熟练者;Peewee 代表了 "封装、高效、可维护",适合中大型项目和追求开发效率的团队。两者没有绝对的 "优劣",而是对应不同的需求场景。​

在实际开发中,我们可以这样决策:​

  • 若项目是 "一次性脚本" 或 "简单数据存储",优先用sqlite3,避免引入额外依赖;
  • 若项目需要 "长期维护""复杂 CRUD" 或 "跨数据库兼容",优先用 Peewee,通过 ORM 的封装降低长期成本。

最后需要强调的是,ORM 并非 "银弹"------ 对于超复杂的 SQL 查询(如多表嵌套子查询、自定义函数调用等),Peewee 的链式 API 可能无法完全覆盖,此时仍需结合raw_sql()方法编写原生 SQL,或直接使用sqlite3处理。但这并不影响 ORM 的价值 ------ 它能覆盖 90% 以上的常规开发场景,将开发者从重复的 SQL 编写中解放出来,专注于业务逻辑实现。​

五、实践建议:从入门到落地​

了解两种方式的差异后,我们可以通过具体的实践步骤,将知识转化为项目成果。​

  1. 快速上手 Peewee 的步骤​

若你决定尝试 Peewee,可按照以下流程快速搭建环境并实现基础功能:​

  • 第一步:安装依赖

通过 pip 安装 Peewee(支持 Python 3.6+):​

复制代码
pip install peewee​

  • 第二步:定义数据模型

参考前文的User模型,根据项目需求定义表结构,注意合理使用字段类型(如DateTimeField存储时间、DecimalField存储金额)和约束(如ForeignKeyField实现表关联)。​

示例:实现 "用户 - 订单" 的一对多关联:​

python 复制代码
class Order(Model):
    user = ForeignKeyField(User, backref='orders')  # 关联User表,支持反向查询
    order_no = CharField(unique=True)  # 订单号
    total_amount = DecimalField(max_digits=10, decimal_places=2)  # 订单金额
    create_time = DateTimeField(default=datetime.datetime.now)  # 创建时间

    class Meta:
        database = db
  • 第三步:测试 CRUD 操作

编写简单的测试代码,验证数据新增、查询、修改、删除功能是否正常,同时熟悉 Peewee 的常用 API(如get()获取单条数据、count()统计数量、prefetch()优化关联查询):​

python 复制代码
# 新增订单(关联已有用户)
user = User.get(User.username == "bob")
Order(user=user, order_no="20240501001", total_amount=99.9).save()

# 反向查询:获取用户的所有订单
orders = user.orders.select()
for order in orders:
    print(f"订单号:{order.order_no},金额:{order.total_amount}")

# 统计用户订单总数
order_count = user.orders.count()
print(f"用户{user.username}的订单总数:{order_count}")

  • 第四步:集成到项目

在 Web 项目(如 Flask)中,可将数据库连接和模型定义放在单独的models.py文件中,通过db.connect()初始化连接,在请求结束时自动关闭连接(Flask 中可结合@app.teardown_appcontext装饰器):​

python 复制代码
# Flask项目集成示例(models.py)
from flask import Flask
from peewee import *
import datetime

app = Flask(__name__)
db = SqliteDatabase("app.db")

# 模型定义...(User、Order)

@app.teardown_appcontext
def close_db(exception):
    # 请求结束后关闭数据库连接
    if hasattr(db, 'connection'):
        db.close()

  1. 优化sqlite3使用体验的技巧​

若你选择使用sqlite3,可通过以下技巧减少代码冗余、降低出错概率:​

  • 封装工具函数

将 "连接创建 - 执行 SQL - 结果转换 - 连接关闭" 的流程封装为通用函数,避免重复代码:​

python 复制代码
def sqlite_execute(db_path, sql, params=()):
    """
    执行SQL语句并返回结果
    :param db_path: 数据库路径
    :param sql: SQL语句
    :param params: SQL参数(避免SQL注入)
    :return: 查询结果(SELECT返回列表,其他返回影响行数)
    """
    conn = None
    try:
        conn = sqlite3.connect(db_path)
        conn.row_factory = sqlite3.Row  # 支持按字段名获取结果
        cursor = conn.cursor()
        cursor.execute(sql, params)
        if sql.strip().upper().startswith("SELECT"):
            # 转换为字典列表
            return [dict(row) for row in cursor.fetchall()]
        else:
            conn.commit()
            return cursor.rowcount  # 影响行数
    except sqlite3.Error as e:
        print(f"SQL执行错误:{e}")
        if conn:
            conn.rollback()
        return None
    finally:
        if conn:
            conn.close()

# 使用示例:查询用户
users = sqlite_execute(
    db_path,
    "SELECT username, email FROM user WHERE age > ?",
    (25,)
)
print(users)  # 直接返回字典列表:[{"username": "bob", "email": "bob@example.com"}, ...]
  • 启用行工厂(row_factory)

通过conn.row_factory = sqlite3.Row,使查询结果支持按字段名获取(如row["username"]),避免依赖元组索引,提高代码可读性。​

  • 使用事务批量操作

对于大量数据插入 / 更新,通过事务批量处理(而非单次提交)可大幅提升性能:​

python 复制代码
def sqlite_execute(db_path, sql, params=()):
    """
    执行SQL语句并返回结果
    :param db_path: 数据库路径
    :param sql: SQL语句
    :param params: SQL参数(避免SQL注入)
    :return: 查询结果(SELECT返回列表,其他返回影响行数)
    """
    conn = None
    try:
        conn = sqlite3.connect(db_path)
        conn.row_factory = sqlite3.Row  # 支持按字段名获取结果
        cursor = conn.cursor()
        cursor.execute(sql, params)
        if sql.strip().upper().startswith("SELECT"):
            # 转换为字典列表
            return [dict(row) for row in cursor.fetchall()]
        else:
            conn.commit()
            return cursor.rowcount  # 影响行数
    except sqlite3.Error as e:
        print(f"SQL执行错误:{e}")
        if conn:
            conn.rollback()
        return None
    finally:
        if conn:
            conn.close()

# 使用示例:查询用户
users = sqlite_execute(
    db_path,
    "SELECT username, email FROM user WHERE age > ?",
    (25,)
)
print(users)  # 直接返回字典列表:[{"username": "bob", "email": "bob@example.com"}, ...]

六、最终结语:技术选择的底层逻辑​

在 Python 操作 SQLite 的场景中,sqlite3与 Peewee 的选择,本质是 "原生控制" 与 "开发效率" 的权衡。没有任何一种工具能适用于所有场景,优秀的开发者会根据项目的 "生命周期" 和 "团队能力" 做出决策:​

  • 对于 "短期、简单、一次性" 的需求,sqlite3的轻量和零依赖是优势;
  • 对于 "长期、复杂、需协作" 的项目,Peewee 的封装和可维护性更值得投入。

但无论选择哪种方式,核心目标都是 "用最合适的工具解决问题"------ 避免为了 "使用 ORM 而使用 ORM",也避免因 "排斥新工具" 而重复编写低效代码。随着项目的演进,你甚至可以在同一项目中结合两者的优势:用 Peewee 处理常规 CRUD,用sqlite3处理超复杂 SQL 查询,实现 "效率与灵活性" 的平衡。​

希望本文的对比分析,能帮助你在实际开发中少走弯路,快速找到适合自己的数据库操作方案。

最后以一个例子结束:

python 复制代码
from peewee import SqliteDatabase, Model, CharField, IntegerField, DoesNotExist
from pathlib import Path


class UserManager:
    def __init__(self, db_name="app2.db"):
        """初始化数据库连接和用户模型"""
        # 构建数据库路径
        self.db_path = Path(__file__).parent.resolve() / db_name
        # 建立数据库连接
        self.db = SqliteDatabase(self.db_path)
        print(f'数据库路径: {self.db_path}')

        # 定义用户模型(内部类)
        class User(Model):
            username = CharField(unique=True)
            email = CharField()
            age = IntegerField()

            class Meta:
                database = self.db  # 关联到当前数据库

        self.User = User  # 将内部类赋值为实例属性
        self._create_tables()  # 创建数据表

    def _create_tables(self):
        """创建数据表(如果不存在)"""
        self.db.create_tables([self.User])

    def add_user(self, username, email, age):
        """添加用户,避免重复添加"""
        try:
            # 检查用户是否已存在
            self.User.get(self.User.username == username)
            print(f"用户 {username} 已存在,不重复添加")
            return False
        except DoesNotExist:
            # 创建新用户
            user = self.User.create(
                username=username,
                email=email,
                age=age
            )
            print(f"已添加用户:{username},ID: {user.id}")
            return True

    def get_users_by_age(self, min_age):
        """查询年龄大于指定值的用户"""
        return self.User.select(
            self.User.username,
            self.User.email
        ).where(self.User.age > min_age)

    def print_users(self, users):
        """打印用户列表"""
        for user in users:
            print(f"用户名:{user.username},邮箱:{user.email}")


# 使用示例
if __name__ == "__main__":
    # 初始化用户管理器
    user_manager = UserManager()

    # 添加示例用户
    print("\n添加示例用户:")
    user_manager.add_user("zhangsan", "zhangsan@example.com", 28)
    user_manager.add_user("lisi", "lisi@example.com", 23)
    user_manager.add_user("wangwu", "wangwu@example.com", 30)
    user_manager.add_user("zhaoliu", "zhaoliu@example.com", 26)

    # 查询并打印年龄大于25的用户
    print("\n查询结果(年龄大于25的用户):")
    users = user_manager.get_users_by_age(25)
    user_manager.print_users(users)
相关推荐
lph0095 小时前
Android compose Room Sqlite 应用 (注入式)
android·数据库·sqlite
大可门耳5 小时前
Qt读写SQLite示例
jvm·qt·sqlite
安当加密5 小时前
DBG数据库透明加密网关:SQLServer应用免改造的安全防护方案,不限制开发语言的加密网关
网络·数据库
承悦赋6 小时前
初识Redis:解锁高性能缓存的魔法钥匙
数据库·spring boot·redis·分布式·缓存·中间件
cdcdhj6 小时前
在window中创建第2个mongodb数据的方法
数据库·mongodb
疯狂大嘴6 小时前
MongoDB备份数据库
数据库·mongodb
凄凉山谷的风 OL6 小时前
解决MySQL的sql_mode=only_full_group_by错误提示
数据库
TDengine (老段)6 小时前
TDengine 聚合函数 SPREAD 用户手册
大数据·数据库·sql·物联网·时序数据库·tdengine·涛思数据
TDengine (老段)6 小时前
TDengine 时区配置问题全解
大数据·数据库·时序数据库·tdengine·涛思数据