Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Изучаем.SQL

.pdf
Скачиваний:
61
Добавлен:
19.03.2016
Размер:
980.64 Кб
Скачать

| US/Samoa

|

| UTC

|

| W SU

|

|

WET

|

|

Zulu

|

+ + 545 rows in set (0.01 sec)

Чтобы изменить настройки своего часового пояса, надо выбрать одно из имен из предыдущего запроса, наиболее соответствую щее вашему местоположению.

Таким образом, чтобы заполнить столбец типа datetime показаниями времени 3:30 после полудня 27 марта 2005 года, понадобиться создать следующую строку:

'2005 03 27 15:30:00'

Если сервер ожидает значение datetime, как при обновлении столбца типа datetime или при вызове встроенной функции, принимающей ар гумент datetime, можно предоставить правильно форматированную строку с необходимыми компонентами даты, и сервер выполнит пре образование сам. Вот, например, выражение для корректировки даты банковской операции:

UPDATE transaction

SET txn_date = '2005 03 27 15:30:00'

WHERE txn_id = 99999;

Сервер определяет, что предоставленная строка блока set должна быть значением типа datetime, поскольку строка используется для заполне ния столбца типа datetime. Следовательно, сервер попытается преобра зовать эту строку, разбирая ее на шесть компонентов (год, месяц, день, час, минута, секунда), включенные в формат datetime по умолчанию.

Преобразования строки в дату

Если сервер не ожидает значение типа datetime, необходимо указать ему преобразовать строку в тип datetime. Вот, например, простой за прос, возвращающий значение типа datetime с помощью функции cast() (привести):

mysql> SELECT CAST('2005 03 27 15:30:00' AS DATETIME);

+ + | CAST('2005 03 27 15:30:00' AS DATETIME) | + + | 2005 03 27 15:30:00 | + + 1 row in set (0.00 sec)

Функция cast() будет рассмотрена в конце данной главы. Хотя этот пример демонстрирует построение значений типа datetime, аналогич ная логика применятся и к типам date и time. Следующий запрос ис пользует функцию cast() для формирования значения типа date и зна чения типа time:

mysql> SELECT CAST('2005 03 27' AS DATE) date_field,

> CAST('108:17:57' AS TIME) time_field;

+ + + | date_field | time_field | + + + | 2005 03 27 | 108:17:57 | + + + 1 row in set (0.00 sec)

Конечно, можно явно преобразовывать строки, даже когда сервер ожидает значение date, datetime или time, а не полагаться на неявное преобразование, выполняемое сервером.

При явном или неявном преобразовании строк во временные значения все компоненты даты должны быть предоставлены в требуемом поряд ке. Некоторые серверы очень строги относительно формата даты, но сервер MySQL довольно мягок в отношении разделителя компонентов. Например, MySQL примет все нижеприведенные строки как допусти мые представления времени 3:30 дня 27 марта 2005 года:

'2005 03 27 15:30:00' '2005/03/27 15:30:00' '2005,03,27,15,30,00' '20050327153000'

Хотя это и обеспечивает немногим большую гибкость для вас, возмож на ситуация, в которой требуется сформировать временное значение без стандартных компонентов даты. В следующем разделе будут представ лены встроенные функции, гораздо более гибкие, чем функция cast().

Функции для создания дат

Если требуется сгенерировать временные данные из строки, и форма строки не позволяет использовать функцию cast(), можно обратиться к встроенной функции, позволяющей предоставить вместе со строкой даты строку форматирования. MySQL включает для этой цели функ цию str_to_date(). Например, для обновления столбца date из файла извлекается строка 'March 27, 2005'. Строка не соответствует требуемо му формату YYYY MM DD, но вместо того чтобы переформатировать ее, делая пригодной для применения функции cast(), можно восполь зоваться функцией str_to_date():

UPDATE individual

SET birth_date = STR_TO_DATE('March 27, 2005', '%M %d, %Y')

WHERE cust_id = 9999;

Второй аргумент в вызове str_to_date() определяет формат строки да ты. В данном случае это название месяца (%M), число (%d) и четырех значное число, обозначающее год (%Y). Есть более 30 общепринятых компонентов форматирования. В табл. 7.4 приведено около десятка наиболее широко используемых компонентов.

Таблица 7.4. Компоненты форматирования даты

Компонент форматирования

Описание

%M

Название месяца (от January до December)

%m

Номер месяца (от 01 до 12)

%d

Число (от 01 до 31)

