web-dev-qa-db-fra.com

Comment mettre à jour la valeur de la colonne en ajoutant "1" en fonction d'une condition?

J'ai le tableau suivant fields:

+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+------------------------------+-------------+------------------+
| field_id | form_id | form_section_id | is_required | grid_id | is_base_grid | field_type_id | field_seq |          field_name          | field_class | field_class_data |
+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+------------------------------+-------------+------------------+
|   220481 |    9926 | NULL            |           0 | NULL    | NULL         |             4 |        28 | Test                         | NULL        | NULL             |
|   281863 |    9926 | NULL            |           0 | NULL    | NULL         |            10 |        29 | insert after yes no question | NULL        | NULL             |
|   220496 |    9926 | NULL            |           0 | 11      | 1            |             5 |        30 | test                         | NULL        | NULL             |
|   249234 |    9926 | NULL            |           0 | 12      | 1            |             5 |        32 |                              | NULL        | NULL             |
|   279877 |    9926 | NULL            |           0 | NULL    | NULL         |             4 |        33 | Test Text Questions          | NULL        | NULL             |
|   281860 |    9926 | NULL            |           0 | NULL    | NULL         |            10 |        34 | Something                    | NULL        | NULL             |
|   281914 |    9926 | NULL            |           0 | 23      | 1            |             5 |        35 | sssss                        | NULL        | NULL             |
|   281960 |    9926 | NULL            |           0 | 38      | 1            |             5 |        36 | yuyuyu                       | NULL        | NULL             |
|   281972 |    9926 | NULL            |           0 | 40      | 1            |             5 |        40 | ttttt                        | NULL        | NULL             |
+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+------------------------------+-------------+------------------+

Comme vous pouvez le voir, il existe deux field_seq avec la même valeur 36 dans ce cas.

Supposons que j'insère une nouvelle ligne juste après field_id=281960 et le field_seq pour cette nouvelle ligne arrive comme 36.

J'ai besoin de construire une requête ou même une procédure stockée où je peux savoir s'il y a une ligne avec field_seq égal ou supérieur à 36 et si oui, mettez à jour la valeur de field_seq à la valeur actuelle plus 1.

Ex:

INSERT INTO `fields` VALUES(9999, 9926, NULL, 0, 41, 1, 5, 36, 'lllll', NULL, NULL);

Ayant cela voir les cas possibles ci-dessous (avec des exemples après chacun):

Cas 1: la ligne avec field_seq = 36 existe déjà sur la table

  • Conservez les données INSERT telles qu'elles deviendront les field_seq=36 nouvelle ligne
  • Mettez à jour la valeur de la ligne du tableau field_seq=current+1 qui deviendra 37
  • S'il y a un 37 déjà puis répétez l'étape précédente jusqu'à ce qu'il n'y ait plus de répétition field_seq

Avant:

+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+-------------+-------------+------------------+
| field_id | form_id | form_section_id | is_required | grid_id | is_base_grid | field_type_id | field_seq | field_nanme | field_class | field_class_data |
+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+-------------+-------------+------------------+
|   281914 |    9926 | NULL            |           0 |      23 |            1 |             5 |        32 | sssss       | NULL        | NULL             |
|   281972 |    9926 | NULL            |           0 |      40 |            1 |             5 |        36 | ttttt       | NULL        | NULL             |
|   281960 |    9926 | NULL            |           0 |      38 |            1 |             5 |        37 | yuyuyu      | NULL        | NULL             |
|   281978 |    9926 | NULL            |           0 |      38 |            1 |             5 |        38 | vvvvv       | NULL        | NULL             |
+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+-------------+-------------+------------------+

Après:

+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+---------------------+-------------+------------------+
| field_id | form_id | form_section_id | is_required | grid_id | is_base_grid | field_type_id | field_seq |     field_nanme     | field_class | field_class_data |
+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+---------------------+-------------+------------------+
|   281914 |    9926 | NULL            |           0 | 23      | 1            |             5 |        32 | sssss               | NULL        | NULL             |
|     9999 |    9926 | NULL            |           0 |    41   |   1          |             5 |        36 | lllll               | NULL        | NULL             | => new row inserted here
|   281972 |    9926 | NULL            |           0 | 40      | 1            |             5 |        37 | ttttt               | NULL        | NULL             | => this was 36 now is updated to 37
|   281960 |    9926 | NULL            |           0 | 38      | 1            |             5 |        38 | yuyuyu              | NULL        | NULL             | => this was 37 now is updated to 38
|   281978 |    9926 | NULL            |           0 | 38      | 1            |             5 |        39 | vvvvv               | NULL        | NULL             | => this was 38 now is updated to 39
|   220524 |    9926 | NULL            |           0 | NULL    | NULL         |             5 |        40 | Patient Information | NULL        | NULL             | => we don't care about this cause there is room for one more, if one insert makes the rows above become 40 then this needs to be updated to 41
+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+---------------------+-------------+------------------+

Cas 2: ligne avec field_seq = 36 existe déjà sur la table mais à côté field_seq est supérieur à 37

  • Conservez les données INSERT telles qu'elles deviendront les field_seq=36 nouvelle ligne
  • Mettez à jour la valeur de la ligne du tableau field_seq=current+1 qui deviendra 37
  • Dans ce cas, nous n'avons pas besoin de continuer la mise à jour car il y a suffisamment d'espace pour insérer quelques lignes supplémentaires avant qu'elles ne deviennent les mêmes field_seq

Avant:

