Главная страница
Навигация по странице:

  • DATEPART

  • Список допустимых значений аргумента datepart , описанный выше в данном разделе, дополняется еще одним значением Datepart Допустимые сокращения Weekday — день недели

  • Trip_no Time 1123 2003-04-05 16:20:00.000 1123

  • Решение 1 1. SELECT time_out, MINUTE(time_out)2. FROM trip 3. WHERE HOUR(time_out) = 12; time_out min

  • Функция DATENAME Функция DATENAME

  • лекция. Учебник по sql если вы хотите узнать, что такое sql этот сайт для вас


    Скачать 7.88 Mb.
    НазваниеУчебник по sql если вы хотите узнать, что такое sql этот сайт для вас
    Анкорлекция
    Дата21.12.2022
    Размер7.88 Mb.
    Формат файлаpdf
    Имя файлаMoiseenko_SQL.pdf
    ТипУчебник
    #857633
    страница25 из 47
    1   ...   21   22   23   24   25   26   27   28   ...   47
    Oracle
    Oracle позволяет использовать составные интервалы, например, 1 день и 3 часа:
    1.
    SELECT
    timestamp
    '2018-01-27 13:00:00'
    + interval
    '01 03'
    DAY
    TO
    HOUR
    FROM
    dual; и 1 день 15 секунд
    1.
    SELECT
    timestamp
    '2018-01-27 13:00:00'
    + interval
    '01 00:00:15'
    DAY
    TO
    SECOND
    FROM
    dual;
    Разумеется, можно также прибавить два простых интервала, как и в случае
    PostgreSQL.

    Функция DATEDIFF
    Синтаксис:
    1. DATEDIFF
    (
    datepart, startdate, enddate
    )
    Функция возвращает интервал времени, прошедшего между двумя временными отметками — startdate (начальная отметка) и enddate (конечная отметка). Этот интервал может быть измерен в разных единицах. Возможные варианты определяются аргументом datepart и перечислены выше применительно к функции DATEADD.
    Пример 7.1.2
    Определить количество дней, прошедших между первым и
    последним совершенными рейсами.
    1.
    SELECT
    DATEDIFF
    (
    dd,
    (
    SELECT
    MIN
    (
    date
    )
    2.
    FROM
    pass_in_trip
    3.
    )
    ,
    4.
    (
    SELECT
    MAX
    (
    date
    )
    5.
    FROM
    pass_in_trip
    6.
    )
    7.
    )
    ;
    Пример 7.1.3
    Определить продолжительность рейса 1123 в минутах.
    Здесь следует принять во внимание, что время вылета (time_out) и время прилета (time_in) хранится в полях типа datetime таблицы Trip. Как уже отмечалось,
    SQL
    Server вплоть до версии 2005 не имел отдельных темпоральных типов данных для даты и времени. Поэтому при вставке в поле datetime только времени (например, UPDATE trip SET time_out = ’17:24:00’
    WHERE trip_no = 1123), время будет дополнено значением даты по умолчанию
    (1900-01-01), являющейся начальной точкой отсчета времени.
    Напрашивающееся решение
    1.
    SELECT
    DATEDIFF
    (
    mi, time_out, time_in
    )
    dur
    2.
    FROM
    trip

    3.
    WHERE
    trip_no =
    1123
    ;
    (которое дает 760 минут) будет неверным по двум причинам.
    Во-первых, для рейсов, которые вылетают в один день, а прилетают на следующий, вычисленное таким способом значение будет неправильным;
    Во-вторых, ненадежно делать какие либо предположения относительно дня, который присутствует только в силу необходимости соответствовать типу datetime.
    Но как определить, что самолет приземлился на следующий день? Тут помогает описание предметной области, где говорится, что полет не может продолжаться более суток. Итак, если время прилета не больше, чем время вылета, то этот факт имеет место.
    Теперь второй вопрос: как посчитать только время, с каким бы днем оно ни стояло?
    Здесь может помочь функция
    T-SQL
    DATEPART, речь о которой пойдет в
    следующем пункте
    Следует иметь в виду одну особенность использования функции DATEDIFF.
    Начнем с примеров. Сначала посчитаем число недель с воскресенья 23-10-
    2005 до субботы 29-10-2005. Итак,
    1.
    SELECT
    DATEDIFF
    (
    wk,
    '20051023'
    ,
    '20051029'
    )
    ;
    Здравый смысл подсказывает, что это полная неделя, однако, вышеприведенный запрос дает 0. Теперь возьмем интервал с субботы 29-10-
    2005 до воскресенья 30-10-2005:
    1.
    SELECT
    DATEDIFF
    (
    wk,
    '20051029'
    ,
    '20051030'
    )
    ;
    В результате получим 1, то есть одну неделю. Пора дать объяснения. Дело в том, что функция DATEDIFF фактически считает неделей не число дней, а число переходов с субботы на воскресенье. Если это иметь в виду, то тогда становится понятным еще более удивительный пример:

    1.
    SELECT
    DATEDIFF
    (
    wk,
    '20051029 23:59:00'
    ,
    '20051030 00:01:00'
    )
    ; который тоже дает единицу!
    Странно? Возможно, но, как говорится, кто предупрежден, тот вооружен. То же самое имеет место и для других интервалов. Например, количество дней дает нам не число часов, деленное нацело на 24 (количество часов в сутках), а число переходов через полночь. Хотите подтверждения? Пожалуйста
    1.
    SELECT
    DATEDIFF
    (
    dd,
    '20051029 23:59:00'
    ,
    '20051030 00:01:00'
    )
    ;
    В результате получаем один день, в то же время
    1.
    SELECT
    DATEDIFF
    (
    dd,
    '20051029 00:00:00'
    ,
    '20051029 23:59:59'
    )
    ; дает нам 0.
    Если хотите, можете продолжить эксперименты с другими временными интервалами. Вы можете воспользоваться для этого предоставляемой
    Консолью.
    Задача. Посчитать число минут в интервале между двумя
    датами – '2011-10-07 23:43:00' и '2011-10-08 01:23:00'
    SQL Server
    Встроенная функция DATEDIFF решает проблему:
    1.
    SELECT
    DATEDIFF
    (
    minute,
    '2011-10-07T23:43:00'
    ,
    '2011-10-
    08T01:23:00'
    )
    ;

    Результат – 100 минут.
    Примечание:
    В запросе используется стандартное представление даты
    (ISO) в виде текстовой строки 'yyyy-mm-ddThh:mm:ss', которое не
    зависит ни от локальных настроек сервера, ни и от самого
    сервера.
    MySQL
    Функция DATEDIFF есть и в MySQL, однако она имеет совсем другой смысл.
    DATEDIFF вычисляет число дней между двумя датами, являющихся аргументами этой функции. При этом если дата представлена в формате дата- время, используется только составляющая даты. Поэтому все три нижепредставленных запроса дадут один и тот же результат -1.
    Положительный результат будет получен, если первый аргумент больше второго.
    1.
    SELECT
    DATEDIFF
    (
    '2011-10-07T23:43:00'
    ,
    '2011-10-
    08T01:23:00'
    )
    ;
    2.
    SELECT
    DATEDIFF
    (
    '2011-10-07'
    ,
    '2011-10-08'
    )
    ;
    3.
    SELECT
    DATEDIFF
    (
    '2011-10-07T23:43:00'
    ,
    '2011-10-08'
    )
    ;
    Один день мы получим даже в случае, если интервал между датами составляет все одну секунду:
    1.
    SELECT
    DATEDIFF
    (
    '2011-10-07T23:59:59'
    ,
    '2011-10-
    08T00:00:00'
    )
    ;
    Решение же нашей задачи можно получить при помощи другой встроенной функции – TIMESTAMPDIFF, которая аналогичная функции DATEDIFF в
    SQL
    Server:
    1.
    SELECT
    TIMESTAMPDIFF
    (
    minute,
    '2011-10-07T23:43:00'
    ,
    '2011-10-08T01:23:00'
    )
    ;

    PostgreSQL
    В PostgreSQL нет функции, подобной DATEDIFF (SQL Server) или
    TIMESTAMPDIFF (MySQL). Поэтому для решения задачи можно применить следующую последовательность действий:
    1. представить разность между двумя датами интервалом;
    2. посчитать число секунд в интервале;
    3. поделить полученное на шаге 2 значение на 60, чтобы выразить результат в минутах.
    Для получения интервала можно взять разность двух значений темпорального типа, При этом требуется явное преобразование типа:
    1.
    SELECT
    timestamp
    '2011-10-08T01:23:00'
    - timestamp
    '2011-
    10-07T23:43:00'
    ; или в стандартном исполнении
    1.
    SELECT
    CAST
    (
    '2011-10-08T01:23:00'
    AS
    timestamp
    )
    -
    CAST
    (
    '2011-10-07T23:43:00'
    AS
    timestamp
    )
    ;
    Результат - "01:40:00", который есть не что иное, как один час и сорок минут.
    Можно также воспользоваться встроенной функцией AGE, которая выполняет заодно неявное преобразование типа своих аргументов:
    1.
    SELECT
    AGE
    (
    '2011-10-08T01:23:00'
    ,
    '2011-10-
    07T23:43:00'
    )
    ;
    Для получения числа секунд в интервале воспользуемся функцией
    1. EXTRACT
    (
    EPOCH
    FROM
    < interval >
    )

    Значение интервала представим последним способом как наиболее коротким.
    Для получения окончательного решения задачи поделим полученный результат на 60:
    1.
    SELECT
    EXTRACT
    (
    EPOCH
    FROM
    AGE
    (
    '2011-10-08T01:23:00'
    ,
    '2011-10-07T23:43:00'
    ))
    /
    60
    ;
    В данном случае мы имеем результат, выраженный целым числом, поскольку оба значения времени в задаче не содержали секунд. В противном случае, будет получено десятичное число, например: 99.75:
    1.
    SELECT
    EXTRACT
    (
    EPOCH
    FROM
    AGE
    (
    '2011-10-08T01:23:00'
    ,
    '2011-10-07T23:43:15'
    ))
    /
    60
    ;
    Oracle
    Oracle тоже не имеет в своем арсенале функции типа DATEDIFF. Кроме того, Oracle не поддерживает стандартное текстовое представление даты/времени.
    Мы можем посчитать интервал в минутах, приняв во внимание, что разность дат (значений типа date) в Oracle дает в результате число дней (суток). Тогда для вычисления интервала в минутах нужно просто разность дат умножить на
    24 (число часов в сутках), а затем на 60 (число минут в часе):
    1.
    SELECT
    (
    CAST
    (
    '2011-10-08 01:23:00'
    AS
    date
    )
    -
    CAST
    (
    '2011-
    10-07 23:43:00'
    AS
    date
    ))
    *
    24
    *
    60 2.
    FROM
    dual;
    Подобным образом можно получить и другие временные интервалы.

    Функция DATEPART
    Синтаксис:
    1. DATEPART
    (
    datepart , date
    )
    Эта функция возвращает целое число, представляющее собой указанную аргументом datepart часть заданной вторым аргументом даты (date).
    Список допустимых значений аргумента datepart
    , описанный выше в данном разделе, дополняется еще одним значением
    Datepart
    Допустимые сокращения
    Weekday — день недели
    dw
    Заметим, что возвращаемое функцией DATEPART значение в этом случае
    (номер дня недели) зависит от настроек, которые можно изменить с помощью оператора SET DATEFIRST, устанавливающего первый день недели. Для кого-то понедельник — день тяжелый, а для кого-то — воскресенье. Как и многие региональные настройки, по умолчанию это значение в
    SQL
    Server зависит от выбранного языка.
    Однако вернемся к
    примеру 7.1.3
    . В предположении, что время вылета/прилета является кратным минуте, мы можем его определить как сумму часов и минут. Поскольку функции даты/времени работают с целочисленными значениями, приведем результат к наименьшему интервалу
    — минутам. Итак, время вылета рейса 1123 в минутах:
    1.
    SELECT
    DATEPART
    (
    hh, time_out
    )
    *
    60
    + DATEPART
    (
    mi, time_out
    )
    2.
    FROM
    Trip
    3.
    WHERE
    trip_no =
    1123
    ; и время прилета
    1.
    SELECT
    DATEPART
    (
    hh, time_in
    )
    *
    60
    + DATEPART
    (
    mi, time_in
    )
    2.
    FROM
    Trip
    3.
    WHERE
    trip_no =
    1123
    ;

    Теперь мы должны сравнить, превышает ли время прилета время вылета.
    Если это так, следует вычесть из первого второе, чтобы получить продолжительность рейса. В противном случае к разности нужно добавить одни сутки (24*60 = 1440 минут).
    1.
    SELECT
    CASE
    2.
    WHEN
    time_dep >= time_arr
    3.
    THEN
    time_arr - time_dep +
    1440 4.
    ELSE
    time_arr - time_dep
    5.
    END
    dur
    6.
    FROM
    (
    SELECT
    DATEPART
    (
    hh, time_out
    )
    *
    60
    + DATEPART
    (
    mi, time_out
    )
    time_dep,
    7. DATEPART
    (
    hh, time_in
    )
    *
    60
    +
    DATEPART
    (
    mi, time_in
    )
    time_arr
    8.
    FROM
    Trip
    9.
    WHERE
    trip_no =
    1123 10.
    )
    tm;
    Здесь, чтобы не повторять длинные конструкции в операторе CASE, использован подзапрос. Конечно, результат получился достаточно громоздким, зато абсолютно корректным в свете сделанных к этой задаче замечаний.
    Пример 7.1.4
    Определить дату и время вылета рейса 1123.
    В таблице совершенных рейсов Pass_in_trip содержится только дата рейса, но не время, так как в соответствии с предметной областью каждый рейс может выполняться только один раз в день. Для решения этой задачи нужно к дате, хранящейся в таблице Pass_in_trip, добавить время из таблицы Trip
    1.
    SELECT
    DISTINCT
    pt.trip_no,
    DATEADD
    (
    mi,
    DATEPART
    (
    hh,time_out
    )
    *
    60
    +
    2. DATEPART
    (
    mi,time_out
    )
    , date
    )
    [
    time
    ]
    3.
    FROM
    Pass_in_trip pt
    JOIN
    4. Trip t
    ON
    pt.trip_no = t.trip_no
    5.
    WHERE
    t.trip_no =
    1123
    ;
    Выполнив запрос, получим следующий результат

    Trip_no
    Time
    1123
    2003-04-05 16:20:00.000
    1123
    2003-04-08 16:20:00.000
    DISTINCT необходим здесь, чтобы исключить возможные дубликаты, поскольку номер и дата рейса дублируются в этой таблице для каждого пассажира данного рейса.
    В ряде случаев функцию DATEPART можно заменить более простыми функциями. Вот они:
    DAY(date) — целочисленное представление дня указанной даты. Эта функция эквивалентна функции DATEPART(dd, date).
    MONTH(date) — целочисленное представление месяца указанной даты. Эта функция эквивалентна функции DATEPART(mm, date).
    YEAR(date) — целочисленное представление года указанной даты. Эта функция эквивалентна функции DATEPART(yy, date).
    MySQL
    В MySQL для извлечения каждой составляющей даты имеется соответствующая функция. Так, например, можно получить минуты времени отправления рейсов, которые вылетают в 1-ом часу дня (база данных "Аэропорт"):
    Решение 1
    1.
    SELECT
    time_out, MINUTE
    (
    time_out
    )
    2.
    FROM
    trip
    3.
    WHERE
    HOUR
    (
    time_out
    )
    =
    12
    ;
    time_out
    min
    1900-01-01 12:35:00 35
    1900-01-01 12:00:00 0

    Компоненту времени можно также получить при помощи функции EXTRACT. С помощи этой функции решение задачи можно записать в виде:
    Решение 2
    1.
    SELECT
    time_out, EXTRACT
    (
    MINUTE
    FROM
    time_out
    )
    AS
    MIN
    2.
    FROM
    trip
    3.
    WHERE
    EXTRACT
    (
    HOUR
    FROM
    time_out
    )
    =
    12
    ;
    Дополнительно с помощью функции EXTRACT можно получить составные компоненты даты/времени, например, год и месяц.
    Решим такую задачу.
    Посчитать количество окрасок по месяцам с учетом года (база
    данных "Окраска").
    Решение 3
    1.
    SELECT
    YEAR
    (
    b_datetime
    )
    AS
    y, MONTH
    (
    b_datetime
    )
    AS
    m,
    2. EXTRACT
    (
    YEAR_MONTH
    FROM
    b_datetime
    )
    AS
    ym,
    COUNT
    (
    *
    )
    AS
    qty
    3.
    FROM
    utb
    4.
    GROUP
    BY
    EXTRACT
    (
    YEAR_MONTH
    FROM
    b_datetime
    )
    ;
    y
    m
    ym
    qty
    2000 1 200001 1
    2001 1 200101 1
    2002 1 200201 1

    2002 6 200206 1
    2003 1 200301 69
    2003 2 200302 7
    2003 3 200303 2
    2003 4 200304 2
    2003 5 200305 2
    2003 6 200306 2
    Полный список допустимых компонент можно найти, например, здесь
    Примечание:
    Здесь уместно будет отметить довольно вольную трактовку
    группировки в MySQL, при которой в списке столбцов предложения
    SELECT могут присутствовать столбцы с детализированными
    данными, отсутствующими в предложении GROUP BY. Очевидно,
    что агрегаты здесь подразумеваются, т.к., в противном случае,
    отсутствует
    однозначная
    интерпретация
    операции.
    Предполагаю, что здесь неявно используются функция MIN или
    MAX, но я даже не буду это специально выяснять, т.к.
    предпочитаю в подобных случаях следовать стандарту.
    PostgreSQL
    Функций типа Year, Month и т.д. в PostgreSQL, насколько мне известно, нет.
    Однако есть функция EXTRACT, и второе решение первой задачи, которое мы написали для MySQL, будет работать и под этой СУБД:
    1.
    SELECT
    time_out, EXTRACT
    (
    MINUTE
    FROM
    time_out
    )
    AS
    MIN
    2.
    FROM
    trip
    3.
    WHERE
    EXTRACT
    (
    HOUR
    FROM
    time_out
    )
    =
    12
    ;

    Имеется также функция, аналогичная DATEPART в MSSQL. Различия в синтаксисе, надеюсь, станут очевидны из примера решения той же задачи с использованием этой функции:
    1.
    SELECT
    time_out, DATE_PART
    (
    'MINUTE'
    , time_out
    )
    AS
    MIN
    2.
    FROM
    trip
    3.
    WHERE
    DATE_PART
    (
    'HOUR'
    , time_out
    )
    =
    12
    ;
    Перейдем к решению второй задачи. Составные компоненты не поддерживаются в функции EXTRACT для PostgreSQL. Поэтому можно использовать "классическую" групировку по двум компонентам - году и месяцу:
    Решение 4
    1.
    SELECT
    EXTRACT
    (
    YEAR
    FROM
    b_datetime
    )
    AS
    y,
    2. EXTRACT
    (
    MONTH
    FROM
    b_datetime
    )
    AS
    m,
    COUNT
    (
    *
    )
    AS
    qty
    3.
    FROM
    utb
    4.
    GROUP
    BY
    EXTRACT
    (
    YEAR
    FROM
    b_datetime
    )
    , EXTRACT
    (
    MONTH
    FROM
    b_datetime
    )
    ;
    Тем не менее, мы можем реализовать идею составной компоненты при помощи функции форматирования даты
    TO_CHAR
    :
    1.
    SELECT
    TO_CHAR
    (
    b_datetime,
    'YYYYMM'
    )
    AS
    ym,
    COUNT
    (
    *
    )
    AS
    qty
    2.
    FROM
    utb
    3.
    GROUP
    BY
    TO_CHAR
    (
    b_datetime,
    'YYYYMM'
    )
    ;
    Результат будет аналогичен результату решения 3 за отсутствием первых двух столбцов, которые, разумеется, можно добавить в вывод, включив их одновременно в предложение GROUP BY, как это сделано в решении 4.

    Функция
    DATENAME
    Функция DATENAME( datepart, date ) возвращает символьное представление составляющей (datepart) указанной даты (date). Аргумент, определяющий составляющую даты, может принимать одно из значений, перечисленных в таблице в
    начале этой главы
    Это дает нам простую возможность конкатенировать компоненты даты, получая любой нужный формат представления. Например, конструкция
    1.
    SELECT
    DATENAME
    (
    weekday,
    '20031231'
    )
    +
    ',
    '
    +
    DATENAME
    (
    day,
    '20031231'
    )
    +
    2.
    '
    '
    +
    DATENAME
    (
    month,
    '20031231'
    )
    +
    '
    '
    +
    DATENAME
    (
    year,
    '20031231'
    )
    ; даст нам следующий результат
    Wednesday, 31 December 2003
    Следует отметить, что данная функция выявляет отличие значений day и dayofyear аргумента datepart. Первый дает символьное представление дня указанной даты, в то время как второй дает символьное представление этого дня от начала года. То есть
    1.
    SELECT
    DATENAME
    (
    day,
    '20031231'
    )
    ; даст нам 31, в то время как для
    1.
    SELECT
    DATENAME
    (
    dayofyear,
    '20031231'
    )
    ; результатом будет 365.

    1   ...   21   22   23   24   25   26   27   28   ...   47


    написать администратору сайта