%j

День года (от 001 до 366)

%W

Дни недели (от Sunday до Saturday)

%Y

Год, четырехзначное число

%y

Год, двузначное число

%H

Час (от 00 до 23)

%h

Час (от 01 до 12)

%i

Минуты (от 00 до 59)

%s

Секунды (от 00 до 59)

%f

Микросекунды (от 000000 до 999999)

%p

A.M. или P.M.

 

 

Функция str_to_date() возвращает значение типа datetime, date или time в зависимости от содержимого форматирующей строки. Напри мер, если форматирующая строка включает только %H, %i и %s, будет возвращено значение типа time.

В распоряжении пользователей Oracle Database имеется функ ция to_date(), с которой можно работать так же, как с функцией MySQL str_to_date().

При формировании текущей даты/времени создавать строку не требу ется – следующие встроенные функции организуют доступ к систем ным часам и возвратят текущую дату и/или время в виде строки:

mysql> SELECT CURRENT_DATE( ), CURRENT_TIME( ), CURRENT_TIMESTAMP( );

+ + + +

| CURRENT_DATE( ) | CURRENT_TIME( ) | CURRENT_TIMESTAMP( ) | + + + + | 2005 03 20 | 22:15:56 | 2005 03 20 22:15:56 | + + + +

1 row in set (0.00 sec)

Возвращаемые этими функциями значения имеют формат по умолча нию для возвращаемого временного типа. В Oracle Database есть функ

ции current_date() и current_timestamp(), но нет функции current_time(). SQL Server включает только функцию current_timestamp().

Работа с временными данными

В данном разделе рассматриваются встроенные функции, принимаю щие аргументы даты и возвращающие даты, строки или числа.

Временные функции, возвращающие даты

Многие встроенные временные функции принимают в качестве аргу мента одну дату и возвращают другую. Например, функция MySQL date_add() позволяет добавить любой интервал (т. е. дни, месяцы, года) к заданной дате, чтобы получить другую дату. Вот пример, демонстри рующий, как добавить к текущей дате пять дней:

mysql> SELECT DATE_ADD(CURRENT_DATE( ), INTERVAL 5 DAY);

+ +

| DATE_ADD(CURRENT_DATE( ), INTERVAL 5 DAY) |

+ +

| 2005 03 26

|

+ +

1 row in set (0.00 sec)

Второй аргумент заключает в себе три элемента: ключевое слово interval (интервал), требуемое количество и тип интервала. В табл. 7.5 приве дены некоторые широко используемые типы интервалов.

Таблица 7.5. Общепринятые типы интервалов

Интервал

Описание

Second

Количество секунд

Minute

Количество минут

Hour

Количество часов

Day

Количество дней

Month

Количество месяцев

Year

Количество лет

Minute_second

Количества минут и секунд, разделенные двоеточием

Hour_second

Количества часов, минут и секунд, разделенные двоеточием

Year_month

Количества лет и месяцев, разделенные дефмсом

 

 

Первые шесть типов, перечисленные в табл. 7.5, довольно просты, а по следние три требуют немного более подробного объяснения, поскольку содержат по несколько элементов. Например, если оказалось, что опе рация с ID 9999 на самом деле имела место на 3 часа 27 минут и 11 се кунд позже того значения, которое было отправлено в таблицу Transac tion, исправить это можно следующим образом:

UPDATE transaction

SET txn_date = DATE_ADD(txn_date, INTERVAL '3:27:11' HOUR_SECOND) WHERE txn_id = 9999;

В этом примере функция берет значение столбца txn_date, добавляет к нему 3 часа 27 минут и 11 секунд и изменяет столбец txn_date, встав ляя в него результирующее значение.

Или если в отделе кадров обнаруживают, что сотрудник с ID 4789 по записанным данным моложе, чем на самом деле, можно добавить к да те его рождения, скажем, 9 лет и 11 месяцев:

UPDATE employee

SET birth_date = DATE_ADD(birth_date, INTERVAL '9 11' YEAR_MONTH) WHERE emp_id = 4789;

Для пользователей SQL Server предыдущий пример можно бы ло бы реализовать с помощью функции dateadd():

UPDATE employee

SET birth_date =

DATEADD(MONTH, 119, birth_date)

WHERE emp_id = 4789

В SQL Server нет комбинированных интервалов (т. е. year_month), поэтому 9 лет 11 месяцев были преобразованы в 119 месяцев.

