Cum să extinzi WP_Query pentru a include un tabel personalizat în interogare?
Am petrecut zile întregi cu această problemă. Inițial era vorba despre cum să stochez datele despre urmăritorii unui utilizator în baza de date, pentru care am primit câteva recomandări bune aici pe WordPress Answers. După urmarea recomandărilor, am adăugat un tabel nou astfel:
id leader_id follower_id
1 2 4
2 3 10
3 2 10
În tabelul de mai sus, primul rând are un utilizator cu ID-ul 2 care este urmărit de un utilizator cu ID-ul 4. În al doilea rând, un utilizator cu ID-ul 3 este urmărit de un utilizator cu ID-ul 10. Aceeași logică se aplică și pentru al treilea rând.
Acum, în esență, vreau să extind WP_Query astfel încât să pot limita postările afișate doar la cele ale liderului/liderilor unui utilizator. Astfel, luând în considerare tabelul de mai sus, dacă aș transmite ID-ul de utilizator 10 către WP_Query, rezultatele ar trebui să conțină doar postări ale utilizatorilor cu ID-urile 2 și 3.
Am căutat mult încercând să găsesc un răspuns. Nu am văzut niciun tutorial care să mă ajute să înțeleg cum să extind clasa WP_Query. Am văzut răspunsurile lui Mike Schinkel (despre extinderea WP_Query) la întrebări similare, dar nu am înțeles really cum să le aplic pentru nevoile mele. Ar fi grozav dacă cineva m-ar putea ajuta cu acest lucru.
Link-uri către răspunsurile lui Mike, conform solicitării: Link 1, Link 2

