2009-03-11 4 views
1

Die Frage ist, wie lange diese Kunden an einem bestimmten Datum Idioten waren.SQL-Datumsabfrage - wie lange ist diese Bedingung wahr

Ich arbeite gegen Sybase

Für diese vereinfachte Tabellenstruktur der Tabelle history_data

 
table: history_of_jerkiness 
processing_date name is_jerk 
--------------- ----- ------- 
20090101   Matt true 
20090101   Bob false   
20090101   Alex true   
20090101   Carol true   
20090102   Matt true   
20090102   Bob true   
20090102   Alex false   
20090102   Carol true   
20090103   Matt true   
20090103   Bob true   
20090103   Alex true   
20090103   Carol false   

Der Bericht für das dritte soll zeigen, dass Matt ein Ruck immer war, Alex ein Ruck gerade geworden ist und Bob war 2 Tage lang ein Idiot.

 
name days jerky 
----- ---------- 
Matt 3 
Bob  2 
Alex 1 

Ich möchte diese Spannweiten von Zeit so dynamisch zu finden, wenn ich den Bericht für den 2. Lauf, ich unterschiedliche Ergebnisse erhalten sollten:

 
name days_jerky 
----- ---------- 
Matt 2 
Bob  1 
Carol 2 

Der Schlüssel hier versucht, nur zu finden, kontinuierliche Bereiche älter als ein bestimmtes Datum. Ich habe ein paar Hinweise gefunden, aber es scheint ein Problem zu sein, bei dem es sehr clevere Lösungen gäbe.

+0

Wäre für jede Person immer ein Eintrag für jeden Tag vorhanden? –

+0

Ich habe dafür keine Lösung, aber ich muss sagen, ich lachte laut über den Inhalt "Tage ruckeln". +1: D –

+0

Interessanterweise ist die wirkliche Lösung, immer mit den Benutzern zu sprechen. Nach einer kurzen Diskussion stellte ich fest, dass das, was sie wirklich brauchten, trivial war und dass ein BA angenommen hatte, dass sie den alten und schrecklichen Bericht, den sie zuvor hatten, gemocht hatten. IMMER mit USERS sprechen. – MattK

Antwort

2

Meine Lösung von SQL Server - das gleiche wie Dems, aber ich habe selbst eine minimale Baseline eingegeben. Es wird davon ausgegangen, dass es keine Lücken gibt - das heißt, es gibt einen Eintrag für jeden Tag für jede Person. Wenn das nicht stimmt, müsste ich eine Schleife machen.

DECLARE @run_date datetime 
DECLARE @min_date datetime 

SET @run_date = {d '2009-01-03'} 

-- get day before any entries in the table to use as a false baseline date 
SELECT @min_date = DATEADD(day, -1, MIN(processing_date)) FROM history_of_jerkiness 

-- get last not a jerk date for each name that is before or on the run date 
-- the difference in days between the run date and the last not a jerk date is the number of days as a jerk 
SELECT [name], DATEDIFF(day, MAX(processing_date), @run_date) 
FROM (
    SELECT processing_date, [name], is_jerk 
    FROM history_of_jerkiness 
    UNION ALL 
    SELECT DISTINCT @min_date, [name], 0 
    FROM history_of_jerkiness) as data 
WHERE is_jerk = 0 
    AND processing_date <= @run_date 
GROUP BY [name] 
HAVING DATEDIFF(day, MAX(processing_date), @run_date) > 0 

Ich habe die Test-Tabelle mit den folgenden:

CREATE TABLE history_of_jerkiness (processing_date datetime, [name] varchar(20), is_jerk bit) 

INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Matt', 1) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Bob', 0) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Alex', 1) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-01'}, 'Carol', 1) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Matt', 1) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Bob', 1) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Alex', 0) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-02'}, 'Carol', 1) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Matt', 1) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Bob', 1) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Alex', 1) 
INSERT INTO history_of_jerkiness (processing_date, [name], is_jerk) VALUES ({d '2009-01-03'}, 'Carol', 0) 
1

