Вставить или обновить данные, когда строки совпадают или нет SQL Server

У меня есть таблица ProjectTemplateSection:

    [ProjectID] [int] 
    [ProjectTemplateID]
    [SectionID] [int]
    [IsActive] [bit] 
    [SectionOrderNumber] [int] 

с такими примерами данных:

  ProjectID  TemplateCloneId    SectionCloneId IsActive SectionOrderNumber
 -----------------------------------------------------------------------
    1                   1               1         1         4
    1                   1               2         0         2
    1                   1               3         1         3

У нас есть модуль проекта, шаблона и раздела, которые взаимосвязаны.

  • В одном шаблоне будет несколько разделов
  • Один проект будет иметь один сопоставленный шаблон

Когда пользователь назначает шаблон для проекта, projectid, templateid и sectionid будут вставлены в ProjectTemplateSection.

Мне нужно предложение по приведенному ниже разъяснению

  • ЕСЛИ пользователь приходит и редактирует разделы для шаблона, например, удаляет раздел и добавляет новый раздел. Если существует какое-либо сопоставление между разделами и шаблоном, тогда флаг IsActive должен быть ложным. Если сопоставления нет, его следует вставить.

Должен ли я выполнять запрос выбора для существующего сопоставления для выбранного шаблона & получить записи и сравнить с новым отображением и сделать вставку для новых записей и обновить старое сопоставление, т.е. сделать флаг IsActive ложным

Подскажите, пожалуйста, лучшее решение для этого сценария.

Я не буду закрывать как дубликат (пока), но вы должны проверить ответ, опубликованный Аароном Бертраном на эта почта

Zohar Peled 31.10.2018 11:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
55
1

Ответы 1

Попробуйте использовать MERGE:

CREATE TABLE TestProjectTemplateSection(
  ProjectID int,
  ProjectTemplateID int,
  SectionID int,
  IsActive bit,
  SectionOrderNumber int
)

INSERT TestProjectTemplateSection(ProjectID,ProjectTemplateID,SectionID,IsActive,SectionOrderNumber)VALUES
(1,1,1,1,4), 
(1,1,2,0,2),
(1,1,3,1,3)

И процедура для вставки / обновления / деактивации с помощью:

CREATE PROC SetTestProjectTemplateSection
  @ProjectID int,
  @ProjectTemplateID int,
  @SectionID int,
  @IsActive int=1,
  @SectionOrderNumber int=NULL
AS

  MERGE TestProjectTemplateSection trg
  USING
    (
      SELECT
        @ProjectID ProjectID,
        @ProjectTemplateID ProjectTemplateID,
        @SectionID SectionID,
        @IsActive IsActive,
        @SectionOrderNumber SectionOrderNumber
    ) src
  ON trg.ProjectID=src.ProjectID AND trg.ProjectTemplateID=src.ProjectTemplateID AND trg.SectionID=src.SectionID

  WHEN MATCHED THEN -- update IsActive and SectionOrderNumber
    UPDATE SET
      trg.IsActive=src.IsActive,
      trg.SectionOrderNumber=ISNULL(src.SectionOrderNumber,trg.SectionOrderNumber) -- if NULL then not change it

  WHEN NOT MATCHED BY TARGET THEN -- insert new row
    INSERT(ProjectID,ProjectTemplateID,SectionID,IsActive,SectionOrderNumber)
    VALUES(src.ProjectID,src.ProjectTemplateID,src.SectionID,src.IsActive,src.SectionOrderNumber);

GO

Демо:

-- case 1 - update
EXEC SetTestProjectTemplateSection 1,1,2,1
EXEC SetTestProjectTemplateSection 1,1,2,1,111 -- set new order number

SELECT *
FROM TestProjectTemplateSection

-- case 2 - insert
EXEC SetTestProjectTemplateSection 2,1,2,1,123

SELECT *
FROM TestProjectTemplateSection

-- case 3 - deactive
EXEC SetTestProjectTemplateSection 2,1,2,0

SELECT *
FROM TestProjectTemplateSection

Думаю, как вариант можно использовать STRING_SPLIT для списка разделов (см. @ListOfSectionID):

CREATE PROC SetTestProjectTemplateSections
  @ProjectID int,
  @ProjectTemplateID int,
  @ListOfSectionID varchar(100), -- e.g. '1,2,3,4'
  @IsActive int=1
AS

  MERGE TestProjectTemplateSection trg
  USING
    (
      SELECT
        @ProjectID ProjectID,
        @ProjectTemplateID ProjectTemplateID,
        CAST(value AS int) SectionID,
        @IsActive IsActive
      FROM STRING_SPLIT(@ListOfSectionID,',')
    ) src
  ON trg.ProjectID=src.ProjectID AND trg.ProjectTemplateID=src.ProjectTemplateID AND trg.SectionID=src.SectionID

  WHEN MATCHED THEN
    UPDATE SET
      trg.IsActive=src.IsActive

  WHEN NOT MATCHED BY TARGET THEN
    INSERT(ProjectID,ProjectTemplateID,SectionID,IsActive)
    VALUES(src.ProjectID,src.ProjectTemplateID,src.SectionID,src.IsActive);

