فنڪشنل DBMS

ڊيٽابيس جي دنيا ڊگھي عرصي کان لاڳاپي واري ڊي بي ايم ايس جي تسلط ڪئي وئي آهي، جيڪي SQL ٻولي استعمال ڪندا آهن. ايتري قدر جو اڀرندڙ مختلف قسمن کي NoSQL سڏيو ويندو آهي. انهن هن مارڪيٽ ۾ پاڻ لاءِ هڪ خاص جڳهه ٺاهي ورتي، پر لاڳاپو DBMS مرڻ وارا نه آهن، ۽ انهن جي مقصدن لاءِ فعال طور تي استعمال ٿيڻ جاري آهن.

هن آرٽيڪل ۾ آئون هڪ فنڪشنل ڊيٽابيس جي تصور کي بيان ڪرڻ چاهيان ٿو. بهتر سمجھڻ لاءِ، مان ان کي ڪلاسيڪل رشتي واري ماڊل سان ڀيٽ ڪريان ٿو. انٽرنيٽ تي مليا مختلف SQL ٽيسٽن جا مسئلا مثال طور استعمال ڪيا ويندا.

تعارف

لاڳاپو ڊيٽابيس ٽيبل ۽ فيلڊ تي ڪم ڪن ٿا. هڪ فنڪشنل ڊيٽابيس ۾، ڪلاس ۽ افعال استعمال ڪيا ويندا، ترتيب سان. N keys سان ٽيبل ۾ هڪ فيلڊ کي N پيرا ميٽرز جي فنڪشن طور پيش ڪيو ويندو. جدولن جي وچ ۾ لاڳاپن جي بدران، فنڪشن استعمال ڪيا ويندا جيڪي ڪلاس جون شيون واپس ڪن ٿا جن سان ڪنيڪشن ٺاهيو ويو آهي. JOIN جي بدران فنڪشن جوڙ استعمال ڪيو ويندو.

ڪمن ڏانهن سڌو وڃڻ کان اڳ، مان ڊومين منطق جي ڪم کي بيان ڪندس. DDL لاءِ مان استعمال ڪندس PostgreSQL نحو. فنڪشنل لاءِ ان جو پنهنجو نحو آهي.

ٽيبل ۽ ميدان

نالي ۽ قيمت جي شعبن سان هڪ سادي اسڪو اعتراض:

تعلقي

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

فنڪشنل

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

اسان ٻه اعلان ڪريون ٿا ڪم، جيڪو هڪ پيٽرولر اسڪو کي ان پٽ طور وٺي ٿو ۽ هڪ ابتدائي قسم واپس ڪري ٿو.

اهو فرض ڪيو ويو آهي ته هڪ فنڪشنل ڊي بي ايم ايس ۾ هر اعتراض کي ڪجهه اندروني ڪوڊ هوندو جيڪو خودڪار طور تي ٺاهيل آهي ۽ ضروري هجي ته رسائي ڪري سگهجي ٿو.

اچو ته پراڊڪٽ/ اسٽور/ سپلائر لاءِ قيمت مقرر ڪريون. اهو وقت سان تبديل ٿي سگهي ٿو، تنهنڪري اچو ته ٽيبل تي هڪ ٽائيم فيلڊ شامل ڪريو. مان ڪوڊ کي مختصر ڪرڻ لاءِ لاڳاپيل ڊيٽابيس ۾ ڊائريڪٽرن لاءِ جدولن جو اعلان ڪرڻ ڇڏي ڏيندس:

تعلقي

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

فنڪشنل

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

انڊيڪس

آخري مثال لاءِ، اسان سڀني ڪنجين ۽ تاريخ تي هڪ انڊيڪس ٺاهينداسين ته جيئن اسان هڪ مخصوص وقت جي قيمت کي جلدي ڳولي سگهون.

تعلقي

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

فنڪشنل

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

ڪمن

اچو ته نسبتا سادو مسئلن سان شروع ڪريون جيڪي لاڳاپيل مان ورتل آهن مضمون حبر تي.

پهرين، اچو ته ڊومين منطق جو اعلان ڪريون (رابطي واري ڊيٽابيس لاءِ اهو سڌو مٿي ڏنل مضمون ۾ ڪيو ويو آهي).

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

ڪم 1.1

انهن ملازمن جي هڪ فهرست ڏيکاريو جيڪي پنهنجي فوري سپروائيزر کان وڌيڪ تنخواه وصول ڪن ٿا.

تعلقي

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

فنڪشنل

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

ڪم 1.2

انهن ملازمن جي لسٽ ڪريو جيڪي پنهنجي کاتي ۾ وڌ ۾ وڌ پگهار وصول ڪن ٿا

تعلقي

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

فنڪشنل

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

