ڈیٹا بیس کی دنیا پر طویل عرصے سے متعلقہ 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);
ٹاسکس
آئیے متعلقہ سے لیے گئے نسبتاً آسان مسائل کے ساتھ شروع کریں۔
سب سے پہلے، ڈومین لاجک کا اعلان کرتے ہیں (ریلیشنل ڈیٹا بیس کے لیے یہ براہ راست اوپر کے مضمون میں کیا گیا ہے)۔
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();
آئیے دوسرے سے زیادہ پیچیدہ کاموں کی طرف چلتے ہیں۔
ٹاسک 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 ایک سے زیادہ پولیمورفزم کی تشکیل اور نفاذ کرتا ہے۔ میں شاید مستقبل کے مضامین میں لکھوں گا۔
اگرچہ یہ صرف ایک تصور ہے، ہمارے پاس جاوا میں پہلے سے ہی کچھ نفاذ موجود ہے جو تمام فنکشنل منطق کو رشتہ دار منطق میں ترجمہ کرتا ہے۔ اس کے علاوہ، نمائندگی کی منطق اور بہت سی دوسری چیزیں اس کے ساتھ خوبصورتی سے منسلک ہیں، جس کی بدولت ہمیں ایک مکمل
ماخذ: www.habr.com