0

I have the table ProjectTemplateSection:

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

with sample data like this:

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

We have project, template & section module which are interrelated

  • One template will have multiple sections
  • One project will have one template mapped

When the user assigns the template to project, the projectid, templateid & the sectionid will be inserted into ProjectTemplateSection.

I need suggestion on the below clarification

  • IF the user comes & edits sections for the template like removing the section & adding the new section. If there is any mapping exists between sections & template, then the IsActive flag should be false. If there is not mapping then it should insert.

Should I do the select query for existing mapping for the selected template & get the records & compare with the new mapping & do the insert for new records & update the old mapping i.e making the IsActive flag false

Please suggest which is the best solution for this scenario

Thyagi9510
  • 21
  • 4
  • I will not close as a duplicate (yet) but you should check out the answer posted by Aaron Bertrand on [this post](https://stackoverflow.com/questions/52780121/using-a-if-condition-in-an-insert-sql-server) – Zohar Peled Oct 31 '18 at 10:44

1 Answers1

0

Try to use 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)

And a procedure to insert/update/deactivate with:

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

Demo:

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

I think as variant you can use STRING_SPLIT for the list of sections (see @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

But STRING_SPLIT works for SQL Server 2017. For another version of SQL Server you can find a user defined function for splitting. For example - T-SQL split string

Test:

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

SELECT *
FROM TestProjectTemplateSection

As variant you can use a table parameter:

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

And use this type in your procedure:

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

How to use it:

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

And you can use it from 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();
Sergey Menshov
  • 3,856
  • 2
  • 8
  • 19
  • Thank you Leran for the quick response. I will be sending the **list of sections**, One template will have multiple sections. Should I send the sections as comma separated value & use while loop & inside the while loop use the Merge query you have suggested OR Should I handle in the C# side, like looping through the sections & call the merge query inside the loop through ADO.net – Thyagi9510 Oct 31 '18 at 09:59
  • What version of SQLServer do you use? – Sergey Menshov Oct 31 '18 at 10:03
  • I use both 2014 , 2016, Forgot to mention , I have another attribute for section called Order number, This is used for ordering of the section also. When the project template is edited , there me be change in the section oder number also – Thyagi9510 Oct 31 '18 at 10:35
  • Forgot to mention I have updated the question, I have another attribute for a section called Order number, This is used for ordering of the section also. When the project template is edited, there may be a change in the section order number also – Thyagi9510 Oct 31 '18 at 10:42
  • I've changed my answer again. I think to use a simple loop in the C# side will be better that to use a difficult procedure. – Sergey Menshov Nov 01 '18 at 03:08
  • I've remembered that can use a table as parameter. I also added example to my answer. Try to test it and maybe this variant will be better for you. – Sergey Menshov Nov 01 '18 at 03:37
  • Sure Will test, Thank you for the response – Thyagi9510 Nov 02 '18 at 12:09