Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Высокоуровневые методы программирования..pdf
Скачиваний:
8
Добавлен:
15.11.2022
Размер:
10.32 Mб
Скачать

ЛЕКЦИЯ 3.

РАБОТА С ДАННЫМИ В ЯЗЫКЕ TRANSACT-SQL

На предыдущей лекции были рассмотрены вопросы создания хранилищ данных, а именно команды языка Transact - SQL, позволяющие создавать базу данных и физические таблицы. Однако без данных база данных не имеет смыс­ ла, а данные существуют для того, чтобы когда-либо их просматривать и исполь­ зовать для принятия решений. Поэтому основной задачей текущего занятая яв­ ляется изучение возможностей команд языка TransactSQL, позволяющие полу­ чать данные из базы, записывать новые данные в базу и изменять их.

Работа команд языка Transact - SQL будет рассмотрена применительно к базе данных BD_work_people, в которой структурирована информация отно­ сительно нескольких работников, их месте работы и результатов производствен­ ной деятельности по выпуску нескольких наименований продукции. С целью исключения аномальных ситуаций, могущих возникнуть при выполнении ко­ манд манипулирования данными, информация размещена в нескольких физи­ ческих таблицах, связанных друг с другом с помощью механизма «первичный - внешний ключ». Структурная схема БД представлена на рис. 3.1.

Рис. 3.1. Структурная схема (ER - диаграмма) базы данных BD_work_people

Анализ данной базы данных и содержимого физических таблиц, представ­ ленных на рис. 3.2, показывает, что с большой степенью определенности можно утверждать, что база данных BD_work_people приведена к третьей нормальной форме. Целостность данных в ней поддерживается посредством использования специальных полей (первичных и внешних ключей) и специальных объектов, связей, в качестве которых выступают поименованные ограничения.

В каждой физической таблице первичные ключи имеют обозначения id, обозначения внешних ключей начинается с id_, а далее указывается на­ звание родительской таблицы. Тип данных первичных и внешних ключей

одинаковый: integer. Атрибуты полей: Fam, Podrazd, Kateg, предназначенные для хранения данных о фамилии работника, его места работы и наимено­ вании выпускаемой продукции, имеют символьный тип данных - varchar. Атрибуты полей Obem, Cena, Brak используются для хранения данных о объеме выработке работника, стоимости единицы продукции и величине брака и имеют числовой тип данных - decimal, а поля Datar, Dataw, предна­ значенные для хранения информации о дате рождения работников и дате их работы, имеют тип данных smalldatetime.

Основными командами, предназначенными для работы с данными в язы­ ке Transact - SQL, являются оператор выборки Select, оператор вставки новых записей Insert, оператор обновления данных Update и оператор удаления уста­ ревших записей Delete.

И

_

.

.

jj

T _ p o d r a z d

f

id

 

1Podrazd

 

 

L t

1

 

Цех №71

 

2

 

Цех №73

r

h

i

i ^ P e o p l e

 

 

 

i

 

 

В

i

Id

l ram

1иасаг

на доагага

 

 

 

1’

 

► u —

Г ришин

03.05.1976

1

 

 

2

Иванов

17.04.1981

2

 

 

3

Климов

13.01.1972

1

 

 

4

Петров

06.04.1983

2

 

 

5

Сидоров

07.03.1975

2

 

T W

o r k

 

 

 

 

 

 

id

1id kateg

lid

Dataw lid people

lObem

Ice n a

1Brak

1

1

1

1

1

10

200,25

2

 

2

1

1

3

9

200,25

0

 

3

2

1

2

3,5

458,44

0,5

 

4

3

1

4

7

356,78

1

 

5

3

1

5

б

358,78

1

 

6

1

>

1

11

220,04

3

 

>

 

7

2

2

4

460,57

0

 

>

 

8

3

5

8

356,78

2

 

)

 

9

1

1

12

220,04

0

 

10

1

2

1

10

220,04

2

 

2J

 

11

3

5

7

358,78

0

 

____ „______

__

_____ ________

 

 

______ _________

 

