最近在做一个数据库异构复制的项目,客户表示需要一个数据比对的工具,我就自己写了一个异构数据库的比对python脚本.这个比对脚本只能比对数量,不能比对具体的记录.使用的sql语句也是最基础的select count(*) 这种,没有开并发所以对大表可能比对时间稍长.

基本原理是将需要比对的数据写到一张表里,先读取那个表里的数据,取出需要比对的表.然后创建多进程,同时在原端和目标端count.然后将count的结果写到一个excel文件中.

其中最关键的就是那张表.只要将那张表里的数据搞对了,基本就不会有什么问题.

目前支持的数据库有oracle,mysql,postgresql,sqlserver.程序分为三个部分

1.数据库配置文件

首先需要在python代码的相同目录下写一个名为check.ini的配置文件.下面一个配置文件例子:

[DATA]
#配置原端数据库,下面的ORACLE需要与后面的项匹配
source=ORACLE
#配置目标端数据库,下面的POSTGRESQL需要与后面的项匹配
target=POSTGRESQL
#配置比对表的数据库,需要与下面的配置项匹配
check_node=ORACLE
#配置比对表数据库的用户,如果是oracle是用户,如果是mysql,pg,mssql则是数据库名
check_owner=suq
#配置比对表的表名,区分大小写
check_table=check_table #配置mysql的连接串.注意MYSQL必须大写而且必须是以MYSQL开头,例如想比对多个mysql可以写MYSQL1,MYSQL2等
#下面的几个配置同样需要以相应例子开头,因为程序就是以项的开头来确认是哪种数据库的
[MYSQL]
db_host=192.168.56.25
db_port=3306
db_user=root
db_pwd=root
db_dbname=major [ORACLE]
db_host=192.168.56.30
db_port=1521
db_user=dsg
db_pwd=dsg
db_sid=bre1 [POSTGRESQL]
db_host=192.168.56.50
db_port=5432
db_user=postgres
db_pwd=postgres
db_dbname=msgdb [MSSQL]
db_host=192.168.56.101
db_port=1433
db_user=sa
db_pwd=sa
db_dbname=master

2.创建一个比对表.

例如我上面的例子放在suq用户下的check_table中

具体的表结构如下:

  1. SQL> desc check_table
  2. Name                      Null?    Type
  3. ----------------------------------------- -------- ----------------------------
  4. SOWNER                         VARCHAR2(30)
  5. SNAME                          VARCHAR2(30)
  6. TOWNER                         VARCHAR2(30)
  7. TNAME                          VARCHAR2(30)

分别表示原端的用户名,表名,目标端用户名表名,如果不是用户的那么就是数据库名.

看一下表内我的测试数据:

  1. SQL> select * from check_table;
  2. SOWNER                 SNAME                  TOWNER                 TNAME
  3. ------------------------------ ------------------------------ ------------------------------ ------------------------------
  4. suq                "t1"               suq                t1
  5. suq                "t2"               suq                t2
  6. suq                "t3"               suq                t3
  7. suq                "t4"               suq                t4

这里的数据要特别注意,必须写对否则可能运行会报错.需要注意的一般原因是不同的数据库对大小写敏感不同.因此建议在写好这些数据后,手动到数据库查一下,例如

select count(*) from suq."t1"

看这样的sql对不对.

3.就是主程序

需要注意的是我连接各种数据库分别使用的如下python模块,写excel使用XlsxWriter模块:

  1. C:\Users\think>pip list
  2. cx-Oracle (5.2.1)
  3. MySQL-python (1.2.4)
  4. psycopg2 (2.6.2)
  5. pymssql (2.1.3)
  6. XlsxWriter (0.8.5)

下面是具体的python代码:

