溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

MySQL中怎么編寫UDF

發(fā)布時(shí)間:2021-07-26 11:09:03 來源:億速云 閱讀:215 作者:Leah 欄目:數(shù)據(jù)庫(kù)

MySQL中怎么編寫UDF,相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

1.什么是UDF

UDF顧名思義,就是User defined Function,用戶定義函數(shù)。我們知道,MySQL本身支持很多內(nèi)建的函數(shù),此外還可以通過創(chuàng)建存儲(chǔ)方法來定義函數(shù)。UDF為用戶提供了一種更高效的方式來創(chuàng)建函數(shù)。

UDF與普通函數(shù)類似,有參數(shù),也有輸出。分為兩種類型:?jiǎn)未握{(diào)用型和聚集函數(shù)。前者能夠針對(duì)每一行數(shù)據(jù)進(jìn)行處理,后者則用于處理Group By這樣的情況。

2.為什么用UDF

既然MySQL本身提供了大量的函數(shù),并且也支持定義函數(shù),為什么我們還需要UDF呢?這主要基于以下幾點(diǎn):

1)UDF的兼容性很好,這得益于MySQL的UDF基本上沒有變動(dòng)

2)比存儲(chǔ)方法具有更高的執(zhí)行效率,并支持聚集函數(shù)

3)相比修改代碼增加函數(shù),更加方便簡(jiǎn)單

當(dāng)然UDF也是有缺點(diǎn)的,這是因?yàn)閁DF也處于mysqld的內(nèi)存空間中,不謹(jǐn)慎的內(nèi)存使用很容易導(dǎo)致mysqld crash掉。

3.如何編寫UDF

UDF的API包括

name_init():

在執(zhí)行SQL之前會(huì)被調(diào)用,主要做一些初始化的工作,比如分配后續(xù)用到的內(nèi)存、初始化變量、檢查參數(shù)是否合法等。

name_deinit()

在執(zhí)行完SQL后調(diào)用,大多用于內(nèi)存清理等工作。init和deinit這兩個(gè)函數(shù)都是可選的

name()

UDF的主要處理函數(shù),當(dāng)為單次調(diào)用型時(shí),可以處理每一行的數(shù)據(jù);當(dāng)為聚集函數(shù)時(shí),則返回Group by后的聚集結(jié)果。

name_add()

在每個(gè)分組中每行調(diào)用

name_clear()

在每個(gè)分組之后調(diào)用

為了便于理解,這里給出兩種UDF類型的API調(diào)用圖:

下面將就上述幾個(gè)API進(jìn)行詳細(xì)的講解:

1). name_init

原型:

my_boolname_init(UDF_INIT *initid, UDF_ARGS *args, char *message)

UDF_INIT結(jié)構(gòu)體如下:

UDF_ARGS結(jié)構(gòu)體如下:

Message:用于打印錯(cuò)誤信息,該指針本身提供長(zhǎng)度為MYSQL_ERRMSG_SIZE,來存儲(chǔ)信息;

2).name_deinit

原型:

void name_deinit(UDF_INIT*initid)

該函數(shù)會(huì)進(jìn)行一些內(nèi)存釋放和清理的工作,在之前我們提到initid->ptr,我們可以在該區(qū)域·進(jìn)行內(nèi)存的動(dòng)態(tài)分配,這里就可以直接進(jìn)行內(nèi)存釋放。

3).name()

原型:針對(duì)不同的返回值類型,有不同的函數(shù)原型:

當(dāng)返回值為STRING類型時(shí),參數(shù)result開辟一個(gè)buffer來存儲(chǔ)返回值,但不超過766字節(jié),在length參數(shù)中存儲(chǔ)了字符串的長(zhǎng)度。

每個(gè)函數(shù)原型還包括了is_null和error參數(shù),當(dāng)*is_null被設(shè)置為1時(shí),返回值為NULL,設(shè)置*error為1,表明發(fā)生了錯(cuò)誤。

4).name_add()和name_clear()

原型:

void name_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null,char *error)

void name_clear(UDF_INIT *initid, char *is_null, char *error)

對(duì)于聚合類型的UDF,name_addd和name_clear會(huì)被反復(fù)調(diào)用。

4. 兩個(gè)例子

下面將舉兩個(gè)簡(jiǎn)單的例子,一個(gè)單次調(diào)用型函數(shù),一個(gè)聚集類型函數(shù),來描述寫一個(gè)UDF的過程。

1)接受一個(gè)參數(shù),并返回該參數(shù)的值

//初始化

