Показать местный часовой пояс для каждого местоположения в SELECT - PullRequest
2 голосов
/ 02 июня 2019

У меня есть таблица поездок на поезде, используя следующий пример кода:

CREATE TABLE train_rides (
    trip_id bigserial PRIMARY KEY,
    origination text NOT NULL,
    destination text NOT NULL,
    departure timestamp with time zone NOT NULL,
    arrival timestamp with time zone NOT NULL
);

INSERT INTO train_rides (origination, destination, departure, arrival)
VALUES
    ('Chicago', 'New York', '2017-11-13 21:30 CST', '2017-11-14 18:23 EST'),
    ('New York', 'New Orleans', '2017-11-15 14:15 EST', '2017-11-16 19:32 CST'),
    ('New Orleans', 'Los Angeles', '2017-11-17 13:45 CST', '2017-11-18 9:00 PST'),
    ('Los Angeles', 'San Francisco', '2017-11-19 10:10 PST', '2017-11-19 21:24 PST'),
    ('San Francisco', 'Denver', '2017-11-20 9:10 PST', '2017-11-21 18:38 MST'),
    ('Denver', 'Chicago', '2017-11-22 19:10 MST', '2017-11-23 14:50 CST');

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

SELECT origination || ' to ' || destination AS segment,
       to_char(departure, 'YYYY-MM-DD HH12:MI a.m. TZ') AS departure,
       to_char(arrival, 'YYYY-MM-DD HH12:MI a.m. TZ') AS arrival
FROM train_rides;

Это дает мне следующеевыходные данные:

--------------------------------------------------------------------------------------
| segment                    | departure                 | arrival                   |
--------------------------------------------------------------------------------------
| Chicago to New York        | 2017-11-13 09:30 p.m. CST | 2017-11-14 05:23 p.m. CST |
| New York to New Orleans    | 2017-11-15 01:15 p.m. CST | 2017-11-16 07:32 p.m. CST |
| New Orleans to Los Angeles | 2017-11-17 01:45 p.m. CST | 2017-11-18 11:00 a.m. CST |
| San Francisco to Denver    | 2017-11-20 11:10 a.m. CST | 2017-11-21 07:38 p.m. CST |
| Denver to Chicago          | 2017-11-22 08:10 p.m. CST | 2017-11-23 02:50 p.m. CST |
--------------------------------------------------------------------------------------

Все время отображается в часовом поясе CST, который основан на настройке часового пояса сервера.

Предпочитаемый вывод

Я хотел бы, чтобы выходные данные отражали местный часовой пояс для каждого города отправления или назначения, например:

--------------------------------------------------------------------------------------
| segment                    | departure                 | arrival                   |
--------------------------------------------------------------------------------------
| Chicago to New York        | 2017-11-13 09:30 p.m. CST | 2017-11-14 06:23 p.m. EST |
| New York to New Orleans    | 2017-11-15 02:15 p.m. EST | 2017-11-16 07:32 p.m. CST |
| New Orleans to Los Angeles | 2017-11-17 01:45 p.m. CST | 2017-11-18 09:00 a.m. PST |
| San Francisco to Denver    | 2017-11-20 09:10 a.m. PST | 2017-11-21 06:38 p.m. MST |
| Denver to Chicago          | 2017-11-22 07:10 p.m. MST | 2017-11-23 02:50 p.m. CST |
--------------------------------------------------------------------------------------

Как я могу каждый раз отражать местный часовой пояс?Например, в первой строке выше отправление из Чикаго будет отображаться как CST, а прибытие из Нью-Йорка будет отображаться как EST.

Одним из возможных решений будет использование классификатора AT TIME ZONE, возможно, с использованием таблицы поиска, подобной этой:

----------------------------
| city          | local_tz |
----------------------------
| Chicago       | CST      |
| Denver        | MST      |
| Los Angeles   | PST      |
| New Orleans   | CST      |
| New York      | EST      |
| San Francisco | PST      |
----------------------------

Я думал о создании динамических запросов с использованием блока кода plpgsql, но это было бы очень грязно.Я надеюсь, что есть простой и более элегантный способ сделать это.

Ответы [ 2 ]

1 голос
/ 03 июня 2019

Я нашел способ получить желаемые результаты;это не самый элегантный подход, но он работает.

Сначала я добавляю два дополнительных столбца в таблицу train_rides:

-- Add columns to hold local times 
ALTER TABLE train_rides
    ADD COLUMN departure_local TIMESTAMP WITHOUT TIME ZONE,
    ADD COLUMN arrival_local TIMESTAMP WITHOUT TIME ZONE;

Затем я заполняю локальный столбец отправления, используя связкукода (с использованием не очень элегантного подхода грубой силы):

-- Update departures to reflect local times
UPDATE train_rides
    SET departure_local = departure AT TIME ZONE 'US/Central'
    WHERE origination = 'Chicago';
UPDATE train_rides
   SET departure_local = departure AT TIME ZONE 'US/Mountain'
   WHERE origination = 'Denver';
