2016-10-16 3 views
1

Я пытаюсь создать простой запрос на время разработки, когда часы часов и часы отключены. БД записывает имя человека, если его синхронизация или выход и время. Он записывает более того, но мне не нужна эта информация.Однострочный подзапрос возвращает более одной строки в Oracle

Так пример DB будет:

Id | User | In_out | Date_Time 
---------------------------------- 
2 | Bob | In | 13/Oct/16 9:30:35AM 
3 | Ken | In | 13/Oct/16 9:34:27AM 
4 | Jon | In | 13/Oct/16 9:34:46AM 
5 | Billy | In | 13/Oct/16 9:52:06AM 
6 | Bob | Out | 13/Oct/16 4:30:05PM 
7 | Jon | Out | 13/Oct/16 4:32:55PM 

Результат я хочу, чтобы вернуть в SQL является:

User | Time_In | Time_Out 
------------------------------- 
Bob | 9:30:35AM | 4:30:05PM 
Jon | 9:34:46AM | 4:32:55PM 

SQL, я стараюсь не прав, я знаю, что. Но что мне нужно изменить, чтобы получить результат, который я хочу?

SELECT 
    Clock.Checkin_Checkout.User, 
    Clock.Checkin_Checkout.In_Out, 
    (SELECT TO_CHAR(Clock.Checkin_Checkout.DATETIME, 'hh:mi:ss AM') 
    FROM Clock.Checkin_Checkout 
    WHERE Clock.Checkin_Checkout.In_Out = 'In') AS "Time In", 
    To_Char(Clock.Checkin_Checkout.Create_Datetime, 'hh:mi:ss AM') AS "Time Out" 
FROM 
    Clock.Checkin_Checkout 
WHERE 
    Clock.Checkin_Checkout.In_Out = 'Out' 
    AND Clock.Checkin_Checkout.Datetime >= '13/Oct/2016' 
+0

Это не простая проблема, если вы не можете гарантировать целостность данных. Как вы уверены, что каждый 'in' имеет ровно один« выход »и наоборот? –

+0

Попробуйте использовать аналитические функции. Я считаю, что это лучший способ. – 0xdb

ответ

0

Я не думаю, что вам нужен суб-запрос. Что-то вроде следующего может работать ...

select c1.User, c1.Date_Time as "Time_In", c2.Date_Time as "Time Out" 
from Clock.Checkin_Checkout c1 
inner join Clock.Checkin_Checkout c2 on c2.User = c1.User 
where c1.In_Out = 'In' 
and c2.In_Out = 'Out' 

Извините, пожалуйста, синтаксические ошибки. Я давно не использовал Oracle, и у меня нет возможности проверить это.

+0

Спасибо, Это не сработало для начала, но дало мне хорошую базу, чтобы заставить ее работать с несколькими настройками. – Rob774

+1

@ Rob774 это может быть хорошей идеей, если вы предложите свои изменения пользователю1751825, иначе вопрос останется без полного ответа. – Insac

+0

To Rob774: когда я написал свой последний комментарий, я в основном предлагал вам «отредактировать» @ user1751825 answer; однако [кажется] (http://meta.stackoverflow.com/questions/302816/should-i-edit-an-answer-if-it-is-incomplete), что лучшим вариантом было бы добавить комментарий, чтобы он может добавить бит, который вы должны были настроить – Insac

2

Я создал пример here.

В наивном предположении, что в таблице есть будет alwas один «В» и в большинстве один «Out» для одного имени, следующий запрос должен работать:

Select c1.user, 
     to_char(MAX(CASE WHEN c1.In_Out='In' then c1.Date_Time end), 'hh:mi:ss AM') time_in, 
     to_char(MAX(CASE WHEN c1.In_Out='Out' then c1.Date_Time end), 'hh:mi:ss AM') time_out 
    From Clock.Checkin_Checkout as c1 
group by c1.user 
having MAX(CASE WHEN c1.In_Out='Out' then c1.Date_Time end) is not null; 

Если вы хотите также случаи, когда нет никакого «Out», просто опустить «имеющий» пункт

Отредактировано: более реалистичный сценарий

Если вы хотите, чтобы запрос, чтобы иметь возможность распознавать несколько смен от того же человека, вы могли бы нас е этот запрос:

select c1.user, c1.Date_Time as Time_In, c2.Date_Time as Time_Out 
from Clock.Checkin_Checkout c1 
     join Clock.Checkin_Checkout c2 
     on c2.user= c1.user 
where c1.In_Out= 'In' 
    and c2.In_Out= 'Out' 
    and c1.Date_Time <c2.Date_Time 
    and not exists (select 1 from Clock.Checkin_Checkout c3 
        where c1.user=c3.user 
         and c1.Date_Time <c3.Date_Time 
         and c3.Date_Time <c2.Date_Time) 

Я также проверил это решение на rextester.

Отказ от ответственности: Я начал с запроса @ user1751825 для этого второго сценария, так как было просто добраться до этого решения из его запроса, а не из предыдущего.

1

Не понятно, почему вы не хотите, чтобы показать, Кен и Билли в своем выходе (показывая, что они с тактовой частотой в России, но никогда не разгонял выход), но если это ваше требование, вы могли бы сделать что-то вроде этого:

select user, min(date_time) as time_in, max(date_time) as time_out 
from  checkin_checkout 
group by user 
having count(*) = 2; 

Это должно быть более эффективным, чем любое совместное решение. Это то, что делает запрос: сначала он группируется пользователем; то он удерживает группы, у которых есть две строки (одна для IN, а другая для OUT); затем он выбирает пользователя, более раннее время (из двух для этой группы) и вызывает его time_in, а более позднее время он называет его time_out. Я не думаю, что это может быть проще, чем это.

Примечания:

(1) Это предполагает, что таблица уже «сгруппированы» по дате (в таблице все даты одинаковы).В противном случае вам также нужно будет группировать по trunc(date_time) и включать trunc(date_time) среди столбцов результатов; кроме этого, запрос будет работать точно так же.

(2) У вас есть столбец user; Я надеюсь, что в ваших реальных данных у вас нет имен, но какой-то идентификатор или код, который уникален для каждого пользователя. «Боб» не является уникальным идентификатором.

(3) Решение предполагает, что данные проверяются, поскольку они вставлены в базовую таблицу; поэтому «пользователь» может иметь не более двух строк, нет строки «Out», если нет строки «In», время «Out» после «In» времени и т. д.

+0

Возможно, приложение сообщает только о полной смене. – user1751825

+0

@ user1751825 - Не для меня судить! – mathguy

Смежные вопросы