ٻئي عملدرآمد برابر آهن. پهرين صورت ۾، هڪ تعلقي ڊيٽابيس ۾ توهان استعمال ڪري سگهو ٿا CREATE VIEW، جيڪو ساڳئي طرح پهريون ڀيرو ان ۾ هڪ مخصوص ڊپارٽمينٽ جي وڌ ۾ وڌ تنخواه جو حساب ڪندو. انهي جي پٺيان، وضاحت لاء، آئون پهريون ڪيس استعمال ڪندس، ڇو ته اهو بهتر حل کي ظاهر ڪري ٿو.

ڪم 1.3

ڊپارٽمينٽ IDs جي هڪ فهرست ڏيکاريو، ملازمن جو تعداد جنهن ۾ 3 ماڻهن کان وڌيڪ نه هجي.

تعلقي

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

فنڪشنل

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

ڪم 1.4

انهن ملازمن جي هڪ فهرست ڏيکاريو جن وٽ هڪ ئي ڊپارٽمينٽ ۾ ڪم ڪندڙ نامزد مينيجر نه آهي.

تعلقي

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

فنڪشنل

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

ڪم 1.5

وڌ ۾ وڌ ملازم جي تنخواه سان ڊپارٽمينٽ IDs جي هڪ فهرست ڳوليو.

تعلقي

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 )

فنڪشنل

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

اچو ته ٻئي کان وڌيڪ پيچيده ڪمن ڏانهن وڃو مضمون. اهو تفصيلي تجزيو تي مشتمل آهي ته هن ڪم کي MS SQL ۾ ڪيئن لاڳو ڪجي.

ڪم 2.1

1997ع ۾ ڪهڙن وڪرو ڪندڙن پراڊڪٽ نمبر 30 جا 1 کان وڌيڪ يونٽ وڪرو ڪيا؟

ڊومين منطق (جيئن اڳي RDBMS تي اسان اعلان کي ڇڏي ڏيون ٿا):

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

تعلقي

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

فنڪشنل

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;

ڪم 2.2

هر خريد ڪندڙ لاءِ (نالو، نالو)، ٻه شيون ڳوليو (نالو) جنهن تي خريد ڪندڙ 1997 ۾ سڀ کان وڌيڪ پئسا خرچ ڪيو.

اسان ڊومين منطق کي پوئين مثال مان وڌايو:

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

customer = DATA Customer (Order);

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

تعلقي

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

فنڪشنل

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;

PARTITION آپريٽر ھيٺ ڏنل اصول تي ڪم ڪري ٿو: اھو SUM (هتي 1) کان پوءِ بيان ڪيل اظهار کي جمع ڪري ٿو، مخصوص گروپن جي اندر (هتي ڪسٽمر ۽ سال، پر ڪو به اظهار ٿي سگھي ٿو)، گروپن جي اندر ترتيب ڏنل ORDER ( هتي خريد ڪيو، ۽ جيڪڏهن برابر، پوء اندروني پراڊڪٽ ڪوڊ جي مطابق).

ڪم 2.3

موجوده آرڊر کي پورو ڪرڻ لاءِ سپلائرز کان ڪيترا سامان آرڊر ڪرڻ گهرجن.

اچو ته ڊومين منطق کي ٻيهر وڌايو:

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

supplier = DATA Supplier (Product);

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

تعلقي

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

فنڪشنل

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;

ستاري سان مسئلو

۽ آخري مثال مون کان ذاتي طور تي آهي. اتي هڪ سماجي نيٽ ورڪ جو منطق آهي. ماڻهو هڪ ٻئي سان دوست ٿي سگهن ٿا ۽ هڪ ٻئي کي پسند ڪن ٿا. هڪ فنڪشنل ڊيٽابيس جي نقطه نظر کان اهو هن طرح نظر ايندو:

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

اهو ضروري آهي ته دوستي لاء ممڪن اميدوارن کي ڳولڻ لاء. وڌيڪ رسمي طور تي، توهان کي سڀني ماڻهن کي ڳولڻ جي ضرورت آهي A، B، C جهڙوڪ A B سان دوستي آهي، ۽ B آهي C سان دوستي، A کي پسند آهي C، پر A، C سان دوست ناهي.
هڪ فنڪشنل ڊيٽابيس جي نقطه نظر کان، سوال هن طرح نظر ايندو:

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

پڙهندڙ کي حوصلا افزائي ڪئي وئي آهي ته هن مسئلي کي حل ڪرڻ لاء SQL ۾ پنهنجي پاڻ تي. اهو فرض ڪيو ويو آهي ته توهان جي پسند ماڻهن جي ڀيٽ ۾ تمام گهٽ دوست آهن. تنهن ڪري اهي جدا جدا جدولن ۾ آهن. جيڪڏهن ڪامياب ٿيو، اتي پڻ ٻه تارن سان گڏ ڪم آهي. ان ۾، دوستي هڪجهڙائي نه آهي. هڪ فنڪشنل ڊيٽابيس تي اهو هن طرح نظر ايندو:

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: مسئلي جو حل پهرين ۽ سيڪنڊ اسٽرڪ سان 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 

ٿڪل