Notă importantă: metoda corectă de a face acest lucru NU este modificarea structurii tabelei, ci utilizarea wp_usermeta. În acest fel, nu va fi nevoie să creați interogări SQL personalizate pentru postări (deși tot va trebui să utilizați SQL personalizat pentru a obține o listă cu toți cei care raportează unui anumit supervizor - în secțiunea Admin, de exemplu). Totuși, deoarece OP a întrebat despre scrierea de SQL personalizat, iată cea mai bună practică actuală pentru injectarea de SQL personalizat într-o interogare WordPress existentă.
Dacă faci join-uri complexe, nu poți folosi doar filtrul posts_where, deoarece va trebui să modifici secțiunile join, select și posibil group by sau order by ale interogării.
Cea mai bună opțiune este să folosești filtrul 'posts_clauses'. Acesta este un filtru extrem de util (care nu ar trebui abuzat!) care îți permite să adaugi/modifici diferite porțiuni din SQL generat automat de numeroasele linii de cod din nucleul WordPress. Semnătura funcției de callback este:
function posts_clauses_filter_cb( $clauses, $query_object ){ }
și se așteaptă să returnezi $clauses
.
Clauzele
$clauses
este un array care conține următoarele chei; fiecare cheie reprezintă un șir SQL care va fi folosit direct în interogarea finală trimisă bazei de date:
- where
- groupby
- join
- orderby
- distinct
- fields
- limits
Dacă adaugi o tabelă în baza de date (fă asta doar dacă nu poți folosi post_meta, user_meta sau taxonomii), probabil va trebui să modifici mai multe dintre aceste clauze, de exemplu, fields
(porțiunea "SELECT" din interogare), join
(toate tabelele tale, în afară de cea din clauza "FROM") și poate orderby
.
Modificarea clauzelor
Cea mai bună metodă este să faci o subreferință la cheia relevantă din array-ul $clauses
obținut din filtru:
$join = &$clauses['join'];
Acum, dacă modifici $join
, vei modifica direct $clauses['join']
, astfel încât modificările vor fi în $clauses
când îl returnezi.
Păstrarea clauzelor originale
Este foarte probabil (serios, ascultă acum) să vrei să păstrezi SQL-ul generat de WordPress. Dacă nu, ar trebui să te uiți la filtrul posts_request
- acesta reprezintă întreaga interogare mySQL chiar înainte de a fi trimisă la bază, așadar poți să o suprascrii complet cu a ta. De ce ai vrea asta? Probabil că nu.
Deci, pentru a păstra SQL-ul existent în clauze, amintește-ți să adaugi la clauze, nu să le suprascrii (adică: folosește $join .= ' {NOU SQL}';
nu $join = '{SQL SUPRASCRIS}';
). Reține că, deoarece fiecare element din array-ul $clauses
este un șir, dacă vrei să adaugi la el, probabil va trebui să inserezi un spațiu înaintea altor tokeni, altfel vei crea erori de sintaxă SQL.
Poți presupune că întotdeauna va exista ceva în fiecare clauză și să începi fiecare șir nou cu un spațiu, ca în: $join .= ' my_table
, sau poți adăuga o linie care adaugă un spațiu doar dacă este necesar:
$join = &$clauses['join'];
if (! empty( $join ) ) $join .= ' ';
$join .= "JOIN my_table... "; // <-- observă spațiul la sfârșit
$join .= "JOIN my_other_table... ";
return $clauses;
Aceasta este mai degrabă o chestiune de stil. Important este să-ți amintești: lasă întotdeauna un spațiu ÎNAINTE de șirul tău dacă adaugi la o clauză care deja conține SQL!
Punând totul cap la cap
Prima regulă în dezvoltarea WordPress este să încerci să folosești cât mai mult din funcționalitățile de bază. Acesta este cel mai bun mod de a-ți viitoriza munca. Să presupunem că echipa WordPress decide să folosească SQLite sau Oracle sau alt limbaj de baze de date. Orice mySQL scris manual poate deveni invalid și poate strica plugin-ul sau tema ta! Mai bine lași WordPress să genereze cât mai mult SQL și să adaugi doar părțile necesare.
Deci, primul pas este să folosești WP_Query
pentru a genera cât mai mult din interogarea ta de bază. Metoda exactă depinde de unde ar trebui să apară această listă de postări. Dacă este o sub-secțiune a paginii (nu interogarea principală), poți folosi get_posts()
; dacă este interogarea principală, poți folosi query_posts()
, dar metoda corectă este să interceptezi interogarea principală înainte să ajungă la bază (și să consume resurse), deci folosește filtrul request
.
Deci, ai generat interogarea și SQL-ul este pe cale să fie creat. De fapt, a fost deja creat, dar nu a fost trimis la bază. Folosind filtrul posts_clauses
, vei adăuga tabela de relații angajați în interogare. Să numim această tabelă {$wpdb->prefix} . 'user_relationship', și este o tabelă de intersecție. (Apropo, recomand să generalizezi structura acestei tabele și să o transformi într-o tabelă de intersecție corectă cu următoarele câmpuri: 'relationship_id', 'user_id', 'related_user_id', 'relationship_type'; este mult mai flexibil și puternic... dar mă abat).
Dacă am înțeles corect, vrei să transmiți ID-ul unui Lider și apoi să vezi doar postările Urmăritorilor acelui Lider. Sper că am înțeles corect. Dacă nu, va trebui să adaptezi ce spun eu la nevoile tale. Voi rămâne la structura ta: avem un leader_id
și un follower_id
. Deci JOIN-ul va fi pe {$wpdb->posts}.post_author
ca cheie străină către 'follower_id' din tabela ta 'user_relationship'.
add_filter( 'posts_clauses', 'filter_by_leader_id', 10, 2 ); // avem nevoie de 2 pentru a obține toți parametrii
function filter_by_leader_id( $clauses, $query_object ){
// Nu știu cum intenționezi să transmiți leader_id, așa că presupun că este o variabilă globală
global $leader_id;
// În acest exemplu, vreau să afectez doar interogarea de pe pagina principală
// Aici folosim $query_object pentru a evita să afectăm TOATE interogările
// (deoarece TOATE interogările trec prin acest filtru)
if ( $query_object->is_home() ){
// Acum, să adăugăm tabela ta în SQL
$join = &$clauses['join'];
if (! empty( $join ) ) $join .= ' '; // adaugă un spațiu doar dacă este necesar
$join .= "JOIN {$wpdb->prefix}employee_relationship EMP_R ON EMP_R.follower_id = {$wpdb->posts}.author_id";
// Și să ne asigurăm că o adăugăm la criteriile de selecție
$where = &$clauses['where'];
// Întotdeauna începe cu AND, deoarece există întotdeauna o declarație '1=1' ca primă declarație în clauza WHERE adăugată de WP
// Nu uita spațiul de la început!
$where .= " AND EMP_R.leader_id={$leader_id}"; // presupunând că $leader_id este întotdeauna (int)
// Și presupun că vei dori să grupezi postările după ID-ul utilizatorului, deci să modificăm clauza groupby
$groupby = &$clauses['groupby'];
// Trebuie să adăugăm la început, deci...
if (! empty( $groupby ) ) $groupby = ' ' . $groupby; // Pentru perfecționiști
$groupby = "{$wpdb->posts}.post_author" . $groupby;
}
// În orice caz, trebuie să returnăm clauzele noastre...
return $clauses;
}

