Интересная задачка есть у меня для разминки Ваших мозгов. В моём ЖЖ её никто решить не смог, что странно, давайте среди 1сников устроим челлендж, докажем, что мы умнее сишников. Чтобы веселее было думать, добавляю приз 1 см.

Есть таблица проходов через проходную с полями: Сотрудник, Время (уникальное) , Вход (истина - вход, ложь - выход).

За день могут входить-выходить много раз.

Нужно SQL запросом выдать таблицу: Сотрудник, Время прихода, Время ухода, Секунд внутри. Запись таблицы по каждой последовательной паре приход - уход.

Фишка в том, что нельзя использовать Group By, агрегатные функции Min/Max/..., конструкцию IN. Нужно использовать только один запрос без временных таблиц и пользоваться только JOIN.

Думаем!
---
Вот, кажется, подходящее решение:
```1c
    ВЫБРАТЬ
    Приходы.Сотрудник,
    Приходы.Время КАК ВремяПрихода,
    Уходы.Время КАК ВремяУхода
    ИЗ
    Дано КАК Приходы
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Дано КАК Уходы
    ПО Приходы.Сотрудник = Уходы.Сотрудник
    И Приходы.Время < Уходы.Время
    ЛЕВОЕ СОЕДИНЕНИЕ Дано КАК Вставки
    ПО (Вставки.Сотрудник = Приходы.Сотрудник)
    И Приходы.Время < Вставки.Время
    И (Вставки.Время < Уходы.Время)
    ГДЕ
    Приходы.Вход
    И НЕ Уходы.Вход
    И Вставки.Время ЕСТЬ NULL
```

Вариант для проверки в консоли:
```1c
    ВЫБРАТЬ
    "Иванов" КАК Сотрудник,
    ДАТАВРЕМЯ(2018, 2, 19, 23, 0, 0) КАК Время,
    ИСТИНА КАК Вход
    ПОМЕСТИТЬ Дано

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    "Иванов",
    ДАТАВРЕМЯ(2018, 2, 19, 23, 59, 59),
    ЛОЖЬ

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    "Иванов",
    ДАТАВРЕМЯ(2018, 2, 20, 0, 0, 0),
    ИСТИНА

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    "Иванов",
    ДАТАВРЕМЯ(2018, 2, 20, 8, 0, 0),
    ЛОЖЬ
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    Приходы.Сотрудник,
    Приходы.Время КАК ВремяПрихода,
    Уходы.Время КАК ВремяУхода
    ИЗ
    Дано КАК Приходы
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Дано КАК Уходы
    ПО Приходы.Сотрудник = Уходы.Сотрудник
    И Приходы.Время < Уходы.Время
    ЛЕВОЕ СОЕДИНЕНИЕ Дано КАК Вставки
    ПО (Вставки.Сотрудник = Приходы.Сотрудник)
    И Приходы.Время < Вставки.Время
    И (Вставки.Время < Уходы.Время)
    ГДЕ
    Приходы.Вход
    И НЕ Уходы.Вход
    И Вставки.Время ЕСТЬ NULL
```
---
добавляю приз 1 см Малавата будет!
---
да ну? Ггг
---
как-то так?

```1c
    sel ect
    table_in.id as id,
    table_in.time as time_in,
    table_out.time as time_out,
    datediff(ss, table_in.time, table_out.time) AS datedif

    fr om timetable as table_in
    left join timetable as table_out
    on table_out.id = table_in.id
    and table_out.state = 0
    and table_out.time = (
    select top 1
    t.time
    fr om timetable as t
    where t.state = 0
    and t.id = table_in.id
    and t.time > table_in.time
    order by t.time
    )

    where table_in.state = 1
```
---
(5) нет, никаких селект из селект, чисто используя JOIN
---
Ну или так, вообще без joina