+----------+---------+-----------------+-------------+---------+---------------+---------------+-----------+------------+-------------+
| field_id | form_id | form_section_id | is_required | grid_id | is_base_grid  | field_type_id | field_seq | field_name | field_class |
+----------+---------+-----------------+-------------+---------+---------------+---------------+-----------+------------+-------------+
|   281914 |    9926 | NULL            |           0 |      23 |             1 |             5 |        32 | sssss      | NULL        |
|   281972 |    9926 | NULL            |           0 |      40 |             1 |             5 |        36 | ttttt      | NULL        |
|   281972 |    9926 | NULL            |           0 |      40 |             1 |             5 |        40 | ooooo      | NULL        |
+----------+---------+-----------------+-------------+---------+---------------+---------------+-----------+------------+-------------+

Après:

+----------+---------+-----------------+-------------+---------+---------------+---------------+-----------+------------+-------------+
| field_id | form_id | form_section_id | is_required | grid_id | is_base_grid  | field_type_id | field_seq | field_name | field_class |
+----------+---------+-----------------+-------------+---------+---------------+---------------+-----------+------------+-------------+
|   281914 |    9926 | NULL            |           0 |      23 |             1 |             5 |        32 | sssss      | NULL        |
|   281972 |    9926 | NULL            |           0 |      41 |             1 |             5 |        36 | lllll      | NULL        | => new row inserted here
|   281972 |    9926 | NULL            |           0 |      40 |             1 |             5 |        37 | ttttt      | NULL        | => previous row with field_seq=36 was updated to 37
|   281972 |    9926 | NULL            |           0 |      40 |             1 |             5 |        40 | ooooo      | NULL        | => nothing happen to this one since there is room for more
+----------+---------+-----------------+-------------+---------+---------------+---------------+-----------+------------+-------------+

J'utilise Microsoft SQL Server 2016 (SP1). Comment puis-je y parvenir?

4
ReynierPM

Vous pouvez essayer ceci:

--enter procedure with insert parameters
DECLARE @field_seq INT = 36
DECLARE @field_seq_range INT

IF EXISTS(SELECT * FROM fields WHERE field_seq = @field_seq)
  BEGIN
    SELECT  @field_seq_range = MIN(f.field_seq)
    FROM    (
        SELECT  field_seq, LEAD(field_seq, 1, NULL) OVER (ORDER BY field_seq) next_field_seq
        FROM    fields
    ) f
    WHERE   f.field_seq >= @field_seq
    AND f.field_seq + 1 < f.next_field_seq

    UPDATE  f
    SET f.field_seq = f.field_seq + 1
    FROM    fields f
    WHERE   f.field_seq BETWEEN @field_seq AND @field_seq_range
  END

--perform insert

Le code va vérifier s'il y a une collision sur field_seq. S'il y en a un, il va scanner le tableau pour trouver le prochain écart, mettre à jour tous les field_seq valeurs dans cette plage et laissez un espace pour insérer le nouvel enregistrement. Si aucune collision n'est trouvée, la mise à jour est ignorée. Cependant, je ne peux garantir aucune performance. Je suis sûr qu'il existe des moyens plus optimaux de le faire.

Voici le dbfiddle - vous pouvez voir avant et après la mise à jour, créant un espace pour l'insertion.

4
mathewb

N'oubliez pas que vous devez ajouter un gestionnaire d'erreurs et utiliser une TRANSACTION pour vous assurer que tous les enregistrements concernés sont mis à jour.

CREATE TABLE T(field_id int, field_seq int);

INSERT INTO T VALUES 
(22156, 28),
(22759, 29),
(23458, 30),
(28000, 31),
(28101, 32),
(29355, 33),
(30000, 34),
(30125, 35);
GO
 8 lignes affectées 
CREATE PROCEDURE InsertNewID(@new_id int)
AS
BEGIN

    DECLARE @field_seq int = 0,
            @field_id int = 0;

    -- try to find if there is some field_id > @new_id
    SELECT   TOP 1 
             @field_id = COALESCE(field_id, 0),
             @field_seq = COALESCE(field_seq, 0)
    FROM     T
    WHERE    field_id > @new_id
    ORDER BY field_id ASC;

    -- if there isn't any field_id > @new_id
    -- get MAX(field_seq) OR 1 in case there is no records
    IF @field_seq = 0
    BEGIN
        SELECT @field_seq = COALESCE(MAX(field_seq), 0) + 1
        FROM   T
    END

    IF @field_id > 0
    BEGIN
        UPDATE T
        SET    field_seq = field_seq + 1
        WHERE  field_id >= @field_id
    END

    INSERT INTO T (field_id, field_seq) VALUES (@new_id, @field_seq);
END
GO

Insérer un nouvel enregistrement après field_id=29355

EXEC InsertNewId @new_id = 29999;

SELECT * FROM T ORDER BY field_id;
GO
 field_id | field_seq 
 -------: | --------: 
 22156 | 28 
 22759 | 29 
 23458 | 30 
 28000 | 31 
 28101 | 32 
 29355 | 33 
 29999 | 34 
 30000 | 35 
 30125 | 36 

Insérez un nouvel enregistrement à la fin.

EXEC InsertNewId @new_id = 31000;

SELECT * FROM T ORDER BY field_id;
GO
 field_id | field_seq 
 -------: | --------: 
 22156 | 28 
 22759 | 29 
 23458 | 30 
 28000 | 31 
 28101 | 32 
 29355 | 33 
 29999 | 34 
 30000 | 35 
 30125 | 36 
 31000 | 37 

dbfiddle ici

2
McNets

Vous pouvez toujours utiliser une sous-requête dans votre insert, quelque chose comme ....

INSERT INTO `fields` VALUES(9999, 9926, NULL, 0, 41, 1, 5, (SELECT MAX(field_seq) + 1 FROM fields), 'lllll', NULL, NULL);

pourrait fonctionner

1
David Fowler