Sunteți pe pagina 1din 66

BAZE DE DATE

Procesarea interogrilor

Mihaela Elena Breabn


FII 2014-2015

Cuprins
Etapele procesrii interogrilor
Expresii n algebra relaional

Operatori (revizitat)
Expresii
Echivalena expresiilor

Estimarea costului interogrii


Algoritmi pentru evaluarea operatorilor/expresiilor n algebra
relaional

Etapele procesrii interogrilor


Compilarea interogrii

Analiza sintactic

Parsare

Parsare
Preprocesare

Arbore de parsare

Analiza semantic

Preprocesare si rescriere n AR
Selecia reprezentrii algebrice

Generare a planului logic

Generare a planului fizic

Plan logic

Selecia algoritmilor i a ordinii

Plan fizic
Execuia interogrii
metadate

Compilarea
interogrii

Optimizarea
interogrii

Optimizarea interogrilor
interogare SQL

parsare
rspuns

arbore de
parsare

execut

conversie
plan logic al interogrii

Genereaz/transform p.l.
p.l.mbuntit

estimarea dimensiunii
rezultatului

statistici

Pi

alege cel mai bun


{(P1,C1),(P2,C2)...}

estimeaz costuri

dimensiunile planului logic

Genereaz/transform p.f.

generarea planului fizic


{P1,P2,..}
4

Analiza sintactic
Gramatic independent de context

<query> ::= <SFW> | (<query>)


<SFW> ::= SELECT <select_list> FROM <table_list> WHERE <where_cond>
<select_list> ::= <identifier>, <select_list> | <identifier>
<table_list> ::= <identifier>, <table_list> | <identifier >

Rezultatul parsrii: arbore de parsare

Gramatica SQL in forma BNF: http://savage.net.au/SQL/index.html


5

Analiza semantic
Preprocesare

Rescrierea apelurilor la view-uri

Verificarea relaiilor

Verificarea atributelor i a ambiguitii

Verificarea tipurilor

Dac arborele de parsare este valid el este transformat ntr-o expresie cu


operatori din algebra relaional

Analiza semantic
Rescriere n AR (1)
<Query>
<SFW>

SELECT < select_list >

FROM

< identifier >

< table_list >


< identifier >

title

WHERE

< where_cond >

<Tuple> IN <Query>

StarsIn

< identifier >

( <Query> )

starName
SELECT

< select_list >


< identifier >

name
7

FROM

< table_list >

< identifier >

MovieStar

WHERE

<SFW>
< where_cond >

< identifier > LIKE <Pattern>

birthDate

%1960

Analiza semantic
Rescriere n AR (2)

title

title

starName=name

StarsIn
<tuple>
<attribute>
starName
8

<where_cond>
IN

name

birthdate LIKE %1960


MovieStar

StarsIn

name
birthdate LIKE %1960
MovieStar

Analiza semantic
Optimizarea planului logic
SELECT Theater
FROM Movie M, Schedule S
WHERE
M.Title = S.Title
AND M.Actor=Winger

Parsare/
Conversie

p Theater

Schedule

Generatorul de planuri logice


aplic rescrieri algebrice

p Theater

M.Actor=Winger
M.Title=S.Title

JOIN
Movie

M.Title=S.Title AND M.Actor=Winger

x
Movie

alt plan logic

Generatorul
de planuri
logice

Schedule

alt plan logic

M.Actor=Winger

M.Title=S.Title

x
Movie

plan logic iniial

p Theater

Schedule

Analiza semantic
Optimizarea planului logic
p Theater

p Theater
M.Actor=Winger

S.Title=M.Title

Generatorul
M.Title=S.Title de planuri
logice

Movie

M.Actor=Winger
Schedule Movie

Schedule

cond dac cond


face referire
x
R
10

doar la S

Analiza semantic
Optimizarea planului fizic
p Theater
S.Title=M.Title

Schedule

M.Actor=Winger

Generatorul de
plan fizic

Movie

Generatorul de plan fizic alege


primitivele de execuie
index pe Actor i
p Theater
Title, tabele nesortate
LEFT INDEX
S.Title=M.Title
INDEX
Actor=Winger
Schedule
11

Movie

index pe Actor, tabelul


Schedule sortat pe Title,
p Theater
SORT-MERGE
S.Title=M.Title

Plan fizic1