#coding:utf-8
import cx_Oracle as ora
import MySQLdb as my
import psycopg2 as post
import pymssql as ms
import ConfigParser as conf
import multiprocessing as mul
import xlsxwriter
import time def connect(cfg,db):
if db[0:5] == 'MYSQL':
db_host=cfg.get(db,'db_host')
db_port=cfg.get(db,'db_port')
db_user=cfg.get(db,'db_user')
db_pwd=cfg.get(db,'db_pwd')
db_dbname=cfg.get(db,'db_dbname')
conn = my.connect(host=db_host,port=int(db_port),user=db_user,passwd=db_pwd,db=db_dbname)
return conn
elif db[0:6] == 'ORACLE':
db_host=cfg.get(db,'db_host')
db_port=cfg.get(db,'db_port')
db_user=cfg.get(db,'db_user')
db_pwd=cfg.get(db,'db_pwd')
db_sid=cfg.get(db,'db_sid')
conn = ora.connect(db_user,db_pwd,db_host+':'+db_port+'/'+db_sid)
return conn
elif db[0:10] == 'POSTGRESQL':
db_host=cfg.get(db,'db_host')
db_port=cfg.get(db,'db_port')
db_user=cfg.get(db,'db_user')
db_pwd=cfg.get(db,'db_pwd')
db_dbname=cfg.get(db,'db_dbname')
conn = post.connect(host=db_host,port=db_port,user=db_user,password=db_pwd,database=db_dbname)
return conn
elif db[0:5] == 'MSSQL':
db_host=cfg.get(db,'db_host')
db_port=cfg.get(db,'db_port')
db_user=cfg.get(db,'db_user')
db_pwd=cfg.get(db,'db_pwd')
db_dbname=cfg.get(db,'db_dbname')
conn = ms.connect(host=db_host,port=db_port,user=db_user,password=db_pwd,database=db_dbname)
return conn def check(cfg,db,check_owner,check_table):
conn=connect(cfg,db)
cursor=conn.cursor()
sql='select * from '+check_owner+'.'+check_table
cursor.execute(sql)
table_list=[]
alldata=cursor.fetchall()
for i in alldata:
table_list.append(i)
#print table_list
return table_list def getcount(cfg,db,sql,q):
conn = connect(cfg,db)
cursor=conn.cursor()
try:
cursor.execute(sql)
countval = cursor.fetchall()[0][0]
q.put(countval)
except Exception,e:
countval="Error : "+str(e)
q.put(countval) def isdigit(num):
try:
int(num)
return True
except:
return False def comp(cfg,source,target,tablelist):
###excel start
xlsxname='check_'+str(time.strftime("%Y%m%d%H%M", time.localtime()))+'.xlsx'
workbook=xlsxwriter.Workbook(xlsxname)
top=workbook.add_format({'border':6,'align':'center','bg_color':'cccccc','font_size':13,'bold':True})
format_data_normal=workbook.add_format({'align':'center','font_size':13})
format_data_warn=workbook.add_format({'align':'center','font_size':13,'bg_color':'ff0000'})
format_data_err=workbook.add_format({'align':'center','font_size':13,'bg_color':'ffff00'})
worksheet = workbook.add_worksheet('sheet1')
worksheet.set_column('A:A',12)
worksheet.set_column('B:B',40)
worksheet.set_column('C:C',12)
worksheet.set_column('D:D',12)
worksheet.set_column('E:E',40)
worksheet.set_column('F:F',12)
worksheet.set_column('G:G',12)
title=[u'源端用户',u'源端表名',u'源端数据量',u'目标端用户',u'目标端表名',u'目标端数据量',u'差异条数']
worksheet.write_row('A1',title,top)
###excel stop
length=len(tablelist)
for i in range(length):
check_result=[]
sowner=tablelist[i][0]
sname=tablelist[i][1]
towner=tablelist[i][2]
tname=tablelist[i][3]
sql_s='select count(*) from '+sowner+'.'+sname
sql_t='select count(*) from '+towner+'.'+tname
#sql_t='select count(*) from '+towner+'.'+'\"'+tname+'\"'
q1=mul.Queue()
q2=mul.Queue()
p1=mul.Process(target = getcount,args = (cfg,source,sql_s,q1))
p2=mul.Process(target = getcount,args = (cfg,target,sql_t,q2))
p1.start()
p2.start()
count_s=q1.get()
count_t=q2.get()
p1.join
p2.join
check_result.append(sowner)
check_result.append(sname)
check_result.append(count_s)
check_result.append(towner)
check_result.append(tname)
check_result.append(count_t)
print '%s %s %s %s %s %s' %(sowner,sname,count_s,towner,tname,count_t)
#print check_result
if isdigit(count_s) and isdigit(count_t):
check_result.append(count_s-count_t)
if count_s == count_t:
worksheet.write_row('A'+str(2+i),check_result,format_data_normal)
else:
worksheet.write_row('A'+str(2+i),check_result,format_data_warn)
else:
check_result.append("Error")
worksheet.write_row('A'+str(2+i),check_result,format_data_err)
workbook.close() if __name__ == "__main__":
print "AT time {0}".format(time.ctime())
print "Begin compare ..."
cfg=conf.ConfigParser()
cfg.read('check.ini')
source=cfg.get('DATA','source')
target=cfg.get('DATA','target')
check_node=cfg.get('DATA','check_node')
check_owner=cfg.get('DATA','check_owner')
check_table=cfg.get('DATA','check_table') tablelist=check(cfg,check_node,check_owner,check_table)
comp(cfg,source,target,tablelist)
print "AT time {0}".format(time.ctime())
print "compare complete!"
raw_input("Press <ENTER>")

