DBMS fonksîyonel

Cîhana databasan demek dirêj ji hêla DBMS-yên têkildar ve, ku zimanê SQL bikar tînin, serdest e. Ji ber vê yekê ku guhertoyên derketine NoSQL têne gotin. Wan karî di vê sûkê de ji xwe re cîhek diyar bikin, lê DBMS-yên têkildar namirin, û berdewam dikin ku bi rengek çalak ji bo armancên xwe werin bikar anîn.

Di vê gotarê de ez dixwazim têgeha databasek fonksiyonel diyar bikim. Ji bo têgihiştinek çêtir, ez ê vê yekê bi berhevdana wê bi modela têkiliya klasîk re bikim. Pirsgirêkên ji ceribandinên SQL yên cihêreng ên li ser Înternetê têne dîtin dê wekî mînak werin bikar anîn.

Pîrozbahiyê

Databasên peywendîdar li ser tablo û zeviyan dixebitin. Di danegehek fonksiyonel de, dê li şûna wan ders û fonksiyon werin bikar anîn. Zeviyek di tabloyek bi N bişkokan de dê wekî fonksiyonek N parametreyan were destnîşan kirin. Li şûna têkiliyên di navbera tabloyan de, fonksiyonên ku tiştên çîna ku pêwendiya pê re tê çêkirin vedigerînin dê werin bikar anîn. Dê li şûna JOIN kompozîsyona fonksiyonê were bikar anîn.

Berî ku rasterast derbasî peywiran bibe, ez ê peywira mantiqa domainê diyar bikim. Ji bo DDL ez ê hevoksaziya PostgreSQL bikar bînim. Ji bo fonksiyonel, hevoksaziya wê heye.

Tablo û zeviyan

Tiştek Sku ya hêsan a bi nav û qadên bihayê:

Relational

CREATE TABLE Sku
(
    id bigint NOT NULL,
    name character varying(100),
    price numeric(10,5),
    CONSTRAINT id_pkey PRIMARY KEY (id)
)

Fonksiyonel

CLASS Sku;
name = DATA STRING[100] (Sku);
price = DATA NUMERIC[10,5] (Sku);

Em du diyar dikin fonksiyonên, ku yek parametreyek Sku wekî têketinê digire û celebek primitive vedigerîne.

Tê texmîn kirin ku di DBMSek fonksiyonel de her tişt dê kodek hundurîn hebe ku bixweber tête çêkirin û heke hewce be dikare were gihîştin.

Ka em bihayê hilber / firotgeh / dabînker destnîşan bikin. Dibe ku ew bi demê re biguhere, ji ber vê yekê em qada demê li tabloyê zêde bikin. Ez ê ji danûstendina tabloyên ji bo pelrêçan di databasek pêwendiyê de derbas bikim da ku kodê kurt bikim:

Relational

CREATE TABLE prices
(
    skuId bigint NOT NULL,
    storeId bigint NOT NULL,
    supplierId bigint NOT NULL,
    dateTime timestamp without time zone,
    price numeric(10,5),
    CONSTRAINT prices_pkey PRIMARY KEY (skuId, storeId, supplierId)
)

Fonksiyonel

CLASS Sku;
CLASS Store;
CLASS Supplier;
dateTime = DATA DATETIME (Sku, Store, Supplier);
price = DATA NUMERIC[10,5] (Sku, Store, Supplier);

Indexes

Ji bo nimûneya paşîn, em ê li ser hemî kilîtan û tarîxê pêdekek ava bikin da ku em zû zû bihayê ji bo demek taybetî bibînin.

Relational

CREATE INDEX prices_date
    ON prices
    (skuId, storeId, supplierId, dateTime)

Fonksiyonel

INDEX Sku sk, Store st, Supplier sp, dateTime(sk, st, sp);

erkên

Ka em bi pirsgirêkên nisbeten hêsan ên ku ji yên têkildar hatine girtin dest pê bikin gotar li ser Habr.

Pêşîn, bila em mantiqa domainê ragihînin (ji bo databasa têkildar ev rasterast di gotara jorîn de tête kirin).

