فنکشنل DBMS

ڈیٹا بیس کی دنیا پر طویل عرصے سے متعلقہ DBMSs کا غلبہ رہا ہے، جو SQL زبان استعمال کرتے ہیں۔ اتنا کہ ابھرتی ہوئی مختلف حالتوں کو NoSQL کہا جاتا ہے۔ وہ اس مارکیٹ میں اپنے لیے ایک خاص جگہ بنانے میں کامیاب ہو گئے، لیکن متعلقہ DBMS مرنے والے نہیں ہیں، اور اپنے مقاصد کے لیے فعال طور پر استعمال ہوتے رہتے ہیں۔

اس مضمون میں میں ایک فنکشنل ڈیٹا بیس کے تصور کو بیان کرنا چاہتا ہوں۔ بہتر تفہیم کے لیے، میں اسے کلاسیکی رشتہ دار ماڈل کے ساتھ موازنہ کر کے کروں گا۔ انٹرنیٹ پر پائے جانے والے مختلف ایس کیو ایل ٹیسٹوں کے مسائل کو بطور مثال استعمال کیا جائے گا۔

تعارف

متعلقہ ڈیٹا بیس میزوں اور فیلڈز پر کام کرتے ہیں۔ فنکشنل ڈیٹا بیس میں، کلاسز اور فنکشنز بالترتیب اس کے بجائے استعمال کیے جائیں گے۔ N کیز کے ساتھ ٹیبل میں ایک فیلڈ کو N پیرامیٹرز کے فنکشن کے طور پر دکھایا جائے گا۔ ٹیبلز کے درمیان تعلقات کے بجائے، فنکشنز استعمال کیے جائیں گے جو اس کلاس کی اشیاء کو واپس کرتے ہیں جس سے کنکشن بنایا گیا ہے۔ JOIN کے بجائے فنکشن کمپوزیشن استعمال کیا جائے گا۔

کاموں پر براہ راست جانے سے پہلے، میں ڈومین لاجک کے کام کو بیان کروں گا۔ DDL کے لیے میں PostgreSQL نحو استعمال کروں گا۔ فنکشنل کے لیے اس کا اپنا نحو ہے۔

میزیں اور فیلڈز

نام اور قیمت کے شعبوں کے ساتھ ایک سادہ Sku آبجیکٹ:

رشتہ دار

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

ہم دو کا اعلان کرتے ہیں۔ افعال، جو ایک پیرامیٹر Sku کو بطور ان پٹ لیتے ہیں اور ایک قدیم قسم واپس کرتے ہیں۔

یہ فرض کیا جاتا ہے کہ ایک فنکشنل DBMS میں ہر شے کے پاس کچھ اندرونی کوڈ ہوگا جو خود بخود تیار ہوتا ہے اور اگر ضروری ہو تو اس تک رسائی حاصل کی جاسکتی ہے۔

آئیے پروڈکٹ/اسٹور/سپلائر کے لیے قیمت مقرر کریں۔ یہ وقت کے ساتھ بدل سکتا ہے، تو آئیے ٹیبل میں ٹائم فیلڈ شامل کریں۔ میں کوڈ کو مختصر کرنے کے لیے متعلقہ ڈیٹا بیس میں ڈائریکٹریوں کے لیے جدولوں کا اعلان کرنا چھوڑ دوں گا:

رشتہ دار

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

ٹاسکس

آئیے متعلقہ سے لیے گئے نسبتاً آسان مسائل کے ساتھ شروع کریں۔ مضامین Habré پر.

سب سے پہلے، ڈومین لاجک کا اعلان کرتے ہیں (ریلیشنل ڈیٹا بیس کے لیے یہ براہ راست اوپر کے مضمون میں کیا گیا ہے)۔

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

ڈیپارٹمنٹ آئی ڈیز کی فہرست دکھائیں، ملازمین کی تعداد جس میں 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

زیادہ سے زیادہ کل ملازمین کی تنخواہ کے ساتھ ڈیپارٹمنٹ آئی ڈی کی فہرست تلاش کریں۔

رشتہ دار

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 سے ​​زیادہ یونٹ فروخت کیے؟

ڈومین منطق (آر ڈی بی ایم ایس پر پہلے کی طرح ہم اعلان کو چھوڑ دیتے ہیں):

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

قارئین کو اس مسئلے کو ایس کیو ایل میں خود ہی حل کرنے کی ترغیب دی جاتی ہے۔ یہ فرض کیا جاتا ہے کہ آپ کی پسند کے لوگوں سے بہت کم دوست ہیں۔ لہذا وہ الگ الگ میزوں میں ہیں۔ کامیاب ہونے کی صورت میں دو ستاروں والا ٹاسک بھی ہے۔ اس میں، دوستی سڈول نہیں ہے. فنکشنل ڈیٹا بیس پر یہ اس طرح نظر آئے گا:

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: پہلے اور دوسرے ستارے سے مسئلہ کا حل ڈی ایس ایس_کالیکا:

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 

