TSQL - INSERT ENTRY, если в группе не существует конкретной записи
Я пытаюсь вставить запись, если нет записи о том, что пользователь из группы вышел из системы.
declare @test table (grp varchar(2),logged varchar(4),time datetime)
insert into @test (grp,logged,time)
values ('A1', 'IN','20181111 09:00:00')
,('A1', 'OUT','20181111 10:00:00')
,('A2', 'IN','20181111 09:10:00')
,('A2', 'IN','20181111 09:20:00')
,('A3', 'IN','20181111 09:30:00')
,('A3', 'OUT','20181111 10:30:00')
Желаемый вывод
+-----+--------+-------------------------+
| grp | logged | time |
+-----+--------+-------------------------+
| A1 | IN | 2018-11-11 09:00:00.000 |
| A1 | OUT | 2018-11-11 10:00:00.000 |
| A2 | IN | 2018-11-11 09:10:00.000 |
| A2 | IN | 2018-11-11 09:20:00.000 |
| A2 | OUT | NULL |
| A2 | OUT | NULL |
| A3 | IN | 2018-11-11 09:30:00.000 |
| A3 | OUT | 2018-11-11 10:30:00.000 |
| A4 | IN | 2018-11-11 09:40:00.000 |
| A4 | OUT | NULL |
+-----+--------+-------------------------+
Есть идеи?
1 ответ
У меня есть 2 версии для вас
CREATE TABLE SignIn(grp varchar(10), logged varchar(3), [time] datetime)
INSERT INTO SignIn
VALUES
('A1','IN' , '2018-11-11 09:00:00.000' ),
('A1','OUT', '2018-11-11 10:00:00.000' ),
('A2','IN' , '2018-11-11 09:10:00.000' ),
('A2','IN' , '2018-11-11 09:20:00.000' ),
('A3','IN' , '2018-11-11 09:30:00.000' ),
('A3','OUT', '2018-11-11 10:30:00.000' ),
('A4','IN', '2018-11-11 09:40:00.000' )
Эта версия, как вы ожидали. За каждую запись вы получите копию OUT
INSERT INTO SignIn (grp, logged, time)
SELECT s.grp, 'OUT', NULL
FROM SignIn s
INNER JOIN (
SELECT grp
FROM SignIn
GROUP BY grp
HAVING MAX(CASE WHEN Logged = 'OUT' THEN 1 ELSE 0 END) = 0
) notSignedOut ON s.grp = notSignedOut.grp
Но если вы не хотите дублировать записи и хотите получить только одну запись для группы, вы можете использовать опцию ниже:
INSERT INTO SignIn (grp, logged, time)
SELECT grp, 'OUT', NULL
--ISLoggedOut = MAX(CASE WHEN Logged = 'OUT' THEN 1 ELSE 0 END)
FROM SignIn
GROUP BY grp
HAVING MAX(CASE WHEN Logged = 'OUT' THEN 1 ELSE 0 END) = 0
основная логика скрыта под ISLoggedOut = MAX(CASE WHEN Logged = 'OUT' THEN 1 ELSE 0 END). Для каждой группы записей я добавил индикатор, если есть запись. и в случае отсутствия - значение будет 0.
другой вариант - использовать пункт "не существует"
INSERT INTO SignIn (grp, logged, time)
SELECT s.grp, 'OUT', NULL
FROM SignIn s
WHERE NOT EXISTS (SELECT TOP 1 1 FROM SignIn i WHERE i.grp = s.grp AND Logged = 'OUT')
лично я предпочитаю использовать HAVING, основываясь на некотором опыте, иногда он бывает быстрее для больших таблиц. например, для нашего примера заявления с тем, что производит следующую статистику
Стол "Рабочий стол". Сканирование счетчик 0, логическое чтение 0, физическое чтение 0, чтение с опережением 0, чтение логического объекта 0, чтение с физического объекта 0, чтение с опережением 0. Таблица 'Вход в систему'. Сканирование 2, логическое чтение 2, физическое чтение 0, чтение с опережением 0, логическое чтение с 0, физическое чтение с 0, чтение с опережением 0.
и существует утверждение:
Стол "Вход". Сканирование 2, логическое чтение 8, физическое чтение 0, чтение с опережением 0, чтение логического объекта 0, физическое чтение 1, чтение с опережением 0.
похоже, что оператор HAVING читает меньше, чем EXISTS