Critère {pagination X} et requête SQL #5714

Open
opened 2 months ago by marcimat · 4 comments
Owner

Le critère {pagination 10} utilise une requête SQL qui retourne tous les résultats sans pagination. C’est ensuite avec l’iterateur que l’on cherche le début des éléments de la liste ->seek(...).

Lorsqu’une table contient de nombreuses entrées, il semble que ça soit très pénalisant comme comportement.

Alternativement, je suppose, il faudrait pouvoir dire que {pagination 10} crée 2 requêtes :

  • 1 avec LIMIT {10} OFFSET {pagenumber * recordsperpage}
  • 1 avec SELECT COUNT(*) as nb_items ... pour que #PAGINATION et autre #GRAND_TOTAL connaisse le nombre total d’éléments sans pagination.

Lu sur l’IRC donc:

<Guest48>
I was debugging a squelette with pagination and I have noticed that spip
in the query always get the whole table without limit
how is that possible?

Ex:

SELECT articles.date, articles.id_article, articles.date_modif, articles.titre, articles.descriptif, articles.texte, articles.lang
FROM `dbname`.spip_articles AS `articles`
WHERE (articles.statut = 'publie')
AND ((articles.id_rubrique  IN (1)))
ORDER BY articles.date DESC;
<BOUCLE_rubrica(ARTICLES){branche}{!par date}{doublons}{pagination 10}>

I mean I thought it should apply a limit somehow

is that pretty inefficient? I mean suppose that I want just the first 10 records and the query return 100k record, spip will get that from db in memory and return just the first 10?

is there anyway to implement pagination and have the page links without having spip download
but it seems what happen behind the scene is that the query download all records even if I then display just 10
if I have a rubrique with 100k items and I want a pagination with 10 items per page, the query load in memory (on php) all the records anyway and the just extract the items of the page
I understood correctly?
instead of doing something like SELECT column FROM table LIMIT {10} OFFSET {pagenumber * recordsperpage};
i have several websites with up to 200k articles each

Le critère `{pagination 10}` utilise une requête SQL qui retourne tous les résultats sans pagination. C’est ensuite avec l’iterateur que l’on cherche le début des éléments de la liste `->seek(...)`. Lorsqu’une table contient de nombreuses entrées, il semble que ça soit très pénalisant comme comportement. Alternativement, je suppose, il faudrait pouvoir dire que `{pagination 10}` crée 2 requêtes : - 1 avec `LIMIT {10} OFFSET {pagenumber * recordsperpage}` - 1 avec `SELECT COUNT(*) as nb_items ...` pour que `#PAGINATION` et autre `#GRAND_TOTAL` connaisse le nombre total d’éléments sans pagination. --- Lu sur l’IRC donc: > `<Guest48>` > I was debugging a squelette with pagination and I have noticed that spip > in the query always get the whole table without limit > how is that possible? Ex: ```sql SELECT articles.date, articles.id_article, articles.date_modif, articles.titre, articles.descriptif, articles.texte, articles.lang FROM `dbname`.spip_articles AS `articles` WHERE (articles.statut = 'publie') AND ((articles.id_rubrique IN (1))) ORDER BY articles.date DESC; ``` ```html <BOUCLE_rubrica(ARTICLES){branche}{!par date}{doublons}{pagination 10}> ``` > I mean I thought it should apply a limit somehow > is that pretty inefficient? I mean suppose that I want just the first 10 records and the query return 100k record, spip will get that from db in memory and return just the first 10? > is there anyway to implement pagination and have the page links without having spip download > but it seems what happen behind the scene is that the query download all records even if I then display just 10 > if I have a rubrique with 100k items and I want a pagination with 10 items per page, the query load in memory (on php) all the records anyway and the just extract the items of the page > I understood correctly? > instead of doing something like `SELECT column FROM table LIMIT {10} OFFSET {pagenumber * recordsperpage};` > i have several websites with up to 200k articles each
marcimat added the
base de données
amélioration
labels 2 months ago
Poster
Owner

En tant qu’expérience de pensée, ce critère gère une une limite dans la requête SQL.

<h1>#SQUELETTE</h1>

<BOUCLE_art(ARTICLES){pagination 2}>
	- #ID_ARTICLE : #TITRE<br>
</BOUCLE_art>
	pagination: #PAGINATION<br>
	total_boucle: #TOTAL_BOUCLE<br>
	grand_total: #GRAND_TOTAL<br>
</B_art>
<hr>

<BOUCLE_new(ARTICLES){pagination_limit 2}>
	- #ID_ARTICLE : #TITRE<br>
</BOUCLE_new>
	pagination: \#PAGINATION<br>
	total_boucle: #TOTAL_BOUCLE<br>
	grand_total: #GRAND_TOTAL<br>
</B_new>
function critere_pagination_limit_dist($idb, &$boucles, $crit) {

	$boucle = &$boucles[$idb];
	// definition de la taille de la page
	$pas = "''";
	if (isset($crit->param[0][0])) {
		$pas = calculer_liste([$crit->param[0][0]], $idb, $boucles, $boucle->id_parent);
	}

	if (!preg_match(_CODE_QUOTE, (string) $pas, $r)) {
		$pas = "((\$a = intval($pas)) ? \$a : 10)";
	} else {
		$r = (int) $r[2];
		$pas = (string) ($r ?: 10);
	}

	// Calcul d'un nommage spécifique de la pagination si précisé.
	// Syntaxe {pagination_limit 20, nom}
	$type = "'$idb'";
	if (isset($crit->param[0][1])) {
		$type = calculer_liste([$crit->param[0][1]], $idb, $boucles, $boucle->id_parent);
	}

	$debut = ($type[0] !== "'") ? "'debut'.$type" : ("'debut" . substr((string) $type, 1));
	$boucle->modificateur['debut_nom'] = $type;

	# hack `intval` (changes compilo behaviour on limit)
	$boucle->limit = "intval($pas * (\$Pile[0][$debut] ?? 0)) . ', ' . $pas";
}

Évidemment, juste avec cela, #PAGINATION (en erreur car le bon critère est absent), et #GRAND_TOTAL sont alors faux.

En tant qu’expérience de pensée, ce critère gère une une limite dans la requête SQL. ```html <h1>#SQUELETTE</h1> <BOUCLE_art(ARTICLES){pagination 2}> - #ID_ARTICLE : #TITRE<br> </BOUCLE_art> pagination: #PAGINATION<br> total_boucle: #TOTAL_BOUCLE<br> grand_total: #GRAND_TOTAL<br> </B_art> <hr> <BOUCLE_new(ARTICLES){pagination_limit 2}> - #ID_ARTICLE : #TITRE<br> </BOUCLE_new> pagination: \#PAGINATION<br> total_boucle: #TOTAL_BOUCLE<br> grand_total: #GRAND_TOTAL<br> </B_new> ``` ```php function critere_pagination_limit_dist($idb, &$boucles, $crit) { $boucle = &$boucles[$idb]; // definition de la taille de la page $pas = "''"; if (isset($crit->param[0][0])) { $pas = calculer_liste([$crit->param[0][0]], $idb, $boucles, $boucle->id_parent); } if (!preg_match(_CODE_QUOTE, (string) $pas, $r)) { $pas = "((\$a = intval($pas)) ? \$a : 10)"; } else { $r = (int) $r[2]; $pas = (string) ($r ?: 10); } // Calcul d'un nommage spécifique de la pagination si précisé. // Syntaxe {pagination_limit 20, nom} $type = "'$idb'"; if (isset($crit->param[0][1])) { $type = calculer_liste([$crit->param[0][1]], $idb, $boucles, $boucle->id_parent); } $debut = ($type[0] !== "'") ? "'debut'.$type" : ("'debut" . substr((string) $type, 1)); $boucle->modificateur['debut_nom'] = $type; # hack `intval` (changes compilo behaviour on limit) $boucle->limit = "intval($pas * (\$Pile[0][$debut] ?? 0)) . ', ' . $pas"; } ``` Évidemment, juste avec cela, `#PAGINATION` (en erreur car le bon critère est absent), et `#GRAND_TOTAL` sont alors faux.
Owner

oui c'est pas simple, je me rappelle qu'à l'époque j'avais essayé de chercher dans ce sens là sans y parvenir, et jusqu'ici on a vécu comme ça.

La piste simple serait d'avoir un LIMIT quand même pour des raisons de taille de bases, mais c'est aussi gérable en mettant un LIMIT sur la boucle
<BOUCLE(TRUC){pagination 10}{limit 0,100000}> (astuce que j'ai utilisé sur certaines boucles paginées qui débordaient en mémoire).

Alternativement gérer proprement la requête en effet, ce qui est peut-être possible en refactorant la partie iterateur pour séparer le count du requetage ?

Attention aussi, il y a la feature debut_truc=@10 qui permet de se placer à la page qui contient le id_truc=10 qu'il faut continuer à gérer. Je ne me rappelle plus comment c'est implémenté en détail, mais il me semble que ce cas repose sur le fait qu'on ait bien la requête en entier, pour rechercher la bonne page...

oui c'est pas simple, je me rappelle qu'à l'époque j'avais essayé de chercher dans ce sens là sans y parvenir, et jusqu'ici on a vécu comme ça. La piste simple serait d'avoir un LIMIT quand même pour des raisons de taille de bases, mais c'est aussi gérable en mettant un LIMIT sur la boucle `<BOUCLE(TRUC){pagination 10}{limit 0,100000}>` (astuce que j'ai utilisé sur certaines boucles paginées qui débordaient en mémoire). Alternativement gérer proprement la requête en effet, ce qui est peut-être possible en refactorant la partie iterateur pour séparer le count du requetage ? Attention aussi, il y a la feature `debut_truc=@10` qui permet de se placer à la page qui contient le `id_truc=10` qu'il faut continuer à gérer. Je ne me rappelle plus comment c'est implémenté en détail, mais il me semble que ce cas repose sur le fait qu'on ait bien la requête en entier, pour rechercher la bonne page...
JLuc commented 2 weeks ago

Tout nouveau critère apporte une nouvelle fonctionnalité et modifie le fonctionnement d'une boucle. Une boucle avec ce nouveau critère, c'est une autre boucle et il est normal (ou acceptable) que ce nouveau critère modifie le sens de la boucle et le résultat de certaines balises, voire rende certaines fonctionnalités inopérantes car inadaptées. La doc doit alors le préciser.

Alors faut il essayer d'obtenir avec ce nouveau critère ce qu'une boucle produisait avec un autre critère ? La course au "faire comme si que" produit parfois de chouettes résultats automagiques, mais c'est parfois un marathon sans fin pour corriger les cas particuliers énervants et les oublis frustrants dans les coins... et parfois sans ultime satisfaction.

Tout nouveau critère apporte une nouvelle fonctionnalité et modifie le fonctionnement d'une boucle. Une boucle avec ce nouveau critère, c'est une autre boucle et il est normal (ou acceptable) que ce nouveau critère modifie le sens de la boucle et le résultat de certaines balises, voire rende certaines fonctionnalités inopérantes car inadaptées. La doc doit alors le préciser. Alors faut il essayer d'obtenir avec ce nouveau critère ce qu'une boucle produisait avec un autre critère ? La course au "faire comme si que" produit parfois de chouettes résultats automagiques, mais c'est parfois un marathon sans fin pour corriger les cas particuliers énervants et les oublis frustrants dans les coins... et parfois sans ultime satisfaction.
cerdic commented 1 week ago
Owner

en fait en y revenant je pense que la conclusion avait été "bah on peut ajouter un limit manuellement si nécessaire, donc laissons comme ça". Et de fait.

en fait en y revenant je pense que la conclusion avait été "bah on peut ajouter un limit manuellement si nécessaire, donc laissons comme ça". Et de fait.
Sign in to join this conversation.
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: spip/spip#5714
Loading…
There is no content yet.