GO

Но STRING_SPLIT работает для SQL Server 2017. Для другой версии SQL Server вы можете найти определяемую пользователем функцию для разделения. Например - Разделенная строка T-SQL

Тестовое задание:

EXEC SetTestProjectTemplateSections 1,1,'1,2,3,4',0

SELECT *
FROM TestProjectTemplateSection

Как вариант вы можете использовать параметр таблицы:

CREATE TYPE TypeForProjectTemplateSection AS table(
  ProjectID int,
  ProjectTemplateID int,
  SectionID int,
  IsActive bit,
  SectionOrderNumber int
)
GO

И используйте этот тип в своей процедуре:

CREATE PROC SetTestProjectTemplateSectionFromTable
  @ProjectTemplateSectionDate dbo.TypeForProjectTemplateSection READONLY -- need to use READONLY here
AS

  MERGE TestProjectTemplateSection trg
  USING @ProjectTemplateSectionDate src
  ON trg.ProjectID=src.ProjectID AND trg.ProjectTemplateID=src.ProjectTemplateID AND trg.SectionID=src.SectionID

  WHEN MATCHED THEN -- update IsActive and SectionOrderNumber
    UPDATE SET
      trg.IsActive=src.IsActive,
      trg.SectionOrderNumber=src.SectionOrderNumber

  WHEN NOT MATCHED BY TARGET THEN -- insert new row
    INSERT(ProjectID,ProjectTemplateID,SectionID,IsActive,SectionOrderNumber)
    VALUES(src.ProjectID,src.ProjectTemplateID,src.SectionID,src.IsActive,src.SectionOrderNumber);

GO

Как это использовать:

ОБЪЯВИТЬ @Table dbo.TypeForProjectTemplateSection

INSERT @Table(ProjectID,ProjectTemplateID,SectionID,IsActive,SectionOrderNumber)VALUES
(1,1,1,1,111), 
(1,1,2,1,222)

EXEC SetTestProjectTemplateSectionFromTable @Table -- use it as parameter

SELECT *
FROM TestProjectTemplateSection

И вы можете использовать его из C#:

SqlCommand cmd = new SqlCommand("SetTestProjectTemplateSectionFromTable", con);
cmd.CommandType = CommandType.StoredProcedure;

DataTable tbl = new DataTable();
tbl.Columns.Add("ProjectID", typeof(int));
tbl.Columns.Add("ProjectTemplateID", typeof(int));
tbl.Columns.Add("SectionID", typeof(int));
tbl.Columns.Add("IsActive", typeof(bool)); // Check it because I don't remember how to use type bit here
tbl.Columns.Add("SectionOrderNumber", typeof(int));

tbl.Rows.Add(1,1,1,true,111);
tbl.Rows.Add(1,1,2,true,222);

cmd.Parameters.AddWithValue("@ProjectTemplateSectionDate", tbl);

cmd.ExecuteNonQuery();

Спасибо, Леран, за быстрый ответ. Я отправлю список разделов, в одном шаблоне будет несколько разделов. Должен ли я отправлять разделы как значения, разделенные запятыми, и использовать цикл while и внутри цикла while использовать предложенный вами запрос слияния ИЛИ Следует ли мне обрабатывать на стороне C#, например, перебирать разделы и вызывать запрос слияния внутри цикла через ADO. сеть

Thyagi9510 31.10.2018 10:59

Какую версию SQLServer вы используете?

Sergey Menshov 31.10.2018 11:03

Я использую как 2014, так и 2016, Забыл упомянуть, у меня есть еще один атрибут для раздела под названием Номер заказа, он также используется для упорядочивания раздела. При редактировании шаблона проекта меняются и номера заказа раздела.

Thyagi9510 31.10.2018 11:35

Забыл упомянуть, что я обновил вопрос, у меня есть еще один атрибут для раздела под названием Номер заказа, он также используется для упорядочивания раздела. При редактировании шаблона проекта может измениться и порядковый номер раздела.

Thyagi9510 31.10.2018 11:42

Я снова изменил свой ответ. Я думаю, что будет лучше использовать простой цикл на стороне C#, чем использовать сложную процедуру.

Sergey Menshov 01.11.2018 04:08

Я вспомнил, что в качестве параметра можно использовать таблицу. Я также добавил пример к своему ответу. Попробуйте протестировать и, возможно, этот вариант вам подойдет.

Sergey Menshov 01.11.2018 04:37

Конечно буду тестировать, спасибо за ответ

Thyagi9510 02.11.2018 13:09

Другие вопросы по теме