Effectivement, dans ton cas, une vue sera mal adaptée. En revanche, pourquoi "SELECT"-tu ta procédure? Selon ce que tu décris, tu as en fait une procédure de calcul des trajets, et tu n'as qu'à l'appeler? En lui passant éventuellement en paramètre le "filtre des lignes" sur lesquelles elle porte.
Schématiquement:
CREATE PROCEDURE calcul_passagers(IN idPlayer INT UNSIGNED) BEGIN
calculs bla bla ... WHERE (player.id = idPlayer OR idPlayer IS NULL)
END
(mais je ne sais pas si c'est très clair?)
• La fonction va être appelée pour chaque ligne du resultset renvoyé par le SELECT, donc si la fonction contient des sous-requêtes, elle seront exécutées pour chacune de ces lignes, ce qui peut vite être trèèèès lent (mais test d'abord: on se fait souvent une fausse idée de cette lenteur, parce qu'on parle en fait de qq ms à peine, donc si t'as quelques milliers de lignes, pour une requête lancée par un CRON, c'est amplement raisonnable). Du coup, entre une fonction faisant des sous-requêtes et une reformulation qui évite cela, la reformulation sera nettement plus rapide (à confirmer quand même). Une idée d'optimisation consiste alors à utiliser un LEFT JOIN pour récupérer les infos nécessaires, et passer les valeurs de ce LEFT à cette fonction: SELECT myFunc(t.x, u.x) FROM u LEFT JOIN t ON t.id_u = u.id au lieu de SELECT myFunc(t.x) FROM u avec myFunc(t.id AS tid, t.x AS tx) { SELECT u.x FROM u WHERE u.id = tid } (pseudo-code)
• Techniquement, les INSERT dans la fonction devraient être faisable, mais tu te diriges vers un bordel sans nom, car le fait d'appeler la fonction va altérer l'état des données du SQL. Perso, je trouve bien plus clair d'avoir des fonctions qui ne font *que* de la lecture (jamais d'écriture SQL) et laisser les écritures au procédures uniquement.
Edit: Pour info, c'est cadeau (vu qu'il n'est plus actif), voici la procédure de simulation d'ECLERD. Elle était lancée périodiquement, toutes les 24H je crois, et peut-être bien à chaque page en y réfléchissant... 0 temps de latence pour un jeu qui devait compter 100-200 cases
Ca peut te permettre de visualiser comment faire la tienne.
Schématiquement:
CREATE PROCEDURE calcul_passagers(IN idPlayer INT UNSIGNED) BEGIN
calculs bla bla ... WHERE (player.id = idPlayer OR idPlayer IS NULL)
END
(mais je ne sais pas si c'est très clair?)
• La fonction va être appelée pour chaque ligne du resultset renvoyé par le SELECT, donc si la fonction contient des sous-requêtes, elle seront exécutées pour chacune de ces lignes, ce qui peut vite être trèèèès lent (mais test d'abord: on se fait souvent une fausse idée de cette lenteur, parce qu'on parle en fait de qq ms à peine, donc si t'as quelques milliers de lignes, pour une requête lancée par un CRON, c'est amplement raisonnable). Du coup, entre une fonction faisant des sous-requêtes et une reformulation qui évite cela, la reformulation sera nettement plus rapide (à confirmer quand même). Une idée d'optimisation consiste alors à utiliser un LEFT JOIN pour récupérer les infos nécessaires, et passer les valeurs de ce LEFT à cette fonction: SELECT myFunc(t.x, u.x) FROM u LEFT JOIN t ON t.id_u = u.id au lieu de SELECT myFunc(t.x) FROM u avec myFunc(t.id AS tid, t.x AS tx) { SELECT u.x FROM u WHERE u.id = tid } (pseudo-code)
• Techniquement, les INSERT dans la fonction devraient être faisable, mais tu te diriges vers un bordel sans nom, car le fait d'appeler la fonction va altérer l'état des données du SQL. Perso, je trouve bien plus clair d'avoir des fonctions qui ne font *que* de la lecture (jamais d'écriture SQL) et laisser les écritures au procédures uniquement.
Edit: Pour info, c'est cadeau (vu qu'il n'est plus actif), voici la procédure de simulation d'ECLERD. Elle était lancée périodiquement, toutes les 24H je crois, et peut-être bien à chaque page en y réfléchissant... 0 temps de latence pour un jeu qui devait compter 100-200 cases
Code :
DROP PROCEDURE IF EXISTS `simulation`;/*$$*/
CREATE PROCEDURE `simulation`()
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY INVOKER
COMMENT ''
BEGIN
DECLARE idCase INT UNSIGNED;
DECLARE elect CURSOR FOR
SELECT m.id
FROM map AS m
WHERE m.`date_election` <= NOW()
;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET @cursorEnd := TRUE;
-- Player's popularity, by laws
-- @TODO This is the main part of the game, so these equations must be well thought and done
UPDATE map m
INNER JOIN laws AS l ON l.id_player = m.id_player
SET
m.insecurite = LEAST(1, GREATEST(0, 1.0*achat_armes_autorise-0.4*etat_urgence-0.15*((1+censure)*(1+propagande))+1.0*(1-m.popularite))),
m.environnement = LEAST(1, GREATEST(0, 0.5-0.25*(1-CAST(plan_ville_verte AS SIGNED)*2))),
m.education = LEAST(1, GREATEST(0, 0.8*scolarisation_obligatoire*(1+0.2*etudes_superieures_obligatoires))),
m.popularite = LEAST(1, GREATEST(0, 0.4*m.environnement+0.6*m.education-0.5*m.insecurite))
;
-- Election's score
UPDATE election_candidat AS ec
SET ec.score = IF(
ec.id_player IS NULL,
0.5,
IFNULL(
(SELECT m.popularite
FROM map AS m
WHERE m.id = ec.id_case AND m.id_player = ec.id_player),
IFNULL(
(SELECT SUM(m.popularite)/SUM(1.5)
FROM map AS m
WHERE m.id_player = ec.id_player),
0.25)))
;
-- Proceed the elections
OPEN elect;
electLoop: LOOP
FETCH elect INTO idCase;
IF (@cursorEnd) THEN
LEAVE electLoop;
END IF;
SET @vainqueur := (SELECT ec.id_player FROM election_candidat AS ec WHERE ec.id_case = idCase ORDER BY ec.score DESC LIMIT 1);
-- Add 1 day to the date election untill we're in the future
-- @TODO Find better (there certainly is, like by counting number of days between today and this election's date)
SET @dateElection := (SELECT m.`date_election` FROM map AS m WHERE m.id = idCase);
WHILE (@dateElection < NOW()) DO
SET @dateElection := DATE_ADD(@dateElection, INTERVAL 1 DAY);
END WHILE;
-- Save that next election date in the DB
UPDATE map AS m
SET m.id_player = @vainqueur,
m.`date_election` = @dateElection
WHERE m.id = idCase;
DELETE FROM election_candidat
WHERE id_case = idCase AND id_player IS NOT NULL AND (@vainqueur IS NULL OR id_player != @vainqueur)
;
END LOOP;
END$$
Ca peut te permettre de visualiser comment faire la tienne.