执行这段代码后就会读取check.ini文件,获取需要比对的原端和目标端数据库的信息,以及比对表的信息,首先将比对的表获取写到一个数组中.然后使用for循环对表进行count,再写到excel中.excel名为check_XXXX.xlsx.xxx为时间.如果在执行sql的时候报错,那么excel中以黄色标出,如果比对原端和目标端数据不一致以红色标出.

下面是我比对oracle和pg中的一个结果:

--------------

 # -*- coding:utf-8 -*-
import os yesterdaynamelist=[]
todaynamelist=[]
differentnamelist=[]
areceivername=[]
test=[]
#读取 昨天生成的namelist 文件 并生成todaynamelist
namelist = open('D:\\python\\Project\\AtuoEmail\\Date\\riqi.txt','r')
linea = namelist.readlines()
# lineb = namelist.readline()
# print (namelist)
# print (linea)
# print (lineb)
# for i in linea:
# print (i)
#
for i in linea:
line=i.split()
# print(line)
yesterdaynamelist.extend(line)
# print(yestdaynamelist) # 将todaynamelist 列表输出成单列的文本。
yesterdaytxt = open("D:\\python\\Project\\AtuoEmail\\Date\\yesterdaytxt.txt","w",encoding="utf-8") #w参数 创建+复写
yesterdaytxt.close()
for i in yesterdaynamelist:
# print (i)
# print(type(i))
yesterdaytxt = open("D:\\python\\Project\\AtuoEmail\\Date\\yesterdaytxt.txt","a",encoding="utf-8")
yesterdaytxt.write(i)
yesterdaytxt.write("\n")
yesterdaytxt.close()
# todaytxt.write(todaynamelist)
# todaytxt.close() #关闭文件 # Yesterdaytxt
# temp # print (line)
# for i in line:
# print i.strip().split()[0]
# print i.strip().split()[1]
# print i.strip().split()[2] #调用 cmd生成当天最新的域控用户名单。
os.system('D:\\python\\Project\\AtuoEmail\\TodayADUser.bat') namelist = open('D:\\python\\Project\\AtuoEmail\\Date\\riqi.txt','r')
linea = namelist.readlines()
todaynamelist=[]
for i in linea:
line=i.split()
# print(line)
todaynamelist.extend(line)
# print(todaynamelist) # print(todaynamelist)
# print(yesterdaynamelist)
# print(list(set(todaynamelist).difference(set(yesterdaynamelist)))) #生成差异名单并导出文件
differentnamelist=list(set(todaynamelist).difference(set(yesterdaynamelist))) #“t”“y”对比,输出“T”中新增的元素
# print(differentnamelist)
for i in differentnamelist:
# print (i)
# print(type(i))
differentnamelist = open("D:\\python\\Project\\AtuoEmail\\Date\\differentnamelist.txt","a",encoding="utf-8")
differentnamelist.write(i)
differentnamelist.write("@dafy.com,")
differentnamelist.close()
# -*- coding:utf-8 -*-
import os yesterdaynamelist=[]
todaynamelist=[]
differentnamelist=[]
areceivername=[]
test=[]
#读取 昨天生成的namelist 文件 并生成todaynamelist
namelist = open('D:\\python\\Project\\AtuoEmail\\Date\\riqi.txt','r')
linea = namelist.readlines()
# lineb = namelist.readline()
# print (namelist)
# print (linea)
# print (lineb)
# for i in linea:
# print (i)
#
for i in linea:
line=i.split()
# print(line)
yesterdaynamelist.extend(line)
# print(yestdaynamelist) # 将todaynamelist 列表输出成单列的文本。
yesterdaytxt = open("D:\\python\\Project\\AtuoEmail\\Date\\yesterdaytxt.txt","w",encoding="utf-8") #w参数 创建+复写
yesterdaytxt.close()
for i in yesterdaynamelist:
# print (i)
# print(type(i))
yesterdaytxt = open("D:\\python\\Project\\AtuoEmail\\Date\\yesterdaytxt.txt","a",encoding="utf-8")
yesterdaytxt.write(i)
yesterdaytxt.write("\n")
yesterdaytxt.close()
# todaytxt.write(todaynamelist)
# todaytxt.close() #关闭文件 # Yesterdaytxt
# temp # print (line)
# for i in line:
# print i.strip().split()[0]
# print i.strip().split()[1]
# print i.strip().split()[2] #调用 cmd生成当天最新的域控用户名单。
os.system('D:\\python\\Project\\AtuoEmail\\TodayADUser.bat') namelist = open('D:\\python\\Project\\AtuoEmail\\Date\\riqi.txt','r')
linea = namelist.readlines()
todaynamelist=[]
for i in linea:
line=i.split()
# print(line)
todaynamelist.extend(line)
# print(todaynamelist) # print(todaynamelist)
# print(yesterdaynamelist)
# print(list(set(todaynamelist).difference(set(yesterdaynamelist)))) #生成差异名单并导出文件
differentnamelist=list(set(todaynamelist).difference(set(yesterdaynamelist))) #“t”“y”对比,输出“T”中新增的元素
# print(differentnamelist)
for i in differentnamelist:
# print (i)
# print(type(i))
differentnamelist = open("D:\\python\\Project\\AtuoEmail\\Date\\differentnamelist.txt","a",encoding="utf-8")
differentnamelist.write(i)
differentnamelist.write("@dafy.com,")
differentnamelist.close()