Răspund la această întrebare extrem de târziu și îmi cer scuze pentru acest lucru. Am fost prea ocupat cu termene limită pentru a mă ocupa de aceasta.
Mulțumesc mult lui @m0r7if3r și @kaiser pentru soluțiile de bază pe care le-am putut extinde și implementa în aplicația mea. Acest răspuns oferă detalii despre adaptarea mea a soluțiilor propuse de @m0r7if3r și @kaiser.
În primul rând, permiteți-mi să explic de ce a fost pusă această întrebare. Din întrebare și din comentariile acesteia, se poate înțelege că încerc să folosesc WP_Query pentru a prelua postările tuturor utilizatorilor (leaderi) pe care un anumit utilizator (follower) îi urmărește. Relația dintre follower și leader este stocată într-un tabel personalizat follow
. Cea mai comună soluție pentru această problemă este să preiau ID-urile tuturor leaderilor unui follower din tabelul follow și să le plasez într-un array. Vezi mai jos:
global $wpdb;
$results = $wpdb->get_results($wpdb->prepare('SELECT leader_id FROM cs_follow WHERE follower_id = %s', $user_id));
foreach($results as $result)
$leaders[] = $result->leader_id;
Odată ce ai array-ul de leaderi, îl poți transmite ca argument către WP_Query. Vezi mai jos:
if (isset($leaders)) $authors = implode(',', $leaders); // Necesar deoarece argumentul authors al WP_Query acceptă doar un șir de caractere care conține ID-uri de autori separate prin virgulă
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'author' => $authors
);
$wp_query = new WP_Query( $args );
// Continuă cu bucla normală WordPress
Soluția de mai sus este cea mai simplă modalitate de a obține rezultatele dorite. Cu toate acestea, nu este scalabilă. În momentul în care un follower urmărește zeci de mii de leaderi, array-ul rezultat de ID-uri de leaderi va deveni extrem de mare și va forța site-ul WordPress să utilizeze 100MB - 250MB de memorie la fiecare încărcare de pagină, ceea ce poate duce la blocarea site-ului. Soluția problemei este să rulezi o interogare SQL direct pe baza de date și să preiei postările relevante. Aici a intrat în joc soluția lui @m0r7if3r. Urmând recomandarea lui @kaiser, am decis să testez ambele implementări. Am importat aproximativ 47K de utilizatori dintr-un fișier CSV pentru a-i înregistra într-o instalație nouă de WordPress. Instalația folosea tema Twenty Eleven. După aceasta, am rulat o buclă pentru a face aproximativ 50 de utilizatori să urmărească fiecare alt utilizator. Diferența de timp de interogare între cele două soluții a fost uluitoare. Soluția lui @kaiser a durat în medie între 2 și 5 secunde pentru fiecare interogare. Presupun că variația apare deoarece WordPress cachează interogările pentru utilizare ulterioară. Pe de altă parte, soluția lui @m0r7if3r a avut un timp mediu de interogare de 0,02 ms. Pentru testare, am activat indexarea pe coloana leader_id. Fără indexare, timpul de interogare a crescut dramatic.
Utilizarea memoriei cu soluția bazată pe array a fost de aproximativ 100-150 MB și a scăzut la 20 MB când am rulat o interogare SQL directă.
Am întâmpinat o problemă cu soluția lui @m0r7if3r când a trebuit să transmit ID-ul followerului către funcția de filtrare posts_where. Din câte știu, WordPress nu permite transmiterea de variabile către funcțiile de filtrare. Poți folosi variabile globale, dar am vrut să le evit. Am ajuns să extind WP_Query pentru a rezolva problema. Iată soluția finală pe care am implementat (bazată pe soluția lui @m0r7if3r).
class WP_Query_Posts_by_Leader extends WP_Query {
var $follower_id;
function __construct($args=array()) {
if(!empty($args['follower_id'])) {
$this->follower_id = $args['follower_id'];
add_filter('posts_where', array($this, 'posts_where'));
}
parent::query($args);
}
function posts_where($where) {
global $wpdb;
$table_name = $wpdb->prefix . 'follow';
$where .= $wpdb->prepare(" AND post_author IN (SELECT leader_id FROM " . $table_name . " WHERE follower_id = %d )", $this->follower_id);
return $where;
}
}
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'follower_id' => $follower_id
);
$wp_query = new WP_Query_Posts_by_Leader( $args );
Notă: Am testat această soluție în cele din urmă cu 1,2 milioane de intrări în tabelul follow. Timpul mediu de interogare a fost de aproximativ 0,060 ms.

