Операция в стиле «Слияние» с литеральными значениями?

#sql #merge #sql-server-2008-r2

#sql #слияние #sql-server-2008-r2

Вопрос:

У меня есть таблица, содержащая отношение «ученик-оценка»:

 Student   Grade   StartDate   EndDate
   1        1    09/01/2009    NULL
   2        2    09/01/2010    NULL
   2        1    09/01/2009   06/15/2010
  

Я пытаюсь написать хранимую процедуру, которая принимает Student , Grade и StartDate , и я хотел бы, чтобы она

  1. убедитесь, что эти значения не являются дубликатами
  2. вставьте запись, если это не дубликат
  3. если существует существующая запись student, и в ней есть EndDate = NULL , то обновите эту запись StartDate новой записью.

Например, если я вызову процедуру и передам 1 , 2 09/01/2010 ,, я хотел бы в итоге получить:

 Student   Grade   StartDate   EndDate
   1        2    09/01/2010    NULL
   1        1    09/01/2009   09/01/2010
   2        2    09/01/2010    NULL
   2        1    09/01/2009   06/15/2010
  

Это звучит так, как будто я мог бы использовать MERGE , за исключением того, что я передаю литеральные значения, и мне нужно выполнить более одного действия. Сегодня утром у меня тоже ужасно болит голова, и, похоже, я не могу ясно мыслить, поэтому я зацикливаюсь на этом MERGE решении. Если есть более очевидный способ сделать это, не бойтесь указать на него.

Ответ №1:

Вы можете использовать MERGE , даже если вы передаете литеральные значения. Вот пример для вашей проблемы:

 CREATE PROCEDURE InsertStudentGrade(@Student INT, @Grade INT, @StartDate DATE)
AS
BEGIN;

    MERGE StudentGrade AS tbl
     USING (SELECT @Student AS Student, @Grade AS Grade, @StartDate AS StartDate) AS row
     ON tbl.Student = Row.Student AND tbl.Grade = row.Grade
    WHEN NOT MATCHED THEN
      INSERT(Student, Grade, StartDate)
       VALUES(row.Student, row.Grade, row.StartDate)
    WHEN MATCHED AND tbl.EndDate IS NULL AND tbl.StartDate != row.StartDate THEN
      UPDATE SET
        tbl.StartDate = row.StartDate;

END;
  

Комментарии:

1. О, отлично. Я не знал, что вы можете использовать литеральные значения с MERGE ! Я пробовал что-то подобное, но не подумал использовать псевдонимы, что сейчас кажется очевидным. Это не совсем то, что мне нужно, поскольку мне нужно вставлять и обновлять при сопоставлении, но я уверен, что смогу найти решение там. Реальная суть этого заключалась в том, как использовать MERGE с моими значениями. Большое спасибо!

2. Вы также могли бы использовать что-то вроде этого: ИСПОЛЬЗУЯ (ЗНАЧЕНИЯ (@Student, @Grade, @StartDate)) В КАЧЕСТВЕ [строки] (студент, оценка, дата начала)

3. @Davos Мне бы очень хотелось увидеть ваш комментарий в ответе.

4. Спасибо @yzorg , но я думаю, что Ламак ответил на каждый аспект этого вопроса (в отличие от другого ответа ниже, который этого не делает), мой вклад здесь был довольно незначительным. Во всяком случае, это может быть и расширением этого ответа.

Ответ №2:

Я предпочитаю следующий, он более чистый и его легче читать и изменять.

 MERGE Definition.tdSection AS Target
USING
    (SELECT *
     FROM   ( VALUES
            ( 1, 1, 'Administrator', 1, GETDATE(), NULL, Current_User, GETDATE())
             ,( 2, 1, 'Admissions', 1, GETDATE(), NULL, Current_User, GETDATE())
             ,( 3, 1, 'BOM', 1, GETDATE(), NULL, Current_User, GETDATE())
             ,( 4, 1, 'CRC', 1, GETDATE(), NULL, Current_User, GETDATE())
             ,( 5, 1, 'ICM', 1, GETDATE(), NULL, Current_User, GETDATE())
             ,( 6, 1, 'System', 1, GETDATE(), NULL,Current_User, GETDATE())
             ,( 7, 1, 'Therapy', 1, GETDATE(), NULL, Current_User, GETDATE()) 
            )
            AS s (SectionId
                  ,BusinessProcessId
                  ,Description, Sequence
                  ,EffectiveStartDate
                  ,EffectiveEndDate
                  ,ModifiedBy
                  ,ModifiedDateTime)
     ) AS Source
ON Target.SectionId = Source.SectionId
WHEN NOT MATCHED THEN
    INSERT (SectionId
           ,BusinessProcessId
           ,Description
           ,Sequence
           ,EffectiveStartDate
           ,EffectiveEndDate
           ,ModifiedBy
           ,ModifiedDateTime
           )
    VALUES (Source.SectionId
           ,Source.BusinessProcessId
           ,Source.Description
           ,Source.Sequence
           ,Source.EffectiveStartDate
           ,Source.EffectiveEndDate
           ,Source.ModifiedBy
           ,Source.ModifiedDateTime
           );
  

Комментарии:

1. Это не отвечает на вопрос, заданный OP о написании хранимой процедуры, которая принимает параметры для вставки. Это также игнорирует часть 3 вопроса, касающуюся обновлений, когда конечная дата равна нулю. В этом примере будут вставляться только новые записи, и как таковой он на самом деле ничем не лучше инструкции insert.

Ответ №3:

Просто:

 --Arrange

CREATE TABLE dbo.Product
(
    Id   INT IDENTITY PRIMARY KEY,
    Name VARCHAR(40),   
)
GO

--Act

MERGE INTO dbo.Product AS Target 
USING 
(
    --Here is the trick :)

    VALUES 
        (1, N'Product A'),
        (2, N'Product B'),
        (3, N'Product C'),
        (4, N'Product D')
) 
AS 
Source 
(
     Id,
     Name
) 

ON Target.Id= Source.Id

WHEN NOT MATCHED BY TARGET THEN 

INSERT 
(
      Name
) 
VALUES 
(
      Name
);
  

Комментарии:

1. Спасибо. Вы экономите мне много времени. Это хороший трюк — использовать инструкцию MERGE и воспользоваться преимуществами чистого синтаксиса, когда у вас на самом деле не две таблицы для объединения, а литералы или переменные вместо них.