INDEX
M.Actor=Winger
Schedule

Movie

Plan fizic 2

Operatori n algebra relaional


(revizitat)

ase operatori de baz

Selecia:
Proiecia:
Reuniunea:
Diferena:
Produsul cartezian: x
Redenumirea:

Operatorii iau ca intrare una sau dou relaii i genereaz


o noua relaie

12

Operatorul de selecie

Realaia r

A=B ^ D > 5 (r)

13

12

23 10

23 10

Operatorul de proiecie

Relaia r

A,C (r)

14

10

20

30

40

Operatorul reuniune

Relaiile r i s

r s:

15

Operatorul diferen

Relaiile r i s
A

r-s

16

Produsul cartezian

Relaiile r i s

10
10
20
10

a
a
b
b

rxs

17

1
1
1
1
2
2
2
2

10
10
20
10
10
10
20
10

a
a
b
b
a
a
b
b

Operatorul de redenumire

x (E) - returneaz expresia E sub numele X


Dac o expresie E n algebra relaional are aritate n atunci
x ( A ,A ,...,A ) (E )
1

returneaz rezultatul expresiei E sub numele X i atributele


redenumite n A1 , A2 , ., An .

18

Compunerea operatorilor
A=C(r x s)
1. r x s

2.

19

A=C(r x s)

1
1
1
1
2
2
2
2

10
10
20
10
10
10
20
10

a
a
b
b
a
a
b
b

1
2
2

10
10
20

a
a
b

Expresii n algebra relaional

Cea mai simpla expresie este o relaie n baza de date


Fie E1 i E2 expresii n algebra relaional; urmtoarele sunt expresii n
algebra relational:

E1 E2

E1 E2

E1 x E2

p (E1), P este un predicat peste atribute din E1

s(E1), S este o list de atribute din E1

x (E1), x este noul nume pentru rezultatul lui E1

20

Exprimarea interogrilor n algebra


relaional

mpumuturile (loan) mai mari de 1200

amount > 1200 (loan)

Numrul mprumutului (loan_number) pentru mprumuturi mai mari de 1200

loan_number (amount > 1200 (loan))


Numele clienilor care au un mprumut, un depozit sau ambele la banc

customer_name (borrower) customer_name (depositor)


21

Interogri

Numele tuturor clienilor care au un mprumut la filiala Perryridge

customer_name (branch_name=Perryridge
(borrower.loan_number = loan.loan_number(borrower x loan)))

Numele tuturor clienilor care au un mprumut la filiala Perryridge dar nu


au un depozit la nici o filial a bncii

customer_name (branch_name = Perryridge


(borrower.loan_number = loan.loan_number(borrower x loan)))

customer_name(depositor)
22

Interogri

Numele tuturor clienilor care au un mprumut la filiala Perryridge

23

customer_name (branch_name = Perryridge (


borrower.loan_number = loan.loan_number (borrower x loan)))
customer_name(loan.loan_number = borrower.loan_number (
(branch_name = Perryridge (loan)) x borrower))

Operatori adiionali

Intersecia pe mulimi
Joinul natural
Agregarea
Joinul extern
Teta-joinul

Toi cu excepia agregrii pot fi exprimai utiliznd operatori


de baz

24

Intersecia pe mulimi

Relaiile r i s
A

1
2
1
r

rs

25

2
3
s

Joinul natural

Relaiile r i s

1
2
4
1
2

a
a
b
a
b

1
3
1
2
3

a
a
a
b
b

1
1
1
1
2

a
a
a
a
b

r s

r.A, r.B, r.C, r.D, s.E (r.B = s.B r.D = s.D (r x s))
26

Agregare
Exemplu

Cea mai mare balan din tabela account

balance(account) - account.balance

(account.balance < d.balance (account x d (account)))

27

Funcii de agregare i operatori

Funcii de agregare:

avg
min
max
sum
count
var

Operatorul de agregare n algebra relaional


G1 ,G2 , ,Gn

28

F ( A ), F ( A ),
1

, Fn ( An )

(E)

E expresie n algebra relaional


G1, G2 , Gn o list de atribute de grupare (poate fi goal)
Fiecare Fi este o funcie de agregare
Fiecare Ai este un atribut

Agregare
Exemplu

relaia r

10

g sum(c) (r)
sum(c )
27

