摘要:最優(yōu)就只能是查詢表的數(shù)量才算是比較合理,完全有足夠的能力寫出優(yōu)雅的代碼很多人可能會(huì)想到的原生查詢了么。但是有強(qiáng)大的關(guān)聯(lián),訪問器修改,查詢范圍等等這些功能讓你的代碼非常簡(jiǎn)潔。相信熟悉的人已經(jīng)知道怎么查詢了,可以達(dá)到最優(yōu)化的,和最優(yōu)雅的寫法。
終于被產(chǎn)品的各種刁鉆不合常理的需求磨煉出用laravel寫出較為優(yōu)雅的代碼,在這里給大家分享一下。
先簡(jiǎn)單介紹一下基本環(huán)境,我們是做一款直播APP的,人很多,所以每個(gè)接口都必須盡量?jī)?yōu)化(主要是SQL的查詢)。
有一天,產(chǎn)品跟我們說(shuō),那個(gè)針對(duì)主播的送禮牌行榜能否顯示30天內(nèi)的用戶送禮數(shù)倒序排列,顯示用戶是否VIP,用戶對(duì)主播的親密度,還有用戶的等級(jí)。
30天內(nèi)的數(shù)據(jù)。也就是說(shuō)之前那張一直累計(jì)數(shù)值的排行表不能使用了,而且這個(gè)30天是個(gè)動(dòng)態(tài)的,也就是說(shuō)這個(gè)數(shù)據(jù)必須只能利用送禮流水group by出來(lái)。我們的送禮流水表是1個(gè)月1張表的
介紹一下基本表的情況
用戶表user
用戶資料表user_ext(你大爺?shù)念^像竟然放這張表,誰(shuí)搞的站出來(lái),看我不弄死你)
禮物表honey_log_201708(XXXX分表日期)
超級(jí)VIP表svip
親密度表qinmi
(這幾張表的關(guān)聯(lián)是無(wú)法避免的,加上分頁(yè)count查詢。SQL最優(yōu)就只能是查詢表的數(shù)量+1才算是比較合理,laravel完全有足夠的能力寫出優(yōu)雅的代碼)
很多人可能會(huì)想到laravel的DB原生查詢了么。但是Eloquent有強(qiáng)大的關(guān)聯(lián),訪問器修改,查詢范圍等等這些功能讓你的代碼非常簡(jiǎn)潔。
我們先寫model
1.用戶表user
關(guān)鍵字段是id用戶ID,nickanem昵稱,exp經(jīng)驗(yàn)值
exp, config("user.level.num")); } }
2.用戶資料表
主要字段uid主鍵,header_name頭像文件名,header_lock頭像是否被鎖(0,1)
header_lock == 1 || $this->header_name == "") { $headerUrl = "http://www.cdn.com/" . "default_header_user.png"; } else { $headerUrl = "http://www.cdn.com/" . $this->header_name; } return $headerUrl; } }
3.SVIP表
主要字段uid主鍵,expire過期時(shí)間
where("expire", ">", LARAVEL_START); } }
4.親民度qinmi表
主要字段uid,beauty_uid(主播主鍵),qinmi_num親密度值
qinmi_num, config("qinmi.qinmi.num")); } }
5.好了,重點(diǎn)來(lái)了。honey_log表,這個(gè)是重點(diǎn),因?yàn)樗欠直淼?,現(xiàn)在我們要封裝一個(gè)union表的方法,讓這個(gè)model自動(dòng)把涉及的分表作為一張表賦予model查詢
=", $startTime], ["time", "<", $endTime]], $wheres); //時(shí)間戳轉(zhuǎn)日期 $startDate = date("Y-m", $startTime); $endDate = date("Y-m", $endTime); //涉及的表數(shù)組 $tables = []; //循環(huán)where數(shù)組,格式是[["字段","表達(dá)式","值"," and|or "],["字段","表達(dá)式","值"," and|or "]] //例子[["beauty_uid", "=", "2011654", "and"]] foreach ($wheres as $val) { //組裝每個(gè)where條件 $val[2] = $val[2] ? $val[2] : """"; if (isset($val[3])) { $whereConditions[] = " {$val[3]} {$val[0]} {$val[1]} {$val[2]}"; } else { $whereConditions[] = " and {$val[0]} {$val[1]} {$val[2]}"; } } //循環(huán)開始日期和結(jié)束日期計(jì)算跨越的表 for ($i = $startDate; $i <= $endDate; $i = date("Y-m", strtotime($i . "+1month"))) { $tables[] = "select " . implode(",", $attributes) . " from cdb_honey_log_" . date("Yn", strtotime($i)) . " where 1" . implode("", $whereConditions); } //會(huì)得到每一個(gè)表的子查詢,因?yàn)槎加屑s束條件,所以每一個(gè)子查詢得結(jié)果集都不會(huì)很多 //用setTable的方法把這個(gè)子查詢union all 后 as一個(gè)表名作為model的table屬性 //sql大概會(huì)是:(select xxx,xxx from honey_log_20177 where time >= 開始日期 and time < 結(jié)束日期 and xxx union all select xxx,xxx from honey_log_20178 where time >= 開始日期 and time < 結(jié)束日期 and xxx) as cdb_honey_log //核心是看你輸入的開始日期和結(jié)束日期和約束條件,組裝成一個(gè)union all的子查詢?nèi)缓笞鳛閠able賦予model return $this->setTable(DB::raw("(" . implode(" union all ", $tables) . ") as cdb_honey_log")); } //關(guān)聯(lián)用戶資料表,要拿頭像 public function userExt() { return $this->belongsTo(UserExt::class, "uid"); } //關(guān)聯(lián)用戶表,要拿昵稱 public function user() { return $this->belongsTo(User::class, "uid"); } //關(guān)聯(lián)SVIP表,要判斷是否VIP public function svip() { return $this->belongsTo(Svip::class, "uid"); } //關(guān)聯(lián)用戶對(duì)于主播的親民值 public function qinmi() { return $this->hasMany(Qinmi::class, "uid", "uid"); } //轉(zhuǎn)化送禮等級(jí),按送禮金額轉(zhuǎn)化 public function getHoneyLevelAttribute() { return section($this->honey_num, config("beauty.honey.num")); } }
以上準(zhǔn)備工作都有了。相信熟悉laravel的人已經(jīng)知道怎么查詢了,可以達(dá)到最優(yōu)化的SQL,和最優(yōu)雅的laravel寫法。
好。我們來(lái)看看控制器如何查詢
input("beauty_uid"); // 每頁(yè)顯示數(shù)量 $pageSize = $request->input("pagesize", 10); // 當(dāng)前頁(yè) $page = $request->input("page"); // 緩存數(shù)據(jù),按查詢的主播,頁(yè)數(shù)作為key分頁(yè) $data = Cache::remember("user_for_beauty_rank_{$beauty_uid}_{$pageSize}_{$page}", 2, function () use ($beauty_uid, $pageSize, $page) { // 計(jì)算30天前 $startTime = Carbon::today()->subDays(30)->getTimestamp(); // 計(jì)算結(jié)束日期 $endTime = Carbon::tomorrow()->getTimestamp(); // 實(shí)例化honeyLog模型,因?yàn)樽远x的setUnionAllTable方法是非靜態(tài)方法,如果誰(shuí)知道如何在model定義非靜態(tài)方法但是可以通過靜態(tài)調(diào)用的話,請(qǐng)告訴我,因?yàn)椴幌敫牡讓樱琹aravel是用了魔法靜態(tài)方法實(shí)例化調(diào)用的,所以我們才可以使用model::select()->where()->get()這樣的鏈?zhǔn)秸{(diào)用,但是在model自己定義的實(shí)體方法好像并沒有繼承到這種調(diào)用 $honeyLog = new HoneyLog; // 查詢?cè)撝鞑D30天有親密值的用戶group by 排序 用分頁(yè)paginate $lists = $honeyLog->setUnionAllTable($startTime, $endTime, ["uid", "honey_num"], [["beauty_uid", "=", $beauty_uid]]) ->select(DB::raw("uid, sum(honey_num) as honey_num"))->groupBy("uid")->orderBy("honey_num", "desc")->paginate($pageSize); // 很多人可能會(huì)問為什么不用with()渴求式加載,因?yàn)橛昧藈ith的話,model會(huì)默認(rèn)去構(gòu)造一次實(shí)例,導(dǎo)致table屬性丟失,你們?cè)囋嚲椭懒?,所以下面我們終于理解到laravel為什么會(huì)還有個(gè)懶惰渴求式加載了,簡(jiǎn)直絕配 // 懶惰渴求式加載頭像,vip,親密值,昵稱 // 好好理解下面的關(guān)聯(lián)約束 $lists->load([ "userExt" => function ($query) { $query->select("uid", "header_name", "header_lock"); }, "user" => function ($query) { $query->select("bid", "nickname", "exp"); }, "svip" => function ($query) { // 這個(gè)validVip是模型定義的范圍約束方法,相當(dāng)于where("expire", ">", LARAVEL_START) $query->select("uid")->validVip(); }, "qinmi" => function ($query) use ($beauty_uid) { // 這里需要傳入主播ID,只查找用戶對(duì)于這個(gè)主播的親密值 $query->select("uid", "qinmi_num")->where("beauty_uid", $beauty_uid); } ]); // 現(xiàn)在需要的數(shù)據(jù)都已經(jīng)全部查出來(lái)了,由于我做的是API,現(xiàn)在要組裝前端需要的格式return出去就可以了, // 如果是自己做的web網(wǎng)頁(yè),就直接丟給視圖遍歷就可以了 $result = []; foreach ($lists as $key => $value) { $result[] = [ // 用戶id "uid" => $value->uid, // 送禮數(shù)量 "honey_num" => $value->honey_num, // 頭像 "header" => $value->userExt->header_url, // 是否vip "svip" => $value->svip ? 1 : 0, // 送禮等級(jí) "honey_level" => $value->honeyLevel, // 親密等級(jí) "qinmi_level" => $value->qinmi->isEmpty() ? 0 : $value->qinmi[0]->level, // 昵稱 "nickname" => $value->user->nickname, // 用戶等級(jí) "level" => $value->user->level, ]; } // 這是前端要求的格式,要這樣組裝沒有什么特別要說(shuō)的,只是前端習(xí)慣這樣的結(jié)構(gòu) $data = [ "page" => [ "last_page" => $lists->lastPage(), "current_page" => $lists->currentPage(), "list" => $result, ], ]; return $data; }); return response()->json($data); } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/25615.html
摘要:前幾天寫了一遍關(guān)于分表關(guān)聯(lián)的查詢,但是我個(gè)人覺得還不算完美,于是今天重新看了一下模型的底層代碼,終于寫出我暫時(shí)覺得最滿意的代碼風(fēng)格,簡(jiǎn)潔優(yōu)雅是核心。關(guān)聯(lián)查詢用了渴求式加載,就能有效減少的條數(shù),保證數(shù)據(jù)庫(kù)的性能。 前幾天寫了一遍關(guān)于laravel分表關(guān)聯(lián)的查詢,但是我個(gè)人覺得還不算完美,于是今天重新看了一下laravel模型的底層代碼,終于寫出我暫時(shí)覺得最滿意的代碼-laravel風(fēng)格,...
摘要:一背景和現(xiàn)狀在美團(tuán),基于構(gòu)建的傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)服務(wù)已經(jīng)難于支撐公司業(yè)務(wù)的爆發(fā)式增長(zhǎng),促使我們?nèi)ヌ剿鞲侠淼臄?shù)據(jù)存儲(chǔ)方案和實(shí)踐新的運(yùn)維方式。隨著近一兩年來(lái)分布式數(shù)據(jù)庫(kù)大放異彩,美團(tuán)團(tuán)隊(duì)聯(lián)合架構(gòu)存儲(chǔ)團(tuán)隊(duì),于年初啟動(dòng)了分布式數(shù)據(jù)庫(kù)項(xiàng)目。 一、背景和現(xiàn)狀 在美團(tuán),基于 MySQL 構(gòu)建的傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)服務(wù)已經(jīng)難于支撐公司業(yè)務(wù)的爆發(fā)式增長(zhǎng),促使我們?nèi)ヌ剿鞲侠淼臄?shù)據(jù)存儲(chǔ)方案和實(shí)踐新的運(yùn)維方式。...
摘要:黑客技術(shù)點(diǎn)擊右側(cè)關(guān)注,了解黑客的世界開發(fā)進(jìn)階點(diǎn)擊右側(cè)關(guān)注,掌握進(jìn)階之路開發(fā)點(diǎn)擊右側(cè)關(guān)注,探討技術(shù)話題作者丨呼延十排版丨團(tuán)長(zhǎng)前言本文主要受眾為開發(fā)人員所以不涉及到的服務(wù)部署等操作且內(nèi)容較多大家準(zhǔn)備好耐心和瓜子礦泉水前一陣系統(tǒng)的學(xué)習(xí)了一下也有 ...
閱讀 1421·2021-09-02 09:53
閱讀 2680·2021-07-29 13:50
閱讀 1727·2019-08-30 11:07
閱讀 1586·2019-08-30 11:00
閱讀 1464·2019-08-29 14:00
閱讀 1858·2019-08-29 12:52
閱讀 2578·2019-08-29 11:11
閱讀 3432·2019-08-26 12:23