CLASS Department;
name = DATA STRING[100] (Department);

CLASS Employee;
department = DATA Department (Employee);
chief = DATA Employee (Employee);
name = DATA STRING[100] (Employee);
salary = DATA NUMERIC[14,2] (Employee);

Kar 1.1

Navnîşek karmendên ku meaşek ji ya serpereştiya xwe ya tavilê mezintir distînin nîşan bidin.

Relational

select a.*
from   employee a, employee b
where  b.id = a.chief_id
and    a.salary > b.salary

Fonksiyonel

SELECT name(Employee a) WHERE salary(a) > salary(chief(a));

Kar 1.2

Karmendên ku di beşa xwe de mûçeya herî zêde digirin navnîş bikin

Relational

select a.*
from   employee a
where  a.salary = ( select max(salary) from employee b
                    where  b.department_id = a.department_id )

Fonksiyonel

maxSalary 'Максимальная зарплата' (Department s) = 
    GROUP MAX salary(Employee e) IF department(e) = s;
SELECT name(Employee a) WHERE salary(a) = maxSalary(department(a));

// или если "заинлайнить"
SELECT name(Employee a) WHERE 
    salary(a) = maxSalary(GROUP MAX salary(Employee e) IF department(e) = department(a));

Her du pêkanîn jî wekhev in. Ji bo doza yekem, di databasek pêwendiyê de hûn dikarin CREATE VIEW bikar bînin, ku bi heman rengî dê pêşî meaşê herî zêde ji bo dezgehek taybetî ya tê de hesab bike. Di tiştê jêrîn de, ji bo zelaliyê, ez ê doza yekem bikar bînim, ji ber ku ew çareseriyê çêtir nîşan dide.

Kar 1.3

Navnîşek nasnameyên beşê nîşan bidin, ku hejmara karmendan ji 3 kesan derbas nabe.

Relational

select department_id
from   employee
group  by department_id
having count(*) <= 3

Fonksiyonel

countEmployees 'Количество сотрудников' (Department d) = 
    GROUP SUM 1 IF department(Employee e) = d;
SELECT Department d WHERE countEmployees(d) <= 3;

Kar 1.4

Navnîşek karmendên ku ne xwediyê rêveberek destnîşankirî ne ku di heman beşê de dixebitin nîşan bidin.

Relational

select a.*
from   employee a
left   join employee b on (b.id = a.chief_id and b.department_id = a.department_id)
where  b.id is null

Fonksiyonel

SELECT name(Employee a) WHERE NOT (department(chief(a)) = department(a));

Kar 1.5

Navnîşek nasnameyên beşê bi mûçeya karmendê ya herî zêde bibînin.

Relational

with sum_salary as
  ( select department_id, sum(salary) salary
    from   employee
    group  by department_id )
select department_id
from   sum_salary a       
where  a.salary = ( select max(salary) from sum_salary )

Fonksiyonel

salarySum 'Максимальная зарплата' (Department d) = 
    GROUP SUM salary(Employee e) IF department(e) = d;
maxSalarySum 'Максимальная зарплата отделов' () = 
    GROUP MAX salarySum(Department d);
SELECT Department d WHERE salarySum(d) = maxSalarySum();

Werin em ji yekî din derbasî karên tevlihevtir bibin gotar. Ew analîzek hûrgulî ya ka meriv çawa vê peywirê di MS SQL de bicîh tîne vedihewîne.

Kar 2.1

Kîjan firoşkaran di sala 1997an de zêdetirî 30 yekîneyên berhema No.

Mantiqa domainê (wek berê li ser RDBMS em danezanê berdidin):

CLASS Employee 'Продавец';
lastName 'Фамилия' = DATA STRING[100] (Employee);

CLASS Product 'Продукт';
id = DATA INTEGER (Product);
name = DATA STRING[100] (Product);

CLASS Order 'Заказ';
date = DATA DATE (Order);
employee = DATA Employee (Order);

CLASS Detail 'Строка заказа';

order = DATA Order (Detail);
product = DATA Product (Detail);
quantity = DATA NUMERIC[10,5] (Detail);

Relational