Пользователи Oracle Database могут для данного примера при менить функцию add_months():

UPDATE employee

SET birth_date = ADD_MONTHS(birth_date, 119)

WHERE emp_id = 4789;

Иногда требуется добавить интервал времени к определенной дате, при этом известна конечная дата выполнения, но неизвестно, сколько дней осталось до этой даты. Например, клиент банка регистрируется в сете вой банковской системе и планирует перевод на конец месяца. Вместо того чтобы писать какой то код, определяющий текущий месяц и вы числяющий количество дней в этом месяце, можно вызвать функцию last_day() (последний день), которая сделает всю работу (и MySQL, и Oracle Database включают функцию last_day(); в SQL Server сопос тавимой функции нет). Если клиент запрашивает перевод 25 марта 2005 года, последний день марта можно найти следующим образом:

mysql> SELECT LAST_DAY('2005 03 25');

+ + | LAST_DAY('2005 03 25') | + + | 2005 03 31 | + + 1 row in set (0.04 sec)

Независимо от того, предоставляется ли значение типа date или date time, функция last_day() всегда возвращает значение типа date. Хотя, может быть, и не заметно, что эта функция существенно экономит вре мя, но если требуется найти последний день февраля и для этого выяс нить, високосный этот год или нет, логика вычисления может быть до вольно сложной.

Еще одна временная функция, возвращающая дату, преобразует зна чение типа datetime из одного временного пояса в другой. Для этого MySQL включает функцию convert_tz(), а Oracle Database – функцию new_time() (новое время). Например, если требуется преобразовать те кущее местное время в UTC, можно сделать следующее:

mysql> SELECT CURRENT_TIMESTAMP( ) current_est,

> CONVERT_TZ(CURRENT_TIMESTAMP( ), 'US/Eastern', 'UTC') current_utc;

+ + +

| current_est

| current_utc

|

+ + +

| 2005 04 18 21:23:25 | 2005 04 19 01:23:25 |

+ + +

1 row in set (0.50 sec)

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

Временные функции, возвращающие строки

Большинство временных функций, возвращающих строковые значе ния, используются для получения отдельной части даты или времени. Например, MySQL включает функцию dayname() (название дня), опре деляющую, на какой день недели приходится определенная дата:

mysql> SELECT DAYNAME('2005 03 22');

+ +

| DAYNAME('2005 03 22') |

+ +

| Tuesday

|

+ +

1 row in set (0.12 sec)

В MySQL много таких функций, предназначенных для извлечения ин формации из значений дат, но я рекомендую пользоваться функцией extract() (извлечь), поскольку проще запомнить несколько разновид ностей одной функции, чем десяток разных функций. Кроме того, ext ract() является частью стандарта SQL:2003 и была реализована не только в MySQL, но и в Oracle Database.

Для определения интересующего элемента даты функция extract() ис пользует те же типы интервалов, что и функция date_add() (см. табл. 7.5). Например, если из значения типа datetime требуется из влечь только год, можно поступить так:

mysql> SELECT EXTRACT(YEAR FROM '2005 03 22 22:19:05');

+ + | EXTRACT(YEAR FROM '2005 03 22 22:19:05') | + + | 2005 | + + 1 row in set (0.02 sec)

В SQL Server нет реализации extract(), но есть функция date part() (часть даты). Вот как можно извлечь год из значения da tetime с помощью datepart():

SELECT DATEPART(YEAR, GETDATE( ))

Временные функции, возвращающие числа

Ранее в этой главе была представлена функция, используемая для до бавления заданного интервала к значению даты и формирующая, та ким образом, другую дату. Другая распространенная операция при ра боте с датами – определение количества интервалов (дней, недель, лет) между двумя датами. MySQL включает предназначенную для этого функцию datediff(), которая возвращает количество полных дней ме жду двумя датами. Например, чтобы узнать, сколько дней будут про должаться школьные каникулы этим летом, можно сделать так:

mysql> SELECT DATEDIFF('2005 09 05', '2005 06 22');

+ + | DATEDIFF('2005 09 05', '2005 06 22') | + +

|

75 |

+ + 1 row in set (0.00 sec)

Итак, до благополучного возвращения детей в школу мне предстоит 75 дневная пытка ядовитым плющом, комариными укусами и разби тыми коленками. Функция datediff() в своих аргументах не учитыва ет время дня. Даже если включить время, задавая для первой даты од ну секунду до полуночи и для второй даты одну секунду после полуно чи, эти данные никак не отразятся на вычислениях:

