2009-10-23 8 views
7

Tengo una tabla que registra el ID de usuario, el curso, el sessionid y la fecha de solicitud cada vez que se carga una página web. Quiero calcular la duración por usuario para un curso determinado. Es problemático hacer esto debido a timespans superpuestos.Consulta SQL para calcular parte de la duración de la visita de la tabla de registro

Los datos proporcionados aquí deberían dar como resultado 10 minutos de duración por usuario para el curso 1. Parece que no puedo hacerlo bien.

CREATE TABLE PageLogSample (
    id INT NOT NULL PRIMARY KEY IDENTITY 
, userid INT 
, courseid INT 
, sessionid INT 
, requestdate DATETIME 
); 

TRUNCATE TABLE PageLogSample; 

INSERT INTO PageLogSample (userid, courseid, sessionid, requestdate) 
-- [0, 10] = 10 minutes 
      SELECT 1, 1, 1, '00:00:00' 
UNION ALL SELECT 1, 1, 1, '00:10:00' 
-- [0, 12] - [3, 5] = 10 minutes 
-- or ... [0, 3] + [5, 12] = 10 minutes 
UNION ALL SELECT 2, 1, 2, '00:00:00' 
UNION ALL SELECT 2, 2, 2, '00:03:00' 
UNION ALL SELECT 2, 2, 2, '00:05:00' 
UNION ALL SELECT 2, 1, 2, '00:12:00' 
-- [0, 12] - [3, 5] = 10 minutes 
-- or ... [0, 3] + [5, 12] = 10 minutes 
UNION ALL SELECT 3, 1, 3, '00:00:00' 
UNION ALL SELECT 3, 2, 3, '00:03:00' 
UNION ALL SELECT 3, 2, 3, '00:05:00' 
UNION ALL SELECT 3, 1, 3, '00:12:00' 
UNION ALL SELECT 3, 2, 3, '00:15:00' 
-- [1, 13] - [3, 5] = 10 minutes 
-- or ... [1, 3] + [5, 13] = 10 minutes 
UNION ALL SELECT 4, 2, 4, '00:00:00' 
UNION ALL SELECT 4, 1, 4, '00:01:00' 
UNION ALL SELECT 4, 2, 4, '00:03:00' 
UNION ALL SELECT 4, 2, 4, '00:05:00' 
UNION ALL SELECT 4, 1, 4, '00:13:00' 
UNION ALL SELECT 4, 2, 4, '00:15:00' 
-- [0, 5] + [10, 15] = 10 minutes 
UNION ALL SELECT 5, 1, 5, '00:00:00' 
UNION ALL SELECT 5, 1, 5, '00:05:00' 
UNION ALL SELECT 5, 1, 6, '00:10:00' 
UNION ALL SELECT 5, 1, 6, '00:15:00' 
-- [0, 10] = 10 minutes (ignoring everything inbetween) 
UNION ALL SELECT 6, 1, 7, '00:00:00' 
UNION ALL SELECT 6, 1, 7, '00:03:00' 
UNION ALL SELECT 6, 1, 7, '00:05:00' 
UNION ALL SELECT 6, 1, 7, '00:07:00' 
UNION ALL SELECT 6, 1, 7, '00:10:00' 
-- [0, 11] - [5, 6] = 10 minutes 
-- or ... [0, 3] + [7, 11] = 6 minutes (good) 
-- or ... [0, 5] + [7, 11] = 9 minutes (better) 
UNION ALL SELECT 7, 1, 8, '00:00:00' 
UNION ALL SELECT 7, 1, 8, '00:03:00' 
UNION ALL SELECT 7, 2, 8, '00:05:00' 
UNION ALL SELECT 7, 2, 8, '00:06:00' 
UNION ALL SELECT 7, 1, 8, '00:07:00' 
UNION ALL SELECT 7, 1, 8, '00:11:00' 
-- [0, 1] + [2, 4] + [5, 7] + [8, 13] = 10 
UNION ALL SELECT 8, 1, 9, '00:00:00' 
UNION ALL SELECT 8, 2, 9, '00:01:00' 
UNION ALL SELECT 8, 1, 9, '00:02:00' 
UNION ALL SELECT 8, 1, 9, '00:03:00' 
UNION ALL SELECT 8, 2, 9, '00:04:00' 
UNION ALL SELECT 8, 1, 9, '00:05:00' 
UNION ALL SELECT 8, 1, 9, '00:06:00' 
UNION ALL SELECT 8, 2, 9, '00:07:00' 
UNION ALL SELECT 8, 1, 9, '00:08:00' 
UNION ALL SELECT 8, 1, 9, '00:13:00' 
; 

