DBMS funzjonali

Id-dinja tad-databases ilha ddominata minn DBMSs relazzjonali, li jużaw il-lingwa SQL. Tant hu hekk li l-varjanti emerġenti jissejħu NoSQL. Huma rnexxielhom jaqtgħu ċertu post għalihom infushom f'dan is-suq, iżda DBMSs relazzjonali mhux se jmutu, u jkomplu jintużaw b'mod attiv għall-iskopijiet tagħhom.

F'dan l-artikolu nixtieq niddeskrivi l-kunċett ta 'database funzjonali. Għal fehim aħjar, se nagħmel dan billi nqabbel mal-mudell relazzjonali klassiku. Problemi minn diversi testijiet SQL misjuba fuq l-Internet se jintużaw bħala eżempji.

Introduzzjoni

Databases relazzjonali joperaw fuq tabelli u oqsma. F'database funzjonali, il-klassijiet u l-funzjonijiet se jintużaw minflok, rispettivament. Field f'tabella b'N ċwievet ikun rappreżentat bħala funzjoni ta' N parametri. Minflok relazzjonijiet bejn it-tabelli, se jintużaw funzjonijiet li jirritornaw oġġetti tal-klassi li magħha ssir il-konnessjoni. Il-kompożizzjoni tal-funzjoni se tintuża minflok JOIN.

Qabel ma nimxu direttament għall-kompiti, ser niddeskrivi l-kompitu tal-loġika tad-dominju. Għal DDL se nuża sintassi PostgreSQL. Għal funzjonali għandha sintassi tagħha stess.

Tabelli u oqsma

Oġġett Sku sempliċi b'oqsma tal-isem u tal-prezz:

Relazzjonali

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

funzjonali

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

Inħabbru tnejn funzjonijiet, li jieħdu parametru wieħed Sku bħala input u jirritornaw tip primittiv.

Huwa preżunt li f'DBMS funzjonali kull oġġett ikollu xi kodiċi intern li jiġi ġġenerat awtomatikament u jista' jiġi aċċessat jekk meħtieġ.

Ejja nissettjaw il-prezz għall-prodott/maħżen/fornitur. Jista' jinbidel maż-żmien, allura ejja nżidu qasam tal-ħin mat-tabella. Se naqbeż niddikjara tabelli għal direttorji f'database relazzjonali biex inqassar il-kodiċi:

Relazzjonali

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)
)

funzjonali

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

Indiċijiet

Għall-aħħar eżempju, se nibnu indiċi fuq iċ-ċwievet kollha u d-data sabiex inkunu nistgħu nsibu malajr il-prezz għal żmien speċifiku.

Relazzjonali

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

funzjonali

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

kompiti

Nibdew bi problemi relattivament sempliċi meħuda mill-korrispondenti oġġetti fuq Habr.

L-ewwel, ejja tiddikjara l-loġika tad-dominju (għad-database relazzjonali dan isir direttament fl-artikolu ta 'hawn fuq).

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);

Kompitu 1.1

Uri lista ta’ impjegati li jirċievu salarju akbar minn dak tas-superviżur immedjat tagħhom.

Relazzjonali

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

funzjonali

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

Kompitu 1.2

Elenka l-impjegati li jirċievu s-salarju massimu fid-dipartiment tagħhom

Relazzjonali

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

funzjonali

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));

Iż-żewġ implimentazzjonijiet huma ekwivalenti. Għall-ewwel każ, f'database relazzjonali tista 'tuża CREATE VIEW, li bl-istess mod l-ewwel tikkalkula s-salarju massimu għal dipartiment speċifiku fih. F'dak li ġej, għal ċarezza, se nuża l-ewwel każ, peress li jirrifletti aħjar is-soluzzjoni.

Kompitu 1.3

Uri lista ta 'IDs dipartiment, in-numru ta' impjegati li fihom ma jaqbiżx 3 persuni.

Relazzjonali

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

funzjonali

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

Kompitu 1.4

Uri lista ta’ impjegati li m’għandhomx maniġer nominat li jaħdem fl-istess dipartiment.

Relazzjonali

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

funzjonali

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

Kompitu 1.5

Sib lista ta 'IDs dipartimenti bis-salarju massimu totali tal-impjegati.

Relazzjonali

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 )

funzjonali

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();

Ejja ngħaddu għal ħidmiet aktar kumplessi minn ieħor oġġetti. Fiha analiżi dettaljata ta' kif timplimenta dan il-kompitu f'MS SQL.

Kompitu 2.1

Liema bejjiegħa biegħu aktar minn 1997 unità tal-prodott Nru 30 fl-1?

Loġika tad-dominju (bħal qabel fuq RDBMS aħna naqbżu d-dikjarazzjoni):

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);

Relazzjonali

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

funzjonali

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;

Kompitu 2.2

Għal kull xerrej (isem, kunjom), sib iż-żewġ oġġetti (isem) li fuqhom ix-xerrej nefaq l-aktar flus fl-1997.

Aħna nestendew il-loġika tad-dominju mill-eżempju preċedenti:

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

customer = DATA Customer (Order);

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

Relazzjonali

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

funzjonali

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;