اهو ياد رکڻ گهرجي ته ڏنل ٻولي نحو صرف ڏنل تصور کي لاڳو ڪرڻ لاء اختيارن مان هڪ آهي. SQL کي بنياد طور ورتو ويو، ۽ مقصد اھو ھو ته اھو ان سان جيترو ٿي سگھي. يقينن، ڪجهه شايد لفظن جا نالا پسند نٿا ڪن، لفظ رجسٽر، وغيره. هتي بنيادي شيء خود تصور آهي. جيڪڏهن گهربل هجي، توهان ڪري سگهو ٿا ٻئي C++ ۽ پٿون هڪجهڙائي نحو.

بيان ڪيل ڊيٽابيس تصور، منهنجي خيال ۾، هيٺيان فائدا آهن:

  • آسانيء. اهو هڪ نسبتا مضموني اشارو آهي جيڪو عام ڪيسن ۾ واضح ناهي. پر جيڪڏهن توهان وڌيڪ پيچيده ڪيسن تي نظر رکون ٿا (مثال طور، ستارن سان مسئلا)، پوء منهنجي خيال ۾، اهڙن سوالن کي لکڻ بلڪل آسان آهي.
  • Инкапсуляция. ڪجھ مثالن ۾ مون وچولي افعال جو اعلان ڪيو (مثال طور، وڪرو ڪيو, خريد ڪيو وغيره)، جنهن مان ايندڙ فنڪشن ٺاهيا ويا. اهو توهان کي اجازت ڏئي ٿو ته ڪجهه ڪمن جي منطق کي تبديل ڪرڻ جي، جيڪڏهن ضروري هجي، انهن جي منطق کي تبديل ڪرڻ کان سواء جيڪي انهن تي منحصر آهن. مثال طور، توهان وڪرو ڪري سگهو ٿا وڪرو ڪيو مڪمل طور تي مختلف شين مان حساب ڪيو ويو، جڏهن ته باقي منطق تبديل نه ٿيندي. ها، اهو هڪ RDBMS ۾ لاڳو ڪري سگهجي ٿو CREATE VIEW استعمال ڪندي. پر جيڪڏهن سمورو منطق هن طرح لکيو وڃي ته اهو تمام گهڻو پڙهڻ لائق نظر نه ايندو.
  • ڪوبه لفظي خال. اهڙو ڊيٽابيس ڪم ۽ طبقن تي هلندو آهي (جدول ۽ فيلڊ جي بدران). جيئن ته ڪلاسيڪل پروگرامنگ ۾ (جيڪڏهن اسان فرض ڪريون ته هڪ طريقو هڪ فنڪشن آهي جنهن ۾ پهرين پيراميٽر سان ڪلاس جي شڪل ۾ اهو تعلق رکي ٿو). ان جي مطابق، عالمي پروگرامنگ ٻولين سان ”دوست بڻائڻ“ تمام آسان ٿيڻ گھرجي. اضافي طور تي، هي تصور وڌيڪ پيچيده ڪارڪردگي کي لاڳو ڪرڻ جي اجازت ڏئي ٿو. مثال طور، توهان ايمبيڊ ڪري سگهو ٿا آپريٽرز جهڙوڪ:

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

  • وراثت ۽ پوليمورفيزم. هڪ فنڪشنل ڊيٽابيس ۾، توهان CLASS ClassP ذريعي ڪيترن ئي ورثي کي متعارف ڪرائي سگهو ٿا: ڪلاس 1، ڪلاس 2 ٺاهي ٿو ۽ ڪيترن ئي پوليمورفيزم کي لاڳو ڪري ٿو. مان شايد لکندس ته ڪيئن صحيح طور تي مستقبل جي مضمونن ۾.

جيتوڻيڪ اهو صرف هڪ تصور آهي، اسان وٽ اڳ ۾ ئي جاوا ۾ ڪجهه عمل درآمد آهي جيڪو سڀني فنڪشنل منطق کي لاڳاپي منطق ۾ ترجمو ڪري ٿو. ان سان گڏ، نمائندگي جي منطق ۽ ٻيون ڪيتريون ئي شيون خوبصورت طور تي ان سان ڳنڍيل آهن، جن جي مهرباني اسان کي مڪمل طور تي پليٽ فارم. لازمي طور تي، اسان RDBMS استعمال ڪريون ٿا (صرف PostgreSQL ھاڻي) "مجازي مشين" طور. مسئلا ڪڏهن ڪڏهن هن ترجمي سان پيدا ٿين ٿا ڇاڪاڻ ته RDBMS سوال اصلاح ڪندڙ ڪجهه انگ اکر نه ٿو ڄاڻي جيڪي FDBMS ڄاڻي ٿو. نظريي ۾، اهو ممڪن آهي ته ڊيٽابيس مينيجمينٽ سسٽم کي لاڳو ڪرڻ لاء، جيڪو هڪ خاص ڍانچي کي اسٽوريج طور استعمال ڪندو، خاص طور تي فنڪشنل منطق لاء ٺاهيل.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو