本文目录导读:

我来详细介绍Python操作数据库维度表的方法,包括常用的库和最佳实践。
常用数据库操作库
MySQL
import pymysql
from sqlalchemy import create_engine
# 方法1:使用pymysql
conn = pymysql.connect(
host='localhost',
user='root',
password='password',
database='dimension_db'
)
cursor = conn.cursor()
# 方法2:使用SQLAlchemy (推荐)
engine = create_engine('mysql+pymysql://root:password@localhost:3306/dimension_db')
PostgreSQL
import psycopg2
from sqlalchemy import create_engine
# 使用psycopg2
conn = psycopg2.connect(
host='localhost',
user='postgres',
password='password',
database='dimension_db'
)
SQL Server
import pyodbc
from sqlalchemy import create_engine
# 使用pyodbc
conn = pyodbc.connect(
'DRIVER={ODBC Driver 17 for SQL Server};'
'SERVER=localhost;DATABASE=dimension_db;'
'UID=sa;PWD=password'
)
完整的维度表操作示例
import pandas as pd
from sqlalchemy import create_engine, Table, MetaData, Column, Integer, String, DateTime
from datetime import datetime
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DimensionTableManager:
def __init__(self, connection_string):
"""
初始化维度表管理器
"""
self.engine = create_engine(connection_string)
self.metadata = MetaData()
def create_date_dimension(self):
"""
创建日期维度表
"""
date_dim = Table('dim_date', self.metadata,
Column('date_key', Integer, primary_key=True),
Column('full_date', DateTime),
Column('year', Integer),
Column('month', Integer),
Column('day', Integer),
Column('quarter', Integer),
Column('day_of_week', Integer),
Column('is_weekend', Integer),
Column('month_name', String(20)),
Column('day_name', String(20))
)
self.metadata.create_all(self.engine)
logger.info("日期维度表创建成功")
return date_dim
def insert_date_dimension(self, start_date='2020-01-01', end_date='2030-12-31'):
"""
填充日期维度数据
"""
dates = pd.date_range(start=start_date, end=end_date, freq='D')
date_data = []
for date in dates:
date_data.append({
'date_key': int(date.strftime('%Y%m%d')),
'full_date': date,
'year': date.year,
'month': date.month,
'day': date.day,
'quarter': (date.month - 1) // 3 + 1,
'day_of_week': date.weekday(),
'is_weekend': 1 if date.weekday() >= 5 else 0,
'month_name': date.strftime('%B'),
'day_name': date.strftime('%A')
})
# 批量插入数据
df = pd.DataFrame(date_data)
df.to_sql('dim_date', self.engine, if_exists='replace', index=False)
logger.info(f"插入了 {len(date_data)} 条日期维度数据")
def create_product_dimension(self):
"""
创建产品维度表
"""
product_dim = Table('dim_product', self.metadata,
Column('product_key', Integer, primary_key=True, autoincrement=True),
Column('product_id', String(50)),
Column('product_name', String(200)),
Column('category', String(100)),
Column('subcategory', String(100)),
Column('brand', String(100)),
Column('unit_price', Integer),
Column('created_date', DateTime, default=datetime.now),
Column('effective_date', DateTime),
Column('expiry_date', DateTime),
Column('is_current', Integer, default=1)
)
self.metadata.create_all(self.engine)
logger.info("产品维度表创建成功")
return product_dim
def scd_type2_update(self, table_name, business_key, current_data, new_data):
"""
实现SCD Type 2缓慢变化维更新
"""
with self.engine.connect() as conn:
# 检查是否有当前有效记录
query = f"""
SELECT * FROM {table_name}
WHERE {business_key} = :business_key
AND is_current = 1
"""
result = conn.execute(query, {'business_key': current_data[business_key]})
existing_record = result.fetchone()
if existing_record:
# 比较数据是否变化
has_changed = False
for key, value in new_data.items():
if key not in ['effective_date', 'expiry_date', 'is_current']:
if getattr(existing_record, key) != value:
has_changed = True
break
if has_changed:
# 过期旧记录
update_query = f"""
UPDATE {table_name}
SET expiry_date = :expiry_date, is_current = 0
WHERE {business_key} = :business_key
AND is_current = 1
"""
conn.execute(update_query, {
'expiry_date': datetime.now(),
'business_key': current_data[business_key]
})
# 插入新记录
new_data['effective_date'] = datetime.now()
new_data['expiry_date'] = None
new_data['is_current'] = 1
insert_query = f"""
INSERT INTO {table_name} ({','.join(new_data.keys())})
VALUES ({','.join([':' + k for k in new_data.keys()])})
"""
conn.execute(insert_query, new_data)
logger.info(f"SCD Type 2更新: 记录{business_key}已变化")
else:
# 新记录,直接插入
conn.execute(f"""
INSERT INTO {table_name}
VALUES ({','.join([':' + k for k in new_data.keys()])})
""", new_data)
logger.info(f"新记录插入: {business_key}")
conn.commit()
# 使用示例
if __name__ == "__main__":
# 配置数据库连接
connection_string = 'mysql+pymysql://user:password@localhost:3306/dw_demo'
manager = DimensionTableManager(connection_string)
# 创建日期维度
manager.create_date_dimension()
manager.insert_date_dimension('2023-01-01', '2024-12-31')
# 创建产品维度
manager.create_product_dimension()
# SCD Type 2 更新示例
new_product_data = {
'product_id': 'PROD001',
'product_name': '新产品A',
'category': '电子产品',
'subcategory': '手机',
'brand': 'ABC手机',
'unit_price': 4999,
'effective_date': datetime.now(),
'expiry_date': None,
'is_current': 1
}
manager.scd_type2_update('dim_product', 'product_id',
{'product_id': 'PROD001'},
new_product_data)
使用pandas简化操作
import pandas as pd
from sqlalchemy import create_engine
class PandasDimensionManager:
def __init__(self, engine):
self.engine = engine
def load_csv_to_dimension(self, csv_file, table_name, if_exists='append'):
"""
从CSV文件加载数据到维度表
"""
df = pd.read_csv(csv_file)
# 数据清洗
df = self._clean_dimension_data(df)
# 写入数据库
df.to_sql(table_name, self.engine, if_exists=if_exists, index=False)
return len(df)
def _clean_dimension_data(self, df):
"""
清洗维度数据
"""
# 去除空值
df = df.dropna()
# 去除重复
df = df.drop_duplicates()
# 字符串标准化
string_columns = df.select_dtypes(include=['object']).columns
for col in string_columns:
df[col] = df[col].str.strip().str.upper()
return df
def merge_dimension_data(self, staging_data, dimension_table, key_columns):
"""
合并维度数据(UPSERT操作)
"""
# 读取现有维度数据
existing_data = pd.read_sql(f"SELECT * FROM {dimension_table}", self.engine)
# 找出新的记录
merged = pd.merge(staging_data, existing_data,
on=key_columns, how='left', indicator=True)
new_records = merged[merged['_merge'] == 'left_only']
if not new_records.empty:
# 只保留新数据的列
new_records = new_records[staging_data.columns]
new_records.to_sql(dimension_table, self.engine,
if_exists='append', index=False)
return len(new_records)
# 使用示例
engine = create_engine('postgresql://user:password@localhost:5432/dw')
manager = PandasDimensionManager(engine)
# 加载数据
count = manager.load_csv_to_dimension('dim_customer.csv', 'dim_customer')
print(f"已加载 {count} 条记录")
最佳实践和注意事项
错误处理和事务管理
import contextlib
from sqlalchemy.exc import SQLAlchemyError
@contextlib.contextmanager
def transaction(engine):
"""事务管理器"""
connection = engine.connect()
transaction = connection.begin()
try:
yield connection
transaction.commit()
except SQLAlchemyError as e:
transaction.rollback()
logger.error(f"事务回滚: {e}")
raise
finally:
connection.close()
# 使用示例
with transaction(engine) as conn:
# 执行维度表操作
conn.execute("INSERT INTO dim_product ...")
conn.execute("UPDATE dim_product ...")
性能优化
def batch_insert(engine, table_name, data_list, batch_size=1000):
"""
批量插入数据,优化性能
"""
from sqlalchemy import text
if not data_list:
return 0
with engine.connect() as conn:
for i in range(0, len(data_list), batch_size):
batch = data_list[i:i + batch_size]
# 构建批量插入语句
columns = batch[0].keys()
values_str = ', '.join([f"({','.join([':%s_%d' % (col, j) for col in columns])})"
for j in range(len(batch))])
params = {}
for j, record in enumerate(batch):
for col in columns:
params[f'{col}_{j}'] = record[col]
query = f"INSERT INTO {table_name} ({','.join(columns)}) VALUES {values_str}"
conn.execute(text(query), params)
conn.commit()
return len(data_list)
这些示例涵盖了Python操作数据库维度表的主要场景和最佳实践,根据你的具体需求选择合适的方案即可。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播