DBMS fungsional

Dunya database geus lila didominasi ku relational DBMSs, nu ngagunakeun basa SQL. Janten seueur varian anu munculna disebut NoSQL. Aranjeunna junun ngukir kaluar hiji tempat nu tangtu pikeun diri di pasar ieu, tapi DBMS relational moal maot, sarta terus aktip dipaké pikeun kaperluan maranéhanana.

Dina artikel ieu abdi hoyong ngajelaskeun konsép database fungsional. Pikeun pamahaman anu langkung saé, kuring bakal ngalakukeun ieu ku ngabandingkeunana sareng modél relasional klasik. Masalah tina sagala rupa tés SQL kapanggih dina Internét bakal dipaké salaku conto.

perkenalan

database Relational beroperasi dina tabel sarta widang. Dina database fungsional, kelas sareng fungsi bakal dianggo masing-masing. Hiji widang dina tabel kalawan N konci bakal digambarkeun salaku fungsi tina parameter N. Gantina hubungan antara tabel, fungsi bakal dipaké nu balik objék tina kelas nu sambungan dijieun. Komposisi fungsi bakal dianggo tibatan JOIN.

Sateuacan ngalih langsung kana tugas, kuring bakal ngajelaskeun tugas logika domain. Pikeun DDL kuring bakal nganggo sintaksis PostgreSQL. Pikeun fungsional, éta ngagaduhan sintaksis sorangan.

Méja jeung widang

Obyék Sku basajan kalayan ngaran sareng widang harga:

Relasional

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

Fungsional

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

Urang ngumumkeun dua fungsi, nu nyandak hiji parameter Sku salaku input sarta balik tipe primitif.

Hal ieu dianggap yén dina DBMS fungsional unggal obyék bakal sababaraha kode internal anu otomatis dihasilkeun sarta bisa diakses lamun perlu.

Hayu urang nyetel harga pikeun produk / toko / supplier. Éta tiasa robih kana waktosna, janten hayu urang tambahkeun widang waktos kana tabél. Kuring bakal skip nyatakeun tabel pikeun diréktori dina database relational pikeun pondok kode:

Relasional

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

Fungsional

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

Indéks

Pikeun conto anu terakhir, urang bakal ngawangun indéks dina sadaya konci sareng tanggal supados urang tiasa gancang mendakan harga pikeun waktos anu khusus.

Relasional

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

Fungsional

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

pancén

Hayu urang mimitian ku masalah kawilang basajan dicokot tina pakait tulisan dina Habr.

Kahiji, hayu urang nyatakeun logika domain (pikeun database relational ieu dipigawé langsung dina artikel di luhur).

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

Tugas 1.1

Témbongkeun daptar karyawan anu narima gaji leuwih badag batan nu ngawas langsung maranéhanana.

Relasional

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

Fungsional

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

Tugas 1.2

Daptar karyawan anu nampi gaji maksimal di jabatanna

Relasional

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

Fungsional

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

Duanana palaksanaan sarimbag. Pikeun kasus anu pertama, dina database relational anjeun tiasa nganggo CREATE VIEW, anu ku cara anu sami bakal mimiti ngitung gaji maksimal pikeun jabatan khusus di jerona. Dina naon kieu, pikeun kajelasan, abdi bakal make kasus nu pertama, saprak éta hadé ngagambarkeun solusi.

Tugas 1.3

Témbongkeun daptar ID departemén, jumlah karyawan nu teu ngaleuwihan 3 urang.

Relasional

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

Fungsional

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

Tugas 1.4

Témbongkeun daptar karyawan anu teu boga manajer ditunjuk gawe di departemen sarua.

Relasional

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

Fungsional

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

Tugas 1.5

Manggihan daptar ID departemén jeung total gaji pagawe maksimum.

Relasional

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 )

Fungsional

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

Hayu urang ngaléngkah ka tugas anu langkung kompleks ti anu sanés tulisan. Ieu ngandung analisis nu detil rupa cara nerapkeun tugas ieu dina MS SQL.

Tugas 2.1

Anu ngajual anu ngajual langkung ti 1997 unit produk No 30 taun 1?

Logika domain (sapertos sateuacanna dina RDBMS kami ngalangkungan deklarasi):

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

Relasional

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

Fungsional

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;

Tugas 2.2

Pikeun unggal pembeli (ngaran, ngaran kulawarga), manggihan dua barang (ngaran) nu meuli méakkeun paling duit taun 1997.

Urang manjangkeun logika domain ti conto saméméhna:

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

customer = DATA Customer (Order);

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

Relasional

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

Fungsional

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 berpungsi dina prinsip di handap ieu: éta nyimpulkeun éksprési anu ditunjuk saatos SUM (di dieu 1), dina grup anu ditangtukeun (di dieu Pelanggan sareng Taun, tapi tiasa janten ekspresi naon waé), nyortir dina grup ku éksprési anu ditunjuk dina ORDER ( didieu meuli, sarta lamun sarua, lajeng nurutkeun kode produk internal).