Care operaii de agregare nu pot fi exprimate pe baza celorlali operatori


relaionali?
29

Join extern
relaia loan
loan_number

branch_name

loan
L-170
L-230

loan

branch_name
Downtown
Redwood

L-170
L-230
L-155

amount
3000
4000

customer_name
Jones
Smith

borrower (join extern stnga)


loan_number

L-170
L-230
L-260
30

Jones
Smith
Hayes

3000
4000
1700

loan_number

borrower (join natural)

loan_number

customer_name

amount

Downtown
Redwood
Perryridge

L-170
L-230
L-260

relaia borrower

branch_name

Downtown
Redwood
Perryridge

amount

3000
4000
1700

customer_name

Jones
Smith
null

Join extern

Join extern dreapta


loan

borrower

loan_number
L-170
L-230
L-155

branch_name
Downtown
Redwood
null

amount
3000
4000
null

customer_name
Jones
Smith
Hayes

Join extern plin

loan

borrower

loan_number
L-170
L-230
L-260
L-155

31

branch_name
Downtown
Redwood
Perryridge
null

amount
3000
4000
1700
null

customer_name
Jones
Smith
null
Hayes

Exemple interogri

Numele clienilor care au un mprumut i un depozit la banc


customer_name (borrower) customer_name (depositor)

Numele clienilor care au un mprumut la banc i suma mprumutat


customer_name, loan_number, amount (borrower

loan)

Clienii care au depozite de la mcar cele dou filiale Downtown i Uptown


customer_name (branch_name = Downtown (depositor
customer_name (branch_name = Uptown (depositor

32

account ))
account))

Echivalena expresiilor

Dou expresii n algebra relaional sunt echivalente dac


acestea genereaz acelai set de tuple pe orice instan a bazei
de date

ordinea tuplelor e irelevant

Obs: SQL lucreaz cu multiseturi

33

n versiunea multiset a algebrei relaionale echivalena se verific relativ


la multiseturi de tuple

Reguli de echivalen
selecia pe baz de conjuncii e echivalent cu o secven de selecii

1.

( E ) ( ( E ))
1

operaiile de selecie sunt comutative

2.

( ( E )) ( ( E ))
1

ntr-un ir de proiecii consecutive doar ultima efectuat e necesar

3.

L1 ( L2 (( Ln ( E )))) L1 ( E )
seleciile pot fi combinate cu produsul cartezian i teta joinurile

4.

a. (E1 X E2) = E1 E2
b. 1(E1 2 E2) = E1 1 2 E2
34

Reguli de echivalen
5.

operaiile de teta-join i de join natural sunt comutative


E1 E2 = E2 E1

6.

a) Operaiile de join natural sunt asociative


(E1 E2) E3 = E1 (E2 E3)
b) Operaiile de teta-join sunt asociative astfel:
(E1 1 E2) 2 3 E3 = E1 1 3 (E2 2 E3)

unde 2 implic atribute doar din E2 i E3

35

Reguli de echivalen

36

Reguli de echivalen
Distribuia seleciei asupra operatorului de teta-join

7.
a)

cnd 0 implic atribute doar din una dintre expresiile (E1) din join:

0E1
b)

E2

cnd 1 implic numai atribute din E1 i 2 implic numai atribute din E2:

1 E1

37

E2) = (0(E1))

E2) = (1(E1))

( (E2))

Reguli de echivalen
Distribuia proieciei asupra teta-joinului

8.
a)

dac implic numai atribute din L1 L2:

L1 L2 ( E1

E2 ) ( L1 ( E1 ))

( L2 ( E2 ))

Fie joinul E1 E2
Fie L1 i L2 mulimi de atribute din E1 i respectiv E2
Fie L3 atribute din E1 care sunt implicate n condiia de join , dar nu sunt n L1 L2,
Fie L4 atribute dinE2 care sunt implicate n condiia de join , dar nu sunt n L1 L2
b)

L1 L2 ( E1

38

E2 ) L1 L2 (( L1 L3 ( E1 ))

( L2 L4 ( E2 )))

Reguli de echivalen
9.

Operaiile de reuniune i intersecie pe mulimi sunt comutative

10.

E1 E2 = E2 E1
E1 E2 = E2 E1
Reuniunea i intersecia pe mulimi sunt asociative
(E1 E2) E3 = E1 (E2 E3)
(E1 E2) E3 = E1 (E2 E3)