حاصل يہ ہوا

واضح رہے کہ دی گئی زبان کا نحو دیے گئے تصور کو نافذ کرنے کے اختیارات میں سے صرف ایک ہے۔ ایس کیو ایل کو بنیاد کے طور پر لیا گیا تھا، اور اس کا مقصد یہ تھا کہ اس سے زیادہ سے زیادہ مماثلت ہو۔ یقیناً، کچھ کو مطلوبہ الفاظ، ورڈ رجسٹرز وغیرہ کے نام پسند نہیں آسکتے ہیں۔ یہاں اہم چیز خود تصور ہے۔ اگر چاہیں تو، آپ C++ اور Python دونوں کو ایک جیسا نحو بنا سکتے ہیں۔

بیان کردہ ڈیٹا بیس تصور، میری رائے میں، مندرجہ ذیل فوائد ہیں:

  • کو کم. یہ نسبتاً ساپیکش اشارے ہے جو سادہ معاملات میں واضح نہیں ہوتا ہے۔ لیکن اگر آپ زیادہ پیچیدہ معاملات کو دیکھیں (مثال کے طور پر، ستاروں کے ساتھ مسائل)، تو، میری رائے میں، اس طرح کے سوالات لکھنا بہت آسان ہے۔
  • انڈونیشیا. کچھ مثالوں میں میں نے انٹرمیڈیٹ افعال کا اعلان کیا (مثال کے طور پر، فروخت, خریدا وغیرہ)، جس سے بعد کے افعال بنائے گئے تھے۔ یہ آپ کو بعض افعال کی منطق کو تبدیل کرنے کی اجازت دیتا ہے، اگر ضروری ہو تو، ان کی منطق کو تبدیل کیے بغیر جو ان پر منحصر ہیں۔ مثال کے طور پر، آپ فروخت کر سکتے ہیں فروخت مکمل طور پر مختلف اشیاء سے شمار کیے گئے تھے، جبکہ باقی منطق تبدیل نہیں ہوگی۔ ہاں، اس کو RDBMS میں CREATE VIEW کا استعمال کرتے ہوئے لاگو کیا جا سکتا ہے۔ لیکن اگر ساری منطق اس طرح لکھی جائے تو یہ زیادہ پڑھنے کے قابل نہیں لگے گی۔
  • کوئی معنوی فرق نہیں۔. اس طرح کا ڈیٹا بیس فنکشنز اور کلاسز (ٹیبلز اور فیلڈز کی بجائے) پر کام کرتا ہے۔ بالکل اسی طرح جیسے کلاسیکل پروگرامنگ میں (اگر ہم فرض کریں کہ کوئی طریقہ ایک فنکشن ہے جس میں کلاس کی شکل میں پہلا پیرامیٹر ہے جس سے اس کا تعلق ہے)۔ اس کے مطابق، یونیورسل پروگرامنگ زبانوں کے ساتھ "دوست بنانا" بہت آسان ہونا چاہیے۔ مزید برآں، یہ تصور بہت زیادہ پیچیدہ فعالیت کو لاگو کرنے کی اجازت دیتا ہے۔ مثال کے طور پر، آپ آپریٹرز کو ایمبیڈ کر سکتے ہیں جیسے:

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

  • وراثت اور پولیمورفزم. ایک فنکشنل ڈیٹا بیس میں، آپ CLASS ClassP کے ذریعے ایک سے زیادہ وراثت کو متعارف کروا سکتے ہیں: Class1، Class2 ایک سے زیادہ پولیمورفزم کی تشکیل اور نفاذ کرتا ہے۔ میں شاید مستقبل کے مضامین میں لکھوں گا۔

اگرچہ یہ صرف ایک تصور ہے، ہمارے پاس جاوا میں پہلے سے ہی کچھ نفاذ موجود ہے جو تمام فنکشنل منطق کو رشتہ دار منطق میں ترجمہ کرتا ہے۔ اس کے علاوہ، نمائندگی کی منطق اور بہت سی دوسری چیزیں اس کے ساتھ خوبصورتی سے منسلک ہیں، جس کی بدولت ہمیں ایک مکمل پلیٹ فارم. بنیادی طور پر، ہم RDBMS (ابھی کے لیے صرف PostgreSQL) کو بطور "ورچوئل مشین" استعمال کرتے ہیں۔ اس ترجمے کے ساتھ بعض اوقات مسائل پیدا ہوتے ہیں کیونکہ RDBMS استفسار کرنے والا کچھ اعداد و شمار نہیں جانتا جو FDBMS جانتا ہے۔ نظریہ میں، ڈیٹا بیس کے انتظام کے نظام کو لاگو کرنا ممکن ہے جو ایک مخصوص ڈھانچے کو اسٹوریج کے طور پر استعمال کرے گا، خاص طور پر فنکشنل منطق کے لیے ڈھال لیا گیا ہے۔

ماخذ: www.habr.com

نیا تبصرہ شامل کریں