Puteți face acest lucru cu o soluție pur SQL folosind filtrul posts_where
. Iată un exemplu:
if( o anumită condiție )
add_filter( 'posts_where', 'wpse50305_leader_where' );
// lol, ID-ul întrebării este același în ambele sensuri
function wpse50305_leader_where( $where ) {
$where .= $GLOBALS['wpdb']->prepare( ' AND post_author '.
'IN ( '.
'SELECT leader_id '.
'FROM nume_tabel_personalizat '.
'WHERE follower_id = %s'.
' ) ', $follower_id );
return $where;
}
Cred că ar putea exista și o metodă de a face asta cu JOIN
, dar nu reușesc să o găsesc. Voi continua să experimentez și voi actualiza răspunsul dacă reușesc.
Alternativ, așa cum a sugerat @kaiser, puteți împărți problema în două părți: obținerea liderilor și efectuarea interogării. Am senzația că această abordare ar putea fi mai puțin eficientă, dar este cu siguranță mai ușor de înțeles. Ar trebui să testați eficiența pentru a determina care metodă este mai bună, deoarece interogările SQL imbricate pot deveni foarte lente.
DIN COMENTARII:
Ar trebui să plasați funcția în fișierul functions.php
și să apelați add_filter()
imediat înainte de apelarea metodei query()
din WP_Query
. Imediat după aceea, ar trebui să folosiți remove_filter()
pentru a evita afectarea altor interogări.

Am editat A-ul tău și am adăugat prepare()
. Sper că nu te deranjează editarea. Și da: Performanța trebuie măsurată de OP. Oricum: Încă cred că ar trebui să fie simplu usermeta și nimic altceva.

@m0r7if3r Mulțumesc pentru încercarea de soluție. Tocmai am postat un comentariu ca răspuns la soluția lui kaiser, cu îngrijorări legate de posibile probleme de scalabilitate. Te rog să iei în considerare.

@m0r7if3r Am testat asta și am observat că face ca WP_Query să returneze toate postările. Prin toate postările înseamnă postări de toate tipurile (articole, atașamente, pagini) ale tuturor utilizatorilor. Acesta este SQL-ul pe care îl afișează echo $wp_query->request;
pentru interogarea SQL_CALC_FOUND_ROWS cs_posts.* FROM cs_posts INNER JOIN cs_postmeta ON (cs_posts.ID = cs_postmeta.post_id) WHERE 1=1 GROUP BY cs_posts.ID ORDER BY cs_posts.post_date DESC LIMIT 0, 30

Uf! greșelile de tipar. Da, acum funcționează. Aș încerca acum să implementez soluția lui @kaiser și să măsoar timpii. Apropo, un efect secundar pe care îl observ la folosirea acestui lucru este că începe să afecteze toate interogările dacă există mai multe dintre ele pe o singură pagină. În plus, acest lucru a stricat rescrierile URL-urilor pentru mine când a fost plasat în fișierul functions.php. Mutând codul chiar înainte de apelul WP_Query am rezolvat interferența cu rescrierile URL-urilor. Deși nu sunt sigur dacă plasarea lui înaintea WP_Query ar fi o practică proastă???

Ar trebui să pui funcția în fișierul tău functions.php
și să faci add_filter()
chiar înainte ca metoda query()
a lui WP_Query
să fie apelată. Imediat după aceea, ar trebui să folosești remove_filter()
astfel încât să nu afecteze celelalte interogări. Nu sunt sigur care ar fi problema cu rescrierea URL-urilor, am folosit posts_where
în multe ocazii și nu am văzut niciodată asta...

@m0r7if3r Mulțumesc pentru observație. Continuând cu testele pe datele de probă. Voi reveni cu rezultate.

@m0r7if3r Ultimul tău comentariu ar fi un citat perfect în interiorul Q :)

Tag-ul Șablon
Pur și simplu plasați ambele funcții în fișierul dumneavoastră functions.php
. Apoi ajustați prima funcție și adăugați numele tabelului personalizat. Apoi aveți nevoie de câteva încercări/erori pentru a elimina ID-ul utilizatorului curent din tabloul rezultat (vezi comentariul).
/**
* Obține "Liderii" utilizatorului curent
* @param int $user_id ID-ul utilizatorului curent
* @return array $query Liderii
*/
function wpse50305_get_leaders( $user_id )
{
global $wpdb;
return $wpdb->query( $wpdb->prepare(
"
SELECT `leader_id`, `follower_id`
FROM %s
WHERE `follower_id` = %s
ORDERBY `leader_id` ASC
",
// Editați numele tabelului
"{$wpdb->prefix}custom_table_name"
$user_id
) );
}
/**
* Obține un tablou de postări care conțin postări de la
* "Liderii" pe care îi urmărește utilizatorul curent
* @return array $posts Postări care sunt de la "Liderul" curent
*/
function wpse50305_list_posts_by_leader()
{
get_currentuserinfo();
global $current_user;
$user_id = $current_user->ID;
$leaders = wpse5035_get_leaders( $user_id );
// este posibil să fie nevoie să parcurgeți $leaders
// și să eliminați ID-urile urmăritorilor
return get_posts( array(
'author' => implode( ",", $leaders )
) );
}
În interiorul șablonului
Aici puteți face orice doriți cu rezultatele dumneavoastră.
foreach ( wpse50305_list_posts_by_leader() as $post )
{
// faceți ceva cu $post
}
NOTĂ Nu avem date de test, etc. așa că cele de mai sus sunt un pic de ghicit. Asigurați-vă că dumneavoastră editați acest răspuns cu ceea ce a funcționat pentru dumneavoastră, astfel încât să avem un rezultat satisfăcător pentru cititorii viitori. Voi aproba editarea în cazul în care aveți prea puțină reputație. Puteți apoi șterge și această notă. Mulțumesc.