mysql> SELECT DATEDIFF('2005 09 05 23:59:59', '2005 06 22 00:00:01');

+ + | DATEDIFF('2005 09 05 23:59:59', '2005 06 22 00:00:01') | + +

|

75 |

+ + 1 row in set (0.00 sec)

Если переставить аргументы, поместив первой более раннюю дату, datediff() вернет отрицательное число:

mysql> SELECT DATEDIFF('2005 06 22', '2005 09 05');

+ + | DATEDIFF('2005 06 22', '2005 09 05') | + + | 75 | + +

В SQL Server тоже есть функция datediff(), но более гибкая, чем в MySQL. В ней можно задавать тип интервала (т. е. год, месяц, день, час), а не только вычислять количество дней между двумя датами. Вот как был бы выполнен предыдущий пример для SQL Server:

SELECT DATEDIFF(DAY, '2005 06 22', '2005 09 05')

Oracle Database позволяет определять число дней между двумя датами простым вычитанием одной даты из другой.

Функции преобразования

Ранее в этой главе было показано, как использовать функцию cast() для преобразования строки в значение типа datetime. Хотя у всех сер веров БД есть собственные функции преобразования данных из одного типа в другой, я рекомендую использовать функцию cast(), включен ную в стандарт SQL:2003 и реализованную в MySQL, Oracle Database и Microsoft SQL Server.

Применяя функцию cast(), необходимо задать значение или выраже ние, ключевое слово as и тип, в который должно быть преобразовано значение. Вот пример преобразования строки в целое:

mysql> SELECT CAST('1456328' AS SIGNED INTEGER);

+ + | CAST('1456328' AS SIGNED INTEGER) | + +

|

1456328 |

+ + 1 row in set (0.01 sec)

При преобразовании строки в число функция cast() попытается преоб разовать всю строку слева направо. Если в строке встретиться любой нечисловой символ, преобразование будет остановлено без формирова ния ошибки. Рассмотрим следующий пример:

mysql> SELECT CAST('999ABC111' AS UNSIGNED INTEGER);

+ + | CAST('999ABC111' AS UNSIGNED INTEGER) | + +

|

999 |

+ + 1 row in set (0.00 sec)

В данном случае преобразуются первые три цифры в строке, все ос тальные символы строки отбрасываются. В результате получаем зна чение 999.

Для преобразования строки в значение типа date, time или datetime по надобится придерживаться форматов по умолчанию для каждого типа, поскольку передать строку формата в функцию cast() невозможно. Ес ли формат строки даты не соответствует применяемому по умолчанию (т. е. YYYY MM DD HH:MI:SS для типов datetime), придется прибег нуть к другой функции, например к функции MySQL str_to_date(), описанной ранее в этой главе.

Упражнения

Эти упражнения позволяют проверить понимание читателем некото рых встроенных функций, упомянутых в данной главе. Ответы приве дены в приложении С.

7.1

Напишите запрос, возвращающий с 17 го по 25 й символы строки

'Please find the substring in this string' (Пожалуйста, найдите под строку в этой строке).

7.2

Напишите запрос, возвращающий абсолютную величину и знак ( 1, 0 или 1) числа –25,768 23. Также возвратите число, округленное до сотых.

7.3

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

Группировка и агрегаты

Обычно данные хранятся с самым низким уровнем детализации, кото рый может понадобиться какому либо пользователю базы данных. Если Чаку (Chuck) для бухгалтерского учета требуется посмотреть операции одного клиента, в БД должна быть таблица, хранящая операции отдель ного клиента. Однако это не означает, что все пользователи должны ра ботать с данными в том виде, в каком они хранятся в БД. Основное вни мание уделено группировке и агрегированию данных, которые обеспе чивают пользователям возможность работать с данными на более вы соком уровне детализации, чем тот, с которым они хранятся в БД.

Принципы группировки

Иногда необходимо выявить в данных некоторые тенденции, что по требует от сервера некоторой подготовки данных, прежде чем можно будет получить искомые результаты. Например, вы отвечаете за опе рации в банке и хотели бы выяснить, сколько счетов открывает каж дый операционист банка. Можно было бы создать простой запрос для просмотра необработанных данных:

mysql> SELECT open_emp_id

> FROM account;

+ + | open_emp_id | + +

|1 |

|1 |

|1 |

|1 |

|1 |

|1 |

|1 |

|

1 |