select LastName
from Employees as e
where (
  select sum(od.Quantity)
  from [Order Details] as od
  where od.ProductID = 1 and od.OrderID in (
    select o.OrderID
    from Orders as o
    where year(o.OrderDate) = 1997 and e.EmployeeID = o.EmployeeID)
) > 30

Fonksiyonel

sold (Employee e, INTEGER productId, INTEGER year) = 
    GROUP SUM quantity(OrderDetail d) IF 
        employee(order(d)) = e AND 
        id(product(d)) = productId AND 
        extractYear(date(order(d))) = year;
SELECT lastName(Employee e) WHERE sold(e, 1, 1997) > 30;

Kar 2.2

Ji bo her kiriyarekî (nav, paşnav), du tiştên (nav) ku kiryar di sala 1997-an de herî zêde pere li ser xerc kiriye bibînin.

Em mantiqa domainê ji mînaka berê dirêj dikin:

CLASS Customer 'Клиент';
contactName 'ФИО' = DATA STRING[100] (Customer);

customer = DATA Customer (Order);

unitPrice = DATA NUMERIC[14,2] (Detail);
discount = DATA NUMERIC[6,2] (Detail);

Relational

SELECT ContactName, ProductName FROM (
SELECT c.ContactName, p.ProductName
, ROW_NUMBER() OVER (
    PARTITION BY c.ContactName
    ORDER BY SUM(od.Quantity * od.UnitPrice * (1 - od.Discount)) DESC
) AS RatingByAmt
FROM Customers c
JOIN Orders o ON o.CustomerID = c.CustomerID
JOIN [Order Details] od ON od.OrderID = o.OrderID
JOIN Products p ON p.ProductID = od.ProductID
WHERE YEAR(o.OrderDate) = 1997
GROUP BY c.ContactName, p.ProductName
) t
WHERE RatingByAmt < 3

Fonksiyonel

sum (Detail d) = quantity(d) * unitPrice(d) * (1 - discount(d));
bought 'Купил' (Customer c, Product p, INTEGER y) = 
    GROUP SUM sum(Detail d) IF 
        customer(order(d)) = c AND 
        product(d) = p AND 
        extractYear(date(order(d))) = y;
rating 'Рейтинг' (Customer c, Product p, INTEGER y) = 
    PARTITION SUM 1 ORDER DESC bought(c, p, y), p BY c, y;
SELECT contactName(Customer c), name(Product p) WHERE rating(c, p, 1997) < 3;

Operatorê PARTITION li ser prensîba jêrîn dixebite: ew îfadeya ku piştî SUM (li vir 1) hatî destnîşan kirin berhev dike, di nav komên diyarkirî de (li vir Xerîdar û Sal, lê dibe ku her bêjeyek be), di nav koman de li gorî bêjeyên ku di ORDER de hatine destnîşankirin ( li vir kirî, û heke wekhev be, wê hingê li gorî koda hilberê hundurîn).

Kar 2.3

Ji bo bicihanîna fermanên heyî çend tişt hewce ne ku ji dabînkeran werin ferman kirin.

Ka em dîsa mantiqa domainê berfireh bikin:

CLASS Supplier 'Поставщик';
companyName = DATA STRING[100] (Supplier);

supplier = DATA Supplier (Product);

unitsInStock 'Остаток на складе' = DATA NUMERIC[10,3] (Product);
reorderLevel 'Норма продажи' = DATA NUMERIC[10,3] (Product);

Relational

select s.CompanyName, p.ProductName, sum(od.Quantity) + p.ReorderLevel — p.UnitsInStock as ToOrder
from Orders o
join [Order Details] od on o.OrderID = od.OrderID
join Products p on od.ProductID = p.ProductID
join Suppliers s on p.SupplierID = s.SupplierID
where o.ShippedDate is null
group by s.CompanyName, p.ProductName, p.UnitsInStock, p.ReorderLevel
having p.UnitsInStock < sum(od.Quantity) + p.ReorderLevel

Fonksiyonel

orderedNotShipped 'Заказано, но не отгружено' (Product p) = 
    GROUP SUM quantity(OrderDetail d) IF product(d) = p;
