2016-10-16 3 views
1

Ich versuche eine einfache Abfrage für Arbeitszeiten zu erstellen, wenn Mitarbeiter ein- und auschecken. Die Datenbank zeichnet den Namen der Person auf, wenn sie ein- oder aussteigt und die Uhrzeit angibt. Es zeichnet mehr als das auf, aber ich brauche diese Information nicht.Einreihige Unterabfrage gibt mehr als eine Zeile in Oracle zurück

So wäre ein Beispiel DB sein:

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 

Das Ergebnis, das ich wieder in einer SQL bringen wollen ist:

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

Die SQL ich versuche ist nicht richtig, das weiß ich. Aber was muss ich ändern, um das gewünschte Ergebnis zu erhalten?

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

Dies ist kein einfaches Problem, es sei denn, Sie können die Datenintegrität garantieren. Wie bist du sicher, dass jedes "in" genau ein "out" hat und umgekehrt? –

+0

Versuchen Sie, analytische Funktionen zu verwenden. Ich glaube, es ist der beste Weg. – 0xdb

Antwort

0

Ich glaube nicht, dass Sie eine Unterabfrage benötigen. So etwas wie das Folgende funktioniert möglicherweise ...

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' 

Bitte entschuldigen Sie mögliche Syntaxfehler. Ich habe Oracle seit Ewigkeiten nicht mehr benutzt, und ich habe keine Möglichkeit, das zu testen.

+0

Danke, es hat anfangs nicht funktioniert, aber gab mir eine gute Basis, um es mit ein paar Anpassungen arbeiten zu lassen. – Rob774

+1

@ Rob774 es könnte eine gute Idee sein, wenn Sie Ihre Änderungen an user1751825 vorschlagen, sonst würde die Frage ohne eine vollständige Antwort bleiben. – Insac

+0

Zu Rob774: Als ich meinen letzten Kommentar schrieb, schlug ich im Grunde vor, dass Sie "editieren" @ user1751825 answer; jedoch [scheint] (http://meta.stackoverflow.com/questions/302816/should-i-edit-an-answer-if-it-is-incomplete), dass eine bessere Option wäre, einen Kommentar hinzuzufügen, damit er kann die Bits hinzufügen, die Sie anpassen mussten – Insac

2

Ich habe ein Beispiel here erstellt.

In der naiven Annahme, dass es in der Tabelle alwas ein „In“ und man höchstens „Out“ für einen einzelnen Namen sein wird, soll die folgende Abfrage arbeiten:

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; 

Wenn Sie auch die wollen Fälle, in denen es keine ‚Out‘ ist, lassen Sie einfach die „mit“ -Klausel

Edited: realistischeres Szenario

Wenn Sie die Abfrage in der Lage sein wollen, mehrere Schichten von der gleichen Person zu erkennen, könnte man uns e diese Abfrage:

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) 

ich auch diese Lösung auf rextester getestet haben.

Haftungsausschluss: Ich begann von @ user1751825 Abfrage für dieses zweite Szenario, wie es einfach zu dieser Lösung von seiner Abfrage statt von meinem vorherigen eintreffen.

1

Es ist nicht klar, warum Sie wollen Ken und Billy nicht in Ihrer Ausgabe zeigen (zeigen sie getaktet aber getaktet nie aus), aber wenn das Ihre Anforderung ist, könnten Sie so etwas tun:

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

Dies sollte effizienter als jede Join-basierte Lösung sein. Dies ist, was die Abfrage tut: Zuerst gruppiert sie nach Benutzer; dann behält es die Gruppen, die zwei Zeilen haben (eine für IN und die andere für OUT); Dann wählt es den Benutzer, die frühere Zeit (von den zwei für diese Gruppe) und ruft es time_in, und die spätere Zeit und ruft es time_out. Ich denke nicht, dass es einfacher sein kann.

Bemerkungen:

(1) Dies setzt voraus, in der Tabelle "gruppiert" von DATE ist bereits (in der Tabelle alle Daten sind gleich).Andernfalls müssten Sie auch nach trunc(date_time) gruppieren und auch trunc(date_time) in die Ergebnisspalten aufnehmen; Ansonsten funktioniert die Abfrage genauso.

(2) Sie haben die user Spalte; Ich hoffe, dass Sie in Ihren realen Daten keine Namen haben, sondern eine ID oder einen Code, der für jeden Benutzer einzigartig ist. "Bob" ist kein eindeutiger Bezeichner.

(3) Die Lösung geht davon aus, dass die Daten validiert werden, sobald sie in die Basistabelle eingefügt werden. So kann ein "Benutzer" höchstens zwei Zeilen haben, es gibt keine "Out" -Zeile, wenn es keine "In" -Zeile gibt, die "Out" -Zeit ist nach der "In" -Zeit usw.

+0

Vielleicht berichtet die Anwendung nur über volle Schichten. – user1751825

+0

@ user1751825 - Nicht für mich zu beurteilen! – mathguy

Verwandte Themen