【转】用python比对数据库表数据的脚本的更多相关文章

  1. mssql sqlserver 使用sql脚本 清空所有数据库表数据的方法分享

    摘要: 下文讲述清空数据库中所有表信息的方法分享,如下所示: 实验环境:sql server 2008 实现思路: 1.禁用所有约束,外键 2.禁用所有触发器 3.删除表数据 4.开启触发器 5.开启 ...

  2. jmeter 获取数据库表数据作为参数

    jmeter - 获取数据库表数据作为参数 在jmeter中使用数据库表数据首先需要设置数据库连接,然后在创建JDBC取样器 1.创建配置元件 JDBC Connection Configuratio ...

  3. MSSQL 删除数据库表数据

    --删除数据库表数据 慎用 create PROCEDURE sp_DeleteAllData AS ) ) ) ) ) ) begin try begin tran -- 失效索引,触发器 open ...

  4. (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句

    (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 在我们RDIFramework.NET代码生成器中,有这样一个应用,就是通过数据库表自动生成表的CREA ...

  5. 利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句

    利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 在我们RDIFram ...

  6. linux下python导出sybase 数据库 表记录的方式

    导出sybase 数据库 表记录的方式 1 执行启动sybase 数据库命令 code : dbeng7 gkdb 2 执行 连接sybase 数据库命令code : dbisql -c " ...

  7. Python向mysql数据库插入数据

    一.向表tcolor中插入数据的主要流程如下: import datetimeimport pymysql.cursorsconnection = pymysql.connect(host='loca ...

  8. 使用python将mysql数据库的数据转换为json数据

    由于产品运营部需要采用第三方个推平台,来推送消息.如果手动一个个键入字段和字段值,容易出错,且非常繁琐,需要将mysql的数据转换为json数据,直接复制即可. 本文将涉及到如何使用Python访问M ...

  9. SQL2008将服务器的数据库表数据插入到本地数据库

    一,配置参数 exec sp_configure reconfigure exec sp_configure RECONFIGURE 若不配置参数会出现,提示这个错误: SQL Server 阻止了对 ...

随机推荐

  1. 易混点总结--JS

    1.defer与 async 的区别是: defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行:async一旦下载完,渲染引擎就会中断渲染,执行这个脚本 ...

  2. Spring Web Flow

    Spring Web Flow是一个Web框架,它适用于元素按规定流程运行的程序.Spring Web Flow是Spring MVC的扩展,它支持开发基于流程的应用程序.它将流程的定义与实现流程行为 ...

  3. [转]Poisson Distribution

    Poisson Distribution Given a Poisson process, the probability of obtaining exactly successes in tria ...

  4. Java 经典面试题 —— 性能

    1. 性能 String.StringBuffer 与 StringBuilder 两个字符串相加,str1+str2,相当于执行: StringBuilder strBuilder1 = new S ...

  5. SDM(Supervised Descent Method and its Applications to Face Alignment )

    sdm SDM 人脸对齐的核心内容很简单,就是特征到偏移量的映射:                                           Ix = R I 是特征,x是映射矩阵,R是偏移 ...

  6. JS中的变量与常量

    变量 1.创建变量 1.先声明,后赋值 使用var关键字进行变量的声明 使用=进行变量的赋值 自定义变量名 2.声明的同时赋值 var age = 20: 2.命名规范 1.由数字,字母,下划线和$组 ...

  7. Unity调用Windows对话框保存时另存为弹框

    Unity开发VR之Vuforia 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- ...

  8. Gym101002 2016NAIPC(队内第7次训练)

    (由于先看的最后一题,然后又一直WA,导致这场有点爆炸,我背锅. A .Fancy Antiques 题意: 选择最多k个商店,买n个物品,每个物品分别对应两个店售卖,求最小花费是多少.n<10 ...

  9. ios开发常用封装的实用方法

    #pragma mark 获取设备id + (NSString *)getDeviceId { NSString *identifierForVendor = [[UIDevice currentDe ...

  10. cell-augmented

    https://zhuanlan.zhihu.com/p/33147353 https://blog.csdn.net/thm225679/article/details/79689008 https ...