2014-12-08 20 views
8

Si consideri la seguente query:Arel: seleziona dalla funzione

SELECT DATE_TRUNC('hour', date_range) 
FROM GENERATE_SERIES(:start_date, :end_date, :interval) as date_range 

E 'possibile utilizzare GENERATE_SERIES(...) come una tabella (fonte dati)? Idealmente, sarebbe simile a questa:

t = series(start, end, as: 'date_range') 
dt = Arel::Nodes::NamedFunction.new('DATE_TRUNC', ['hour', t[:date_range]]) 
t.project(dt) 

UPD1. Perché ho bisogno di GENERATE_SERIES? Ho alcuni dati che ho bisogno di elaborare per quanto riguarda i timestamp su cui erano disponibili questi dati e li ho prodotti come un grafico 2D. Come esempio semplice si consideri un clickstream = (id, created_at). Voglio tracciare un numero di clic effettuati prima di una certa data rispetto a una data griglia datetime (cioè Nov 17, Nov 18, Nov 19, ..., Nov 30). E il fatto è che voglio che tutto questo avvenga all'interno di PostgreSQL.

Aggiornamento2. query di esempio, senza variabili potrebbe essere la seguente:

SELECT DATE_TRUNC('hour', date_range) FROM GENERATE_SERIES('2015-01-01 00:15:38'::TIMESTAMP, '2015-01-10 23:59:59'::TIMESTAMP, '1 HOUR') as date_range; 
+0

Può descrivere brevemente cosa stai cercando di raggiungere, a parte il codice che hai usato? Può esserci una soluzione migliore o diversa che non implichi la funzione 'generate_series'. –

+0

Fornire una query di esempio reale senza variabili. – dankohn

+0

@SimoneCarletti Ho aggiornato la domanda con una motivazione dietro generare serie. Ha senso ora? –

risposta

4

Un modo è quello di costruire semplicemente tutte le parti necessarie del AST manualmente

def timestamp(ts) 
    Arel::Nodes::NamedFunction.new(
    'CAST', [ 
     Arel::Nodes::As.new(
     Arel::Nodes.build_quoted(ts), 
     Arel::Nodes::SqlLiteral.new('timestamp') 
    ) 
    ] 
) 
end 

def series(from, to, by, options = {}) 
    Arel::Nodes::NamedFunction.new(
    'GENERATE_SERIES', [ 
     timestamp(from), 
     timestamp(to), 
     Arel::Nodes::SqlLiteral.new(by) 
    ] 
).as(options.fetch(:as, 'series')) 
end 

def date_trunc(by, attribute) 
    Arel::Nodes::NamedFunction.new(
    'DATE_TRUNC', [Arel.sql("'#{by}'"), attribute] 
) 
end 

date_range = Arel::Table.new('date_range') 
result = date_range. 
    from(series(5.days.ago, 4.days.ago, "'1 hour'", as: 'date_range')). 
    project(date_trunc('hour', date_range[:date_range])) 

prova:

User.find_by_sql(result.to_sql).to_a.map &:attributes  

SELECT DATE_TRUNC('hour', "date_range"."date_range") 
FROM GENERATE_SERIES(
    CAST('2015-02-03 21:29:22.729188' AS timestamp), 
    CAST('2015-02-04 21:29:22.729633' AS timestamp), 
    '1 hour' 
) AS date_range 

[{"date_trunc"=>2015-02-03 21:00:00 UTC, "id"=>nil}, 
{"date_trunc"=>2015-02-03 22:00:00 UTC, "id"=>nil}, 
{"date_trunc"=>2015-02-03 23:00:00 UTC, "id"=>nil}, 
{"date_trunc"=>2015-02-04 00:00:00 UTC, "id"=>nil}, 
# ... 
{"date_trunc"=>2015-02-04 21:00:00 UTC, "id"=>nil}] 
+0

È davvero un lavoro. Penso che dovrebbe essere 'Arel :: Nodes :: SqlLiteral.new (timestamp (from))' – hqt