#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
, и я хотел бы, чтобы она
- убедитесь, что эти значения не являются дубликатами
- вставьте запись, если это не дубликат
- если существует существующая запись 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 и воспользоваться преимуществами чистого синтаксиса, когда у вас на самом деле не две таблицы для объединения, а литералы или переменные вместо них.