Mulțumesc pentru soluția oferită. Cu toate acestea, această abordare are limitări în ceea ce privește scalabilitatea. Problema este în obținerea ID-urilor liderilor ca un array. Array-ul ar putea deveni extrem de mare în funcție de numărul de utilizatori pe care îi urmărește un utilizator. Am văzut acest lucru consumând cantități mari de memorie. Acest lucru trebuie făcut printr-o singură interogare SQL care ar putea implica efectuarea unui JOIN pe tabela personalizată și tabela WP posts și obținerea rezultatelor. Cea mai bună soluție este extinderea WP_Query. Problema este că nu știu cum să o fac. Din nou, mulțumesc pentru încercare. Feedback-ul este binevenit.

JOIN
este mult mai costisitor. În plus: După cum am menționat, nu avem date de test, așa că te rog să testezi ambele răspunsuri și să ne luminezi cu rezultatele tale.

WP_Query în sine lucrează cu JOIN-uri între tabela posts și postmeta atunci când interoghează. Am văzut utilizarea memoriei PHP crescând la 70MB - 200MB pe încărcare de pagină. Rularea a ceva de genul acesta cu mulți utilizatori simultan ar necesita o infrastructură extremă. Presupunerea mea este că, deoarece WordPress implementează deja o tehnică similară, JOIN-urile ar trebui să fie mai puțin solicitante în comparație cu lucrul cu un array de ID-uri.

Nu, îmi pare rău, dar nu. Există și încercări de a scăpa de JOIN
-uri suplimentare. Exemplu: WP adaugă un JOIN
al tabelei de termeni atunci când interoghează pentru mai multe taxonomii, ceea ce este contraperformant. Pur și simplu folosește timer_start()
înainte de interogare și timer_stop()
după interogare pentru a măsura diferența.

Plus: Asigură-te că ai INDEX
-ul pe coloana corectă. Aceasta face o diferență uriașă în performanță. Într-un proiect la care am lucrat, unde aveam nevoie să fac interogări pentru lat/lng, am o tabelă suplimentară mică, bine indexată și care funcționează mult mai rapid decât orice JOIN
pe care l-am încercat. Pentru o înțelegere mai profundă despre JOIN
-uri, citește asta.

Deci sfatul tău ar fi să rămân la array decât să rulez o interogare SQL cu join, corect?

Da, dar cum a spus @m0r7if3r: Măsoară. Ți-am dat linkurile. Trebuie doar să cronometrezi și să ne arăți ce ai obținut. Dacă nu o faci, atunci am făcut doar niște frumoase presupuneri :)

Cu siguranță voi încerca ambele soluții. Dar, pentru a cronometra asta, va trebui să încarc baza de date cu date de test, ceea ce va dura câteva ore (voi popula baza cu 50k de utilizatori). O voi face și vă voi ține la curent.

Ok, iată rezultatele testelor. Pentru asta am adăugat aproximativ 47K utilizatori dintr-un fișier csv. Apoi, am rulat un for loop pentru a face ca primii 45 de utilizatori să îi urmărească pe toți ceilalți. Acest lucru a rezultat în 3,704,951 de înregistrări salvate în tabelul meu personalizat. Inițial, soluția lui @m0r7if3r mi-a dat un timp de interogare de 95 secunde, care a scăzut la 0.020 ms după activarea indexării pe coloana leader_id. Memoria PHP totală consumată a fost în jur de 20MB. Pe de altă parte, soluția ta a durat între 2 și 5 secunde pentru interogare cu indexarea activată. Memoria PHP totală consumată a fost în jur de 117MB.

@John Poți te rog să adaugi asta ca răspuns separat? Sunt foarte bucuros că ai făcut acest test și ne-ai furnizat date reale. :) Ce aș aprecia și mai mult este un test mai "real": Fiecare utilizator să urmărească un $leader_amount = rand( 0, 5 );
și apoi să adaugi numărul de $leader_amount
x $random_ids = rand( 0, 47000 );
la fiecare utilizator. Până acum știm că: Soluția mea ar fi extrem de proastă dacă un utilizator ar urmări pe fiecare alt utilizator. Mai departe: Va trebui să arăți cum ai făcut testul și unde exact ai adăugat cronometrele. Aceasta este o întrebare unică și chiar îmi place :)

