2016-04-17

PHP 模擬 SQL ORDER BY 語法

最近在下編寫 PHP 拆解 JSON 資料
由於 JSON 的資料有很多需要不同的欄目排序來檢查的資料,本來打算使用資料庫編寫 SQL 的 ORDER BY 來進行排序
但 JSON 資料要經常更新,要經常更新資料庫再讀取資料感覺有點累贅,所以在下打算不使用資料庫

在下翻查 PHP 官方文件時發現 array_multisort 能達到效果
例如閣下的 array 像
$array = array(
    array(
        'id' => 1,
        'age' => 18,
        'height' => 180,
    ),
    array(
        'id' => 2,
        'age' => 22,
        'height' => 160,
    ),
    array(
        'id' => 3,
        'age' => 18,
        'height' => 160,
    ),
    array(
        'id' => 4,
        'age' => 22,
        'height' => 180,
    ),
);
若閣下需要將 $array 以 age 倒序、height 順序
需要先將 age 及 height 各獨立成 array
$age = array();
$height = array();
foreach ($array as $key => $value){
    $age[$key] = $value['age'];
    $height[$key] = $value['height'];
}
最後使用 array_multisort
array_multisort($age, SORT_DESC, $height, SORT_ASC, $array);
但問題是
使用者不能確定排序的欄目數量,若要改變方法,便需要借用的功能就需要借用 call_user_func_array
call_user_func_array 能以 callback 的方法使用指定功能,並且能接收不確定長度的參數
function array_orderby($array, $orders){
    if (count($orders) > 0){
        $fields = array();
        foreach ($array as $key => $value){
            foreach ($value as $k => $v){
                $fields[$k][$key] = $v;
            }
        }
        $params = array(&$array);
        foreach (array_reverse($orders) as $key => $value){
            if (isset($fields[$key])){
                array_unshift($params, $value);
                array_unshift($params, $fields[$key]);
            }
        }
        call_user_func_array('array_multisort', $params);
    }
    return $array;
}
功能中,由於 $array 需要 pass by referece 才能傳回新排序資料
因此 $params 作為 array_multisort 經 call_user_func_array 需要以 &$array 的加入至 $params 中
而 &$array 需要成為 $params 的最後一個元素,所以 $params 需要使用 array_reverse 及 array_unshift 的方法倒序將參數加入
當需要排序時,只需要調用 array_orderby 即可
$orders = array(
    'age' => SORT_DESC,
    'height' => SORT_ASC,
);

$array = array_orderby($array, $orders);
然而在模擬操作上與真正的 SQL ORDER BY 仍有些微分別
例如資料庫加入資料本身都有次序
在沒有指定排序欄目
例如
SELECT * FROM `array`;

print_r($array);
是相同
但當有指定排序欄目
例如
SELECT * FROM `array` ORDER BY `age` DESC ;

print_r(array_orderby($array, array('age' => SORT_DESC)));
有機會有分別,但由於測試的資料量太少,未必有顯著的分別
原因是 age 經排序後,遇到相同的 age 後沒有其他指定的排序方法;或欄目的資料並非數值
便會由 SQL 及 PHP 各自的方式處理,但這種方式未必相同

沒有留言 :

張貼留言