2009-03-11 2 views
1

La question est de savoir depuis combien de temps ces clients ont été saccadés à une date donnée.SQL Dates Requête - depuis combien de temps cette condition est-elle vraie?

Je travaille contre Sybase

Pour cette structure de table simplifiée de la table 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   

Le rapport du 3 devrait montrer que Matt a toujours été une secousse, Alex vient de devenir une secousse , et Bob a été un crétin pendant 2 jours.

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

Je voudrais trouver ces travées de temps dynamique, donc si je lance le rapport pour la 2e, je devrais obtenir des résultats différents:

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

La clé ici tente de trouver seulement travées continues plus d'une certaine date. J'ai trouvé quelques pistes, mais il semble y avoir un problème où il y aurait des solutions très intelligentes.

+0

Y aurait-il toujours une entrée pour chaque jour pour chaque personne? –

+0

Je n'ai pas de solution à cela mais je dois dire que j'ai ri à haute voix au contenu "jours saccadés". +1: D –

+0

assez intéressant, la vraie solution est de toujours parler aux utilisateurs. Après une petite discussion, j'ai trouvé que ce dont ils avaient vraiment besoin était insignifiant, et qu'un BA avait supposé qu'ils aimaient le vieux et horrible rapport qu'ils avaient auparavant. TOUJOURS parler aux UTILISATEURS. – MattK

Répondre

2

Ma solution de SQL Server - même que Dems mais je mets dans une base de référence min moi-même. Il suppose qu'il n'y a pas de lacunes - c'est-à-dire qu'il y a une entrée pour chaque jour pour chaque personne. Si ce n'est pas vrai alors je devrais boucler.

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 

J'ai créé la table de test avec les éléments suivants:

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

Cela peut être simple si vous structurez les données de manière à répondre aux critères suivants ...

Toutes les personnes doivent avoir un enregistrement initial où ils ne sont pas une secousse

Vous peut faire quelque chose comme ...

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

vous savez déjà ce que la date de base est (« une date »), et maintenant vous connaissez le dernier jour, ils ne sont pas une secousse. Je ne sais pas mais je sybase suis sûr qu'il ya des commandes que vous pouvez utiliser pour obtenir le nombre de jours entre « une donnée » et « last_day_jerk_free »

EDIT:

Il y a plusieurs façons de créer artificiellement un initialisation de l'enregistrement "pas saccadé". Celui suggéré par Will Rickards utilise une sous-requête contenant une union. Cela, cependant, a deux côtés vers le bas ...
1. Les masques de sous-requête tous les index qui peuvent par ailleurs avoir été utilisés
2. Il suppose que toutes les personnes ont des données à partir du même point

Alternativement, prendre la suggestion de Will Rickard et passer l'agrégation de la requête externe dans la requête interne (utilisation maximisant ainsi des indices), et de l'union avec une généralisée 2ème sous requête pour créer la saccadée à partir = false record ...

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 

La requête externe doit toujours faire un max sans index, mais sur un nombre réduit d'enregistrements (2 par nom, plutôt que n par nom). Le nombre d'enregistrements est également réduit en n'exigeant pas que chaque nom ait une valeur pour chaque date d'utilisation. Il y a beaucoup d'autres façons de le faire, certaines peuvent être vues dans mon historique d'édition.

0

Que diriez-vous ceci:

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

J'ai testé sur le serveur SQL et cela ne fonctionne pas. Je pense que je comprends ce que vous essayez. J'ai essayé de le réparer mais je n'ai pas eu le temps de finir. –

1

« Cela peut être simple si vous structurez les données de manière à répondre à la les critères suivants ...

Toutes les personnes doivent avoir un enregistrement initial où elles ne sont pas un abruti "

Les critères auxquels les données doivent et ne doivent pas répondre dépendent de l'utilisateur et non du développeur.

+0

mais l'utilisateur n'est généralement pas certain. Un peu de négociation autour de ce dont ils ont réellement besoin peut grandement simplifier les problèmes. – MattK

Questions connexes