@John Am legat și distribuit întrebarea ta și participarea și voiam să menționez că toată lumea se bucură foarte mult de munca ta la acest subiect (vezi upvote-urile). :)

Voi testa în continuare pe baza sugestiei tale pentru a efectua un test mai apropiat de realitate și ulterior voi scrie un răspuns cu detalii. Totuși, pentru un test cu adevărat realist, există alte două variabile pe care în prezent nu le pot simula. Unu: Cel puțin 20 de postări de la fiecare utilizator. În prezent, am testat doar cu aproximativ 50 de postări între 2-3 utilizatori. Doi: Un trafic mare. Aceste două variabile ar afecta și ele rezultatele obținute în condiții reale.

Mai analizam sugestia ta de a randomiza următoarele. Totuși, nu am reușit să înțeleg fragmentele de cod pe care le-ai scris. Un snippet complet ar fi mai util. Iată ce am făcut pentru a face primii 50 de utilizatori să îi urmărească pe toți ceilalți: for ($j = 2; $j <= 52; $j++) { for ($i = ($j + 1); $i <= 47000; $i++) { $rows_affected = $wpdb->insert($table_name, array( 'leader_id' => $i, 'follower_id' => $j)); } }
. Un simplu loop for imbricat.

Despre trafic) Ai putea folosi cele mai recente versiuni de Apache, Nginx, minificare, caching, compresie etc. Fiecare ar avea un impact asupra rezultatelor. Dar noi urmărim rezultate într-un mediu local, simplu (fără plugin-uri, temă implicită) care poate fi cu adevărat cântărit.

Ai primit o notificare pentru comentariul meu legat de randomizare? Cred că am uitat să menționez handle-ul tău în comentariu. Oricum, presupun că proprietarul postării este notificat. Aștept input-ul tău înainte să continui.

Notă: Acest răspuns este pentru a evita discuții extinse în comentarii
Iată codul OP din comentarii, pentru a adăuga primul set de utilizatori de test. Trebuie modificat pentru un exemplu din lumea reală.
for ( $j = 2; $j <= 52; $j++ ) { for ( $i = ($j + 1); $i <= 47000; $i++ ) { $rows_affected = $wpdb->insert( $table_name, array( 'leader_id' => $i, 'follower_id' => $j ) ); } }
OP Despre Test Pentru asta am adăugat aproximativ 47K de utilizatori dintr-un fișier csv. Ulterior, am rulat un for loop pentru a face primii 45 de utilizatori să îi urmărească pe toți ceilalți utilizatori.
- Asta a rezultat în 3.704.951 de înregistrări salvate în tabela mea personalizată.
- Inițial, soluția lui @m0r7if3r mi-a dat un timp de interogare de 95 secunde, care a scăzut la 0,020 ms după activarea indexării pe coloana leader_id. Memoria PHP totală consumată a fost de aproximativ 20MB.
- Pe de altă parte, soluția ta a durat între 2 și 5 secunde pentru interogare cu indexarea ACTIVATĂ. Memoria PHP totală consumată a fost de aproximativ 117MB.
Răspunsul meu la acest ↑ test:
un test mai "realist": Fiecare utilizator să urmărească un
$leader_amount = rand( 0, 5 );
și apoi să adauge numărul de$leader_amount x $random_ids = rand( 0, 47000 );
pentru fiecare utilizator. Până acum știm următoarele: Soluția mea ar fi extrem de proastă dacă un utilizator îi urmărește pe toți ceilalți. În plus: Trebuie să arăți cum ai făcut testul și unde exact ai adăugat cronometrele.Trebuie să menționez și că ↑ măsurarea timpului de mai sus nu poate fi cu adevărat măsurată, deoarece ar include și timpul necesar pentru calculul buclei. Mai bine ar fi să parcurgi setul rezultat de ID-uri într-o a doua buclă.
continuare aici