toOrder 'К заказу' (Product p) = orderedNotShipped(p) + reorderLevel(p) - unitsInStock(p);
SELECT companyName(supplier(Product p)), name(p), toOrder(p) WHERE toOrder(p) > 0;

Pirsgirêka stêrkek

Û mînaka dawî ji min bi xwe ye. Mantiqa tora civakî heye. Mirov dikarin bi hev re bibin dost û ji hev hez bikin. Ji perspektîfa databasa fonksiyonel ew ê bi vî rengî xuya bike:

CLASS Person;
likes = DATA BOOLEAN (Person, Person);
friends = DATA BOOLEAN (Person, Person);

Pêdivî ye ku ji bo hevaltiyê namzetên mimkun bibînin. Bi awayekî fermî, hûn hewce ne ku hemî mirovên A, B, C bibînin ku A bi B re heval e, û B bi C re heval e, A ji C hez dike, lê A ne hevalê C ye.
Ji perspektîfek databasa fonksiyonel, pirs dê wiha xuya bike:

SELECT Person a, Person b, Person c WHERE 
    likes(a, c) AND NOT friends(a, c) AND 
    friends(a, b) AND friends(b, c);

Xwendevan tê teşwîq kirin ku bi serê xwe vê pirsgirêkê di SQL de çareser bike. Tê texmîn kirin ku hevalên ku hûn jê hez dikin pir hindiktir in. Ji ber vê yekê ew di tabloyên cuda de ne. Ger serketî be, peywirek bi du stêrkan jî heye. Di wê de, hevaltî ne simetrîk e. Li ser databasek fonksiyonel ew ê bi vî rengî xuya bike:

SELECT Person a, Person b, Person c WHERE 
    likes(a, c) AND NOT friends(a, c) AND 
    (friends(a, b) OR friends(b, a)) AND 
    (friends(b, c) OR friends(c, b));

UPD: çareseriya pirsgirêkê bi stêrka yekem û duyemîn ji dss_kalika:

SELECT 
   pl.PersonAID
  ,pf.PersonAID
  ,pff.PersonAID
FROM Persons                 AS p
--Лайки                      
JOIN PersonRelationShip      AS pl ON pl.PersonAID = p.PersonID
                                  AND pl.Relation  = 'Like'
--Друзья                     
JOIN PersonRelationShip      AS pf ON pf.PersonAID = p.PersonID 
                                  AND pf.Relation = 'Friend'
--Друзья Друзей              
JOIN PersonRelationShip      AS pff ON pff.PersonAID = pf.PersonBID
                                   AND pff.PersonBID = pl.PersonBID
                                   AND pff.Relation = 'Friend'
--Ещё не дружат         
LEFT JOIN PersonRelationShip AS pnf ON pnf.PersonAID = p.PersonID
                                   AND pnf.PersonBID = pff.PersonBID
                                   AND pnf.Relation = 'Friend'
WHERE pnf.PersonAID IS NULL 

;WITH PersonRelationShipCollapsed AS (
  SELECT pl.PersonAID
        ,pl.PersonBID
        ,pl.Relation 
  FROM #PersonRelationShip      AS pl 
  
  UNION 

  SELECT pl.PersonBID AS PersonAID
        ,pl.PersonAID AS PersonBID
        ,pl.Relation
  FROM #PersonRelationShip      AS pl 
)
SELECT 
   pl.PersonAID
  ,pf.PersonBID
  ,pff.PersonBID
FROM #Persons                      AS p
--Лайки                      
JOIN PersonRelationShipCollapsed  AS pl ON pl.PersonAID = p.PersonID
                                 AND pl.Relation  = 'Like'                                  
--Друзья                          
JOIN PersonRelationShipCollapsed  AS pf ON pf.PersonAID = p.PersonID 
                                 AND pf.Relation = 'Friend'
--Друзья Друзей                   
JOIN PersonRelationShipCollapsed  AS pff ON pff.PersonAID = pf.PersonBID
                                 AND pff.PersonBID = pl.PersonBID
                                 AND pff.Relation = 'Friend'
