Il s'agit de stocker des commandes dans une base de données au lieu d'un fichier texte ou d'une feuille de calcul. Je cherche une solution, qui imite le comportement des numéros de ligne dans un éditeur de texte.
E.g. Compte tenu du tableau ci-dessous avec la colonne "Commander" comme clé primaire, lorsque je supprime la deuxième ligne (commande = 2), je me retrouverais avec une lacune dans la colonne de commande (1, 3), qui doit être corrigée à (1, 2) P>
| Order | Command | | Order | Command | |-------|--------------| |-------|--------------| | 1 | CAM - ON | ==> | 2 | CAM - ON | | 2 | Turn left | | 1 | Turn left | | 3 | Take picture | | 3 | Take picture |
4 Réponses :
Ne vous inquiétez pas avec les déclencheurs: ce que vous recherchez est une valeur dérivée, que vous pouvez calculer à la volée en cas de besoin, en utilisant Vous Peut créer une vue pour vous épargner l'effort de taper la fonction de fenêtre répétitible: p> Notez que Row_Number () code>. En outre, il ne s'agit généralement pas d'une bonne pratique de jouer avec la clé primaire d'une table (qui est ce que votre
commande code> est à moi).
commande code> n'est pas un choix sage pour un nom de colonne , puisqu'il s'affronte avec un mot réservé. Je l'ai renommé à
ord code> les requêtes ci-dessus. P> p>
Merci, mais cela ne fonctionnait pas si bien. Lorsqu'il n'y a pas de clé primaire, je peux me retrouver avec 5 lignes avec ORD = 1. Lorsque je veux alors insréter un nouvel enregistrement quelque part entre ces 5, je ne peux plus choisir où exactement.
Pourquoi n'y a-t-il pas de clé primaire?
@Joop: Cela fait partie de la difficulté. Si j'ai 5 enregistrements et que l'ORD de colonne est la clé principale, je ne peux pas insérer un nouveau record avec Dites, ORD = 2, car il existe déjà. Les deux voies, je n'ai pas encore de solution complète.
Vous ne voulez pas faire cela. Jamais sous les opérations normales change le PK. C'est le composant principal de maintenir vos données correctes. Par exemple: Supposons que vous obteniez tout cela a tout fonctionné (douteux au mieux, mais vous le faites). Maintenant, le client qui a placé la commande 3 a un problème et raconte que. L'une des commandes 3 n'existe plus ou pire, une autre commande a cet identifiant? Maintenant que faites-vous? Vous dites que vous souhaitez imiter les numéros de ligne dans un éditeur, mais ces numéros de ligne n'existent pas réellement, ils sont générés à la volée, mais les données qu'aucune donnée ne change quand elles le font.
@Belayer: En effet, je n'ai pas besoin d'être la clé principale. Il devrait être unique, commencer à 1 et n'avoir aucune lacune. Mais je dois stocker les informations, dans quelle commande les commandes sont envoyées à mon robot. C'est la raison pour laquelle ils doivent exister et ne peuvent être générés à la volée.
Pourquoi ne pas avoir un horodatage est arrivé_at code>, au lieu de votre numéro d'enregistrement i>? Beaucoup de lacunes et commandes complètes!
@Joop: C'était en fait mon approche précédente. Mais j'ai été critiqué par les experts qui livrent les commandes et ils ont raison. Ils ne peuvent pas toujours fournir du timing. Par exemple. Vous ne pouvez pas estimer le temps d'exposition d'une caméra en mode d'exposition automatique. Pourrait être 1Micro deuxième ou 50 secondes. De plus, nous testons toujours une procédure avec de la vraie HW. Après avoir testé, nous obtenons le calendrier comme une mesure, ce qui est beaucoup mieux que les estimations de toute façon. Ainsi, les estimations du timing se sont avérées être une perte de temps dans notre flux de travail.
Vous ne voulez pas d'horodatage réel pour le temps en soi, vous ne voulez que votre ordre absolu. Mais imho c'est une très mauvaise idée de Changer de i> certains enregistrements car un autre enregistrement arrive à modifier la valeur.
Je ne vois pas pourquoi "pas de lacunes" est une exigence difficile. Si vous pouvez commander par la valeur de cette colonne, vous pouvez toujours obtenir vos valeurs consécutives en utilisant Rownum dans la sélection. J'avais deuxièmes l'idée d'utiliser des horodatages ou peut-être laisser de grandes lacunes pour l'insertion sans renuméroter d'autres rangées.
Vous n'avez pas vraiment besoin d'une gâchette pour y parvenir. Il suffit de faire la clé principale repérable, et vous pouvez supprimer et renumber dans une seule instruction:
with to_move(old_no, new_no) as ( values (2,4) ), move_row as ( update data set order_no = new_no from to_move where order_no = old_no ) update data set order_no = order_no - 1 from to_move where order_no > old_no and order_no <= new_no ;
Ma question était basée sur l'hypothèse qu'un déclencheur ne peut pas être désactivé alors qu'une procédure de déclenchement est exécutée.
tandis que cela reste vrai, Cette question de sorte que la question montre que vous pouvez réellement utiliser la fonction pg_trigger_depth () code> pour vérifier la profondeur de déclenchement
CREATE TABLE commands (
step integer primary key NOT NULL DEFAULT 1 CONSTRAINT positive_order CHECK (step >= 0),
command character varying
);
ALTER TABLE commands OWNER TO kagan;
CREATE OR REPLACE FUNCTION update_commands()
RETURNS TRIGGER AS $$
DECLARE max_step integer;
DECLARE rec RECORD;
BEGIN
select max(step) into max_step from commands;
if NEW.step is null then RAISE EXCEPTION 'step must have a value'; end if;
if NEW.step < 1 then RAISE EXCEPTION 'step (%) must be >= 1', NEW.step; end if;
if NEW.step > max_step then RAISE EXCEPTION 'step (%) must be <= max(step) (%)', NEW.step, max_step; end if;
-- Temporarily, move the current record at the old position "out of the way"
-- Don't forget the other columns
UPDATE commands set step = 0, command = NEW.command where step = OLD.step;
if NEW.step > OLD.step then
FOR rec IN
select step from commands
where step > OLD.step and step <= NEW.step
order by step ASC
LOOP
UPDATE commands set step = step - 1 where step = rec.step;
END LOOP;
else
FOR rec IN
select step from commands
where step >= NEW.step and step < OLD.step
order by step DESC
LOOP
UPDATE commands set step = step + 1 where step = rec.step;
END LOOP;
end if;
-- Put the current row back to the new position
UPDATE commands set step = NEW.step where step = 0;
RETURN NULL; -- DO NOT PROCEED
END;
$$ language 'plpgsql';
CREATE OR REPLACE FUNCTION insert_commands()
RETURNS TRIGGER AS $$
DECLARE max_step integer;
DECLARE rec RECORD;
BEGIN
if NEW.step < 1 then RAISE EXCEPTION 'step (%) must be >= 1)', NEW.step; end if;
select max(step) into max_step from commands;
if max_step is null then
NEW.step = 1;
elsif NEW.step > max_step + 1 then
RAISE EXCEPTION 'step (%) must be <= max(step) + 1 (%)', NEW.step, max_step + 1;
else
FOR rec IN select step from commands where step >= NEW.step order by step DESC LOOP
UPDATE commands set step = step + 1 where step = rec.step;
END LOOP;
end if;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE OR REPLACE FUNCTION delete_commands()
RETURNS TRIGGER AS $$
DECLARE rec RECORD;
BEGIN
FOR rec IN select step from commands where step > OLD.step order by step ASC LOOP
UPDATE commands set step = step - 1 where step = rec.step;
END LOOP;
RETURN OLD;
END;
$$ language 'plpgsql';
CREATE TRIGGER insert_commands_trigger BEFORE INSERT ON commands FOR EACH ROW WHEN (pg_trigger_depth() = 0) EXECUTE PROCEDURE insert_commands();
CREATE TRIGGER delete_commands_trigger AFTER DELETE ON commands FOR EACH ROW WHEN (pg_trigger_depth() = 0) EXECUTE PROCEDURE delete_commands();
CREATE TRIGGER update_commands_trigger BEFORE UPDATE ON Commands FOR EACH ROW WHEN (pg_trigger_depth() = 0) EXECUTE PROCEDURE update_commands();
COPY commands (step, command) FROM stdin;
1 CAM - ON
2 Turn left
3 Take picture
\.
Vos données ne sont pas vraiment un bon match pour une base de données relationnelle - si c'était moi, et que le nombre de commandes n'est pas trop grand, je ne le stockerais pas comme un clob JSON ou XML.
mais si vous doit utiliser le stockage relationnel, je pense que devoir mettre à jour la moitié moyenne de toutes les lignes après chaque insertion n'est pas un bon design. P>
Je suggérerais de ne pas stocker le nombre réel, mais il suffit d'avoir la colonne Pour insérer de nouvelles lignes sans avoir à renuméroter les existants, laissez "trous"
Dans la numérotation: p> Soit min et max être le nombre minimum et maximum que vous souhaitez utiliser dans le
pour insérer une nouvelle ligne entre deux lignes existantes avec des numéros de commande
ord code>
définir un ordre relatif. Comme @gmb a déjà écrit , vous pouvez ensuite utiliser
Row_Number CODE> Pour obtenir vos numéros consécutifs à partir de 1. P>
ord code> colonne. Ensuite, la toute première ligne que vous insérez doit obtenir p>
o1 code> et
o2 code>, utilisez p>
let next = SELECT MIN(ord) FROM commands WHERE ord > :o1
if next IS NULL then
if o1 == MAX then panic_or_renumber
if o1 == MAX - 1 then MAX
else (o1 + MAX) / 2
else
if next == o1 + 1 then panic_or_renumber
else (o1 + next) / 2
Dupliqué possible de Stackoverflow.com/q/9536262 ?