2010-12-12 1 views
0

(Dies ist in DB2 Version 9.5)Ist das mit plain SQL möglich? Oder ist ein Verfahren erforderlich?

sagen, dass ich eine Tabelle mit vier Spalten:

C1: [EmployeeId] 
C2: [StartDate] yyyy-mm-dd format 
C3: [EndDate] yyyy-mm-dd format 
C4: [Age] 

Die Kombination [EmployeeId + Startdate] bildet den Primärschlüssel.
nun die folgenden Beispielreihen berücksichtigen:

1  2010-01-16  2010-04-16  29 
2  2010-02-16  2010-03-16  33 
3  2010-05-16  2010-05-16  31 

Es gibt eine Garantie, dass alle Tage wird der 16. Tag, über alle Termine, in allen Reihen sein. Keine andere Möglichkeit.

Ich versuche, eine Abfrage zu schreiben, die die folgenden tun:

Für jede Zeile, in dem Startmonat ist kleiner als die Endung Monat (wie Zeile 1), replizieren die Zeile für jeden Monat zwischen einschließlich der letzten Reihe. So, nachdem die Abfrage ausgeführt wird, werden die neuen Zeilen für Zeile 1:

1  2010-01-16  2010-04-16  29 
1  2010-02-16  2010-04-16  29 
1  2010-03-16  2010-04-16  29 
1  2010-04-16  2010-04-16  29 

Für Zeilen, in denen die Startmonat und das Ende Monat die gleichen sind, nichts tun. Für Zeilen, bei denen der Endmonat der Monat unmittelbar nach dem Startmonat ist, wird eine neue Zeile eingefügt (siehe die letzte Zeile oben, um den Grund zu verstehen).

Fragen:

  1. Ist das möglich in Klar SQL? Oder sind Verfahren erforderlich?
  2. Wenn ja, wie? (Hinweise, Code, alles)

Danke fürs Lesen Jungs. Die Problemstellung ist komplex, ich hoffe, ich habe einen fairen Job gemacht, um es zu erklären. Jede Unklarheit oder Inkonsistenz: Bitte weisen Sie darauf hin und ich werde es so schnell wie möglich bearbeiten.

+0

Die größte Sorge sehe ich nichts hat mit dem SQL zu tun, sondern mit der Garantie, dass die Tage wird der 16. Tag. Die Erfahrung sagt mir, dass sich dies ändern wird, sobald genug Zeit vergangen ist, in der du nicht verstehst, warum du es so codiert hast. –

Antwort

1

ich denke, das sollte es tun:

 
SELECT e.employee_id, 
     date(extract(year from startdate)||'-'||mm.month_nr||'-'||extract(day from startdate)) as stardate, 
     first_value(e.enddate) over (partition by employee_id order by e.enddate) as enddate 
FROM (
    VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12) 
) mm (month_nr) 
JOIN emp_test e 
    ON mm.month_nr = extract(month from e.startdate) 
ORDER BY e.employee_id, 
     e.startdate, 
     e.enddate

Dies setzt natürlich voraus, dass der Datentyp für startdate und enddate DATE ist.
Und dies funktioniert nur, wenn alle Daten innerhalb eines einzigen Jahres sind.

Ich habe das nur mit DB2 9.7 getestet, aber ich denke, es sollte genauso gut mit 9.5 funktionieren.

Ich denke, es gibt eine Lösung für das Jahr Cross Problem:

 
WITH yrs (year_month) AS 
(
    SELECT DATE(year_nr||'-'||month_nr||'-16') 
    FROM (
    VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12) 
) mon (month_nr) 
    CROSS JOIN (
    VALUES (2007), (2008), (2009), (2010), (2011), (2012), (2013) 
) yr (year_nr) 
) 
SELECT e.employee_id, 
     yrs.year_month as startdate, 
     first_value(e.enddate) over (partition by employee_id order by e.enddate) as enddate 
FROM emp_test e 
    JOIN yrs ON yrs.year_month between e.startdate and e.enddate 

Wie Sie die Jahre „Hardcoded“ in den CTE sind (allgemeiner Tabellenausdruck) sehen kann. Die einfachste Lösung wäre, eine "Monat/Jahr" -Tabelle zu erstellen, die alle möglichen Werte enthält - im Wesentlichen die Daten, die vom CTE generiert werden.

+0

Habe es noch nicht versucht, aber ja, die Möglichkeit (und Wahrscheinlichkeit), dass die Daten über Jahre hinweg reichen können, ist ein großes Problem. Werde das versuchen und versuchen, darauf aufzubauen. Danke für die Einsicht, sehr geschätzt. –

+0

Das grundlegende Muster, um dieses Jahr zu arbeiten, ist, die möglichen Jahre irgendwie in die Abfrage einzubeziehen. Mein DB2 ist ein wenig eingerostet, und ich weiß nicht, wie ich das dynamisch erzeugen soll (es ist einfach für "feste" Werte wie die Monatszahl) –

0

FWIW eine Reihe von Attribut- und Tupel Einschränkungen würde zu einem gewissen Grad vereinfachen:

CHECK (EXTRACT(DAY FROM StartDate) = 16), 
CHECK (EXTRACT(DAY FROM EndDate) = 16), 
CHECK (StartDate < EndDate)