--Ещё не дружат                   
LEFT JOIN PersonRelationShipCollapsed AS pnf ON pnf.PersonAID = p.PersonID
                                   AND pnf.PersonBID = pff.PersonBID
                                   AND pnf.Relation = 'Friend'
WHERE pnf.[PersonAID] IS NULL 

encamê

Divê were zanîn ku hevoksaziya zimanê hatî dayîn tenê yek ji vebijarkên pêkanîna têgîna hatî dayîn e. SQL wekî bingeh hate girtin, û armanc ew bû ku ew bi qasî ku pêkan dişibihe wê. Bê guman, dibe ku hin ji navên keywords, tomarên peyvan, hwd hez nekin. Ya sereke li vir têgîn bixwe ye. Ger bixwaze, hûn dikarin hem C++ û hem jî Python hevoksaziya wekhev bikin.

Têgeha databasa diyarkirî, li gorî min, avantajên jêrîn hene:

  • sivikî. Ev nîşanek nisbeten subjektîf e ku di rewşên hêsan de ne diyar e. Lê heke hûn li dozên tevlihevtir binêrin (mînak, pirsgirêkên bi stêrkan), wê hingê, bi dîtina min, nivîsandina pirsên weha pir hêsantir e.
  • Încîl. Di hin mînakan de min fonksiyonên navîn diyar kirin (mînak, firotin, kirrîn hwd.), ji kîjan fonksiyonên paşîn hatine çêkirin. Ev dihêle hûn mentiqê hin fonksiyonan biguhezînin, ger hewce be, bêyî guheztina mantiqa yên ku bi wan ve girêdayî ne. Mînakî, hûn dikarin firotanê bikin firotin ji tiştên bi tevahî cûda hatine hesibandin, dema ku mantiqê mayî nayê guhertin. Erê, ev dikare di RDBMS-ê de bi karanîna CREATE VIEW ve were bicîh kirin. Lê eger hemû mantiq bi vî awayî were nivîsandin, ew ê pir xweş neyê xwendin.
  • Xala semantîk tune. Databasek wusa li ser fonksiyon û çînan (li şûna tablo û qadan) dixebite. Mîna di bernamesaziya klasîk de (heke em bihesibînin ku rêbazek fonksiyonek bi parametreya yekem e di forma çîna ku tê de ye). Li gorî vê yekê, divê bi zimanên bernamesaziya gerdûnî re "hevaltiya" pir hêsantir be. Wekî din, ev têgeh dihêle ku fonksiyonek pir tevlihevtir were bicîh kirin. Mînakî, hûn dikarin operatorên mîna:

    CONSTRAINT sold(Employee e, 1, 2019) > 100 IF name(e) = 'Петя' MESSAGE  'Что-то Петя продает слишком много одного товара в 2019 году';

  • Mîrasbûn û polymorphism. Di databasek fonksiyonel de, hûn dikarin bi navgîniya CLASS ClassP-ya mîrata pirjimar bidin nasîn: Class1, Class2 pirmorfîzma pirjimar ava dike û bicîh tîne. Ez ê belkî di gotarên pêşerojê de bi rastî çawa binivîsim.

Her çend ev tenê têgehek e, jixwe me di Java de hin pêkanînek heye ku hemî mantiqa fonksiyonel werdigerîne mantiqa pêwendiyê. Zêdeyî, mantiqa nûnertiyê û gelek tiştên din bi xweşikî pê ve girêdayî ne, bi saya wan em tevahiyek digirin rawesta axaftevan. Di bingeh de, em RDBMS-ê (ji bo nuha tenê PostgreSQL) wekî "makîneyek virtual" bikar tînin. Pirsgirêk carinan bi vê wergerê re çêdibin ji ber ku optimîzatorê pirsê RDBMS hin statîstîkên ku FDBMS dizane nizane. Di teorîyê de, gengaz e ku meriv pergalek rêveberiya databasê bicîh bîne ku dê avahiyek diyarkirî wekî hilanînê bikar bîne, ku bi taybetî ji bo mantiqa fonksiyonel hatî adaptekirin.

Source: www.habr.com

Add a comment