Заставить dbGetQuery вернуть отметку времени POSIXct - PullRequest
0 голосов
/ 25 марта 2020

Я извлекаю данные из базы данных SQL Сервера, где каждая строка использует метку времени ISO в качестве своего ключа. В базе данных временные метки хранятся как datetime2 тип данных.

Когда я запускаю следующий оператор:

data <- dbGetQuery(con, "SELECT timestamp from table1")

, он возвращает фрейм данных, где столбец отметки времени имеет тип chr. Вот что str () возвращает во фрейме данных:

$ timestamp: chr  "2020-03-25 12:19:48.0000000" "2020-03-25 12:20:48.0000000"...

С этим я могу преобразовать его обратно в объект POSIXct, используя следующий код:

data$timestamp <- as.POSIXct(data$timestamp, format = '%Y-%m-%d %H:%M:%S')

Однако база данных хранит значения каждую минуту (а иногда и каждую секунду), поэтому, если я попытаюсь преобразовать метки времени на несколько месяцев или лет с помощью функции as.POSIXct (), это может занять много времени (в диапазоне минут). Функция dbGetQuery относительно быстра при извлечении такого большого количества данных (в диапазоне нескольких секунд).

Есть ли параметр в dbGetQuery или другом методе, который будет напрямую интерпретировать метки времени как дату, а не как строки?

Вот моя команда подключения:

con <- dbConnect(odbc(),
                 Driver = "SQL Server",
                 Server = "server1",
                 Database = "db1",
                 UID = "user", 
                 PWD = "pwd")

1 Ответ

0 голосов
/ 25 марта 2020

TL; DR

(немного обновлено из моего комментария)

DBI::dbGetQuery(con, "select cast ( SYSDATETIMEOFFSET() at time zone 'UTC' as DATETIME ) as now")
#                       now
# 1 2020-03-25 20:30:33.026
Sys.time()
# [1] "2020-03-25 13:30:31.177 PDT"

(мой ноутбук и удаленный сервер sql не синхронизированы)

Пояснение

Драйвер odbc (с использованием библиотеки nanodbc C ++) распознает данные SQL Тип сервера DATETIME. Тем не менее, этот тип не включает часовой пояс, поэтому перебивание данных может привести к ошибке, если две строки не ссылаются на один и тот же TZ.

DBI::dbExecute(con, "create table r2mt (id INTEGER, tm DATETIMEOFFSET)")
# [1] 0
DBI::dbExecute(con, "insert into r2mt (id,tm) values (1,'2020-03-23 12:34:56 +00:00'),(2,'2020-03-23 12:34:56.100 -04:00')")
# [1] 2

dat <- DBI::dbGetQuery(con, "select id, tm from r2mt")
str(dat)
# 'data.frame': 2 obs. of  2 variables:
#  $ id: int  1 2
#  $ tm: chr  "2020-03-23 12:34:56.0000000 +00:00" "2020-03-23 12:34:56.5000000 -04:00"
as.POSIXct(gsub("([-+]?[0-9]{2}):([0-9]{2})$", "\\1\\2", dat$tm),
           format = "%Y-%m-%d %H:%M:%OS %z")
# [1] "2020-03-23 05:34:56.0 PDT" "2020-03-23 09:34:56.5 PDT"
diff( as.POSIXct(gsub("([-+]?[0-9]{2}):([0-9]{2})$", "\\1\\2", dat$tm),
      format = "%Y-%m-%d %H:%M:%OS %z") )
# Time difference of 4.000139 hours

dat <- DBI::dbGetQuery(con, "select id, cast(tm as DATETIME) as tm from r2mt")
str(dat)
# 'data.frame': 2 obs. of  2 variables:
#  $ id: int  1 2
#  $ tm: POSIXct, format: "2020-03-23 12:34:56.0" "2020-03-23 12:34:56.5"
diff(dat$tm)
# Time difference of 0.5 secs

(В R часовой пояс является атрибутом вектора весь столбец, поэтому не будет меняться между различными элементами в этом столбце.)

Поскольку вы пытаетесь сделать как можно больше в SQL (хорошая идея), когда вы приводите к DATETIME class, убедитесь, что вы установили часовой пояс для всех, чтобы, по крайней мере, все времена были сопоставимы.

dat <- DBI::dbGetQuery(con, "select id, cast(tm at time zone 'UTC' as DATETIME) as tm from r2mt")
str(dat)
# 'data.frame': 2 obs. of  2 variables:
#  $ id: int  1 2
#  $ tm: POSIXct, format: "2020-03-23 12:34:56.0" "2020-03-23 16:34:56.5"

dat <- DBI::dbGetQuery(con, "select id, cast(tm at time zone 'Central European Standard Time' as datetime) as tm from r2mt")
str(dat)
# 'data.frame': 2 obs. of  2 variables:
#  $ id: int  1 2
#  $ tm: POSIXct, format: "2020-03-23 13:34:56.0" "2020-03-23 17:34:56.5"

(К сожалению, часовые пояса, используемые в SQL Server, не такие, как в R. Я предпочитаю 'UTC' из-за отсутствия двусмысленности перед вами.)

...