ተግባራዊ DBMS

የመረጃ ቋቱ ዓለም የSQL ቋንቋን በሚጠቀሙ ተዛማጅ ዲቢኤምኤስዎች ተወስዷል። በጣም ብዙ ብቅ ብቅ ያሉ ዝርያዎች NoSQL ይባላሉ. በዚህ ገበያ ውስጥ ለራሳቸው የተወሰነ ቦታ ማሸነፍ ችለዋል ፣ ግን ተዛማጅ ዲቢኤምኤስ አይሞቱም ፣ እና ለራሳቸው ዓላማ በንቃት መጠቀማቸውን ቀጥለዋል።

በዚህ ጽሑፍ ውስጥ የተግባር ዳታቤዝ ጽንሰ-ሐሳብን መግለጽ እፈልጋለሁ. ለተሻለ ግንዛቤ፣ ይህንን ከጥንታዊው የግንኙነት ሞዴል ጋር በማነፃፀር አደርጋለሁ። እንደ ምሳሌ በበይነ መረብ ላይ ከሚገኙ የተለያዩ የ SQL ሙከራዎች የተወሰዱ ስራዎች ጥቅም ላይ ይውላሉ።

መግቢያ

ተዛማጅ የውሂብ ጎታዎች በጠረጴዛዎች እና በመስኮች ላይ ይሰራሉ. በተግባራዊ ዳታቤዝ ውስጥ፣ ክፍሎች እና ተግባራት በቅደም ተከተል ጥቅም ላይ ይውላሉ። በ N ቁልፎች በሰንጠረዥ ውስጥ ያለ መስክ እንደ N መለኪያዎች ተግባር ይወከላል። በሰንጠረዦች መካከል ካለው አገናኞች ይልቅ፣ አገናኙ የሚሄድበትን ክፍል ዕቃዎች የሚመልሱ ተግባራት ጥቅም ላይ ይውላሉ። የተግባር ቅንብር ከመቀላቀል ይልቅ ጥቅም ላይ ይውላል።

ወደ ተግባራቶቹ በቀጥታ ከመቀጠልዎ በፊት, የዶሜይን ሎጂክን ተግባር እገልጻለሁ. ለ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);

ሁለት እናስታውቃለን። ተግባራት, አንድ 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

የመምሪያ መታወቂያዎችን ዝርዝር ያሳዩ, ከ 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 የሸጡት ሻጮች የትኞቹ ናቸው?

የጎራ አመክንዮ (እንደበፊቱ፣ በ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 ማግኘት አለቦት እንደ ሀ ከ B ጋር ጓደኛ ነው፣ እና B ከ C፣ A like 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 ++ እና Python ተመሳሳይ አገባብ ማድረግ ይችላሉ።

የተገለጸው የውሂብ ጎታ ጽንሰ-ሀሳብ, በእኔ አስተያየት, የሚከተሉት ጥቅሞች አሉት.

  • ቀላልነት።. ይህ በቀላል ጉዳዮች ላይ የማይታወቅ በአንጻራዊ ሁኔታዊ አመላካች ነው። ግን የበለጠ ውስብስብ ጉዳዮችን (ለምሳሌ ፣ ከኮከቦች ጋር ያሉ ተግባራት) ከተመለከቱ ፣ በእኔ አስተያየት ፣ እንደዚህ ያሉ ጥያቄዎችን መጻፍ በጣም ቀላል ነው።
  • Нкапсуляция. በአንዳንድ ምሳሌዎች መካከለኛ ተግባራትን አውጃለሁ (ለምሳሌ፡- ተሽጧል, ገዝቷል ወዘተ), ከየትኛው ተከታይ ተግባራት ተገንብተዋል. ይህ የተወሰኑ ተግባራትን ሎጂክ እንዲቀይሩ ያስችልዎታል, አስፈላጊ ከሆነ, በእነሱ ላይ ጥገኛ የሆኑትን አመክንዮዎች ሳይቀይሩ. ለምሳሌ, ሽያጭ መስራት ይችላሉ ተሽጧል ሙሉ ለሙሉ ከተለያዩ ነገሮች የተሰላ ነበር, የተቀረው አመክንዮ ግን አይለወጥም. አዎ፣ በRDBMS ውስጥ ይህ በ CREATE እይታ ሊከናወን ይችላል። ግን ሁሉንም አመክንዮዎች በዚህ መንገድ ከጻፉ ፣ ከዚያ በጣም የሚነበብ አይመስልም።
  • የትርጉም ክፍተት የለም።. እንዲህ ዓይነቱ የውሂብ ጎታ በተግባሮች እና ክፍሎች (ከጠረጴዛዎች እና መስኮች ይልቅ) ይሰራል. ልክ እንደ ክላሲካል ፕሮግራሚንግ በተመሳሳይ መንገድ (አንድ ዘዴ ከመጀመሪያው ግቤት ጋር በክፍል ውስጥ ባለው ክፍል ውስጥ ያለ ተግባር እንደሆነ በማሰብ)። በዚህ መሠረት ከዓለም አቀፍ የፕሮግራም አወጣጥ ቋንቋዎች ጋር "ጓደኛ ማፍራት" በጣም ቀላል መሆን አለበት. በተጨማሪም, ይህ ጽንሰ-ሐሳብ በጣም ውስብስብ ተግባራትን እንዲፈጽሙ ያስችልዎታል. ለምሳሌ ፣ እንደዚህ ያሉ መግለጫዎችን በመረጃ ቋቱ ውስጥ መክተት ይችላሉ-

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

  • ውርስ እና ፖሊሞርፊዝም. በተግባራዊ ዳታቤዝ ውስጥ፣ በ CLASS ClassP በኩል ብዙ ውርስ ማስተዋወቅ ይችላሉ፡ ክፍል 1፣ ክፍል 2 በርካታ ፖሊሞርፊዝምን ይገነባል እና ይተገበራል። እንዴት በትክክል, ምናልባት በሚቀጥሉት ጽሁፎች ውስጥ እጽፋለሁ.

ምንም እንኳን ይህ ፅንሰ-ሀሳብ ቢሆንም፣ ሁሉንም ተግባራዊ አመክንዮ ወደ ተዛማጅ አመክንዮ የሚተረጎም በጃቫ ውስጥ የተወሰነ አተገባበር አለን። በተጨማሪም ፣ የውክልና አመክንዮ እና ሌሎች ብዙ ነገሮች በእሱ ላይ በሚያምር ሁኔታ ተስተካክለዋል ፣ ለዚህም ምስጋና ይግባው መድረክ. በመሰረቱ፣ RDBMS (እስካሁን PostgreSQL ብቻ) እንደ “ምናባዊ ማሽን” እየተጠቀምን ነው። ይህ ትርጉም አንዳንድ ጊዜ ችግር ይፈጥራል ምክንያቱም የRDBMS መጠይቅ አመቻች ኤፍዲቢኤምኤስ የሚያደርጋቸውን የተወሰኑ ስታቲስቲክስ ስለማያውቅ ነው። በንድፈ ሀሳብ ፣ የተወሰነ መዋቅር እንደ ማከማቻ የሚጠቀም የውሂብ ጎታ አስተዳደር ስርዓትን መተግበር ይቻላል ፣ በተለይም ለተግባራዊ አመክንዮ የተስተካከለ።

ምንጭ: hab.com

አስተያየት ያክሉ