11.

Selecia se distribuie peste , i .

(E1

E2) = (E1) (E2)


similar pentru i n locul

(E1

E2) = (E1) E2

similar pentru in locul , dar nu pentru


12.

Proiecia se distribuie peste reuniune


L(E1 E2) = (L(E1)) (L(E2))

39

Optimizri
mpingerea seleciilor

Numele clienilor care au un cont la o filial din Brooklyn

customer_name(branch_city = Brooklyn(branch

(account

Pe baza regulii 7a

customer_name ((branch_city =Brooklyn (branch))

depositor)))

(account

depositor))

Realizarea seleciei n primele etape reduce dimensiunea relaiei care


particip n join

40

Optimizri
mpingerea seleciilor

Numele clienilor cu un cont la o filial din Brooklyn care are balana peste
1000

customer_name(branch_city = Brooklyn balance > 1000


(branch (account
depositor)))

Regula 6a (asociativitatea la join)

customer_name((branch_city = Brooklyn balance > 1000


(branch account))
depositor)

A doua form furnizeaz oportunitatea de a efectua selecia devreme

branch_city = Brooklyn (branch)

41

balance > 1000 (account)

42

Optimizri
mpingerea proieciilor
customer_name((branch_city = Brooklyn (branch)

depositor)

Eliminarea atributelor care nu sunt necesare din rezultatele intermediare

customer_name ((
account_number (branch_city = Brooklyn (branch)
depositor )

account)

account )

Realizarea devreme a proieciei reduce dimensiunea relaiilor din join

43

Optimizri
Ordonarea joinurilor

Pentru orice relaii r1, r2, si r3,


(r1 r2) r3 = r1 (r2 r3 )
Dac r2 r3 are dimensiuni mari i r1 r2 e de dimensiuni mai mici, alegem
(r1 r2) r3
Exemplu

customer_name ((branch_city = Brooklyn (branch))


(account depositor))
Numai un mic procent din clieni au conturi n filiale din Brooklyn deci e mai
bine s se execute mai nti

branch_city = Brooklyn (branch) account


Pentru n relaii exist (2(n 1))!/(n 1)! ordonri diferite pentru join.

n = 7 -> 665280, n = 10 ->176 billion!

Pentru a reduce numrul de ordonri supuse evalurii se utilizeaz


programarea dinamic
44

Estimarea costurilor

lr: dimensiunea unui tuplu din r.


nr: numrul de tuple n relaia r.
br: numrul de blocuri coninnd tuple din r.
fr: factorul de bloc al lui r nr. de tuple din r ce intr ntr-un bloc
Dac tuplele lui r sunt stocate mpreun ntr-un fiier, atunci:

nr
br
f r

V(A, r): numrul de valori distincte care apar in r pentru atributul A; e


echivalent cu dimensiunea proieciei A(r) (pe seturi).

45

Estimarea dimensiunii seleciei

A=v(r)

nr / V(A,r) : numrul de nregistrri ce satisfac selecia


pentru atribut cheie: 1

AV(r) (cazul A V(r) este simetric)

dac sunt disponibile min(A,r) i max (A,r)

0 dac v < min(A,r)


v min( A, r )
nr .
max( A, r ) min( A, r )

altfel

dac sunt disponibile histograme se poate rafina estimarea anterioar

n lipsa oricrei informaii statistice dimensiunea se consider a fi nr / 2.

46

Estimarea dimensiunii seleciilor complexe

Selectivitatea unei condiii i este probabilitatea ca un tuplu n relaia r s


satisfac i

dac numrul de tuple ce satisfac i este si , selectivitatea e si /nr.

Conjuncia (n ipoteza independenei)


1 2. . . n (r):

Disjuncia

Negaia

nr

s1 s2 . . . sn
nrn

s
s
s
1 2 . . . n (r): nr 1 (1 1 ) (1 2 ) ... (1 n )
nr
nr
nr

(r):

47

nr size((r))

Estimarea dimensiunii joinului

pentru produsul cartezian r x s: nr * ns tuple, fiecare tuplu ocup sr + ss


octei
pentru r s

R S = : nr * ns

R S este o (super)cheie pentru R: <= ns

48

sau nr ns
V ( A, r )
V ( A, s )

R S = {A} nu e cheie pentru R sau S: nr ns

minimul este considerat de acuratee mai mare

dac sunt disponibile histograme se calculeaz formulele anterioare pe fiecare celul pentru
cele dou relaii

Estimarea dimensiunii pentru alte operaii

Proiecia A(r) : V(A,r)

Agregarea: AgF(r) : V(A,r)

Operaii pe mulimi

r s : nr + ns.
r s : min(nr , ns)

r-s : nr

Join extern

r
r

s: dim(r
s) + nr
s = dim(r
s) + nr + ns

1 (r) 2 (r) echivalent cu 1 2 (r)

Estimatorii furnizeaz n general margini superioare

49

Optimizarea planului fizic

50

Estimarea costului la nivelul planului fizic

Costul e n general msurat ca durata de timp necesar pentru returnarea


rspunsului
Accesul la disc este costul predominant
Numrul de cutri * tS (timpul pentru o localizare a unui bloc pe disc)
Numrul de blocuri citite/scrise * tT (timpul de transfer)
costul CPU e ignorat pentru simplitate
Costul pentru transferul a b blocuri plus S cutri:
b * tT + S * tS

51

Algoritmi pentru selectie

Cutare liniar (full scan)

Cutarea binar

cost: br * tT + tS
dac selecia e pe un atribut cheie, costul estimativ: br/2 * tT + tS
poate fi aplicat indiferent de condiia de selecie, ordonarea
nregistrrilor n fiier, existena indecilor

aplicabil pentru condiii de selecie de tip egalitate pe atributul dup


care e ordonat fiierul
costul gsirii primului tuplu ce satisface condiia: log2(br) * (tT + tS); dac
exist mai multe tuple se adaug timpul de transfer al blocurilor

Scanarea indexului condiia de selecie = cheia de cutare a


indexului

52

index primar pe cheie candidat, egalitate: (hi + 1) * (tT + tS)


index primar pe non-cheie, egalitate: hi * (tT + tS) + tS + tT * b
index secundar, egalitate, n tuple returnate: (hi + n) * (tT + tS)
index primar, comparaie: hi * (tT + tS) + tS + tT * b

Algoritmi pentru selecii complexe

Conjuncie: 1 2. . . n(r)

utilizarea unui index pentru I i verificarea celorlalte condiii


pe msur ce tuplele sunt aduse n memorie
utilizarea unui index multi-cheie
intersecia identificatorilor (pointerilor la nregistrri) returnai
de indecii asociai condiiilor urmat de citirea nregistrrilor

Disjuncie: 1 2 . . . n (r)

53

reuniunea identificatorilor

Algoritmi pentru join

Algoritmi:

join cu bucle imbricate (nested-loop join)


join indexat cu bucle imbricate
join cu fuziune (merge join)
join hash

Alegerea se face pe baza estimrii costului


Sunt necesare estimri realizate la nivelul planului logic

54

Join cu bucle imbricate

Pentru teta-join: r

for each tuplu tr in r do begin


for each tuplu ts in s do begin
if (tr,ts) satisface
adaug tr ts la rezultat
end
end
relaia interioar s
relaia exterioar r

Costul estimat: (nr bs + br)*tT + (nr + br )*tS

55

Join indexat cu bucle imbricate

Cutrile n index pot nlocui scanarea fiierelor dac:

e un echi-join sau join natural


exist un index pe atributul de join al relaiei interioare

pentru fiecare tuplu tr n relaia exterioar r se utilizeaz indexul pentru


localizarea tuplelor din s care satisfac condiia de join cu uplul tr.

costul: br (tT + tS) + nr c

c este costul parcurgerii indexului pentru a returna tuple din s care se potrivesc
pentru un tuplu din r (echivalent cu selecia pe s cu condiia de join)
dac exist indeci pentru ambele relaii, relaia cu mai puine tuple va fi preferat
drept relaie exterioar n join

Exemplu

56

depositor customer, depositor relaie exterioar


customer are asociat un index primar de tip B+-arbore pe atributul de join customername, cu 20 intrri pe nod
customer: 10,000 tuple(f=25), depositor:5000 tuple (f=50)
costul: 100 + 5000 * 5 = 25,100 blocuri transferate i cutri (corespondentul n
joinul neindexat:2,000,100 blocuri transferate i = 5100 cutri)

Join cu fuziune
Algoritm

1.
2.

se sorteaz ambele relaii n funcie de atributul de join


are loc fuziunea relaiilor

Poate fi utilizat doar pentru echi-joinuri


Costul:

br + bs blocuri transferate
+ costul sortrii relaiilor

Join cu fuziune hibrid: o relaie este sortat iar a doua are un index secundar pe

57

atributul de join de tip B+-arbore


relaia sortat fuzioneaz cu intrrile de pe nivelul frunz al arborelui
se sorteaz rezultatul dup adresele tuplelor relaiei nesortate
se scaneaz relaia nesortat n ordinea adreselor fizice i se realizeaz fuziunea cu
rezultatul anterior pentru a nlocui adresele cu tuplele asociate

Join hash

aplicabil pentru echi-join


o funcie hash h ce ia la intrare atributele de join partiioneaz tuplele
ambelor relaii n blocuri ce ncap n memorie

r1, r2,rn
s1,s2,sn

tuplele din ri sunt comparate doar cu tuplele din si

58

Joinuri complexe

Condiie de tip conjuncie: r

(r

59

bucle imbricate cu verificarea tuturor condiiilor sau


se calculeaz un join mai simplu r i s i se realizeaz selecia pentru celelalte
condiii

Condiie de tip disjuncie: r

1 2... n

1 2 ... n s

bucle imbricate cu verificarea condiiilor sau


calculul reuniunii joinurilor individuale (aplicabil numai versiunii set a reuniunii)
1 s)

(r

s) . . . (r

s)

Eliminarea duplicatelor

Sortarea tuplelor sau hashing

Fiindca e costisitoare, SGBD-urile nu elimina duplicatele decat


la cerere

60

Evaluare expresiilor

Alternative:

Materializarea: (sub)expresiile sunt materializate sub forma unor relaii


stocate pe disc pentru a fi date ca intrare operatorilor de pe nivele
superioare
Pipelining: tuple sunt date ca intrare operaiilor de pe nivele superioare
imediat ce acestea sunt returnate n timpul procesrii unui operator

nu e ntotdeauna posibil (sortare, join hash)


varianta la cerere: nivelul superior solicit noi tuple
varianta la productor: operatorul scrie n buffer tuple iar printele scoate din
buffer (la umplerea bufferului exist timpi de ateptare)

Ex: Numele clientilor care au depozite >2000

61

Planuri de executie
Oracle

Inregistreaza planul:

EXPLAIN PLAN
[SET STATEMENT_ID = <id>]
[INTO <table_name>]
FOR <sql_statement>;

Pentru orice comanda DML

Vizualizeaza planul:
SELECT * FROM table(dbms_xplan.display);
sau
select * from plan_table [where statement_id = <id>];

http://www.oracle.com/technetwork/database/bi-datawarehousing/twpexplain-the-explain-plan-052011-393674.pdf
62

Planuri de executie
Oracle-statistici

Table statistics

Column statistics

Number of distinct values (NDV) in column


Number of nulls in column
Data distribution (histogram)

Index statistics

Number of rows
Number of blocks
Average row length

Number of leaf blocks


Levels
Clustering factor

System statistics

63

I/O performance and utilization


CPU performance and utilization

Planuri de executie
Colectarea statisticilor

Proceduri Oracle din pachetul DBMS_STATS:

GATHER_INDEX_STATS

GATHER_TABLE_STATS

Statistics for all objects in a database

GATHER_SYSTEM_STATS

Statistics for all objects in a schema

GATHER_DATABASE_STATS

Table, column, and index statistics

GATHER_SCHEMA_STATS

Index statistics

CPU and I/O statistics for the system

http://docs.oracle.com/cd/B10500_01/server.920/a96533/stats.htm
64

Planuri de executie
Hints

In cadrul unei comenzi DML este posibil a instrui


optimizatorul Oracle asupra planului de executie:

SELECT /*+ USE_MERGE(employees departments) */ * FROM employees,


departments WHERE employees.department_id =
departments.department_id;

http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements006.htm

65

Bibliografie

Capitolele 13 i 14 n Avi Silberschatz Henry F. Korth S.


Sudarshan. Database System Concepts. McGraw-Hill
Science/Engineering/Math; 4th edition

66

S-ar putea să vă placă și