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