成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

PHP & MySQL 「數(shù)據(jù)關(guān)聯(lián)一對(duì)一」的最佳實(shí)踐

時(shí)飛 / 3005人閱讀

摘要:前言在開發(fā)過程中,通常會(huì)遇到很多一對(duì)一數(shù)據(jù)的處理情況。關(guān)于可以看我的另一篇文章多維數(shù)組中的。最佳實(shí)踐這一次,我們用到了其他兩個(gè)函數(shù)??闭`感謝評(píng)論區(qū)對(duì)文章內(nèi)容錯(cuò)誤之處的指出。

前言

在開發(fā)過程中,通常會(huì)遇到很多 一對(duì)一 數(shù)據(jù)的處理情況。而很多時(shí)候我們會(huì)要取到的是一個(gè)列表,然后列表的單條記錄的對(duì)應(yīng)另外一張表,來實(shí)現(xiàn)業(yè)務(wù)。比如下面的商品信息商品詳情 兩個(gè)表,這里為了演示只是使用了基礎(chǔ)字段,實(shí)際開發(fā)中可能會(huì)復(fù)雜的多,下方演示代碼中數(shù)據(jù)庫連接使用 PDO 進(jìn)行處理。

表結(jié)構(gòu) goods
類型 注釋
id int(11) 自動(dòng)增量 主鍵ID
title varchar(100) 商品名稱
price decimal(10,2) 商品價(jià)格
cover varchar(100) 商品封面
goods_detail
類型 注釋
id int(11) 自動(dòng)增量 主鍵ID
goods_id int(11) 商品ID
content varchar(5000) 商品圖文介紹
初級(jí)

坦言,無論是在公司,還是在一些開源項(xiàng)目上,我都看到過如下的代碼。

$query = $db->query("select * from goods");
$result = $query->fetchAll();
// 方案一
foreach($result as $key => $item){
    $query = $db->query("select * from goods_detail where goods_id=" . $item["id"]);
    $result[$key]["goods_detail"] = $query->fetch();
}
var_dump($result);
// 方案二
foreach($result as &$item){
    $query = $db->query("select * from goods_detail where goods_id=" . $item["id"]);
    $item["goods_detail"] = $query->fetch();
}
unset($item);
var_dump($result);
// 方案三
$result = array_map(function($item){
    $query = $db->query("select * from goods_detail where goods_id=" . $item["id"]);
    $item["goods_detail"] = $query->fetch();
    return $item;
},$result);
var_dump($result);

這是最暴力的方式,也是立桿見影,而且方案一看起來代碼貌似還很繁瑣,不是嗎?如果學(xué)過 引用這一節(jié)的朋友,應(yīng)該知道第二種用法,直接用引用去操作源數(shù)據(jù),當(dāng)然最后最好別忘了 unset 掉 $item,除了第二種,我們還可以用第三種方式,使用 array_map,誠然,這和第二種方式?jīng)]什么區(qū)別,但是這其中有著一個(gè)非常大的問題:數(shù)據(jù)庫查詢的N+1 。從執(zhí)行中我們就可以看到,除了查詢列表的一條 SQL 外,每查詢一條記錄對(duì)應(yīng)的都需要執(zhí)行一條 SQL ,導(dǎo)致了額外的查詢,想想一下如果查詢沒有 limit 限制。會(huì)是什么樣子的情況?

進(jìn)階

看到這里,有人可能會(huì)想到了另一種方案來,先查詢列表,然后取出列表里面的 goods_id 之后使用 in 查詢,然后再循環(huán)分配給列表,看代碼。

$goods_id = array_column($result,"id");
$goods_id_str = implode(",",$goods_id);
$query = $db->query(sprintf("select * from goods_detail where goods_id in (%s)",$goods_id_str));
$goods_detail_list = $query->fetchAll();
foreach($result as &$item){
    $item["goods_detail"] = array_first($goods_detail_list,function($item1){
        return $item["id"] == $item1["goods_id"];
    });
}
unset($item);
var_dump($result);
/**
 * 來自 Laravel
 */
if (!function_exists("value")) {
    function value($value)
    {
        return $value instanceof Closure ? $value() : $value;
    }
}
/**
 * 來自 Laravel
 */
if (!function_exists("array_first")) {
    /**
     * @param               $array
     * @param callable|null $callback
     * @param null          $default
     * @return mixed
     */
    function array_first($array, callable $callback = null, $default = null)
    {
        if (is_null($callback)) {
            if (empty($array)) {
                return value($default);
            }

            foreach ($array as $item) {
                return $item;
            }
        }

        foreach ($array as $key => $value) {
            if (call_user_func($callback, $value, $key)) {
                return $value;
            }
        }

        return value($default);
    }
}

在這個(gè)代碼中,我們完美避開了 N+1 的窘境,使用了in查詢,然后遍歷數(shù)組,再使用 array_first 方法來查找后傳遞給 goods_detail 索引,雖然這樣的效率相比第一次的要高了很多,但是并不完美,接下來來看最后一種方案。關(guān)于 array_first 可以看我的另一篇文章 『PHP 多維數(shù)組中的 array_find』。

最佳實(shí)踐
$goods_detail_list_by_keys = array_column($goods_detail_list,null,"goods_id");
foreach($result as &$item){
    $item["goods_detail"] = array_key_exists($goods_detail_list_by_keys,$item["id"]) ? $goods_detail_list_by_keys[$item["id"]] : null ;
    // php 7.1+
    // $item["goods_detail"] = $goods_detail_list_by_keys[$item["id"]] ?? null;
}
unset($item);
var_dump($result);

這一次,我們用到了其他兩個(gè)函數(shù)。array_column 、 array_key_exists,接下里一一道來,其實(shí)在array_column的官方手冊(cè)中的我們就能 Example #2 中就介紹了我們想要的方法。套用在這里就是重置goods_detail_list 里面元素的 key 為 單個(gè)元素下的 goods_id。在后面我們直接用 array_key_exists 判斷是否存在,然后做出相應(yīng)的處理就好了。在這里我們還可以做另外一個(gè)操作,那就是默認(rèn)值,因?yàn)橛袝r(shí)候,數(shù)據(jù)有可能會(huì)因?qū)Σ簧?,如果查出來直接返回給前端,前端沒有預(yù)料到這種情況沒有做容錯(cuò)處理就會(huì)導(dǎo)致前端頁面崩潰,下面來改寫一下代碼

// 在 「進(jìn)階」 板塊中,我們用到了 「array_first」 函數(shù),該函數(shù)第三個(gè)參數(shù)可以直接設(shè)置默認(rèn)值,我們就不多講了,主要講講最后一個(gè)
$goods_detail_default = [
    "content" => "默認(rèn)內(nèi)容",
    "id"      => null,
    "goods_id"=> null,
];
foreach($result as &$item){
    $tmp = array_key_exists($item["id"] , $goods_detail_list_by_keys) ? $goods_detail_list_by_keys[$item["id"]] : [] ;
    // php 7.1+
    // $tmp = $goods_detail_list_by_keys[$item["id"]] ?? [];
    $item["goods_detail"] = array_merge($goods_detail_default,$tmp);
}
unset($item);
var_dump($result);
結(jié)束

看到這里就算是完結(jié)了但是有的朋友會(huì)說,為什么不用 leftJoin 來處理?確實(shí),在處理一對(duì)一關(guān)系中很多時(shí)候我們都會(huì)選擇 innerJoin 或者 leftJoin 來進(jìn)行處理,一條 SQL 就能搞定,很少會(huì)用到類似于這種方案,其實(shí)不然,在主流的框架中,默認(rèn)的解決方案幾乎都是這樣處理的,比如Laravel、ThinkPHP,考慮到的場(chǎng)景會(huì)有很多,比如有的時(shí)候我只是需要按需取一部分的,或者我需要根據(jù)我后面的業(yè)務(wù)結(jié)果來決定是不是要加載一對(duì)一,然而這種情況下 join 似乎就不太適合。

勘誤

感謝評(píng)論區(qū) @maclxf 對(duì)文章內(nèi)容錯(cuò)誤之處的指出。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/31121.html

相關(guān)文章

  • Q & A | 怎樣讓自己更像一個(gè) Kubernetes 存儲(chǔ)專家?

    摘要:邢舟開源與開放標(biāo)準(zhǔn)工程院軟件工程師背景回顧月日,中國社區(qū)全新改版線上課堂,邀請(qǐng)邢舟老師以直播的方式進(jìn)行了一場(chǎng)以存儲(chǔ)概覽為題的線上講解,反響熱烈。為更好地為學(xué)員整合問答,中國社區(qū)特別整理了本期模塊,感謝邢舟老師百忙之中進(jìn)行校對(duì)。 邢舟 /IBM 開源與開放標(biāo)準(zhǔn)工程院軟件工程師 背景回顧:8 月 2 日 20:00,K8sMeetup 中國社區(qū)全新改版線上課堂,邀請(qǐng)邢舟老師以直播的方式進(jìn)行...

    guyan0319 評(píng)論0 收藏0
  • 干貨:構(gòu)建復(fù)雜 Eloquent 搜索過濾

    摘要:最近,我需要在開發(fā)的事件管理系統(tǒng)中實(shí)現(xiàn)搜索功能。今天,我會(huì)介紹整個(gè)過程以及如何構(gòu)建靈活且可擴(kuò)展的搜索系統(tǒng)。這將是個(gè)挑戰(zhàn)前端的條件過濾的截圖。像剛剛的情況下搜索用戶時(shí)加上一個(gè)過濾器再返回搜索結(jié)果。 showImg(https://segmentfault.com/img/remote/1460000018654283?w=1680&h=494); 最近,我需要在開發(fā)的事件管理系統(tǒng)中實(shí)現(xiàn)搜...

    Shisui 評(píng)論0 收藏0
  • 分布式系統(tǒng)「伸縮性」大招之——「水平&垂直切分」詳解

    摘要:如果要消滅大程序,那就得切分,做好切分必然離不開高內(nèi)聚低耦合的核心思想。分布式系統(tǒng)關(guān)注點(diǎn)高內(nèi)聚低耦合詳解這篇聊的就是這個(gè)。也是分布式系統(tǒng)的分治思想體現(xiàn)。垂直切分垂直切分有時(shí)候也會(huì)被稱作縱向切分。題外話不到迫不得己,盡量避免進(jìn)行水平切分。 如果第二次看到我的文章,歡迎下方掃碼訂閱我的個(gè)人公眾號(hào)(跨界架構(gòu)師)喲~本文長(zhǎng)度為5389字,建議閱讀14分鐘。堅(jiān)持原創(chuàng),每一篇都是用心之作~ 沒...

    LdhAndroid 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<