first trying the naive approach. Esto genera errores al superponer partes de la sesión.

DECLARE @courseid INT; 
SET @courseid = 1; 

SELECT subquery.userid 
, COUNT(DISTINCT subquery.sessionid) AS sessioncount 
, SUM(subquery.duration) AS duration 
, CASE SUM(subquery.duration) 
    WHEN 10 THEN 'ok' 
    ELSE 'ERROR' 
END 
FROM (
    SELECT userid 
    , sessionid 
    , DATEDIFF(MINUTE, MIN(requestdate), MAX(requestdate)) AS duration 
    FROM PageLogSample 
    WHERE courseid = @courseid 
    GROUP BY userid 
    , sessionid 
) subquery 
GROUP BY subquery.userid 
ORDER BY subquery.userid; 

-- userid sessioncount duration 
-- 1  1    10  ok 
-- 2  1    12  ERROR 
-- 3  1    12  ERROR 
-- 4  1    12  ERROR 
-- 5  2    10  ok 

Segunda oportunidad. Evita las superposiciones. Esto solo funciona parcialmente

DECLARE @courseid INT; 
SET @courseid = 1; 

WITH cte (userid, courseid, sessionid, start, finish, duration) 
AS (
    SELECT userid 
    , courseid 
    , sessionid 
    , MIN(requestdate) 
    , MAX(requestdate) 
    , DATEDIFF(MINUTE, MIN(requestdate), MAX(requestdate)) 
    FROM PageLogSample 
    GROUP BY userid 
    , courseid 
    , sessionid 
) 
SELECT naive.userid 
, naive.sessioncount 
, naive.duration AS naiveduration 
, correction.duration AS correctionduration 
, naive.duration - ISNULL(correction.duration, 0) AS duration 
, CASE naive.duration - ISNULL(correction.duration, 0) 
    WHEN 10 THEN 'ok' 
    ELSE 'ERROR' 
END 
FROM (
    SELECT cte.userid 
    , COUNT(DISTINCT cte.sessionid) AS sessioncount 
    , SUM(cte.duration) AS duration 
    FROM cte 
    WHERE cte.courseid = @courseid 
    GROUP BY cte.userid 
) naive 
LEFT JOIN (
    SELECT errors.userid 
    , SUM(errors.duration) AS duration 
    FROM cte errors 
    WHERE errors.courseid <> @courseid 
    AND EXISTS (
     SELECT * 
     FROM cte 
     WHERE cte.start <= errors.start 
     AND cte.finish >= errors.finish 
     AND cte.courseid = @courseid 
    ) 
    GROUP BY errors.userid 
) correction 
ON naive.userid = correction.userid 
; 

-- userid sessioncount naiveduration correctionduration duration 
-- 1  1    10    NULL    10  ok 
-- 2  1    12    2     10  ok 
-- 3  1    12    NULL    12  ERROR 
-- 4  1    12    NULL    12  ERROR 
-- 5  2    10    NULL    10  ok 

Actualización: Ed Harpers comment realmente me hizo repensar mi enfoque.

Así que aquí viene la tercera prueba. Aquí primero busco las filas que representan una entrada al curso y que representan a alguien que se va. Luego tomo la suma de todos los tiempos del fin y restamos la suma de todas las begintimes. Creo que es más correcto, aunque no perfecto.

