Functional nga DBMS

Ang kalibutan sa mga database dugay na nga gidominar sa mga relational nga DBMS, nga naggamit sa SQL nga pinulongan. Daghan kaayo nga ang mga nag-uswag nga mga variant gitawag nga NoSQL. Nahimo nila ang pagkulit sa usa ka lugar alang sa ilang kaugalingon sa kini nga merkado, apan ang mga relational nga DBMS dili mamatay, ug padayon nga aktibo nga gigamit alang sa ilang mga katuyoan.

Niini nga artikulo gusto nakong ihulagway ang konsepto sa usa ka functional database. Para sa mas maayong pagsabot, buhaton nako kini pinaagi sa pagtandi niini sa classical relational model. Ang mga problema gikan sa lain-laing mga pagsulay sa SQL nga makita sa Internet gamiton isip mga pananglitan.

Pasiuna

Ang mga database sa relasyon naglihok sa mga lamesa ug natad. Sa usa ka functional database, ang mga klase ug mga gimbuhaton gamiton hinuon, matag usa. Ang usa ka uma sa usa ka lamesa nga adunay N nga mga yawe irepresentar ingon usa ka function sa mga parameter sa N. Imbis nga mga relasyon tali sa mga lamesa, gamiton ang mga function nga magbalik sa mga butang sa klase diin gihimo ang koneksyon. Ang komposisyon sa function gamiton imbes nga JOIN.

Sa dili pa mobalhin direkta sa mga buluhaton, akong ihulagway ang buluhaton sa domain logic. Para sa DDL akong gamiton ang PostgreSQL syntax. Alang sa pag-andar kini adunay kaugalingon nga syntax.

Mga lamesa ug mga uma

Usa ka yano nga butang sa Sku nga adunay mga natad sa ngalan ug presyo:

Relasyon

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

magamit

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

Gipahibalo namo ang duha gimbuhaton, nga nagkuha sa usa ka parameter nga Sku isip input ug ibalik ang usa ka primitive nga tipo.

Gituohan nga sa usa ka functional DBMS ang matag butang adunay pipila ka internal nga code nga awtomatik nga namugna ug mahimong ma-access kon gikinahanglan.

Atong ibutang ang presyo sa produkto/tindahan/supplier. Mahimong magbag-o kini sa paglabay sa panahon, busa magdugang kita og field sa oras sa lamesa. Laktawan nako ang pagdeklara sa mga lamesa alang sa mga direktoryo sa usa ka relational database aron mapamubo ang code:

Relasyon

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

magamit

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

Mga indeks

Alang sa katapusang pananglitan, maghimo kami usa ka indeks sa tanan nga mga yawe ug petsa aron dali namon makit-an ang presyo sa usa ka piho nga oras.

Relasyon

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

magamit

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

buluhaton

Magsugod kita sa medyo yano nga mga problema nga gikuha gikan sa katugbang mga artikulo sa Habr.

Una, atong ipahayag ang domain logic (alang sa relational database nga kini gihimo direkta sa ibabaw nga artikulo).

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

Buluhaton 1.1

Ipakita ang usa ka lista sa mga empleyado nga nakadawat og suweldo nga mas dako kaysa sa ilang diha-diha nga superbisor.

Relasyon

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

magamit

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

Buluhaton 1.2

Ilista ang mga empleyado nga nakadawat sa labing taas nga suweldo sa ilang departamento

Relasyon

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

magamit

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

Parehas ang duha nga pagpatuman. Alang sa unang kaso, sa usa ka relational database mahimo nimong gamiton ang CREATE VIEW, nga sa samang paagi una nga kuwentahon ang pinakataas nga suweldo alang sa usa ka partikular nga departamento niini. Sa mosunod, alang sa katin-awan, akong gamiton ang unang kaso, tungod kay kini mas maayo nga nagpakita sa solusyon.

Buluhaton 1.3

Ipakita ang usa ka lista sa mga ID sa departamento, ang gidaghanon sa mga empleyado diin dili molapas sa 3 ka tawo.

Relasyon

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

magamit

countEmployees 'ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΡΠΎΡ‚Ρ€ΡƒΠ΄Π½ΠΈΠΊΠΎΠ²' (Department d) = 
    GROUP SUM 1 IF department(Employee e) = d;
SELECT Department d WHERE countEmployees(d) <= 3;

Buluhaton 1.4

Ipakita ang listahan sa mga empleyado nga walay gitudlo nga manedyer nga nagtrabaho sa samang departamento.

Relasyon

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

magamit

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

Buluhaton 1.5

Pangitag listahan sa mga department ID nga adunay kinatas-ang kinatibuk-ang suweldo sa empleyado.

Relasyon

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 )

magamit

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

Mopadayon kita sa mas komplikado nga mga buluhaton gikan sa lain mga artikulo. Naglangkob kini usa ka detalyado nga pagtuki kung giunsa ipatuman kini nga buluhaton sa MS SQL.

Buluhaton 2.1

Kinsa nga mga namaligya ang nagbaligya og sobra sa 1997 ka yunit sa produkto No. 30 sa 1?

Logic sa domain (sama kaniadto sa RDBMS gilaktawan namon ang deklarasyon):

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

Relasyon

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

magamit

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;

Buluhaton 2.2

Alang sa matag pumapalit (ngalan, apelyido), pangitaa ang duha ka mga butang (ngalan) diin ang pumapalit migasto sa labing daghang salapi kaniadtong 1997.

Gipadako namon ang lohika sa domain gikan sa miaging pananglitan:

CLASS Customer 'ΠšΠ»ΠΈΠ΅Π½Ρ‚';
contactName 'ЀИО' = DATA STRING[100] (Customer);

customer = DATA Customer (Order);

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

Relasyon

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

magamit

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;

Ang operator sa PARTITION nagtrabaho sa mosunod nga prinsipyo: gisumada niini ang ekspresyon nga gipiho human sa SUM (dinhi 1), sulod sa gipiho nga mga grupo (dinhi Customer ug Tuig, apan mahimong bisan unsa nga ekspresyon), paghan-ay sulod sa mga grupo pinaagi sa mga ekspresyon nga gipiho sa ORDER ( dinhi gipalit, ug kung managsama, nan sumala sa internal nga code sa produkto).

Buluhaton 2.3

Pila ka mga butang ang kinahanglan nga i-order gikan sa mga supplier aron matuman ang mga karon nga order.

Atong palapdan pag-usab ang domain logic:

CLASS Supplier 'ΠŸΠΎΡΡ‚Π°Π²Ρ‰ΠΈΠΊ';
companyName = DATA STRING[100] (Supplier);

supplier = DATA Supplier (Product);

unitsInStock 'ΠžΡΡ‚Π°Ρ‚ΠΎΠΊ Π½Π° ΡΠΊΠ»Π°Π΄Π΅' = DATA NUMERIC[10,3] (Product);
reorderLevel 'Норма ΠΏΡ€ΠΎΠ΄Π°ΠΆΠΈ' = DATA NUMERIC[10,3] (Product);

Relasyon

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

magamit

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 sa asterisk

Ug ang katapusang pananglitan gikan kanako sa personal. Adunay lohika sa usa ka social network. Ang mga tawo mahimong managhigala sa usag usa ug ganahan sa usag usa. Gikan sa usa ka functional database perspective kini tan-awon sama niini:

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

Kinahanglang pangitaon ang posibleng mga kandidato alang sa panaghigalaay. Sa mas pormal nga paagi, kinahanglan nimo nga pangitaon ang tanan nga mga tawo nga A, B, C sa ingon nga si A higala ni B, ug si B higala ni C, gusto ni A si C, apan si A dili higala ni C.
Gikan sa usa ka functional database perspective, ang pangutana ingon niini:

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

Ang magbabasa giawhag sa pagsulbad niini nga problema sa SQL sa iyang kaugalingon. Gituohan nga adunay mas gamay nga mga higala kay sa mga tawo nga imong gusto. Busa sila anaa sa managlahing mga lamesa. Kung malampuson, adunay usa usab ka buluhaton nga adunay duha ka bituon. Niini, ang panaghigalaay dili simetriko. Sa usa ka functional database kini tan-awon sama niini:

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: solusyon sa problema sa una ug ikaduha nga asterisk gikan sa 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 

konklusyon

