Функция для запроса различных временных рядов в R с вектором в качестве входных данных - PullRequest
1 голос
/ 12 января 2020

Я получал минимальные и максимальные даты для указанного c временного ряда в таблице фактов базы данных следующим образом:

auxiliar.dates <- function(machine, signal) {
  q.Aux1 <- paste("SELECT
         t1.machine,
       t1.signal,
       t2.signal_name,
       t1.min_snsr_dt,
       t1.max_snsr_dt,
       t1.min_snsr_ts,
       t1.max_snsr_ts,
       t1.min_etl_dt,
       t1.max_etl_dt,
       t1.rec_cnt
       FROM ", config$SF_CONFIG$my_schema_name1, ".mytable1 AS t1 
       LEFT JOIN ", config$SF_CONFIG$my_schema_name1, ".mytable2", "AS t2
       ON t1.signal=t2.signal 
       WHERE t1.unit_key=")
  q.Aux2 <- " AND t1.signal="
  q.Aux.final <- str_c(q.Aux1, machine, q.Aux2, signal)
  res <- dbSendQuery(myConn, q.Aux.final)
  df <- as.data.table(dbFetch(res, n=-1))
  dbClearResult(res)
  return(df)
}

dates <-auxiliar.dates("machine", "signal")

Вывод этой функции представляет собой таблицу данных следующим образом:

enter image description here

Затем я использовал выходные данные для запроса указанного c сигнала между min и max ts следующим образом:

signalQuery <- function(machine, signal, min_ts, max_ts) {

  q1.aux1 <- paste("SELECT snsr_val, 
                      snsr_ts, 
                      snsr_dt, 
                      signal,
                      qual, 
                      machine 
                      FROM ", config$SF_CONFIG$schema_name1, 
                     ".mytable1 AS v
                      WHERE machine=", sep="")

  q3.aux1 <-paste(" AND signal=", signal, " AND snsr_ts BETWEEN ", "'", min_ts, "'",
                    " AND ", "'", max_ts, "'", " ORDER BY v.snsr_ts", sep = "")

  qt.auxtotal <- str_c(q1.aux1,
                     machine,
                     q3.aux1) #we join que full query with stringr library

  res <- dbSendQuery(myConn, qt.auxtotal)
  df <- as.data.table(dbFetch(res,n=-1))
  dbClearResult(res) #cleaning memory
  return(df)
}

Например, чтобы вызвать сигнал 71, я делал:

    signal71.dates <- auxiliar.dates(machine, 71)
    df   <- signalQuery(machine, 71, signal71.dates$min_snsr_dt, signal71.dates$max_snsr_dt)

В случае, если мне нужно было запросить больше сигналов, я делал точно такую ​​же процедуру, но я принимал минимальное значение max_snsr_dt для моего кадра данных, вызывающего signal_number. даты и максимальное значение min_snsr_dt моих dataframes signal_number.dates.

Я хотел бы знать, чтобы немного изменить процесс и иметь возможность вводить вектор, сигналы которого я хочу, как в auxiliar.dates, так и в Функция signalQuery.

Моим первым испытанием было изменить auxiliar.dates:

q.Aux2 <- " AND t1.signal="

на:

q.Aux2 <- " AND t1.signal IN ("
q.Aux.final <- str_c(q.Aux1, machine, q.Aux2, paste(signal, ")", sep = ""))

Однако, когда я вызываю функцию как:

test <- auxiliar.dates(984, c(70,71))

Я получаю следующее ошибка:

Ошибка в new_result (connection@ptr, оператор): ожидание единственного строкового значения: [тип = символ; экстент = 2].

Кто-нибудь сможет поддержать?

BR

Ответы [ 2 ]

2 голосов
/ 12 января 2020

Рассмотрим следующие изменения:

  • Параметризация : Избегайте слишком большого числа конкатенаций строк, что ухудшает читабельность и удобство обслуживания. Вместо этого используйте параметризацию, которая поддерживается в DBI + odbc с sqlInterpolate. В идеале вы должны жестко закодировать имена таблиц в строковом операторе SQL, но, поскольку идентификаторы не могут быть параметризованы, все равно придется использовать paste (или paste0 без пробелов).

  • Один SQL запрос : Объедините два SQL запроса, используя Общее табличное выражение (CTE) , которое поддерживается в Snowflake. В частности, первый запрос присоединяется к последнему запросу с помощью машины и сигнала и интервала даты BETWEEN. В свою очередь, вы комбинируете обе функции, сокращаете количество обращений к базе данных и избегаете промежуточных вспомогательных объектов.

  • Использование dbGetQuery: если загрузка данных не проблема с необходимостью извлечения больших результирующих наборов кусками, используйте dbGetQuery для объединения шагов dbSendQuery и dbFetch для краткости.

  • Функциональные входы : Как отмечает @ r2evans, избегайте полагаться на переменные окружения неизвестных родительских источников, которые находятся внутри локальной функции. Вместо этого передайте все необходимые входные параметры для локальных переменных области действия.

  • Итерация : поскольку эти функции используют скалярные параметры, вы должны выполнять итерацию по значениям, таким как lapply запускать функции несколько раз, а затем результаты связывания строк для окончательной таблицы данных.

Одиночная функция

signalQuery <- function(my_schema, machine, signal) { 
    # PREPARED STATEMENT 
    sql <- paste0("WITH sub AS 
                     (SELECT t1.machine, t1.signal,  t2.signal_name, 
                             t1.min_snsr_dt, t1.max_snsr_dt,
                             t1.min_snsr_ts, t1.max_snsr_ts, 
                             t1.min_etl_dt,  t1.max_etl_dt, t1.rec_cnt
                      FROM ", my_schema, ".mytable1 AS t1 
                      LEFT JOIN ", my_schema, ".mytable2", "AS t2
                          ON t1.signal = t2.signal 
                      WHERE t1.unit_key = ?m_param AND t1.signal= ?s_param)

                   SELECT v.snsr_val, v.snsr_ts, v.snsr_dt, v.signal, 
                          v.qual, v.machine 
                   FROM ", my_schema, ".mytable1 AS v
                   INNER JOIN sub
                     ON v.machine = sub.machine
                     AND v.signal = sub.signal
                     AND v.snsr_ts BETWEEN sub.min_snsr_dt AND sub.max_snsr_dt
                   ORDER BY v.snsr_ts")

    # BIND PARAMS TO ?MARK PLACEHOLDERS
    query <- sqlInterpolate(conn, sql, m_param = machine, s_param = signal)

    # RUN QUERY
    dt <- as.data.table(dbGetQuery(myConn, query))

    return(dt)    
}

Вызовы функций

# SINGLE SIGNAL VALUE
q.Aux.final <- signalQuery(myschema = config$SF_CONFIG$my_schema_name1,
                           machine = 984, signal = 70)

# MULTIPLE SIGNAL VALUES
dt_list <- lapply(c(70,71), function(i) 
                    signalQuery(myschema = config$SF_CONFIG$my_schema_name1,
                                machine = 984, signal = i)
           )

q.Aux.final <- data.table::rbindlist(dt_list)

Несколько функций

В случае, если вам нужен первый набор результатов для аналитических нужд, продолжайте тот же процесс без CTE:

auxiliar.dates <- function(my_schema, machine, signal) { 

    sql <- paste0("SELECT t1.machine, t1.signal,  t2.signal_name, 
                          t1.min_snsr_dt, t1.max_snsr_dt,
                          t1.min_snsr_ts, t1.max_snsr_ts, 
                          t1.min_etl_dt,  t1.max_etl_dt, t1.rec_cnt
                   FROM ", my_schema, ".mytable1 AS t1 
                   LEFT JOIN ", my_schema, ".mytable2", "AS t2
                         ON t1.signal=t2.signal 
                   WHERE t1.unit_key = ?m_param AND t1.signal= ?s_param")

    query <- sqlInterpolate(conn, sql, m_param = machine, s_param = signal)
    dt <- as.data.table(dbGetQuery(myConn, query))

    return(dt)    
}


signalQuery <- function(my_schema, machine, signal, min_ts, max_ts) {

    sql <- paste0("SELECT v.snsr_val, v.snsr_ts, v.snsr_dt, v.signal, 
                          v.qual, v.machine 
                   FROM ", my_schema, ".mytable1 AS v
                   WHERE v.machine = ?m_param
                     AND v.signal = ?s_param
                     AND v.snsr_ts BETWEEN ?min_ts_prm AND ?max_ts_prm
                   ORDER BY v.snsr_ts")

    query <- sqlInterpolate(conn, sql, m_param = machine, s_param = signal,
                            min_ts_prm = min_ts, max_ts_prm = max_ts)
    dt <- as.data.table(dbGetQuery(myConn, query))

    return(dt)    
}

Функциональные вызовы

# SINGLE SIGNAL VALUE
signal71.dates <- auxiliar.dates(config$SF_CONFIG$my_schema_name1, 984, 71)

q.Aux.final <- signalQuery(config$SF_CONFIG$my_schema_name1, 984, 71,
                           signal71.dates$min_snsr_dt, signal71.dates$max_snsr_dt)

# MULTIPLE SIGNAL VALUES
dt_list <- lapply(c(70,71), function(i) 
                    signalQuery(myschema = config$SF_CONFIG$my_schema_name1,
                                machine = 984, signal = i)
           )

signal.dates_dt <- data.table::rbindlist(dt_list)


dt_list <- lapply(1:nrow(signal.dates_dt), function(i) 
                    signalQuery(myschema = config$SF_CONFIG$my_schema_name1,
                                machine  = signal.dates_dt$machine[i], 
                                signal   = signal.dates_dt$signal[i],
                                min_ts   = signal.dates$min_snsr_dt[i],
                                max_ts   = signal.dates$max_snsr_dt[i])
           )

q.Aux.final <- data.table::rbindlist(dt_list)
0 голосов
/ 30 января 2020

Обновление: ошибка устранена, срок действия коннектора истек. Мне нужно снова подключиться

Очень ценю ваше решение. Тем не менее, я получаю сообщение об ошибке при использовании в качестве входных данных двух схем.

auxiliar.dates <- function(connection, my_schema1, my_schema2, machine, signal) { 

  sql <- paste0("SELECT t1.machine, t1.signal,  t2.signal_name, 
                          t1.min_snsr_dt, t1.max_snsr_dt,
                          t1.min_snsr_ts, t1.max_snsr_ts, 
                          t1.min_etl_dt,  t1.max_etl_dt, t1.rec_cnt
                   FROM ", my_schema1, ".table1 AS t1 
                   LEFT JOIN ", my_schema2, ".table2", " AS t2
                         ON t1.snsr_key = t2.snsr_key
                   WHERE t1.machine = ?m_param AND t1.signal = ?s_param")

  query <- sqlInterpolate(connection, sql, m_param = machine, s_param = signal)
  dt <- as.data.table(dbGetQuery(connection, query))

  return(dt)    
}`

Однако я получаю следующую ошибку:

 signal1.dates <- auxiliar.dates(myConn, config$SF_CONFIG$my_schema1, config$SF_CONFIG$my_schema2, machine.number, signal.number)
 Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘sqlInterpolate’ for signature ‘"Snowflake"’ 

Знаете ли вы, почему это происходит? Когда я пытаюсь использовать только один вход и не указывать соединение как часть функции, он просто отлично работает.

...