DECLARE @courseid INT; 
SET @courseid = 1; 

WITH numberedcte (rn, id, userid, courseid, sessionid, requestdate) 
AS (
    SELECT ROW_NUMBER() OVER (PARTITION BY sessionid, userid ORDER BY id) 
    , id 
    , userid 
    , courseid 
    , sessionid 
    , requestdate 
    FROM PageLogSample 
) 
, typedcte (rowtype, id, userid, courseid, sessionid, requestdate, nextrequestdate) 
AS (
    SELECT CASE 
     WHEN previousrequest.courseid = nextrequest.courseid 
      THEN 'between' 
     WHEN previousrequest.courseid IS NULL 
      OR nextrequest.courseid = numberedcte.courseid 
      THEN 'begin' 
     WHEN nextrequest.courseid IS NULL 
      OR previousrequest.courseid = numberedcte.courseid 
      THEN 'end' 
     ELSE 'error?' 
    END AS rowtype 
    , numberedcte.id 
    , numberedcte.userid 
    , numberedcte.courseid 
    , numberedcte.sessionid 
    , numberedcte.requestdate 
    , nextrequest.requestdate 
    FROM numberedcte 
    LEFT JOIN numberedcte previousrequest 
     ON previousrequest.userid = numberedcte.userid 
     AND previousrequest.sessionid = numberedcte.sessionid 
     AND previousrequest.rn = numberedcte.rn - 1 
    LEFT JOIN numberedcte nextrequest 
     ON nextrequest.userid = numberedcte.userid 
     AND nextrequest.sessionid = numberedcte.sessionid 
     AND nextrequest.rn = numberedcte.rn + 1 
    WHERE numberedcte.courseid = @courseid 
    AND (
     nextrequest.courseid = @courseid 
     OR previousrequest.courseid = @courseid 
    ) 
) 
, beginsum (userid, value) 
AS (
    SELECT userid, SUM(DATEPART(MINUTE, requestdate)) 
    FROM typedcte 
    WHERE rowtype = 'begin' 
    GROUP BY userid 
) 
, endsum (userid, value) 
AS (
    SELECT userid, SUM(DATEPART(MINUTE, ISNULL(nextrequestdate, requestdate))) 
    FROM typedcte 
    WHERE rowtype = 'end' 
    GROUP BY userid 
) 
SELECT beginsum.userid 
, endsum.value - beginsum.value AS duration 
FROM beginsum 
INNER JOIN endsum 
    ON beginsum.userid = endsum.userid 
; 

El único problema aquí es que solo me dan salida para el usuario 1 y 5 de mis datos de las muestras originales. El usuario agregado 6 también da salida correcta. El usuario agregado 7 me da un resultado satisfactorio ahora. El usuario 8 es casi perfecto, pierdo un minuto desde la primera fila hasta la segunda.

-- userid duration 
-- 1  10 
-- 5  10 
-- 6  10 
-- 7  9 
-- 8  9 

Siento que estoy a centímetros de conseguir esto completamente bien. Las únicas duraciones que faltan son las páginas solicitadas que no ocurrieron en grupos. ¿Alguien puede ayudarme a encontrar la forma de obtener vistas de página solitarias?

Actualización: Aquí viene una cuarta prueba. Aquí asigno un valor a cada solicitud y las resumo. No me da exactamente el resultado que esperaba, pero parece que podría ser lo suficientemente bueno.

DECLARE @courseid INT; 
SET @courseid = 1; 

WITH numberedcte (rn, userid, courseid, sessionid, requestdate) 
AS (
    SELECT ROW_NUMBER() OVER (PARTITION BY sessionid, userid ORDER BY id) 
    , userid 
    , courseid 
    , sessionid 
    , requestdate 
    FROM PageLogSample 
) 
, valuecte (value, userid, courseid, sessionid) 
AS (
    SELECT CASE 
     --alone 
     WHEN (previousrequest.courseid IS NULL 
      OR previousrequest.courseid <> numberedcte.courseid 
      ) 
      AND nextrequest.courseid <> numberedcte.courseid 
      THEN DATEDIFF(MINUTE, numberedcte.requestdate, nextrequest.requestdate) 
     --between 
     WHEN previousrequest.courseid = nextrequest.courseid 
      THEN 0 
     --begin 
     WHEN previousrequest.courseid IS NULL 
      OR nextrequest.courseid = numberedcte.courseid 
      THEN -1 * DATEPART(MINUTE, numberedcte.requestdate) 
     --ignored (end with no next request) 
     WHEN nextrequest.courseid IS NULL 
      AND previousrequest.courseid <> numberedcte.courseid 
      THEN 0 
     --end 
     WHEN nextrequest.courseid IS NULL 
      OR previousrequest.courseid = numberedcte.courseid 
      THEN DATEPART(MINUTE, ISNULL(nextrequest.requestdate, numberedcte.requestdate)) 
     --impossible? 
     ELSE 0 
    END 
    , numberedcte.userid 
    , numberedcte.courseid 
    , numberedcte.sessionid 
    FROM numberedcte 
    LEFT JOIN numberedcte previousrequest 
     ON previousrequest.userid = numberedcte.userid 
     AND previousrequest.sessionid = numberedcte.sessionid 
     AND previousrequest.rn = numberedcte.rn - 1 
    LEFT JOIN numberedcte nextrequest 
     ON nextrequest.userid = numberedcte.userid 
     AND nextrequest.sessionid = numberedcte.sessionid 
     AND nextrequest.rn = numberedcte.rn + 1 
    WHERE numberedcte.courseid = @courseid 
) 
SELECT userid 
, courseid 
, COUNT(DISTINCT sessionid) AS sessioncount 
, SUM(value) AS duration 
FROM valuecte 
GROUP BY userid 
, courseid 
ORDER BY userid 
; 

Como puede ver, los resultados no son del todo lo que esperaba.

-- userid courseid sessioncount duration 
-- 1  1   1    10 
-- 2  1   1    3 
-- 3  1   1    6 
-- 4  1   1    4 
-- 5  1   2    10 
-- 6  1   1    10 
-- 7  1   1    9 
-- 8  1   1    10 

El rendimiento es horrible en mi copia local de la base de datos real. Entonces, si alguien tiene ideas para escribir esto de una manera más eficiente ... disparar.

Actualización: El rendimiento está arriba. Agregué un índice y funciona un encanto ahora.

+2

Buena pregunta, en términos de secuencia de comandos y muestra para que sea más fácil de resolver. – Andrew

+0

La dificultad con los datos es que requestdate no tiene un significado consistente. A veces es la hora de inicio y algunas veces la hora de finalización de un curso. –

+0

Buen comentario a la buena pregunta. –

Respuesta

0

Algunos más datos de muestra y una hipótesis, con suerte lógica, de cuánto tiempo pasó cada usuario en cada curso.

INSERT INTO PageLogSample (userid, courseid, sessionid, requestdate) 
-- [0, 10] = 10 minutes 
      SELECT 1, 1, 1, '00:00:00' 
UNION ALL SELECT 1, 1, 1, '00:10:00' 
-- [0, 3] = 3 minutes 
-- there is no way to know how long the user was on that last page 
UNION ALL SELECT 2, 1, 2, '00:00:00' 
UNION ALL SELECT 2, 2, 2, '00:03:00' 
UNION ALL SELECT 2, 2, 2, '00:05:00' 
UNION ALL SELECT 2, 1, 2, '00:12:00' 
-- [0, 3] + [12, 15] = 6 minutes 
-- the [5, 12] part was spent on a page of course 2 
UNION ALL SELECT 3, 1, 3, '00:00:00' 
UNION ALL SELECT 3, 2, 3, '00:03:00' 
UNION ALL SELECT 3, 2, 3, '00:05:00' 
UNION ALL SELECT 3, 1, 3, '00:12:00' 
UNION ALL SELECT 3, 2, 3, '00:15:00' 
-- [1, 3] + [13, 15] = 4 minutes 
UNION ALL SELECT 4, 2, 4, '00:00:00' 
UNION ALL SELECT 4, 1, 4, '00:01:00' 
UNION ALL SELECT 4, 2, 4, '00:03:00' 
UNION ALL SELECT 4, 2, 4, '00:05:00' 
UNION ALL SELECT 4, 1, 4, '00:13:00' 
UNION ALL SELECT 4, 2, 4, '00:15:00' 
-- [0, 5] + [10, 15] = 10 minutes 
UNION ALL SELECT 5, 1, 5, '00:00:00' 
UNION ALL SELECT 5, 1, 5, '00:05:00' 
UNION ALL SELECT 5, 1, 6, '00:10:00' 
UNION ALL SELECT 5, 1, 6, '00:15:00' 
-- [0, 10] = 10 minutes (ignoring everything inbetween) 
UNION ALL SELECT 6, 1, 7, '00:00:00' 
UNION ALL SELECT 6, 1, 7, '00:03:00' 
UNION ALL SELECT 6, 1, 7, '00:05:00' 
UNION ALL SELECT 6, 1, 7, '00:07:00' 
UNION ALL SELECT 6, 1, 7, '00:10:00' 
-- [0, 5] + [7, 11] = 9 minutes 
UNION ALL SELECT 7, 1, 8, '00:00:00' 
UNION ALL SELECT 7, 1, 8, '00:03:00' 
UNION ALL SELECT 7, 2, 8, '00:05:00' 
UNION ALL SELECT 7, 2, 8, '00:06:00' 
UNION ALL SELECT 7, 1, 8, '00:07:00' 
UNION ALL SELECT 7, 1, 8, '00:11:00' 
-- [0, 1] + [2, 4] + [5, 7] + [8, 13] = 10 
UNION ALL SELECT 8, 1, 9, '00:00:00' 
UNION ALL SELECT 8, 2, 9, '00:01:00' 
UNION ALL SELECT 8, 1, 9, '00:02:00' 
UNION ALL SELECT 8, 1, 9, '00:03:00' 
UNION ALL SELECT 8, 2, 9, '00:04:00' 
UNION ALL SELECT 8, 1, 9, '00:05:00' 
UNION ALL SELECT 8, 1, 9, '00:06:00' 
UNION ALL SELECT 8, 2, 9, '00:07:00' 
UNION ALL SELECT 8, 1, 9, '00:08:00' 
UNION ALL SELECT 8, 1, 9, '00:13:00' 
-- there is nothing we can say about either of there requests 
-- 0 minutes 
UNION ALL SELECT 9, 1, 10, '00:10:00' 
UNION ALL SELECT 9, 1, 11, '00:20:00' 
; 

ahora tenemos nuestros datos así:

