web-dev-qa-db-ja.com

条件に基づいて「1」を追加して列の値を更新するにはどうすればよいですか?

次の表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             |
+----------+---------+-----------------+-------------+---------+--------------+---------------+-----------+------------------------------+-------------+------------------+

ご覧のとおり、この場合、同じ値field_seqを持つ2つの36があります。

field_id=281960の直後に新しい行を挿入していて、そのような新しい行のfield_seq36として来るとします。

field_seq36以上の行があるかどうかを確認できるクエリまたはストアドプロシージャを作成する必要があります。その場合は、field_seqの値を現在の値と1に更新します。

例:

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

これにより、以下の可能なケースを確認できます(それぞれの後に例を示します)。

ケース1: field_seq = 36の行はすでにテーブルに存在します

  • INSERTデータをそのままにして、現在のfield_seq=36新しい行になります。
  • field_seq=current+1になるテーブル行37の値を更新します
  • 37がすでにある場合は、field_seqが繰り返されなくなるまで前のステップを繰り返します。

前:

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

後:

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

ケース2: field_seq = 36の行はすでにテーブルに存在しますが、次のfield_seq37より大きいです

  • INSERTデータをそのままにして、現在のfield_seq=36新しい行になります。
  • field_seq=current+1になるテーブル行37の値を更新します
  • この場合、同じfield_seqになる前に数行を挿入するのに十分なスペースがあるため、更新を続ける必要はありません。

前:

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

後:

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

Microsoft SQL Server 2016(SP1)を使用しています。どうすればこれを達成できますか?

4
ReynierPM

あなたはこれを試してみることができます:

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

コードは、field_seq.に競合があるかどうかを確認します。競合がある場合は、テーブルをスキャンして次のギャップを見つけ、その範囲のfield_seq値をすべて更新して、新しいレコードを挿入するギャップ。衝突が見つからない場合、更新はスキップされます。ただし、これについてはパフォーマンスを保証できません。それを行うためのより最適な方法があると確信しています。

これが dbfiddle です。更新が行われる前と後に、挿入が行われるためにギャップが生じていることがわかります。

4
mathewb

エラーハンドラーを追加し、TRANSACTIONを使用して、影響を受けるすべてのレコードが確実に更新されるようにしてください。

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行が影響を受けました
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

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 

最後に新しいレコードを挿入します。

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

2
McNets

挿入ではサブクエリをいつでも使用できます。

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

うまくいくかもしれません

1
David Fowler