Dies kann einfach gemacht werden, wenn Sie die Datenstruktur, um die folgenden Kriterien ...

Alle Menschen einen anfänglichen Datensatz müssen gerecht zu werden, wo sie

Sie nicht ein Ruck sind tun kann, so etwas wie ...

SELECT 
    name, 
    MAX(date) last_day_jerk_free 
FROM 
    jerkiness AS [data] 
WHERE 
    jerk = 'false' 
    AND date <= 'a date' 
GROUP BY 
    name 

Sie wissen bereits, was das Basisdatum ist (‚a date‘), und jetzt wissen Sie, den letzten Tag, der sie nicht ein Ruck waren. Ich weiß nicht, sybase, aber ich bin sicher, es gibt Befehle Sie die Anzahl der Tage, um zwischen ‚einer Daten‘ und ‚last_day_jerk_free‘

EDIT verwenden:

Es gibt mehrere Möglichkeiten, künstlich Schaffung eines Initialisierung "nicht ruckeln" Rekord. Der von Will Rickards vorgeschlagene verwendet eine Unterabfrage, die eine Union enthält. so jedoch tun, hat zwei Seiten nach unten ...
1. Die Unterabfragemasken alle Indizes, die sonst verwendet wurden
2. Es wird davon ausgegangen, alle Menschen haben Ausgangsdaten aus dem gleichen Punkt

Alternativ Will Rickard Vorschlag übernehmen und die Aggregation von der äußeren Abfrage in die innere Abfrage bewegen (so Verwendung von Indizes zu maximieren), und die Vereinigung mit einer verallgemeinerten zweiten Sub-Abfrage des Start ruckelt = false Datensatz erstellen ...

SELECT name, DATEDIFF(day, MAX(processing_date), @run_date) AS days_jerky 
FROM (

    SELECT name, MAX(processing_date) as processing_date 
    FROM history_of_jerkiness 
    WHERE is_jerk = 0 AND processing_date <= @run_date 
    GROUP BY name 

    UNION 

    SELECT name, DATEADD(DAY, -1, MIN(processing_date)) 
    FROM history_of_jerkiness 
    WHERE processing_date <= @run_date 
    GROUP BY name 

    ) as data 
GROUP BY 
    name 

Die äußere Abfrage muss immer noch ein Maximum ohne Indizes, aber über eine reduzierte Anzahl von Datensätzen (2 pro Name, anstatt n pro Name). Die Anzahl der Datensätze wird ebenfalls reduziert, da nicht jeder Name für jedes verwendete Datum einen Wert haben muss. Es gibt viele andere Möglichkeiten, dies zu tun, einige können in meiner Bearbeitungsgeschichte gesehen werden.

0

Wie wäre es damit:

select a.name,count(*) from history_of_jerkiness a 
left join history_of_jerkiness b 
on a.name = b.name 
and a.processing_date >= b.processing_date 
and a.is_jerk = 'true' 
where not exists 
(select * from history_of_jerkiness c 
    where a.name = c.name 
    and c.processing_date between a.processing_date and b.processing_date 
    and c.is_jerk = 'false' 
) 
and a.processing_date <= :a_certain_date; 
+0

Ich habe in SQL Server getestet und es funktioniert nicht. Ich denke, ich verstehe, was du versuchst. Ich habe versucht, es zu beheben, aber ich hatte keine Zeit, es zu beenden. –

1

„Diese einfach gemacht werden können, wenn Sie die Datenstruktur, um die treffen folgende Kriterien ...

Alle Leute müssen eine erste Aufzeichnung haben, wo sie kein Idiot sind "

Welche Kriterien die Daten erfüllen sollten und nicht erfüllen sollten, liegt beim Benutzer und nicht beim Entwickler.

+0

aber der Benutzer ist normalerweise nicht sicher. Ein bisschen Verhandeln um das, was sie wirklich brauchen, kann Probleme erheblich vereinfachen. – MattK