Tugas 2.3

Sabaraha barang anu kedah dipesen ti supplier pikeun minuhan pesenan ayeuna.

Hayu urang dilegakeun deui logika domain:

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

supplier = DATA Supplier (Product);

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

Relasional

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

Fungsional

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;

Masalah sareng tanda bintang

Sareng conto anu terakhir ti kuring pribadi. Aya logika jaringan sosial. Jalma-jalma bisa jadi babaturan jeung silih pikaresep. Tina sudut pandang database fungsional bakal katingali sapertos kieu:

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

Ieu diperlukeun pikeun manggihan mungkin calon silaturahim. Sacara resmi, anjeun kedah milarian sadayana jalma A, B, C sapertos A janten babaturan sareng B, sareng B babaturan sareng C, A resep C, tapi A henteu babaturan sareng C.
Tina sudut pandang database fungsional, pamundut bakal katingali sapertos kieu:

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

Nu maca didorong pikeun ngajawab masalah ieu dina SQL sorangan. Hal ieu dianggap yén aya leuwih saeutik babaturan ti jalma anjeun resep. Kituna aranjeunna dina tabel misah. Upami suksés, aya ogé tugas sareng dua béntang. Di dinya, silaturahim teu simetris. Dina database fungsional bakal kasampak kawas kieu:

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: solusi pikeun masalah jeung tanda bintang kahiji jeung kadua ti 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 

kacindekan

Perlu dicatet yén sintaksis basa anu dipasihkeun ngan ukur salah sahiji pilihan pikeun ngalaksanakeun konsép anu dipasihkeun. SQL dicandak salaku dadasar, sarta tujuanana nya éta pikeun jadi sarimbag mungkin jeung eta. Tangtu, sababaraha bisa jadi teu resep ngaran kecap konci, registers kecap, jsb. Hal utama di dieu nyaeta konsep sorangan. Upami hoyong, anjeun tiasa ngadamel duanana C ++ jeung Python sintaksis sarupa.

Konsep database anu dijelaskeun, dina pamanggih kuring, ngagaduhan kaunggulan ieu:

  • katentreman. Ieu indikator rélatif subjektif nu teu atra dina kasus basajan. Tapi upami anjeun ningali kasus anu langkung kompleks (contona, masalah sareng tanda bintang), maka, dina pamanggih kuring, nyerat patarosan sapertos kitu langkung gampang.
  • Инкапсуляция. Dina sababaraha conto kuring nyatakeun fungsi perantara (contona, dijual, meuli jsb), ti mana fungsi saterusna diwangun. Ieu ngidinan Anjeun pikeun ngarobah logika fungsi nu tangtu, lamun perlu, tanpa ngarobah logika maranéhanana anu gumantung kana eta. Salaku conto, anjeun tiasa ngadamel penjualan dijual diitung tina objék lengkep beda, sedengkeun sesa logika moal robah. Leres, ieu tiasa dilaksanakeun dina RDBMS nganggo CREATE VIEW. Tapi lamun kabeh logika ditulis ku cara kieu, éta moal kasampak pisan dibaca.
  • Taya gap semantis. database sapertos ngoperasikeun dina fungsi jeung kelas (gaganti tabel sarta widang). Kawas dina programming klasik (lamun urang nganggap yén métode mangrupa fungsi jeung parameter munggaran dina bentuk kelas nu eta milik). Sasuai, kudu leuwih gampang "nyieun babaturan" jeung basa programming universal. Salaku tambahan, konsép ieu ngamungkinkeun fungsionalitas anu langkung kompleks pikeun dilaksanakeun. Contona, anjeun tiasa ngalebetkeun operator sapertos:

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

  • Warisan sareng polimorfisme. Dina database fungsional, anjeun tiasa ngawanohkeun sababaraha warisan ngaliwatan CLASS ClassP: Class1, Class2 constructs sarta ngalaksanakeun sababaraha polymorphism. Kuring meureun bakal nulis kumaha kahayang dina artikel hareup.

Sanaos ieu ngan ukur konsép, urang parantos ngagaduhan sababaraha palaksanaan di Java anu narjamahkeun sadaya logika fungsional kana logika relasional. Tambih Deui, logika répréséntasi sareng seueur hal-hal sanés anu napel saé, hatur nuhun anu urang kéngingkeun sadayana. platform. Intina, urang nganggo RDBMS (ngan PostgreSQL ayeuna) salaku "mesin virtual". Masalah kadang timbul dina tarjamahan ieu kusabab RDBMS query optimizer teu nyaho statistik tangtu nu FDBMS terang. Dina tiori, kasebut nyaéta dimungkinkeun pikeun nerapkeun sistem manajemen database anu bakal ngagunakeun struktur nu tangtu salaku gudang, diadaptasi husus pikeun logika fungsional.

sumber: www.habr.com

Tambahkeun komentar