Python如何编写合适的while循环 等待MSSQL触发器触发后拉取数据发送Telegram
核心问题定位
你的代码运行后直接退出,本质是几个逻辑层面的根本性错误导致的:
- 完全误解MSSQL触发器的运行机制:触发器是数据库内部的事件响应逻辑,执行后不会主动向Python连接返回结果集。你在循环中反复执行
CREATE TRIGGER语句,第一次运行就会因触发器对象已存在触发报错,根本无法获取到任何数据。 - 循环判断逻辑完全失效:你初始将
row设置为字符串'0',但CREATE TRIGGER属于数据定义类操作,不会生成可返回的结果集,cursor.fetchone()会直接返回None,循环条件瞬间不成立,进程直接退出。 - 结构变更类操作(建表、建触发器)不需要放在运行时循环中执行,这类操作仅需提前在数据库中执行一次即可长期生效。
正确实现思路
要实现“触发器识别到符合条件的插入记录后,Python脚本拉取数据并推送至Telegram”的需求,最稳定易维护的方案是结合数据库触发器+长驻轮询:
- 提前在数据库中创建一张待处理通知表,用来存储触发器识别到的、需要推送的业务数据。
- 在业务表上配置触发器,当有新插入记录时,将符合筛选、关联规则的数据自动写入待处理通知表。
- Python脚本长驻运行,每隔固定时间查询待处理表中未标记为已推送的记录,拿到数据后调用Telegram接口发消息,再将对应记录标记为已处理,避免重复推送。
修正步骤与代码
第一步:提前在MSSQL中执行数据库结构配置
注意:这段SQL仅需在数据库中运行一次,不要放到Python的运行循环中执行:
-- 新建待推送通知表 CREATE TABLE dbo.PendingTelegramNotify( NotifyID bigint IDENTITY(1,1) PRIMARY KEY, TimeVal datetime, Remark nvarchar(200), Name nvarchar(100), FirstName nvarchar(100), IsProcessed bit DEFAULT 0 ) GO -- 创建关联业务表的触发器 CREATE OR ALTER TRIGGER Server_enter ON pLogData AFTER INSERT AS BEGIN -- 关闭冗余的行数返回提示,避免干扰应用读取数据 SET NOCOUNT ON; -- 关联本次插入的临时表,仅拉取本次新插入的对应数据 INSERT INTO dbo.PendingTelegramNotify(TimeVal, Remark, Name, FirstName) SELECT [TimeVal],[Remark],[Name],[FirstName] FROM [data1] INNER JOIN [data2] ON [ID] = [ID] -- 必须关联系统临时表inserted,匹配本次新插入的pLogData记录,否则会全表重复写入 INNER JOIN inserted ON inserted.关联外键 = data2.对应ID字段 -- 此处替换为你实际的表关联外键 WHERE [Remark] LIKE '%server%' END GO
第二步:Python长驻脚本实现
import pymssql import time import requests # 数据库连接配置 DB_CONFIG = { "server": "serv", "user": "user", "password": "pwd", "database": "DB" } # Telegram机器人配置 TELEGRAM_BOT_TOKEN = "你的Telegram机器人Token" TELEGRAM_RECEIVER_CHAT_ID = "接收消息的Chat ID" def get_db_connection(): """获取数据库连接,每次断开重连时调用""" return pymssql.connect(**DB_CONFIG) def push_to_telegram(content): """调用Telegram接口推送消息""" api_endpoint = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage" requests.post(api_endpoint, json={ "chat_id": TELEGRAM_RECEIVER_CHAT_ID, "text": content }) if __name__ == "__main__": print("数据库连接成功,开始监听待推送事件") db_conn = get_db_connection() # 无限循环保持脚本运行 while True: try: with db_conn.cursor() as cursor: # 查询所有未处理的待推送记录 cursor.execute(""" SELECT NotifyID, TimeVal, Remark, Name, FirstName FROM PendingTelegramNotify WHERE IsProcessed = 0 """) pending_rows = cursor.fetchall() if pending_rows: for row in pending_rows: notify_id, event_time, remark, name, first_name = row message = f"检测到服务器事件触发:\n事件时间:{event_time}\n备注:{remark}\n相关人员:{first_name} {name}" push_to_telegram(message) # 将当前记录标记为已处理,避免重复推送 cursor.execute(""" UPDATE PendingTelegramNotify SET IsProcessed = 1 WHERE NotifyID = %s """, (notify_id,)) # 提交事务,确保更新生效 db_conn.commit() # 循环间隔2秒,避免过度占用数据库资源 time.sleep(2) except Exception as e: print(f"运行异常:{e}") # 异常后断开旧连接,等待5秒重新建立连接 db_conn.close() time.sleep(5) db_conn = get_db_connection()
关键注意事项
- 循环中必须加入
time.sleep()控制轮询间隔,否则脚本会以满CPU状态高频查询数据库,耗尽数据库服务资源。 - 必须做好连接异常重连逻辑,避免网络波动、数据库临时重启导致脚本直接退出。
- 触发器中必须关联MSSQL内置的
inserted临时表,否则每次新插入日志记录时,会将全表所有符合筛选条件的记录重复写入待推送表,造成大量重复消息。 - 触发器中的
SET NOCOUNT ON必须保留,否则数据库会返回冗余的行数提示,导致pymssql读取结果集时出现错乱。
内容的提问来源于stack exchange,提问作者Bardysya
相关产品推荐
相关产品推荐

