2009-11-02 11 views
5

Tengo una tabla que es una lista de juegos que se han reproducido en una base de datos sqlite3. El campo "fecha y hora" es la fecha de finalización del juego. El campo "duración" es la cantidad de segundos que duró el juego. Quiero saber qué porcentaje de las últimas 24 horas tuvo al menos 5 juegos funcionando simultaneamente. Me di cuenta de que contar el número de juegos que se ejecutan en un momento dado son:Sqlite3: Necesidad de cartesiano Fecha

select count(*) 
from games 
where strftime('%s',datetime)+0 >= 1257173442 and 
     strftime('%s',datetime)-duration <= 1257173442 

Si tuviera una mesa que era simplemente una lista de cada segundo (o cada 30 segundos o algo así) que podía hacer un producto cartisian intencional de esta manera:

select count(*) 
from (
    select count(*) as concurrent, d.second 
    from games g, date d 
    where strftime('%s',datetime)+0 >= d.second and 
     strftime('%s',datetime)-duration <= d.second and 
     d.second >= strftime('%s','now') - 24*60*60 and 
     d.second <= strftime('%s','now') 
    group by d.second) x 
where concurrent >=5 

¿hay una manera de crear esta tabla fecha sobre la marcha? ¿O que puedo obtener un efecto similar a este sin tener que crear realmente una nueva tabla que es simplemente una lista de todos los segundos de esta semana?

Gracias

Respuesta

2

¡Una gran pregunta!

Aquí hay una consulta que I piensa que le da lo que quiere sin usar una tabla separada. Tenga en cuenta que esto no se ha probado (por lo que probablemente contenga errores) y he supuesto que datetime es una columna int con # de segundos para evitar una tonelada de strftime.

select sum(concurrent_period) from (
    select min(end_table.datetime - begin_table.begin_time) as concurrent_period 
    from (
    select g1.datetime, g1.num_end, count(*) as concurrent 
    from (
     select datetime, count(*) as num_end 
      from games group by datetime 
    ) g1, games g2 
    where g2.datetime >= g1.datetime and 
      g2.datetime-g2.duration < g1.datetime and 
      g1.datetime >= strftime('%s','now') - 24*60*60 and 
      g1.datetime <= strftime('%s','now')+0 
) end_table, (
    select g3.begin_time, g1.num_begin, count(*) as concurrent 
    from (
     select datetime-duration as begin_time, 
      count(*) as num_begin 
      from games group by datetime-duration 
    ) g3, games g4 
    where g4.datetime >= g3.begin_time and 
      g4.datetime-g4.duration < g3.begin_time and 
      g3.begin_time >= strftime('%s','now') - 24*60*60 and 
      g3.begin_time >= strftime('%s','now')+0 
) begin_table 
    where end_table.datetime > begin_table.begin_time 
     and begin_table.concurrent < 5 
     and begin_table.concurrent+begin_table.num_begin >= 5 
     and end_table.concurrent >= 5 
     and end_table.concurrent-end_table.num_end < 5 
    group by begin_table.begin_time 
) aah 

La idea básica es hacer dos tablas: una con el # de juegos simultáneos en el momento comenzará de cada juego, y uno con el # de juegos simultáneos en el tiempo final. Luego únanse a las mesas juntas y solo tomen filas en "puntos críticos" donde cruce # de juegos simultáneos. Para cada hora crítica de inicio, tomen el tiempo final crítico que sucedió lo más pronto posible y que con suerte da todos los períodos en los que se ejecutaron al menos 5 juegos al mismo tiempo.

Espero que no sea complicado para ser útil!

0

¿Por qué no se recorta la fecha y mantener sólo el tiempo, si filtra los datos para una fecha determinada cada vez que es único. De esta forma, solo necesitará una tabla con números del 1 al 86400 (o menos si toma intervalos más grandes), puede crear dos columnas, "desde" y "hasta" para definir los intervalos. No estoy familiarizado con las funciones SQLite, pero de acuerdo con el manual debe usar la función strftime con este formato: HH: MM: SS.

3

Primero, no puedo pensar en una forma de abordar su problema creando una mesa sobre la marcha o sin la ayuda de una mesa extra. Lo siento.

Mi sugerencia es que confíe en una tabla de Números estáticos.

Crear una mesa fija con el formato:

CREATE TABLE Numbers (
    number INTEGER PRIMARY KEY 
); 

rellenarla con el número de segundos en 24h (24 * 60 * 60 = 84600). Me gustaría utilizar cualquier lenguaje de script para hacer que el uso de la instrucción de inserción:

insert into numbers default values; 

Ahora la tabla Números tiene los números del 1 al 84600. Su consulta se les pueden modificar para ser:

select count(*) 
    from (
     select count(*) as concurrent, strftime('%s','now') - 84601 + n.number second 
      from games g, numbers n 
     where strftime('%s',datetime)+0 >= strftime('%s','now') - 84601 + n.number and 
       strftime('%s',datetime)-duration <= strftime('%s','now') - 84601 + n.number 
     group by second) x 
where concurrent >=5 

Sin lenguaje de procedimiento en la mezcla, eso es lo mejor que podrá hacer, creo.

+0

El truco tabla de números es común cuando no se puede utilizar la factorización recursiva CTE/subconsulta ... –

+0

Sí, y la tabla de números será útil en más de este escenario particular. Siempre puede limitarlo y obtener solo el subconjunto que necesita: 'seleccione el número de los números límite 100;' es simplemente una construcción conveniente para tener alrededor ... – cethegeek

2

Kevin mejoró, me golpeó hasta el final allí (+1), pero publicaré esta variación ya que difiere al menos un poco.

Las ideas fundamentales son

  • asignar los datos a un flujo de eventos con atributos de tiempo y 'polaridad' (= inicio o al final del partido)
  • Mantener un total acumulado de cuántos juegos se abierto en el momento de cada evento (esto se hace formando una autocombinación en la secuencia de eventos)
  • Encuentra los tiempos de los eventos donde el número de juegos (como Kevin dice) transiciones hasta 5, o hasta 4
  • Un pequeño truco: sume todas las veces 4 veces y quítate que hasta a 5 años - el orden no es importante
  • El resultado es el número de segundos que pasa con 5 o más juegos abiertos

no tengo SQLLite, así que he estado probando con MySQL , y no me he molestado en limitar la ventana de tiempo para preservar la cordura. No debería ser difícil de revisar.

Además, y más importante aún, ¡no he considerado qué hacer si los juegos están abiertos al principio o al final del período!

Algo me dice que aquí hay una gran simplificación, pero aún no lo he visto.

SELECT SUM(event_time) 
FROM (
SELECT -ga.event_type * ga.event_time AS event_time, 
    SUM( ga.event_type * gb.event_type) event_type 
FROM 
    (SELECT UNIX_TIMESTAMP(g1.endtime - g1.duration) AS event_time 
      , 1 event_type 
     FROM games g1 
     UNION 
     SELECT UNIX_TIMESTAMP(g1.endtime) 
      , -1 
     FROM games g1) AS ga, 
    (SELECT UNIX_TIMESTAMP(g1.endtime - g1.duration) AS event_time 
      , 1 event_type 
     FROM games g1 
     UNION 
     SELECT UNIX_TIMESTAMP(g1.endtime) 
      , -1 
     FROM games g1) AS gb 
WHERE 
    ga.event_time >= gb.event_time 
GROUP BY ga.event_time 
HAVING SUM(ga.event_type * gb.event_type) IN (-4, 5) 
) AS gr