my_booludf_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message) 

    if (args->arg_count != 1){             //檢查參數(shù)個(gè)數(shù) 

        strcpy(message, 

                "udf_intexample() can onlyaccept one argument"); 

        return 1; 

    }   

    if (args->arg_type[0] != INT_RESULT){   //檢查參數(shù)類型 

        strcpy(message, 

                "udf_intexample() argumenthas to be an integer"); 

        return 1; 

    }   

    return 0; 

//清理操作

voidudf_int_deinit(UDF_INIT  *initid) 

//主函數(shù)

long long udf_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) 

    long long num = (*(long long *)args->args[0]);   //獲取第一個(gè)參數(shù)值 

    return num; 

2)接受一個(gè)浮點(diǎn)數(shù)類型的參數(shù),并對(duì)每個(gè)分組進(jìn)行求和

//初始化 

my_booludf_floatsum_init(UDF_INIT *initid, UDF_ARGS *args, char *message) 

    double *total = (double *) malloc (sizeof(double)); 

    if (total == NULL){                  //內(nèi)存分配失敗 

        strcpy(message,"udf_floatsum:alloc mem failed!"); 

        return 1; 

    } 

    *total = 0; 

    initid->ptr = (char *)total; 

    if (args->arg_count != 1){            //檢查參數(shù)個(gè)數(shù) 

        strcpy(message, "too moreargs,only one!"); 

        return 1; 

    } 

    if (args->arg_type[0] != REAL_RESULT){  //檢查參數(shù)類型 

        strcpy(message, "wrongtype"); 

        return 1; 

    } 

    initid->decimals = 3;         //設(shè)置返回值精度 

    return 0; 

//清理、釋放在init函數(shù)中分配的內(nèi)存

voidudf_floatsum_deinit(UDF_INIT *initid) 

    free(initid->ptr); 

//每一行都會(huì)調(diào)用到該函數(shù)

voidudf_floatsum_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) 

    double* float_total; 

    float_total = (double*)initid->ptr; 

    if (args->args[0]) 

        *float_total += *(double*)args->args[0]; 

//每個(gè)分組完成后,返回結(jié)果

doubleudf_floatsum(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) 

    double* float_total; 

    float_total = (double *)initid->ptr; 

    return *float_total; 

//在進(jìn)行下一個(gè)分組前調(diào)用,設(shè)置initid->ptr指向的值為0,以便下一次分組統(tǒng)計(jì)

voidudf_floatsum_clear(UDF_INIT *initid, char *is_null, char *error) 

    double *float_total; 

    float_total = (double *)initid->ptr; 

    *float_total = 0; 

3) Mysql-udf-http是一個(gè)開源的UDF,可以利用HTTP協(xié)議進(jìn)行REST操作。什么是REST操作呢?REST是一種web service架構(gòu)風(fēng)格,其實(shí)現(xiàn)基于HTTP協(xié)議的四種方法:POST、GET、PUT以及DELETE操作,在mysql-udf-http里分別對(duì)應(yīng)的函數(shù)是http_post、http_get()、http_put()、http_delete()。

源碼:http://curl.haxx.se/download/curl-7.21.1.tar.gz

./configure–prefix={安裝目錄} –with-mysql=/usr/local/webserver/mysql/bin/mysql_config

Make&& make install

該UDF的實(shí)現(xiàn)原理比較簡(jiǎn)單,主要使用libcurl庫(kù)函數(shù)來實(shí)現(xiàn)http協(xié)議通信,總共三百多行代碼。這里有使用和介紹http://blog.s135.com/mysql-udf-http/

有些比較有趣的功能:

例如,我們可以通過GET方法獲取微博中的個(gè)人信息,其中1821798401為用戶ID

selecthttp_get('http://api.t.sina.com.cn/statuses/user_timeline/1821798401.json?count=1&source=1561596835')

UDF具有非常高的自由度,你可以編寫你任何想要實(shí)現(xiàn)的功能函數(shù),甚至可以引用MySQL內(nèi)核的代碼和變量。

當(dāng)然,UDF也有著局限性,如下:

a)        在mysql庫(kù)下必須有func表,并且在??skip?grant?tables開啟的情況下,UDF會(huì)被禁止;

b)        當(dāng)UDF掛掉時(shí),有可能會(huì)導(dǎo)致mysqld crash掉;

c)        所有的UDF的函數(shù)必須是線程安全的,如果非要用全局變量,需要加互斥,盡量在name_init中分配資源,并在name_deinit中釋放

d)        需要有insert權(quán)限

看完上述內(nèi)容,你們掌握MySQL中怎么編寫UDF的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI