概述:小弟的程序可以通過輸入數(shù)據(jù)庫類型、數(shù)據(jù)庫連接字符串、執(zhí)行 sql 或者含有 sql 的文件等,將數(shù)據(jù)庫中的 sql 查詢出來并寫入 excel
性能:目前導(dǎo)出 25W 條數(shù)據(jù),每行數(shù)據(jù) 5 個(gè)字段,數(shù)據(jù)庫類型 oracle,驅(qū)動(dòng) goracle,excel 生成庫使用 excelize,用時(shí) 1 分 33 秒;同環(huán)境使用 python3.6,數(shù)據(jù)庫驅(qū)動(dòng) cx_Oracle,excel 生成庫使用 pyexcelerate,同樣的查詢語句,用時(shí) 51s (是的…沒干過 python/(ㄒoㄒ)/~~)
程序描述:為了提升執(zhí)行效率,小弟使用了 goruntine,一個(gè)線程專門執(zhí)行 sql 并將結(jié)果生成[]interface{}并裝入通道,另外一個(gè)線程不斷的從通道中取出[]interface{}并寫入 excel
個(gè)人感覺可能存在的問題點(diǎn):
1、golang 的數(shù)據(jù)庫查詢方式只能一條一條生成,同時(shí),機(jī)制用到了反射,而不像 python 可以通過 fetchmany 一次性獲取大量數(shù)據(jù),不知道此處是否會(huì)有性能差距
2、當(dāng)字段類型是 date 類型時(shí),當(dāng)字段為空時(shí),如果不做 isZero 判斷,輸出到 excel 的日期零值很異常(值為-5XXXXX,顯示為##########)。所以每取出一條[]interface{},都需要挨個(gè)判斷類型是不是日期,如果是日期的話,是不是零值,此處可能會(huì)影響效率。而 python 沒有這個(gè)問題
請(qǐng)各位 golang 大大給提點(diǎn)優(yōu)化意見吧,謝謝大家
代碼如下:
package main
import (
_ "gopkg.in/goracle.v2"
_ "github.com/asifjalil/cli"
"github.com/jmoiron/sqlx"
"flag"
"fmt"
"github.com/axgle/mahonia"
"strings"
"os"
"strconv"
"io/ioutil"
"time"
"github.com/360EntSecGroup-Skylar/excelize"
"runtime"
)
func DataGetter(db *sqlx.DB,query string,rowChan chan <- []interface{},columnChan chan <- []string){
defer db.Close()
row,err := db.Queryx(query)
if err != nil{
panic(err)
}
defer row.Close()
columns,err := row.Columns()
columnChan <- columns
close(columnChan)
if err !=nil {
panic(fmt.Sprint("failed to add sheet:%s",err.Error()))
}
for row.Next(){
r,err := row.SliceScan()
if err !=nil{
panic("db row query failed")
}
rowChan <- r
}
close(rowChan)
}
func ExcelWriter(sheetHead string,fileName string,rowChan <- chan[]interface{},columnChan <- chan[]string){
cnt := 2
sheetcnt := 1
var r []interface{}
columns := <- columnChan
hasNext := true
excel := excelize.NewFile()
excel.NewSheet(sheetHead)
excel.SetSheetRow(sheetHead,"A1",columns)
//excel.SetSheetRow(sheetHead,"A"+strconv.Itoa(cnt),columns)
for hasNext{
r,hasNext = <- rowChan
for a := 0;a<len(columns);a++{
t,ok := r[a].(time.Time)
if ok{
if t.IsZero(){
excel.SetCellValue(sheetHead,excelize.ToAlphaString(a)+strconv.Itoa(cnt),"")
}else{
excel.SetCellValue(sheetHead,excelize.ToAlphaString(a)+strconv.Itoa(cnt),t)
}
}else{
excel.SetCellValue(sheetHead,excelize.ToAlphaString(a)+strconv.Itoa(cnt),r[a])
}
}
cnt = cnt + 1
if cnt >= 100000{
excel.NewSheet(sheetHead+strconv.Itoa(sheetcnt))
sheetHead = sheetHead+strconv.Itoa(sheetcnt)
excel.SetSheetRow(sheetHead,"A1",columns)
cnt = 2
sheetcnt = sheetcnt + 1
}
}
excel.SaveAs(fileName+".xlsx")
}
func getConn(dbconn string,dbtype string)(db *sqlx.DB){
if dbtype == "oracle"{
driver := "goracle"
return sqlx.MustOpen(driver,strings.Replace(dbconn,":","/",1))
}else if dbtype == "db2"{
driver := "cli"
userPart := strings.Split(dbconn,"@")[0]
username := strings.Split(userPart,":")[0]
password := strings.Split(userPart,":")[1]
dbPart := strings.Split(dbconn,"@")[0]
dbname := strings.Split(dbPart,"/")[1]
dbip := strings.Split(strings.Split(dbPart,"/")[0],":")[0]
dbport := strings.Split(strings.Split(dbPart,"/")[0],":")[1]
connString := fmt.Sprintf("Driver={IBM DB2 ODBC Driver};Hostname=%s;Port=%s;Protocol=TCPIP;Database=%s;CurrentSchema=%s;UID=%s;PWD=%s;",
dbip,dbport,dbname,username,password)
return sqlx.MustOpen(driver,connString)
}else if dbtype == "postgres"{
driver := "postgres"
connString := "postgres://" + dbconn
return sqlx.MustOpen(driver,connString)
}else{
fmt.Println("dbtype not matched!")
os.Exit(-1)
return
}
}
func main() {
//輸入?yún)?shù)解析
dbconn := flag.String("d","",`Database connect string,use "user:password@ip:port/dbname" for db2 or "user:password@tnsname" for oracle`)
dbtype := flag.String("t","","Database type:oracle db2 mysql mssql")
filetype := flag.String("f","xlsx","exported file type:xlsx or txt default:xlsx")
//xlsx require options
sheetname := flag.String("h","Sheet1","sheet name: default Sheet1")
//txt require options
//charset := flag.String("c","utf-8","charset for exported text file:gbk utf-8 and so on")
//separator := flag.String("s","/","separator: default:/")
//sql options
query := flag.String("q","","sql in one line")
sqlfile := flag.String("l","","sqlfile")
filename := flag.String("n",time.Now().Format("20060102150405"),"filename")
flag.Parse()
if *dbconn == "" || *dbtype == "" || *filetype == ""{
flag.Usage()
return
}
if *query == "" && *sqlfile == ""{
flag.Usage()
return
}
if *sqlfile != "" {
sqlbyte,err := ioutil.ReadFile(*sqlfile)
if err != nil{
panic("read sqlfile failed!")
}
utf8 := mahonia.NewEncoder("utf-8")
*query = utf8.ConvertString(string(sqlbyte))
}
runtime.GOMAXPROCS(2)
if *filetype == "xlsx"{
rowChan := make(chan []interface{},50000)
columnsChan := make(chan []string)
db := getConn(*dbconn,*dbtype)
go DataGetter(db,*query,rowChan,columnsChan)
ExcelWriter(*sheetname,*filename,rowChan,columnsChan)
//}else if *filetype == "txt"{
// db := getConn(*dbconn,*dbtype)
// TextFileExporter(db,*charset,*separator,*filename,*query)
//}else{
flag.Usage()
return
}
}北大青鳥APTECH成立于1999年。依托北京大學(xué)優(yōu)質(zhì)雄厚的教育資源和背景,秉承“教育改變生活”的發(fā)展理念,致力于培養(yǎng)中國IT技能型緊缺人才,是大數(shù)據(jù)專業(yè)的國家
達(dá)內(nèi)教育集團(tuán)成立于2002年,是一家由留學(xué)海歸創(chuàng)辦的高端職業(yè)教育培訓(xùn)機(jī)構(gòu),是中國一站式人才培養(yǎng)平臺(tái)、一站式人才輸送平臺(tái)。2014年4月3日在美國成功上市,融資1
北大課工場(chǎng)是北京大學(xué)校辦產(chǎn)業(yè)為響應(yīng)國家深化產(chǎn)教融合/校企合作的政策,積極推進(jìn)“中國制造2025”,實(shí)現(xiàn)中華民族偉大復(fù)興的升級(jí)產(chǎn)業(yè)鏈。利用北京大學(xué)優(yōu)質(zhì)教育資源及背
博為峰,中國職業(yè)人才培訓(xùn)領(lǐng)域的先行者
曾工作于聯(lián)想擔(dān)任系統(tǒng)開發(fā)工程師,曾在博彥科技股份有限公司擔(dān)任項(xiàng)目經(jīng)理從事移動(dòng)互聯(lián)網(wǎng)管理及研發(fā)工作,曾創(chuàng)辦藍(lán)懿科技有限責(zé)任公司從事總經(jīng)理職務(wù)負(fù)責(zé)iOS教學(xué)及管理工作。
浪潮集團(tuán)項(xiàng)目經(jīng)理。精通Java與.NET 技術(shù), 熟練的跨平臺(tái)面向?qū)ο箝_發(fā)經(jīng)驗(yàn),技術(shù)功底深厚。 授課風(fēng)格 授課風(fēng)格清新自然、條理清晰、主次分明、重點(diǎn)難點(diǎn)突出、引人入勝。
精通HTML5和CSS3;Javascript及主流js庫,具有快速界面開發(fā)的能力,對(duì)瀏覽器兼容性、前端性能優(yōu)化等有深入理解。精通網(wǎng)頁制作和網(wǎng)頁游戲開發(fā)。
具有10 年的Java 企業(yè)應(yīng)用開發(fā)經(jīng)驗(yàn)。曾經(jīng)歷任德國Software AG 技術(shù)顧問,美國Dachieve 系統(tǒng)架構(gòu)師,美國AngelEngineers Inc. 系統(tǒng)架構(gòu)師。