0
votes

Comment prétendre enregistrer des enregistrements comme des numéros de ligne d'un éditeur

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 |


1 commentaires

Dupliqué possible de Stackoverflow.com/q/9536262 ?


4 Réponses :


2
votes

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 Row_Number () . 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 est à moi). XXX

Vous Peut créer une vue pour vous épargner l'effort de taper la fonction de fenêtre répétitible: xxx

Notez que commande 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 les requêtes ci-dessus.


9 commentaires

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 , au lieu de votre numéro d'enregistrement ? 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 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.



2
votes

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 
;


0 commentaires

0
votes

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 avant strong> un déclencheur la procédure est appelée. De cette façon, la vérification de la profondeur de déclenchement = 0 fait l'affaire pour empêcher une procédure de déclenchement d'appeler un autre. Par exemple. P>

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
\.


0 commentaires

1
votes

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 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>

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 ord code> colonne. Ensuite, la toute première ligne que vous insérez doit obtenir p> xxx pré>

pour insérer une nouvelle ligne entre deux lignes existantes avec des numéros de commande 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


0 commentaires