D66【python 接口自动化学习】- python基础之数据库

day66 SQL-DQL-排序分页

学习日期:20241112

学习目标:MySQL数据库-- 135 SQL-DQL-排序分页

学习笔记:

结果查询

结果分页限制

总结
  1. 排序和分页限制的语法

2. 关键字总结:

import streamlit as st import pyodbc import pandas as pd import time from datetime import datetime, timedelta import os import sys import plotly.graph_objects as go import plotly.express as px # 页面配置 - 设置更宽的布局和标题 st.set_page_config( page_title="KUSO", layout="wide", page_icon="📊", initial_sidebar_state="expanded" ) # 配置数据库连接参数 server = 'svcntpj02.leoni.local' database = 'LEP_FAAR_CN' username = 'sqlfacility' password = '52s0nszj' connection_string = f'DRIVER={{SQL Server}};SERVER={server};DATABASE={database};UID={username};PWD={password}' # Excel文件路径 excel_file_path = r'\\SVCNTFILE01\infos\Public\08 FST\2.Production plan\JIS\JIS Production Plan 2018.xlsx' # 班次时间常量 SHIFT_START = "08:00:00" SHIFT_C1 = "20:00:00" SHIFT_C2 = "08:00:00" # 黑色主题CSS样式 - 更新表格样式 st.markdown(""" <style> /* 全局黑色背景 */ .stApp { background-color: #000000; color: #ffffff; } /* 主标题样式 */ .main-title { font-size: 3rem; color: #00b4d8; text-align: center; margin-bottom: 30px; font-weight: 700; text-shadow: 0 0 10px rgba(0, 180, 216, 0.7); } /* KOSU表格样式 - 更新列宽 */ .kosu-table { width: 100%; border-collapse: collapse; margin: 20px 0; font-size: 1.1rem; table-layout: fixed; } .kosu-table th { background-color: #1a1a1a; color: #00b4d8; padding: 12px 15px; text-align: center; border: 1px solid #333; font-weight: bold; } .kosu-table td { padding: 12px 15px; text-align: center; border: 1px solid #333; background-color: #1a1a1a; } .kosu-table tr:hover td { background-color: #2a2a2a; } /* 表头行样式 */ .table-header { background-color: #004d66 !important; color: white !important; font-size: 1.2rem; font-weight: bold; } /* 高完成率样式 */ .high-productivity { color: #00ff00; font-weight: bold; } /* 低完成率样式 */ .low-productivity { color: #ff3333; font-weight: bold; } /* 中等完成率样式 */ .medium-productivity { color: #ffa500; font-weight: bold; } /* 侧边栏样式 */ .css-1d391kg, .css-1d391kg button { background-color: #121212; color: #ffffff; } /* 按钮样式 */ .stButton button { background-color: #00b4d8; color: #000000; border-radius: 6px; border: none; padding: 10px 24px; font-weight: 500; transition: all 0.3s; } .stButton button:hover { background-color: #0096c7; box-shadow: 0 0 10px rgba(0, 180, 216, 0.5); } /* 其他样式 */ .stSlider { color: #00b4d8; } .stAlert { border-radius: 10px; background-color: #1a1a1a; border: 1px solid #333; } .update-time { font-size: 0.9rem; color: #888; text-align: right; font-style: italic; } .stSpinner > div { text-align: center; color: #00b4d8; } /* 下拉选择器样式 */ .stSelectbox > div > div { background-color: #1a1a1a; color: white; border: 1px solid #333; } .stSelectbox label { color: #00b4d8; font-weight: bold; } /* 时间显示样式 */ .time-display { font-size: 1.5rem; color: #00b4d8; text-align: center; margin-bottom: 20px; font-weight: bold; } /* 日期显示样式 - 更新字体和大小 */ .date-cell { color: #00b4d8; font-weight: bold; font-size: 4.5rem; /* 72px 约等于 4.5rem */ font-family: SimHei, "黑体", sans-serif; /* 黑体字体 */ line-height: 1.2; } /* 数字和百分比样式 - 更新字体和大小 */ .number-cell { font-size: 4.5rem; /* 72px 约等于 4.5rem */ font-family: SimSun, "宋体", serif; /* 宋体字体 */ line-height: 1.2; } /* 饼图容器样式 */ .pie-chart-container { background-color: #001f3f; border-radius: 10px; padding: 15px; margin: 10px 0; border: 1px solid #00b4d8; } /* 设置特定列宽 */ .col-line { width: 65.38mm !important; } .col-order { width: 36.38mm !important; } .col-trend { width: 34mm !important; } .col-produced { width: 36mm !important; } .col-productivity { width: 55mm !important; } .col-rework { width: 26.5mm !important; } .col-diff { width: 30.63mm !important; } </style> """, unsafe_allow_html=True) # 应用标题 st.markdown('<div class="main-title">KOSU 生产监控系统</div>', unsafe_allow_html=True) # 计算完成率函数 def calculate_productivity(completed, planned): if planned == 0: return 0.00 return (completed / planned) * 100 # 根据完成率获取CSS类名 def get_productivity_class(percentage): if percentage >= 80: return "high-productivity" elif percentage >= 50: return "medium-productivity" else: return "low-productivity" # 从Excel文件读取数据的函数 - 扩展读取所有必要数据 @st.cache_data(ttl=300, show_spinner="正在从Excel获取数据...") def get_excel_data(): try: # 读取Capacity工作表数据 df_capacity = pd.read_excel( excel_file_path, sheet_name='Capacity', header=None ) # 读取"请勿修改"工作表数据 df_no_modify = pd.read_excel( excel_file_path, sheet_name='请勿修改', header=None ) # 获取计划订单数据 (F列) planned_data = { 'ASS30': int(df_capacity.iloc[4, 5]) if not pd.isna(df_capacity.iloc[4, 5]) else 0, # F5 'ASS31': int(df_capacity.iloc[5, 5]) if not pd.isna(df_capacity.iloc[5, 5]) else 0, # F6 'ASS32': int(df_capacity.iloc[6, 5]) if not pd.isna(df_capacity.iloc[6, 5]) else 0 # F7 } # 获取工作时间数据 (G列) work_start_times = { 'ASS30': df_capacity.iloc[4, 6] if not pd.isna(df_capacity.iloc[4, 6]) else "08:00:00", # G5 'ASS31': df_capacity.iloc[5, 6] if not pd.isna(df_capacity.iloc[5, 6]) else "08:00:00", # G6 'ASS32': df_capacity.iloc[6, 6] if not pd.isna(df_capacity.iloc[6, 6]) else "08:00:00" # G7 } # 获取效率数据 (N列) - 修复了缺少逗号的问题 efficiency_data = { 'ASS30': float(df_capacity.iloc[4, 13]) if not pd.isna(df_capacity.iloc[4, 13]) else 13.47, # N5 'ASS31': float(df_capacity.iloc[5, 13]) if not pd.isna(df_capacity.iloc[5, 13]) else 13.47, # N6 'ASS32': float(df_capacity.iloc[6, 13]) if not pd.isna(df_capacity.iloc[6, 13]) else 13.47 # N7 } # 获取午餐开始时间 (请勿修改工作表的F3, F4, F5) lunch_start_times = { 'ASS30': df_no_modify.iloc[2, 5] if not pd.isna(df_no_modify.iloc[2, 5]) else "12:00:00", # F3 'ASS31': df_no_modify.iloc[3, 5] if not pd.isna(df_no_modify.iloc[3, 5]) else "12:00:00", # F4 'ASS32': df_no_modify.iloc[4, 5] if not pd.isna(df_no_modify.iloc[4, 5]) else "12:00:00" # F5 } return planned_data, work_start_times, efficiency_data, lunch_start_times except Exception as e: st.error(f"读取Excel文件失败: {str(e)}") # 返回默认数据 planned_data = {'ASS30': 0, 'ASS31': 0, 'ASS32': 0} work_start_times = {'ASS30': "08:00:00", 'ASS31': "08:00:00", 'ASS32': "08:00:00"} efficiency_data = {'ASS30': 13.47, 'ASS31': 13.47, 'ASS32': 13.47} lunch_start_times = {'ASS30': "12:00:00", 'ASS31': "12:00:00", 'ASS32': "12:00:00"} return planned_data, work_start_times, efficiency_data, lunch_start_times # 计算Trend(应完成)的逻辑函数 def calculate_trend_value(current_time, work_start_time_str, lunch_start_time_str, efficiency): """ 计算Trend(应完成)值,考虑午餐休息时间 Args: current_time: 当前时间 (datetime对象) work_start_time_str: 工作开始时间字符串 (格式: "HH:MM:SS") lunch_start_time_str: 午餐开始时间字符串 (格式: "HH:MM:SS") efficiency: 效率 (每小时完成数量) Returns: int: 应完成数量 """ try: # 获取当前日期 current_date = current_time.date() # 转换为完整的时间字符串 work_start_full = f"{current_date} {work_start_time_str}" lunch_start_full = f"{current_date} {lunch_start_time_str}" # 转换为datetime对象 work_start = datetime.strptime(work_start_full, "%Y-%m-%d %H:%M:%S") lunch_start = datetime.strptime(lunch_start_full, "%Y-%m-%d %H:%M:%S") # 计算时间差(分钟) minutes_to_start = (current_time - work_start).total_seconds() / 60 minutes_to_lunch = (current_time - lunch_start).total_seconds() / 60 minutes_lunch_to_start = (lunch_start - work_start).total_seconds() / 60 # 午餐休息逻辑判断 if minutes_to_start < 0: # 当前时间在工作开始时间之前 worktime_hours = 0 elif minutes_to_lunch > 0 or minutes_lunch_to_start > 0: # 条件1: 当前时间在午餐开始时间之前 或 午餐开始时间在工作开始时间之后 worktime_hours = minutes_to_start / 60 elif abs(minutes_to_lunch) < 30: # 条件2: 当前时间在午餐开始后的30分钟内(午餐休息期间) worktime_hours = (lunch_start - work_start).total_seconds() / 3600 else: # 条件3: 午餐休息结束后 worktime_hours = minutes_to_start / 60 - 0.5 # 减去30分钟午餐时间 # 计算应完成数量 = 工作时间(小时) × 效率(每小时数量) trend_value = int(worktime_hours * efficiency) return max(0, trend_value) # 确保不为负数 except Exception as e: print(f"计算Trend值错误: {e}") return 0 # 处理跨班次的时间逻辑 def get_shift_adjusted_time(current_time, work_start_time, lunch_start_time, efficiency, line_name): """ 处理跨班次的时间调整逻辑 Args: current_time: 当前时间 work_start_time: 工作开始时间字符串 lunch_start_time: 午餐开始时间字符串 efficiency: 效率 line_name: 生产线名称 Returns: int: 调整后的Trend值 """ # 班次判断逻辑 shift_start_time = datetime.strptime(f"{current_time.date()} {SHIFT_START}", "%Y-%m-%d %H:%M:%S") shift_c1_time = datetime.strptime(f"{current_time.date()} {SHIFT_C1}", "%Y-%m-%d %H:%M:%S") shift_c2_time = datetime.strptime(f"{current_time.date()} {SHIFT_C2}", "%Y-%m-%d %H:%M:%S") if shift_start_time <= current_time <= shift_c1_time: # 白班 - 直接计算 return calculate_trend_value(current_time, work_start_time, lunch_start_time, efficiency) elif current_time > shift_c2_time and current_time < shift_start_time: # 不工作时间段 return 0 elif current_time.hour <= int(SHIFT_C2.split(':')[0]): # 夜班(跨天情况)- 使用前一天的时间 prev_day = current_time.date() - timedelta(days=1) prev_work_start = f"{prev_day} {work_start_time}" prev_lunch_start = f"{prev_day} {lunch_start_time}" # 计算到夜班结束的时间 night_end = datetime.strptime(f"{current_time.date()} {SHIFT_C2}", "%Y-%m-%d %H:%M:%S") trend_at_night_end = calculate_trend_value(night_end, work_start_time, lunch_start_time, efficiency) return trend_at_night_end else: # 正常夜班 return calculate_trend_value(current_time, work_start_time, lunch_start_time, efficiency) # 优化查询函数 def fetch_data(): try: # 建立数据库连接 conn = pyodbc.connect(connection_string) st.sidebar.success("数据库连接成功!", icon="✅") # 获取今天的日期 today = datetime.now().strftime('%Y-%m-%d') tomorrow = (datetime.now() + timedelta(days=1)).strftime('%Y-%m-%d') # 修改查询语句以获取更详细的数据,使用今天的日期 query = f""" SELECT A.[MachineName], B.ResultName, COUNT(DISTINCT a.[Identifier]) AS Ruilin FROM [LEP_FAAR_CN].[TRK_Item].[_ProcessLog_1] A INNER JOIN [LEP_FAAR_CN].[TRK_Item].[_ProcessLog_1] B ON A.Identifier = B.Identifier WHERE A.StepName = 'MA031' AND A.Completed >= '{today} 00:00:00' AND A.Completed < '{tomorrow} 00:00:00' AND B.ResultName IN ('OK', 'Rework') GROUP BY A.MachineName, B.ResultName """ # 使用您提供的批量处理方法 cursor = conn.cursor() cursor.execute(query) # 获取列名 columns = [column[0] for column in cursor.description] # 分批获取数据 data = [] while True: batch = cursor.fetchmany(5000) if not batch: break data.extend(batch) # 转为DataFrame df = pd.DataFrame.from_records(data, columns=columns) return df except pyodbc.Error as e: error_msg = f"数据库连接失败: {str(e)}" st.sidebar.error(error_msg, icon="🚨") return pd.DataFrame() except Exception as e: st.error(f"查询错误: {str(e)}") return pd.DataFrame() # 处理数据库数据,提取各ASS的OK和Rework数量 def process_db_data(df): # 初始化结果字典 result = { 'ASS30': {'OK': 0, 'Rework': 0}, 'ASS31': {'OK': 0, 'Rework': 0}, 'ASS32': {'OK': 0, 'Rework': 0} } if not df.empty: for _, row in df.iterrows(): machine = row['MachineName'] result_type = row['ResultName'] ruilin = row['Ruilin'] # 标准化机器名称(去除空格,转换为大写) machine_clean = machine.strip().upper() # 标准化结果类型(处理大小写问题) result_type_clean = result_type.strip().upper() # 检查是否是有效的机器名 if machine_clean in result: # 处理OK/Ok大小写问题 if result_type_clean == 'OK': result[machine_clean]['OK'] = ruilin elif result_type_clean == 'REWORK': result[machine_clean]['Rework'] = ruilin return result # 创建饼状图函数 def create_pie_chart(ass_name, planned, completed, productivity): # 计算剩余数量 remaining = max(0, planned - completed) # 创建饼图数据 labels = ['已完成', '剩余'] values = [completed, remaining] # 创建饼图 fig = go.Figure() # 添加环形图 fig.add_trace(go.Pie( labels=labels, values=values, hole=0.6, # 空心饼图 marker=dict( colors=['#00b4d8', 'rgba(255, 165, 0, 0.3)'], # 蓝色已完成,橙色空心部分 line=dict(color='#ffa500', width=2) # 橙色边框 ), textinfo='none', # 不显示文本标签 hoverinfo='label+percent' )) # 更新布局 fig.update_layout( title={ 'text': f'{ass_name} - 完成率: {productivity:.1f}%', 'x': 0.5, 'xanchor': 'center', 'font': {'size': 16, 'color': '#00b4d8'} }, showlegend=True, legend=dict( orientation="h", yanchor="bottom", y=-0.2, xanchor="center", x=0.5, font=dict(color='white') ), paper_bgcolor='#001f3f', # 科技感蓝色背景 plot_bgcolor='#001f3f', font=dict(color='white'), height=300, margin=dict(l=20, r=20, t=50, b=50) ) # 在中心添加文本 fig.add_annotation( text=f"{productivity:.1f}%", x=0.5, y=0.5, xref="paper", yref="paper", showarrow=False, font=dict(size=20, color='#00b4d8') ) return fig # 侧边栏配置 with st.sidebar: st.header("控制面板") # 添加页面选择下拉框 page_option = st.selectbox( "选择页面", ["四面大屏", "新项目1", "新项目2"], help="选择要查看的监控页面" ) # 只在四面大屏页面显示刷新率控制 if page_option == "四面大屏": refresh_rate = st.slider("刷新频率(秒)", 1, 300, 60, help="设置数据自动刷新的时间间隔") st.divider() # 添加日期选择器 selected_date = st.date_input("选择日期", datetime.now()) st.divider() # 添加导出功能 if st.button('导出当前数据', key="export_btn"): df = fetch_data() if not df.empty: csv = df.to_csv(index=False).encode('utf-8-sig') st.download_button( label="下载CSV", data=csv, file_name=f"返工数据_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", mime='text/csv', key="download_btn" ) else: st.warning("无数据可导出") # 根据选择的页面显示不同内容 if page_option == "四面大屏": # 主展示区 placeholder = st.empty() while True: # 获取Excel数据 planned_data, work_start_times, efficiency_data, lunch_start_times = get_excel_data() with placeholder.container(): # 获取当前日期和时间 current_date = datetime.now().strftime("%Y-%m-%d") current_time = datetime.now().strftime("%H:%M") # 只显示时间,不显示日期 current_datetime = datetime.now() # 完整的当前时间 st.markdown(f'<div class="time-display">{current_time}</div>', unsafe_allow_html=True) # 获取数据库数据并处理 db_data = process_db_data(fetch_data()) # 计算各生产线的完成率 prod_30 = calculate_productivity(db_data['ASS30']['OK'], planned_data['ASS30']) prod_31 = calculate_productivity(db_data['ASS31']['OK'], planned_data['ASS31']) prod_32 = calculate_productivity(db_data['ASS32']['OK'], planned_data['ASS32']) # 计算Trend(应完成)值 - 应用新的逻辑 trend_30 = get_shift_adjusted_time( current_datetime, str(work_start_times['ASS30']), str(lunch_start_times['ASS30']), efficiency_data['ASS30'], 'ASS30' ) trend_31 = get_shift_adjusted_time( current_datetime, str(work_start_times['ASS31']), str(lunch_start_times['ASS31']), efficiency_data['ASS31'], 'ASS31' ) trend_32 = get_shift_adjusted_time( current_datetime, str(work_start_times['ASS32']), str(lunch_start_times['ASS32']), efficiency_data['ASS32'], 'ASS32' ) # 创建KOSU表格,使用从Excel获取的数据和处理后的数据库数据 html_table = f""" <table class="kosu-table"> <tr class="table-header"> <th class="col-line">Line</th> <th class="col-order">Order (计划订单)</th> <th class="col-trend">Trend (应完成)</th> <th class="col-produced">Produced (已完成)</th> <th class="col-productivity">Productivity (完成率)</th> <th class="col-rework">Rework (返工)</th> <th class="col-diff">Diff. (差额)</th> </tr> <tr> <td class="date-cell col-line">ASS30</td> <td class="number-cell col-order">{int(planned_data['ASS30'])}</td> <td class="number-cell col-trend">{trend_30}</td> <td class="number-cell col-produced">{db_data['ASS30']['OK']}</td> <td class="number-cell col-productivity {get_productivity_class(prod_30)}">{prod_30:.2f}%</td> <td class="number-cell col-rework">{db_data['ASS30']['Rework']}</td> <td class="number-cell col-diff">{int(planned_data['ASS30'] - db_data['ASS30']['OK'])}</td> </tr> <tr> <td class="date-cell col-line">ASS31</td> <td class="number-cell col-order">{int(planned_data['ASS31'])}</td> <td class="number-cell col-trend">{trend_31}</td> <td class="number-cell col-produced">{db_data['ASS31']['OK']}</td> <td class="number-cell col-productivity {get_productivity_class(prod_31)}">{prod_31:.2f}%</td> <td class="number-cell col-rework">{db_data['ASS31']['Rework']}</td> <td class="number-cell col-diff">{int(planned_data['ASS31'] - db_data['ASS31']['OK'])}</td> </tr> <tr> <td class="date-cell col-line">ASS32</td> <td class="number-cell col-order">{int(planned_data['ASS32'])}</td> <td class="number-cell col-trend">{trend_32}</td> <td class="number-cell col-produced">{db_data['ASS32']['OK']}</td> <td class="number-cell col-productivity {get_productivity_class(prod_32)}">{prod_32:.2f}%</td> <td class="number-cell col-rework">{db_data['ASS32']['Rework']}</td> <td class="number-cell col-diff">{int(planned_data['ASS32'] - db_data['ASS32']['OK'])}</td> </tr> </table> """ st.markdown(html_table, unsafe_allow_html=True) # 显示工作时间信息(可选) with st.expander("生产参数详情", expanded=False): col1, col2, col3 = st.columns(3) with col1: st.subheader("ASS30") st.write(f"工作开始时间: {work_start_times['ASS30']}") st.write(f"午餐开始时间: {lunch_start_times['ASS30']}") st.write(f"效率: {efficiency_data['ASS30']} 个/小时") with col2: st.subheader("ASS31") st.write(f"工作开始时间: {work_start_times['ASS31']}") st.write(f"午餐开始时间: {lunch_start_times['ASS31']}") st.write(f"效率: {efficiency_data['ASS31']} 个/小时") with col3: st.subheader("ASS32") st.write(f"工作开始时间: {work_start_times['ASS32']}") st.write(f"午餐开始时间: {lunch_start_times['ASS32']}") st.write(f"效率: {efficiency_data['ASS32']} 个/小时") # 创建三个饼状图 st.subheader("生产完成率饼状图") col1, col2, col3 = st.columns(3) with col1: st.markdown('<div class="pie-chart-container">', unsafe_allow_html=True) fig30 = create_pie_chart("ASS30", planned_data['ASS30'], db_data['ASS30']['OK'], prod_30) st.plotly_chart(fig30, use_container_width=True) st.markdown('</div>', unsafe_allow_html=True) with col2: st.markdown('<div class="pie-chart-container">', unsafe_allow_html=True) fig31 = create_pie_chart("ASS31", planned_data['ASS31'], db_data['ASS31']['OK'], prod_31) st.plotly_chart(fig31, use_container_width=True) st.markdown('</div>', unsafe_allow_html=True) with col3: st.markdown('<div class="pie-chart-container">', unsafe_allow_html=True) fig32 = create_pie_chart("ASS32", planned_data['ASS32'], db_data['ASS32']['OK'], prod_32) st.plotly_chart(fig32, use_container_width=True) st.markdown('</div>', unsafe_allow_html=True) # 可折叠的SQL查询部分 with st.expander("执行SQL查询", expanded=False): if st.button("执行SQL查询", key="sql_query_btn"): with st.spinner("正在查询数据库..."): df = fetch_data() if not df.empty: st.subheader("SQL查询结果") st.dataframe( df.style .highlight_max(axis=0, color="#C2A516") .set_properties(**{'background-color': '#1a1a1a', 'color': 'white'}), use_container_width=True, height=400 ) # 显示处理后的数据 db_data = process_db_data(df) st.subheader("处理后的数据") st.json(db_data) else: st.info("数据库查询无结果") # 更新时间显示 st.markdown(f'<div class="update-time">最后更新: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</div>', unsafe_allow_html=True) time.sleep(refresh_rate) elif page_option == "新项目1": st.info("新项目1页面尚未更新,敬请期待!") st.image("https://via.placeholder.com/800x400/1a1a1a/00b4d8?text=新项目1+开发中", use_column_width=True) elif page_option == "新项目2": st.info("新项目2页面尚未更新,敬请期待!") st.image("https://via.placeholder.com/800x400/1a1a1a/00b4d8?text=新项目2+开发中", use_column_width=True) 将python语句按功能拆分成多个模块,并在excel模块中加入验证的功能。我是新手你要注释每一行代码
09-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值