UPDATE train_rides
    SET departure_local = departure AT TIME ZONE 'US/Pacific'
    WHERE origination = 'Los Angeles';
UPDATE train_rides
    SET departure_local = departure AT TIME ZONE 'US/Central'
    WHERE origination = 'New Orleans';
UPDATE train_rides
   SET departure_local = departure AT TIME ZONE 'US/Eastern'
   WHERE origination = 'New York';
UPDATE train_rides
   SET departure_local = departure AT TIME ZONE 'US/Pacific'
   WHERE origination = 'San Francisco';

Далее я заполняю локальные поступления таким же образом:

-- Update arrivals to reflect local times
UPDATE train_rides
    SET arrival_local = arrival AT TIME ZONE 'US/Central'
    WHERE origination = 'Chicago';
UPDATE train_rides
   SET arrival_local = arrival AT TIME ZONE 'US/Mountain'
   WHERE origination = 'Denver';
UPDATE train_rides
    SET arrival_local = arrival AT TIME ZONE 'US/Pacific'
    WHERE origination = 'Los Angeles';
UPDATE train_rides
    SET arrival_local = arrival AT TIME ZONE 'US/Central'
    WHERE origination = 'New Orleans';
UPDATE train_rides
   SET arrival_local = arrival AT TIME ZONE 'US/Eastern'
   WHERE origination = 'New York';
UPDATE train_rides
   SET arrival_local = arrival AT TIME ZONE 'US/Pacific'
   WHERE origination = 'San Francisco';

Наконец, яможно использовать некоторую магию SQL для получения окончательных результатов:

SELECT origination || ' to ' || destination AS segment,
       to_char(departure_local, 'YYYY-MM-DD HH12:MI a.m. ') ||
       (SELECT local_tz from local_timezones tz
           WHERE tr.origination = tz.city) AS departure,
       to_char(arrival_local, 'YYYY-MM-DD HH12:MI a.m. ') ||
       (SELECT local_tz from local_timezones tz
           WHERE tr.destination = tz.city) AS arrival
FROM train_rides tr;

Если бы был способ, которым мы могли бы динамически заполнить параметр AT TIME ZONE, используя поиск, тогда все эти операторы UPDATE можно было бы сократить до простодва.Этот метод грубой силы не очень хорошо масштабируется, поскольку мы должны вручную кодировать каждый возможный город.

Если, конечно, кто-то не знает лучшего способа.

0 голосов
/ 04 июня 2019

Сегодня я узнал о силе подзапросов SQL, и у меня в голове погасла лампочка. Далее следуют два лучших подхода.

Оба эти подхода используют следующую справочную таблицу с именем trains_local_timezones:

-----------------------------------------
| city          | timezone    | tz_abbr |
-----------------------------------------
| Chicago       | US/Central  | CST     |
| Denver        | US/Mountain | MST     |
| Los Angeles   | US/Pacific  | PST     |
| New Orleans   | US/Central  | CST     |
| New York      | US/Eastern  | EST     |
| San Francisco | US/Pacific  | PST     |
-----------------------------------------

- лучший способ (это может увеличить столько, сколько нужно)

-- Add columns to hold local times
ALTER TABLE train_rides
    ADD COLUMN departure_local TIMESTAMP WITHOUT TIME ZONE,
    ADD COLUMN arrival_local TIMESTAMP WITHOUT TIME ZONE;

-- Update departures to reflect local times
UPDATE train_rides
   SET departure_local = departure AT TIME ZONE
   (SELECT timezone from trains_local_timezones tz
     WHERE origination = tz.city);

-- Update arrivals to reflect local times
UPDATE train_rides
   SET arrival_local = arrival AT TIME ZONE
   (SELECT timezone from trains_local_timezones tz
     WHERE destination = tz.city);


SELECT origination || ' to ' || destination AS segment,
       to_char(departure_local, 'YYYY-MM-DD HH12:MI AM ') ||
       (SELECT tz_abbr from trains_local_timezones tz
           WHERE tr.origination = tz.city) AS local_departure,

       to_char(arrival_local, 'YYYY-MM-DD HH12:MI AM ') ||
       (SELECT tz_abbr from trains_local_timezones tz
           WHERE tr.destination = tz.city) AS local_arrival
FROM train_rides tr;

- Лучший способ (не нужны дополнительные столбцы)

SELECT origination || ' to ' || destination AS segment,
       to_char(departure AT TIME ZONE
           (SELECT timezone from trains_local_timezones tz
            WHERE origination = tz.city), 'YYYY-MM-DD HH12:MI AM ') ||
       (SELECT tz_abbr from trains_local_timezones tz
           WHERE tr.origination = tz.city) AS local_departure,
       to_char(arrival AT TIME ZONE
           (SELECT timezone from trains_local_timezones tz
            WHERE destination = tz.city), 'YYYY-MM-DD HH12:MI AM ') ||
       (SELECT tz_abbr from trains_local_timezones tz
           WHERE tr.destination = tz.city) AS local_arrival
FROM train_rides tr;
...