Я думаю, что Ваш последний оператор является лучшим способом. Можно также попробовать
SELECT A.*
from A left join B on
A.x = B.y
where B.y is null
Да. Если вы реализуете spl ArrayObject в своем объекте person, все обычные функции массива php будут правильно работать с ним.
Вопрос касался неэффективности использования usort из-за накладных расходов на вызов обратного вызова сравнения. В этом ответе рассматривается разница между использованием встроенных функций сортировки и нерекурсивной реализацией быстрой сортировки.
Ответ менялся с течением времени по мере развития PHP с 2009 года, поэтому я постоянно обновляю его. Старый материал, хотя и не актуален, все же интересен!
TL; DR: с php 7.0.1 нерекурсивная быстрая сортировка уже не быстрее, чем использование usort с обратным вызовом. Так было не всегда, поэтому подробности ниже представляют интерес для чтения. Реальный вывод заключается в том, что если вы проанализируете свою проблему и попробуете альтернативные подходы, вы можете получить удивительные результаты.
Итак, мы подошли к php 7. 0 выпущен и 7.1 на подходе! Наконец, для этого набора данных встроенный usort чуть-чуть быстрее!
+-----------+------------+------------+------------+------------+------------+
| Operation | HHVM | php7.0.1 | php5.6.3 | 5.4.35 | 5.3.29 |
+-----------+------------+------------+------------+------------+------------+
| usort | *0.0445 | *0.0139 | 0.1503 | 0.1388 | 0.2390 |
| quicksort | 0.0467 | 0.0140 | *0.0912 | *0.1190 | *0.1854 |
| | 5% slower | 1% slower | 40% faster | 15% faster | 23% faster |
+-----------+------------+------------+------------+------------+------------+
Когда я впервые ответил на этот вопрос в 2009 году, я сравнил использование usort с не- рекурсивная быстрая сортировка, чтобы увидеть, есть ли разница. Как оказалось, была значительная разница, причем быстрая сортировка работала в 3 раза быстрее.
Поскольку сейчас 2015 год, я подумал, что было бы полезно вернуться к этому, поэтому я взял код, который сортирует 15000 объектов, используя usort и quicksort и запустил его на 3v4l.org, который запускает его на множестве разных версий PHP. Полные результаты находятся здесь: http://3v4l.org/WsEEQ
+-----------+------------+------------+------------+------------+------------+
| Operation | HHVM | php7alpha1 | php5.6.3 | 5.4.35 | 5.3.29 |
+-----------+------------+------------+------------+------------+------------+
| usort | *0.0678 | 0.0438 | 0.0934 | 0.1114 | 0.2330 |
| quicksort | 0.0827 | *0.0310 | *0.0709 | *0.0771 | *0.1412 |
| | 19% slower | 30% faster | 25% faster | 31% faster | 40% faster |
+-----------+------------+------------+------------+------------+------------+
Я попробовал usort и отсортировал 15000 объектов Person примерно за 1,8 секунды.
Поскольку вас беспокоит неэффективность вызовов функции сравнения, я сравнил ее с нерекурсивной реализацией Quicksort . На самом деле это выполнялось примерно в одной трети времени, примерно 0,5 секунды.
Вот мой код, который сравнивает два подхода
// Non-recurive Quicksort for an array of Person objects
// adapted from http://www.algorithmist.com/index.php/Quicksort_non-recursive.php
function quickSort( &$array )
{
$cur = 1;
$stack[1]['l'] = 0;
$stack[1]['r'] = count($array)-1;
do
{
$l = $stack[$cur]['l'];
$r = $stack[$cur]['r'];
$cur--;
do
{
$i = $l;
$j = $r;
$tmp = $array[(int)( ($l+$r)/2 )];
// partion the array in two parts.
// left from $tmp are with smaller values,
// right from $tmp are with bigger ones
do
{
while( $array[$i]->age < $tmp->age )
$i++;
while( $tmp->age < $array[$j]->age )
$j--;
// swap elements from the two sides
if( $i <= $j)
{
$w = $array[$i];
$array[$i] = $array[$j];
$array[$j] = $w;
$i++;
$j--;
}
}while( $i <= $j );
if( $i < $r )
{
$cur++;
$stack[$cur]['l'] = $i;
$stack[$cur]['r'] = $r;
}
$r = $j;
}while( $l < $r );
}while( $cur != 0 );
}
// usort() comparison function for Person objects
function personSort( $a, $b ) {
return $a->age == $b->age ? 0 : ( $a->age > $b->age ) ? 1 : -1;
}
// simple person object
class Person {
var $age;
function __construct($age) {
$this->age = $age;
}
}
//---------test internal usort() on 15000 Person objects------
srand(1);
$people=array();
for ($x=0; $x<15000; $x++)
{
$people[]=new Person(rand(1,100));
}
$start=microtime(true);
usort( $people, 'personSort' );
$total=microtime(true)-$start;
echo "usort took $total\n";
//---------test custom quicksort on 15000 Person objects------
srand(1);
$people=array();
for ($x=0; $x<15000; $x++)
{
$people[]=new Person(rand(1,100));
}
$start=microtime(true);
quickSort( $people );
$total=microtime(true)-$start;
echo "quickSort took $total\n";
Интересным предложением было добавить метод __ toString
в класс и использовать sort (), так что я тоже попробовал. Проблема в том, что вы должны передать SORT_STRING в качестве второго параметра для сортировки, чтобы он действительно вызвал волшебный метод, который имеет побочный эффект выполнения строковой, а не числовой сортировки. Чтобы противостоять этому, вам нужно дополнить числа нулями, чтобы они сортировались правильно. В конечном итоге это было медленнее, чем usort и пользовательский quickSort
sort 10000 items took 1.76266698837
usort 10000 items took 1.08757710457
quickSort 10000 items took 0.320873022079
Вот код для sort (), использующего __toString ():
$size=10000;
class Person {
var $age;
function __construct($age) {
$this->age = $age;
$this->sortable=sprintf("%03d", $age);
}
public function __toString()
{
return $this->sortable;
}
}
srand(1);
$people=array();
for ($x=0; $x<$size; $x++)
{
$people[]=new Person(rand(1,100));
}
$start=microtime(true);
sort( $people, SORT_STRING);
$total=microtime(true)-$start;
echo "sort($size) took $total\n"
usort()
or uasort() /* to maintain index association if you were using an associative array */
Вот стабильная реализация Radix Sort для значений 0 ... 256:
function radixsort(&$a)
{
$n = count($a);
$partition = array();
for ($slot = 0; $slot < 256; ++$slot) {
$partition[] = array();
}
for ($i = 0; $i < $n; ++$i) {
$partition[$a[$i]->age & 0xFF][] = &$a[$i];
}
$i = 0;
for ($slot = 0; $slot < 256; ++$slot) {
for ($j = 0, $n = count($partition[$slot]); $j < $n; ++$j) {
$a[$i++] = &$partition[$slot][$j];
}
}
}
Это стоит всего O ( n ), поскольку Radix Sort - это алгоритм сортировки без сравнения.
For that specific scenario, you can sort it using the usort() function, where you define your own function to compare the items in the array.
<?php
class Person {
var $age;
function __construct($age) {
$this->age = $age;
}
}
function personSort( $a, $b ) {
return $a->age == $b->age ? 0 : ( $a->age > $b->age ) ? 1 : -1;
}
$person1 = new Person(14);
$person2 = new Person(5);
$person3 = new Person(32);
$person4 = new Person(150);
$person5 = new Person(39);
$people = array($person1, $person2, $person3, $person4, $person5);
print_r( $people );
usort( $people, 'personSort' );
print_r( $people );
Я не рекомендую свое решение в вашем примере, потому что оно было бы некрасивым (и я не тестировал его), но он работает .... И, в зависимости от необходимости, может помочь. :)
class Person
{
public $age;
function __construct($age)
{
$this->age = $age;
}
public function __toString()
{
return $this->age;
}
}
$person1 = new Person(14);
$person2 = new Person(5);
$persons = array($person1, $person2);
asort($persons);