L-operatur PARTITION jaħdem fuq il-prinċipju li ġej: jiġbor l-espressjoni speċifikata wara SOM (hawnhekk 1), fi ħdan il-gruppi speċifikati (hawn Klijent u Sena, iżda jista 'jkun kwalunkwe espressjoni), jagħżel fi ħdan il-gruppi mill-espressjonijiet speċifikati fl-ORDNI ( hawn mixtrija, u jekk ugwali, allura skond il-kodiċi tal-prodott intern).

Kompitu 2.3

Kemm-il merkanzija trid tiġi ordnata mingħand il-fornituri biex twettaq l-ordnijiet kurrenti.

Ejja nespandu l-loġika tad-dominju mill-ġdid:

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

supplier = DATA Supplier (Product);

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

Relazzjonali

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

funzjonali

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;

Problema b'asterisk

U l-aħħar eżempju huwa minni personalment. Hemm il-loġika ta 'netwerk soċjali. In-nies jistgħu jkunu ħbieb ma’ xulxin u bħal xulxin. Mill-perspettiva tad-database funzjonali tkun tidher bħal din:

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

Huwa meħtieġ li jinstabu kandidati possibbli għall-ħbiberija. B'mod aktar formali, trid issib in-nies kollha A, B, C b'tali mod li A tkun ħbieb ma' B, u B tkun ħbieb ma' C, A jħobb lil C, iżda A mhux ħbieb ma' C.
Mill-perspettiva tad-database funzjonali, il-mistoqsija tkun tidher bħal din:

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

Il-qarrej huwa mħeġġeġ biex isolvi din il-problema fl-SQL waħdu. Huwa preżunt li hemm ħafna inqas ħbieb minn nies li tixtieq. Għalhekk huma f'tabelli separati. Jekk tirnexxi, hemm ukoll kompitu b'żewġ stilel. Fiha, il-ħbiberija mhix simetrika. Fuq database funzjonali tkun tidher bħal din:

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: soluzzjoni għall-problema bl-ewwel u t-tieni asterisk minn 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 

Konklużjoni

Għandu jiġi nnutat li s-sintassi tal-lingwa mogħtija hija biss waħda mill-għażliet għall-implimentazzjoni tal-kunċett partikolari. SQL ttieħed bħala l-bażi, u l-għan kien li jkun simili kemm jista 'jkun għalih. Naturalment, xi wħud jistgħu ma jogħġbux l-ismijiet tal-kliem kjavi, reġistri tal-kliem, eċċ. Il-ħaġa prinċipali hawnhekk hija l-kunċett innifsu. Jekk mixtieq, tista' tagħmel kemm C++ kif ukoll Python sintassi simili.

Il-kunċett tad-database deskritt, fl-opinjoni tiegħi, għandu l-vantaġġi li ġejjin:

  • Is-sempliċità. Dan huwa indikatur relattivament suġġettiv li mhuwiex ovvju f'każijiet sempliċi. Imma jekk tħares lejn każijiet aktar kumplessi (per eżempju, problemi bl-asterisks), allura, fl-opinjoni tiegħi, il-kitba ta 'mistoqsijiet bħal dawn hija ħafna aktar faċli.
  • Инкапсуляция. F'xi eżempji ddikjarajt funzjonijiet intermedji (per eżempju, mibjugħa, mixtrija eċċ.), li minnu nbnew funzjonijiet sussegwenti. Dan jippermettilek tibdel il-loġika ta 'ċerti funzjonijiet, jekk meħtieġ, mingħajr ma tbiddel il-loġika ta' dawk li jiddependu minnhom. Per eżempju, tista 'tagħmel bejgħ mibjugħa ġew ikkalkulati minn oġġetti kompletament differenti, filwaqt li l-bqija tal-loġika mhux se jinbidlu. Iva, dan jista' jiġi implimentat f'RDBMS billi tuża CREATE VIEW. Imma jekk il-loġika kollha tinkiteb b'dan il-mod, ma tantx tidher li tinqara.
  • L-ebda vojt semantiku. Database bħal din topera fuq funzjonijiet u klassijiet (minflok tabelli u oqsma). Eżatt bħal fl-ipprogrammar klassiku (jekk nassumu li metodu huwa funzjoni bl-ewwel parametru fil-forma tal-klassi li jappartjeni għaliha). Għaldaqstant, għandu jkun ħafna aktar faċli li "tagħmel ħbieb" ma 'lingwi ta' programmar universali. Barra minn hekk, dan il-kunċett jippermetti li tiġi implimentata funzjonalità ħafna aktar kumplessa. Pereżempju, tista' tintegra operaturi bħal:

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

  • Wirt u polimorfiżmu. F'database funzjonali, tista' tintroduċi wirt multiplu permezz tal-KLASSI ClassP: Class1, Class2 constructs u timplimenta polimorfiżmu multiplu. Probabbilment nikteb kif eżattament f'artikoli futuri.

Anke jekk dan huwa biss kunċett, diġà għandna xi implimentazzjoni f'Java li tittraduċi l-loġika funzjonali kollha f'loġika relazzjonali. Barra minn hekk, il-loġika tar-rappreżentazzjonijiet u ħafna affarijiet oħra huma mwaħħla sabiħ magħha, li grazzi għaliha nġibu sħaħ pjattaforma. Essenzjalment, nużaw l-RDBMS (PostgreSQL biss għalissa) bħala "magna virtwali". Xi drabi jinqalgħu problemi b'din it-traduzzjoni minħabba li l-RDBMS query optimizer ma jafx ċerta statistika li l-FDBMS jaf. Fit-teorija, huwa possibbli li tiġi implimentata sistema ta 'ġestjoni tad-database li tuża ċerta struttura bħala ħażna, adattata speċifikament għal loġika funzjonali.

Sors: www.habr.com

Żid kumment