WITH numberedcte (rn, userid, courseid, sessionid, requestdate) 
AS (
    SELECT ROW_NUMBER() OVER (PARTITION BY sessionid, userid ORDER BY id) 
    , userid 
    , courseid 
    , sessionid 
    , requestdate 
    FROM PageLogSample 
) 
, valuecte (value, userid, courseid, sessionid) 
AS (
    SELECT CASE 
     --alone in session 
     WHEN previousrequest.courseid IS NULL 
      AND nextrequest.courseid IS NULL 
      THEN 0 
     --alone 
     WHEN (previousrequest.courseid IS NULL 
      OR previousrequest.courseid <> numberedcte.courseid 
      ) 
      AND nextrequest.courseid <> numberedcte.courseid 
      THEN DATEDIFF(MINUTE, numberedcte.requestdate, nextrequest.requestdate) 
     --between 
     WHEN previousrequest.courseid = nextrequest.courseid 
      THEN 0 
     --begin 
     WHEN previousrequest.courseid IS NULL 
      OR nextrequest.courseid = numberedcte.courseid 
      THEN -1 * DATEPART(MINUTE, numberedcte.requestdate) 
     --ignored (end with no next request) 
     WHEN nextrequest.courseid IS NULL 
      AND previousrequest.courseid <> numberedcte.courseid 
      THEN 0 
     --end 
     WHEN nextrequest.courseid IS NULL 
      OR previousrequest.courseid = numberedcte.courseid 
      THEN DATEPART(MINUTE, ISNULL(nextrequest.requestdate, numberedcte.requestdate)) 
     --impossible? 
     ELSE 0 
    END 
    , numberedcte.userid 
    , numberedcte.courseid 
    , numberedcte.sessionid 
    FROM numberedcte 
    LEFT JOIN numberedcte previousrequest 
     ON previousrequest.userid = numberedcte.userid 
     AND previousrequest.sessionid = numberedcte.sessionid 
     AND previousrequest.rn = numberedcte.rn - 1 
    LEFT JOIN numberedcte nextrequest 
     ON nextrequest.userid = numberedcte.userid 
     AND nextrequest.sessionid = numberedcte.sessionid 
     AND nextrequest.rn = numberedcte.rn + 1 
    WHERE numberedcte.courseid = @courseid 
) 
SELECT userid 
, courseid 
, COUNT(DISTINCT sessionid) AS sessioncount 
, SUM(value) AS duration 
FROM valuecte 
GROUP BY userid 
, courseid 
ORDER BY userid 
; 

Este es el resultado que consigo. Estoy bastante satisfecho con eso. Observe cómo el recuento de sesiones sigue siendo correcto para el usuario 9.

userid courseid sessioncount duration 
1  1   1    10 
2  1   1    3 
3  1   1    6 
4  1   1    4 
5  1   2    10 
6  1   1    10 
7  1   1    9 
8  1   1    10 
9  1   2    0 
0

Lo sentimos, pero creo que tiene un problema con los datos. En cuanto a los datos de muestra proporcionados, el usuario 2 está en el curso 1 durante 12 minutos y curso 2 durante 2 minutos.

¿Estás seguro de que has proporcionado los datos correctos?

+0

Los datos son correctos, pero es difícil sacarle un significado relevante. El usuario 2 comienza en el curso 1, va al curso 2 durante dos minutos y luego regresa al curso 1. Quiero el tiempo que pasó en el curso 1 (10 minutos). Por lo tanto, 12 minutos menos los 2 minutos que pasó en otro curso. –

+0

Parece que tenía razón. Mi interpretación original de los datos fue errónea. –

0

Esto es lo más cerca que puedo llegar. Falla para el ID de usuario 4.

Como dije en mi comentario, requestdate a veces es un comienzo y, a veces, un final de un curso, y no puedo ver una regla general simple para deducir qué papel desempeña en una fila determinada.

DECLARE @courseid INT; 
SET @courseid = 1; 

WITH orderCTE 
AS 
(
     SELECT * 

       ,ROW_NUMBER() OVER (PARTITION BY sessionid 
            ORDER BY id 
           ) AS rn 
     FROM PageLogSample 
     --order by rn 
) 
,startendCTE 
AS 
(
     SELECT CASE WHEN start1.rn = 1 
        THEN start1.courseid 
        ELSE end1.courseid 
       END courseid 
       ,start1.sessionid 
       ,start1.userid 
       ,DATEDIFF(mi,start1.requestdate,end1.requestdate) duration 
     FROM orderCTE AS start1 
     JOIN orderCTE AS end1 
     ON end1.rn = start1.rn + 1 
     AND end1.sessionid = start1.sessionid 
) 
SELECT courseid 
     ,COUNT(1) sessionCount 
     ,userid 
     ,SUM(duration) totalDuration 
FROM startendCTE 
WHERE courseid = @courseid 
GROUP BY courseid 
     ,userid; 