```1c
    sel ect
    t.id,
    t.time_in,
    t.time_out,
    datediff(ss, t.time_in, t.time_out) AS datedif
    fr om (
    sel ect
    table_in.id as id,
    table_in.time as time_in,
    (select top 1
    t.time
    fr om timetable as t
    where t.state = 0
    and t.id = table_in.id
    and t.time > table_in.time
    order by t.time) as time_out
    fr om timetable as table_in
    wh ere table_in.state = 1) as t
```
---
Вот, кажется, подходящее решение:
```1c
    ВЫБРАТЬ
    Приходы.Сотрудник,
    Приходы.Время КАК ВремяПрихода,
    Уходы.Время КАК ВремяУхода
    ИЗ
    Дано КАК Приходы
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Дано КАК Уходы
    ПО Приходы.Сотрудник = Уходы.Сотрудник
    И Приходы.Время < Уходы.Время
    ЛЕВОЕ СОЕДИНЕНИЕ Дано КАК Вставки
    ПО (Вставки.Сотрудник = Приходы.Сотрудник)
    И Приходы.Время < Вставки.Время
    И (Вставки.Время < Уходы.Время)
    ГДЕ
    Приходы.Вход
    И НЕ Уходы.Вход
    И Вставки.Время ЕСТЬ NULL
```

Вариант для проверки в консоли:
```1c
    ВЫБРАТЬ
    "Иванов" КАК Сотрудник,
    ДАТАВРЕМЯ(2018, 2, 19, 23, 0, 0) КАК Время,
    ИСТИНА КАК Вход
    ПОМЕСТИТЬ Дано

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    "Иванов",
    ДАТАВРЕМЯ(2018, 2, 19, 23, 59, 59),
    ЛОЖЬ

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    "Иванов",
    ДАТАВРЕМЯ(2018, 2, 20, 0, 0, 0),
    ИСТИНА

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    "Иванов",
    ДАТАВРЕМЯ(2018, 2, 20, 8, 0, 0),
    ЛОЖЬ
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    Приходы.Сотрудник,
    Приходы.Время КАК ВремяПрихода,
    Уходы.Время КАК ВремяУхода
    ИЗ
    Дано КАК Приходы
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Дано КАК Уходы
    ПО Приходы.Сотрудник = Уходы.Сотрудник
    И Приходы.Время < Уходы.Время
    ЛЕВОЕ СОЕДИНЕНИЕ Дано КАК Вставки
    ПО (Вставки.Сотрудник = Приходы.Сотрудник)
    И Приходы.Время < Вставки.Время
    И (Вставки.Время < Уходы.Время)
    ГДЕ
    Приходы.Вход
    И НЕ Уходы.Вход
    И Вставки.Время ЕСТЬ NULL
```
---
Классное решение, мне понравилось.
Интересно оно будет быстрее чем "обычное" с группировками или нет?
---
тоже задумался над этим.
Если будет быстрее, то можно все срезы переделывать...
Пока есть сомнения, но как знать, как знать...
Побежал тестить))))
Но дело неприятно попахивает nested loop'ом ((
---
ну ты это, держи в курсе, у меня тоже есть куча мест где это может пригодится. Только проверять надо хотя-бы на 1кк строк.
---
в том числе, но не только.
Разные выборки - разные статистики - разные оценки - разные планы.
У меня меньше таблиц и не бывает))) базы измеряю сотнями гигов))
Сорри, ещё не добежал.
---
Поржал сам с себя...
Какой тут NL? Не он страшен. Практически декартово произведение. Запрос вообще не работоспособен.
Взял навскидку табличку на 270к записей... и все... ssd стало жалко )

Чтобы примерить эту Идею - то я ведь правильно все понял?
1. Взяли все до @даты (табл Т1)
2.Присоединили все между периодом (Т1) и @датой - и исключили все что примоединилось
Результат в количестве и процентах на картинке(HM).
На второй картинке (NL) добавлен индекс по Field+Period.

PS: по сабжу - олимпиадные задачи такие олимпиадные. Нет чтобы соревноваться по оптимизации, краткости, интересных подходах. ))
---
В общем то с группировками будет быстрее. Можно это запрос ограничить по времени выборки, но то же вряд ли будет быстрее.
---
ну вообще если предположить что между входом и выходом бывает, например, не больше суток, то должно быть вообще шустро.
---
ну или так, на вскидку:
sel ect tin.id, tin.time as time_in, tout.time as time_out
fr om table as tin
left out join table as tout on tout.state = 0 and tin.id = tout.id and tin.time < tout.time
where tin.state = 1
---
Блин, пытался придумать алгоритм выявления неправильных записей, а оказывается все просто, если между временем входа и временем выхода есть промежуточные состояния, то такие записи не верны. Браво
---
да, именно этот вариант и был задуман! Молодцом!
---
Только реальные задачи на вход-выход лучше на стейт-машинах решать.