Kinahanglang hinumdoman nga ang gihatag nga syntax sa pinulongan usa lamang sa mga kapilian sa pagpatuman sa gihatag nga konsepto. Ang SQL gikuha ingon nga basehan, ug ang tumong mao nga kini mahimong susama kutob sa mahimo niini. Siyempre, ang uban tingali dili ganahan sa mga ngalan sa mga keyword, mga rehistro sa pulong, ug uban pa. Ang nag-unang butang dinhi mao ang konsepto mismo. Kung gusto, mahimo nimo ang parehas nga syntax sa C++ ug Python.

Ang gihulagway nga konsepto sa database, sa akong opinyon, adunay mga mosunud nga bentaha:

  • mopagaan sa. Kini usa ka medyo suhetibo nga timailhan nga dili klaro sa yano nga mga kaso. Apan kung imong tan-awon ang labi ka komplikado nga mga kaso (pananglitan, mga problema sa mga asterisk), nan, sa akong opinyon, ang pagsulat sa ingon nga mga pangutana labi ka dali.
  • Π˜Π½ΠΊΠ°ΠΏΡΡƒΠ»ΡΡ†ΠΈΡ. Sa pipila ka mga pananglitan gipahayag nako ang mga intermediate function (pananglitan, gibaligya, gipalit ug uban pa), gikan diin gitukod ang sunod nga mga gimbuhaton. Gitugotan ka niini nga usbon ang lohika sa pipila nga mga gimbuhaton, kung kinahanglan, nga dili usbon ang lohika sa mga nagsalig niini. Pananglitan, makahimo ka sa pagbaligya gibaligya kalkulado gikan sa hingpit nga lain-laing mga butang, samtang ang uban sa lohika dili mausab. Oo, kini mahimong ipatuman sa usa ka RDBMS gamit ang CREATE VIEW. Apan kung ang tanan nga lohika gisulat sa ingon nga paagi, kini dili kaayo mabasa.
  • Walay semantic gap. Ang ingon nga database naglihok sa mga gimbuhaton ug mga klase (imbes sa mga lamesa ug mga uma). Sama sa klasikal nga pagprograma (kung atong hunahunaon nga ang usa ka pamaagi usa ka function nga adunay una nga parameter sa porma sa klase kung diin kini nahisakop). Tungod niini, kinahanglan nga mas sayon ​​​​ang "makighigala" sa mga universal programming language. Dugang pa, kini nga konsepto nagtugot alang sa labi ka komplikado nga pagpaandar nga ipatuman. Pananglitan, mahimo nimong i-embed ang mga operator sama sa:

    CONSTRAINT sold(Employee e, 1, 2019) > 100 IF name(e) = 'ΠŸΠ΅Ρ‚Ρ' MESSAGE  'Π§Ρ‚ΠΎ-Ρ‚ΠΎ ΠŸΠ΅Ρ‚я ΠΏΡ€ΠΎΠ΄Π°Π΅Ρ‚ ΡΠ»ΠΈΡˆΠΊΠΎΠΌ ΠΌΠ½ΠΎΠ³ΠΎ ΠΎΠ΄Π½ΠΎΠ³ΠΎ Ρ‚ΠΎΠ²Π°Ρ€Π° Π² 2019 Π³ΠΎΠ΄Ρƒ';

  • Kabilin ug polymorphism. Sa usa ka functional database, mahimo nimong ipaila ang daghang kabilin pinaagi sa CLASS ClassP: Class1, Class2 constructs ug ipatuman ang daghang polymorphism. Mahimong isulat ko kung unsa gyud ka eksakto sa umaabot nga mga artikulo.

Bisan kung kini usa lamang ka konsepto, aduna na kitay implementasyon sa Java nga naghubad sa tanang functional logic ngadto sa relational logic. Dugang pa, ang lohika sa mga representasyon ug daghang uban pang mga butang matahum nga gilakip niini, salamat nga nakuha namon ang tibuuk plataporma. Sa tinuud, gigamit namon ang RDBMS (PostgreSQL ra karon) ingon usa ka "virtual machine". Ang mga problema usahay motumaw sa kini nga paghubad tungod kay ang RDBMS query optimizer wala mahibal-an ang piho nga estadistika nga nahibal-an sa FDBMS. Sa teorya, posible nga ipatuman ang usa ka sistema sa pagdumala sa database nga mogamit usa ka piho nga istruktura ingon pagtipig, nga piho nga gipahaum alang sa functional logic.

Source: www.habr.com

Idugang sa usa ka comment