+0

Me gusta la idea de buscar primero las filas iniciales y finales. Me has inspirado a tomar un nuevo enfoque. –

0

Esto es bastante complicado, pero parece estar trabajando para courseid 1. No he intentado con otros cursos, por lo que es posible que desee probar eso! : D

La premisa básica es que obtengo el tiempo entre la primera y la última sesión del CourseID objetivo y luego restamos la duración de las sesiones que no fueron del CourseID especificado, pero donde el tiempo de solicitud de sesión cayó dentro de los tiempos de solicitud mínimo y máximo del CourseID objetivo. Espero que tenga sentido.

La consulta definitivamente podría limpiarse, posiblemente con un CTE o algo así. Pregunta interesante por cierto! :)

DECLARE @courseid INT; 
SET @courseid = 1; 

SELECT 
    TargetCourse.UserID, 
    COUNT(Distinct(TargetCourse.SessionID)) as SessionCount, 
    SUM(TargetCourse.Duration - Coalesce(OtherCourses.Duration,0)) as Duration 
FROM 
(
    SELECT 
     TargetCourse.UserID, TargetCourse.SessionID, 
     MIN(TargetCourse.RequestDate) FirstRequest, MAX(TargetCourse.RequestDate) LastRequest, 
     DATEDIFF(MINUTE, MIN(TargetCourse.RequestDate), MAX(TargetCourse.RequestDate)) AS duration 
    FROM 
     PageLogSample TargetCourse 
    WHERE 
     TargetCourse.CourseID = @courseid 
    GROUP BY 
     TargetCourse.UserID, TargetCourse.SessionID  
) as TargetCourse 
LEFT OUTER JOIN 
(
    SELECT 
     OtherCourses.UserID, OtherCourses.SessionID, 
     MIN(OtherCourses.RequestDate) AS FirstRequest, MAX(OtherCourses.RequestDate) AS LastRequest, 
     DATEDIFF(MINUTE, MIN(OtherCourses.RequestDate), MAX(OtherCourses.RequestDate)) AS duration 
    FROM 
     PageLogSample OtherCourses 
    WHERE 
     OtherCourses.CourseID <> @courseid AND 
     OtherCourses.RequestDate between 
      (Select MIN(RequestDate) From PageLogSample T Where T.UserID = OtherCourses.UserID and T.CourseID = @courseid) AND 
      (Select MAX(RequestDate) From PageLogSample T Where T.UserID = OtherCourses.UserID and T.CourseID = @courseid) 
    GROUP BY 
     OtherCourses.UserID, OtherCourses.SessionID 
) as OtherCourses ON 
OtherCourses.UserID = TargetCourse.UserID AND 
OtherCourses.FirstRequest BETWEEN TargetCourse.FirstRequest and TargetCourse.LastRequest 
Group By TargetCourse.UserID 
+0

Muy bien, pero no perfecto todavía. Cuando busca el tiempo para restar, debe tener en cuenta que podría haber más de una "brecha" con el curso. Agregué un usuario adicional a los datos de muestra para mostrar esto. –

+0

Aha, gracias por señalar eso. Completaré el usuario adicional y le daré otra oportunidad. :) – WesleyJohnson

-1

"La información es correcta, pero es difícil sacarle un significado relevante".

Estoy obligado a responder que esto es una contradicción de términos. Datos de los que no sabes lo que significan no son datos.

En cuanto a su pregunta original:

Lo que se necesita es un DBMS que ofrece apoyo decente para este tipo de intervalo. Ningún sistema SQL se reproduce en esa liga. Además de unos pocos sistemas de tutoriales, mi propio DBMS (sin más empuje que en este contexto, por lo que no hay un enlace) es el único que conozco que ofrece el tipo de soporte que realmente se necesita para tales problemas.

Si está interesado, busque en Google los "tipos de intervalo", "forma normal empaquetada", "datos temporales" y lo encontrará eventualmente.

Cuestiones relacionadas