T Kateg

 

■ ----------------- Й

!

||

! T_Dataw

 

jl

 

 

id

|Kateg

I :

j

id

|Dataw

1!

!

1

Подш ипник

j

Ц_ 1

21.01.2008

|

 

 

2

Вал

;

r z

2

23.01.2008

!

У___

3

Ц и ли н др

j

t =

3

25.01.2008

1

Рис. 3.2. Физические таблицы базы данныхBD_work_people

3.1. Особенности использования оператора SELECT

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

SELECT список полей [INTO новая таблица] FROM перечень таблиц

[W HERE условия фильтрации]

[G RO UP BY выражение группировки] [HAVING фильтрация групп]

[O RDER BY сортировка результата выборки]

Рассмотрим особенности использования этого оператора на нескольких типовых примерах.

На рис. 3.3 представлено два варианта решения задачи, связанной с определением среднего возраста работников подразделений, указанных в базе данных.

Определить средний возраст работников каждого подразделения Вариант № 1

Select p.Podrazd as 'Подразделение', avg(datediff(year,pe.datar,getdateO)) as 'Средний возраст'

From T_podrazd p,t_People pe where p.id=pe.id_podrazd Group by p.Podrazd

Вариант № 2

Select p.Podrazd as 'Подразделение',

(select avg(datediff(year,pe.datar,getdate())) fronyl t_People pe ( where p.id=pe.id_podrazd) as 'Средний возраст'N from t_Podrazd p

£

Подразделение

Средний возраст

| Цех

№71

34

2

Цех

№73

28

£

Подразделение

Средний возраст

Цех

№71

34

2

Цех

№73

28

Рис. 3.3. Оптимизация запроса группировки данных

Первый вариант является классическим и предполагает использования оператора Group By, который обеспечивает группировку записей по полю Podrazd. После оператора Select нужно перечислить имя колонки и мате­ матическую функцию Avg, обеспечивающую подсчет среднего значения. Для вычисления возраста в качестве аргумента функция Avg используется встроенная функция datediff, которая возвращает разницу в годах year меж­ ду текущей датой getdate() и датой рождения работника datar. Для большей наглядности результата в запросе используются псевдонимы полей «Подраз­ деление» и «Средний возраст», которые не изменяют имена полей в базе, а влияют только на результирующую таблицу. Поскольку выбираемая инфор­ мация хранится в разных таблицах, имена таблиц и их псевдонимы перечис­ ляются после ключевого слова From, а после предиката where указывается

логическое высказывание, определяющее условие естественного соедине­ ния данных из таблиц, участвующих в выборке данных. Запрос выполня­ ется за несколько шагов. Первоначально производится соединение данных из таблиц, источников информации. Для этого используется условие, запи­ санное после слова where. Затем выполняется разбивка записей, попавших в выборку на группы, то есть сортировка данных по полю Podrazd, и только после этого - применение к каждой группе математической функции Avg.

Количество шагов запроса можно сократить, если применить второй вариант, представленный на рис. 3.4. При этом варианте не используется явная групповая операция, а принимается во внимание тот факт, что при­ ведение базы данных к третьей нормальной форме автоматически структу­ рирует информацию должным образом. Поэтому если запомнить значение ключа id таблицы TPodrazd внешнего запроса и по маске р передавать это значение в подзапрос, то для каждой текущей записи внешнего запроса мож­ но в родительской таблице T People выделять только одну группу, к которой и применять агрегатную функцию. Таким образом, схема выполнения запро­ са второго варианта исключает два первых шага схемы выполнения запроса первого варианта, следовательно, такой запрос будет выполняться быстрее.

Определить подразделение, работники которого имеют: Вариант № 1 (наибольший средний возраст)

select top 1 p.Podrazd as 'Подразделение',

(select avg(datediff(year,pe.datar,getdateO)) from tP eople pe

where p.id=pe.id_podrazd) a s' Средний возраст'

from tPodrazd p order by 2 desc Вариант № 2

(наименьший средний возраст)

select top 1 p.Podrazd as 'Подразделение', (select avg(datediff(year,pe.datar,getdate())) from t People pe

where p.id=pe.id_podrazd) as 'Средний возраст’

П одразделение

Средний в озр аст

Цех №71

34

П одразделение

Средний в озр аст

Цех №73

28

Рис. 3.4. Поиск максимумов или минимумов

На рис. 3.5 представлены примеры, поясняющие способ фильтрации записей, содержащих информацию о подразделении, в котором работники имеют наибольший или наименьший средний возраст.

Данная задача может быть решена на основе схемы запроса варианта 2 рис. 3.4, если результат выполнения последнего отсортировать в поряд­ ке убывания возраста, используя директиву order by 2 desc, или в порядке

возрастания возраста, используя order by 2. А затем к тому, что получилось, применить функцию top 1, которая обеспечивает выбор первой записи ре­ зультата.

На рис. 3.5 представлена схема запроса по определению возраста ста­ рейших работников каждого подразделения. Эта схема предполагает ис­ пользование в предикате where подзапроса, обеспечивающего нахождение значения максимального возраста по подразделению, которое определяется полем id и маской р внешнего запроса. В итоге именно этот параметр пере­ дается в подзапрос и обеспечивает правильную связь результата выполне­ ния подзапроса с лицом указанном во внешнем запросе на очередном шаге его выполнения.

Замечательной особенностью языка Transact-SQL является то, что по маске можно в подзапрос передать значение любого поля таблицы, даже того, которое не присутствует при выполнении операции проекции. Приме­ нительно к данному случаю поля p.id, которое и используется для организа­ ции условия фильтрации p.id=pp.id подзапроса. В результате подзапрос яв­ ляется зависимым и будет выполняться при отборе каждой внешней записи, то есть столько раз, сколько записей образуется после выполнения операции естественного соединения данных таблиц, участвующих в запросе.

Определить в каждом подразделении фамилию и возраст старейших работников

select p.Podrazd as 'Подразделение',pex.Fam as 'Фамилия',

datediff(year,pex.datar,getdateO) as 'Возраст' from tPodrazd p,t_people pex

where p.id=pex.id_podrazd and

datediff(year,pex.datar,getdateO)=

(select max(datediff(year,ppex.datar,getdateO)) from t Podrazd pp,t_people ppex

where pp.id=ppex.id_podrazd and p.id=pp.id)

 

 

 

 

г? *

 

Подразделение Фамилия В озраст

1

Цех

№71

Климов

3 6

2

Цех

№73

Сидоров 33

Рис. 3.5. Использование подзапроса в предикате Where

На рис. 3.7 представлен

SQL-код

запроса, отвечающий на вопрос

о количестве работников, задействованных в изготовлении соответствующих единиц продукции.

Сколько работников задействованы в изготовлении каждой единицы продукции?

select k.kateg,(select count(distinct w.idjpeople) from tw ork w

where k.id=w.id_kateg)as 'Количество работников' From T kateg k

 

k a teg

Количество работников

1

Подшипник 2

2

Вал

1

3

Цилиндр

2

Рис. 3.6. Использование зависимого подзапроса в секции SELECT

Схема запроса предполагает использование в секции SELECT зависимого подзапроса, обеспечивающего подсчет количества работников, занятых изготовленем одной и той же продукции. Это достигается посредством примене­ ния агрегатной функции count с аргументом distinct w.id_people, исключающим дубликаты, и использованием поля id таблицы T_kateg, значения которого пе­ редаются в подзапрос через маску к.

На рис. 3.7 представлен SQL-код запроса, отвечающий на вопрос о количестве работников, задействованных в изготовлении соответствующих единиц продукции и их суммарной стоимости.

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

select * From

(select k.Kateg,

cast(convert(decimal(10,2),sum(w.obem*w.cena)) as varchar(15))+' руб' as 'Сумма' from T_kateg k, Twork w

where k.id=w.id_kateg Group by k.Kateg) x l,

(select kk.Kateg,count(distinct ww.id_people) as 'Кол человек'

from T_kateg kk, T_work ww

 

 

 

 

 

where kk.id=ww.id_kateg

 

 

 

 

 

group by kk.Kateg) x2

 

 

\

^

 

Where xl.Kateg=x2.Kateg

 

|

 

^

 

 

K ateg

Сумма

 

K ateg

Кол человек

1

Вал

3 4 4 6

.8 2

руб

Вал

1

2

Подшипник 1 1 0 6 6 .0 7 руб

Подшипник

2

13

Цилиндр

1 0 7 3

3 .4 0

руб

Цилиндр

2

Рис. 3.7. Использование подзапросов в секции FROM

Примечательной особенностью данного запроса является то, что в сек­ ции FROM вместо указания таблиц представлены запросы, подсчитываю­ щие суммарную стоимость и количество человек, задействованных для из­ готовления соответствующей категории продукции. Причем при подсчете стоимости с помощью функции convert производится округление результата до двух знаков после запятой - тип decimal(10,2), а затем его преобразование к символьному типу varchar с последующим сцеплением с символьной кон­ стантой ‘руб’. Для каждого подзапроса указывается псевдоним х1 и х2. Вне­ шний объединяющий запрос связывает обе полученные таблицы, используя условие xl.Kateg=x2.Kateg. Для таких запросов есть только одно ограниче­ ние - у каждого поля подзапросов секции FROM должно быть имя.

На рис. 3.9 представлен запрос, обеспечивающий подсчет суммарной сто­ имости продукции, выпущенной каждым работником 25.01.2008. В этот день трудились не все работники. Поэтому если в предикате WHERE записать ус­ ловие естественного соединения данных из таблиц, участвующих в выборке p.id=w.id_people, то запрос вернет только две записи. Использование естест­ венного объединения со знаком равенства называется внутренним объедине­ нием. Чтобы увидеть записи, которые не связаны, нужно использовать внешнее объединение, которое бывает левым или правым. В запросе на рис. 3.8 исполь­ зовано левое внешнее объединение данных, для чего слева от знака равенства в условии p.id*=w.id_people стоит знак «*».

Определить сумму произведенных деталей каждым работником 25.01.2008

 

 

1

select p.Fam,

А—----12

cast(convert(decimal(10,2),

V ---- 3

sum(w.obem*w.cena)) as varchar(15))+' руб' as 'Сумма'

4

from T_people p,T_work w

5

where p.id*=w.id_people

 

and w.id_dataw=3

 

Group by p.Fam

 

|Fam

Сумма

 

Гришин

4 8 4 0 .8 8

руб

Иванов

NULL

 

 

Климов

NULL

 

 

Петров

NULL

 

 

Сидоров 2 5 1 1

.4 6

руб

. p r

Рис. 3.8. Использование явного левого (внешнего) объединения данных

На рис. 3.9 представлен запрос, обеспечивающий подсчет смен, который отработал каждый работник 25.01.2008.

Схема выполнения запроса предполагает использование информации из трех таблиц T People p,t_work w,t_dataw d, где p, w, d - маски таблиц, при­ чем условие d.dataw= convert(smalldatetime,’25.01.2008’,104) означает выборку данных, относящихся только к дате 25.01.2008. Для выборки данных, отно­ сящихся к этой дате, символьная константа ’25.01.2008’ с помощью функции convert преобразуется к типу smalldatetime, а константа 104 обеспечивает со­ гласование с германским форматом даты - ‘дд.мм.гпт.’

Определить,сколько смен отработано

каждым работником 25.01.2008

Вариант № 1

select p.Fam,count(w.id) as 'Количество смен' From T_People p,t_work w,t_dataw d

where p.id=w.id_people and d.id=w.id_dataw

and d.dataw=convert(smalldatetime,'25.01.2008',104)

group by p.Fam

 

Вариант № 2

 

select p.Fam,(select count(w.id)

и

from twork w,t_dataw d

<

where p.id=w.id_people and d.id=w.id_dataw N

and d.dataw=convert(smalldatetime,'25.01.2008',104) )as 'Количество смен'

From T_People p Вариант № 3 select p.Fam,

(select case count(w.id) when 1 then

'1-я смена' when 2 then '2-е смены' else

'сегодня отдыхал' end

from t work w,t_dataw d

where p.id=w.id_people and d.id=w.id_dataw

and d.dataw=convert(smalldatetime,'25.01.2008',104) )as 'Количество смен'

From TPeople p

 

|Fain_____ Количество сиен

1

.Гришин 2

2

Сидоров 1

|Fain_____ Количество сиен 1___Гришин 2

2Иванов О

3Климов О

4Петров О

5Сидоров1

Таш

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

1 Гришин

2 -е смены

2Иванов сегодн я отдыхал

3Климов сегодня отдыхал

4Петров сегодня отдыхал

5Сидоров 1 -а смена

Рис. 3.9. Использование неявного левого объединения данных и функции CASE

Первый вариант запроса описывает условие внутреннего (жесткого) объ­ единения данных. Второй и третий, посредством использования подзапроса в секции SELECT, обеспечивают внешнее (левое) объединение данных. Кроме того, в третьем варианте использована функция Case, которая в зависимости от результата возвращаемого функцией count подзапроса отобранные записи снаб­ жает соответствующим комментарием.

На рис. 3.10 представлен запрос, обеспечивающий подсчет статистики ра­ ботников, родившихся зимой и весной.

Данный запрос является перекрестным. Источником строк его являет­ ся поле Podrazd, а источником столбцов —данные, возвращаемые подзапроса­ ми с псевдонимами «Зимой» и «Весной», которые описаны в секции SELECT. Для фильтрации данных, относящихся к определенному периоду, использована функция Datepart, возвращающая при обработке данных поля datar номер меся­ ца. Фильтрация зимних месяцев производится функцией in (12,1,2), а весенних - функцией between 3 and 5. Связь данных внешнего запроса с данными подза­ просов производится через маску р таблицы t Podrazd по полю id.

Определить для каждого подразделения количество работников, родившихся зимой и весной (перекрестный запрос)

select p.Podrazd as 'Подразделение', (select count(pe.id) from t_People pe where p.id=pe.id_podrazd and

(datepart(month,pe.datar) in(12,l,2))) as 'Зимой', (select count(pee.id) from tJPeople pee

where p.id=pee.id_podrazd and (datepart(month,pee.datar) between 3 and 5)) as 'Весной' from t_Podrazd p

[подразделение Зимой Весной

1__ Цех

№71

1

1

2__ ; Цех

№73

О

3

Рис. 3.10. Перекрестный запроси функция datepart

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

Определить для каждого подразделения суммарную стоимость каждой единицы выпущенной продукции (перекрестный запрос)

select p.Podrazd as

'Подразделение',

 

 

(select sum((w.obem

-w.brak)*w.cena) from t_People pe,t_work w,t_kateg k

where p.id=pe.id_podrazd and

 

 

 

pe.id=w.id_people and

 

 

 

k.id=w.id_kateg and

 

 

 

k.Kateg Like'%

цил%') as12Цилиндр ',

 

 

(select sum((w.obem

-w.brak)*w.cena) from t_People pe,t_work w,t_kateg k

where p.id=pe.id_podrazd and

 

 

 

pe.id=w.idjpeople and

 

 

 

k.id=w.id_kateg and

 

 

 

k.Kateg Like'%

вал%') as' Вал',

 

 

 

(select sum((w.obem

-w.brak)*w.cena) from t_People pe,t_work w,t_kateg k

where p.id=pe.id_podrazd and

 

 

 

pe.id=w.id_people and

 

 

 

k.id=w.id_kateg and

 

 

 

k.Kateg Like'%

подш %') as' Подшипник '

 

 

from t_Podrazd p

 

.—

 

 

Подразделение Цилиндр

Вал

Подшипник

1

Цех №71

NULL

NULL

9565.37D 00

2

Цех №73

9304 .28000 3217 .60000

NULL

Рис. 3.11. Перекрестный запрос и функция Like