最近在做一个数据库异构复制的项目,客户表示需要一个数据比对的工具,我就自己写了一个异构数据库的比对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. 【Python】多进程1

    1.    进程定义: (1) 进程是一个实体.每个进程都有他自己的地址空间,一般包括文本区域.数据区域和堆栈.进程是线程的容器. (2) 进程是一个“执行中的程序” 2.    进程的特征: (1) ...

  2. 日期和API

    Java1.0对日期和时间的支持只能依赖java.util.Date类,年份的起始选择是1900你那,月份的起始是从0开始计算的.它的返回值中包含了JVM的默认市区CET,即中欧时间.在Java1.1 ...

  3. react native 第三方组件

    react native 的成功离不开优秀的第三方组件,以下是我见过的一些优秀或者有用的RN第三方组件 按钮 APSL/react-native-button 导航 react-native-simp ...

  4. 2017-2018-2 20165228 实验二《Java面向对象程序设计》实验报告

    2017-2018-2 20165228 实验二<Java面向对象程序设计>实验报告 相关知识点 三种代码 伪代码:注释,与具体编程语言无关 产品代码:由伪代码翻译而来的具体编程语言语法相 ...

  5. presto .vs impala .vs HAWQ query engine

    大数据查询引擎的选型,画了几张架构图,和一些对比分析: 一.Presto 二.Impala 三.HAWQ 四.总体比较: 1)都是MPP架构,且没有明显性能差距2)HAWQ的功能.特性较Presto和 ...

  6. Java中的参数列表

    一. 什么是参数列表 当你在编写一个函数时,你不知道函数形参的类型或者是形参的长度时,你就可以使用参数列表来代替. 样式: public void f(Object...objects) { Syst ...

  7. 【Java】输出目录结构

    import java.io.*; import java.io.File; import java.io.IOException; public class FileUtil { public st ...

  8. Android强制横屏+全屏的几种常用方法

    全屏: 在Activity的onCreate方法中的setContentView(myview)调用之前添加下面代码 1 requestWindowFeature(Window.FEATURE_NO_ ...

  9. Using gcc stack debug skill

    The stack error is hard to debug, but we can debug it assisted by the tool provided by GCC. As we kn ...

  10. 怎样解题 (G. 波利亚 著)

    第一部分 (已看) 目的 1. 帮助学生 2. 问题,建议,思维活动 3. 普遍性 4. 常识 5. 教师和学生,模仿和实践 主要部分,主要问题 6. 四个阶段 7. 理解题目 8. 例子 9. 拟订 ...