خانه/ مدل‌سازی داده (CDM، LDM، PDM)/ تمرین‌های حل‌شده‌ی مدل‌سازی

تمرین‌های حل‌شده‌ی مدل‌سازی داده: رزرو هتل، فروشگاه، شرکت و شبکه‌ی اجتماعی

متوسط ۲۰ دقیقه مطالعه

بهترین راه یادگیری مدل‌سازی، حل تمرین است. این درس چهار سناریوی واقعی دارد؛ برای هر کدام اول خودت موجودیت‌ها، ارتباط‌ها، کاردینالیتی و کلیدها را روی کاغذ یا در طراح تعاملی ER بنویس، سپس پاسخ را باز کن و مقایسه کن.

روش حل هر تمرین

  1. اسم‌ها را زیر خط بکش (موجودیت‌ها و صفت‌ها) و فعل‌ها را (ارتباط‌ها).
  2. برای هر ارتباط دو جمله بنویس و حداقل و حداکثر را مشخص کن.
  3. رابطه‌های چندبه‌چند را با جدول واسط بشکن و صفت‌های رابطه را به همان جدول ببر.
  4. قواعد کسب‌وکار (یکتایی، بازه‌ی مجاز، «حداکثر یک‌بار») را به قید تبدیل کن.
  5. مدل را با چک‌لیست بازبینی بسنج و در نهایت اسکریپت SQL بنویس.
هر تمرین یک «نمونه‌ی آماده» در طراح تعاملی دارد (به‌جز شبکه‌ی اجتماعی). برای تمرین بهتر، ابتدا «مدل خالی» را بزن و خودت بساز؛ بعد با نمونه‌ی آماده مقایسه کن.

تمرین‌ها

چهار تمرین در بخش «تمرین‌های خودآزمایی» پایین همین صفحه آمده است؛ هرکدام را باز کن، خودت حل کن و بعد پاسخ را ببین. پاسخ هر تمرین شامل تحلیل، نمودار منطقی و کد SQL است.

معیار موفقیت

اگر توانستی...یعنی
رابطه‌ی چندبه‌چند را در ۵ ثانیه تشخیص بدهی و جدول واسطش را بنویسیقواعد نگاشت ER به جدول را فهمیده‌ای
بگویی صفت یک رابطه (ساعت کار، قیمت واحد) در کدام جدول می‌نشیندتفاوت صفت موجودیت و صفت رابطه را می‌دانی
قاعده‌های کسب‌وکار را به قید SQL تبدیل کنیاز مرز تحلیل به پیاده‌سازی رسیده‌ای
از روی نمودار، کوئری‌های معنادار بنویسیمدلت واقعاً کاربردی است

بعد از این تمرین‌ها، آماده‌ای پروژه‌ی دانشگاهی خودت را شروع کنی؛ راهنمای پروژه‌ی درس پایگاه‌داده مسیر و ساختار گزارش را می‌دهد.

تمرین‌های خودآزمایی

اول خودت جواب بده، بعد پاسخ را باز کن و مقایسه کن.

۱تمرین ۱ — رزرو هتل: هتلی مهمان‌ها را با نام و تلفن ثبت می‌کند. هر اتاق شماره و قیمت هر شب دارد. مهمان می‌تواند چند رزرو در تاریخ‌های مختلف داشته باشد؛ هر رزرو یک اتاق و بازه‌ی زمانی دارد و چند پرداخت (بیعانه، تسویه) می‌گیرد.نمایش پاسخ
پاسخ

موجودیت‌ها: Guest، Room، Booking، Payment. «رزرو» رابطه‌ی مهمان و اتاق در یک بازه‌ی زمانی است، پس خودش موجودیت می‌شود.

ارتباط‌ها: یک مهمان چند رزرو دارد (1:N)، یک اتاق در طول زمان در چند رزرو می‌آید (1:N) و یک رزرو می‌تواند چند پرداخت داشته باشد (1:N، مثلاً بیعانه و تسویه).

GuestPK GuestIdFullNamePhone BookingPK BookingIdFK GuestIdFK RoomIdCheckInCheckOut RoomPK RoomIdRoomNumberPricePerNight PaymentPK PaymentIdFK BookingIdAmountPaidAt

قیدهای مهم: تاریخ خروج باید بعد از ورود باشد، و یک اتاق در یک بازه دو رزرو هم‌پوشان نداشته باشد (این قید با CHECK ساده بیان نمی‌شود و معمولاً با Trigger یا پرس‌وجوی بررسی پیش از درج اعمال می‌شود).

sql
CREATE TABLE Booking (
  BookingId INT IDENTITY PRIMARY KEY,
  GuestId   INT NOT NULL REFERENCES Guest(GuestId),
  RoomId    INT NOT NULL REFERENCES Room(RoomId),
  CheckIn   DATE NOT NULL,
  CheckOut  DATE NOT NULL,
  CONSTRAINT CK_Booking_Dates CHECK (CheckOut > CheckIn)
);

-- بررسی هم‌پوشانی پیش از ثبت رزرو جدید (@RoomId, @In, @Out)
SELECT 1 FROM Booking
WHERE RoomId = @RoomId AND @In < CheckOut AND @Out > CheckIn;   -- اگر ردیفی آمد، اتاق مشغول است

مدل کامل: باز کردن در طراح تعاملی.

۲تمرین ۲ — فروشگاه اینترنتی: مشتریان سفارش می‌دهند؛ هر سفارش چند محصول با تعداد دارد. محصول‌ها در دسته‌بندی هستند. فاکتورهای قدیمی نباید با تغییر قیمت محصول عوض شوند.نمایش پاسخ
پاسخ

نکته‌ی اصلی: بین «سفارش» و «محصول» رابطه‌ی چندبه‌چند است. جدول واسط OrderItem علاوه بر تعداد، قیمت واحد در لحظه‌ی خرید را هم نگه می‌دارد؛ چون قیمت محصول بعداً تغییر می‌کند ولی فاکتور قدیمی نباید عوض شود.

CustomerPK CustomerIdFullNameEmail OrdersPK OrderIdFK CustomerIdOrderDateStatus OrderItemPK,FK OrderIdPK,FK ProductIdQuantityUnitPrice ProductPK ProductIdFK CategoryIdTitlePrice CategoryPK CategoryIdTitle
sql
CREATE TABLE OrderItem (
  OrderId   INT NOT NULL REFERENCES Orders(OrderId),
  ProductId INT NOT NULL REFERENCES Product(ProductId),
  Quantity  INT NOT NULL CHECK (Quantity > 0),
  UnitPrice DECIMAL(12,2) NOT NULL,
  PRIMARY KEY (OrderId, ProductId)
);

-- جمع هر سفارش
SELECT o.OrderId, SUM(i.Quantity * i.UnitPrice) AS Total
FROM Orders o
JOIN OrderItem i ON i.OrderId = o.OrderId
GROUP BY o.OrderId;

نام جدول Orders عمداً جمع نوشته شده، چون Order کلمه‌ی رزرو‌شده‌ی SQL است. مدل کامل: طراح تعاملی.

۳تمرین ۳ — شرکت و پروژه: کارمندان در بخش‌ها کار می‌کنند و هر کارمند یک مدیر (که او هم کارمند است) دارد. کارمندان در چند پروژه با ساعت کار مشخص مشارکت دارند.نمایش پاسخ
پاسخ

سه ارتباط متفاوت: بخش–کارمند یک‌به‌چند؛ کارمند–مدیر یک ارتباط بازگشتی (کلید خارجی به همان جدول)؛ کارمند–پروژه چندبه‌چند با صفت «ساعت کار».

DepartmentPK DepartmentIdTitle EmployeePK EmployeeIdFK DepartmentIdFK ManagerIdFullName WorksOnPK,FK EmployeeIdPK,FK ProjectIdHours ProjectPK ProjectIdTitle manages (ManagerId)
sql
-- ساختار سازمانی کامل با CTE بازگشتی
WITH Org AS (
  SELECT EmployeeId, FullName, ManagerId, 0 AS Level
  FROM Employee WHERE ManagerId IS NULL
  UNION ALL
  SELECT e.EmployeeId, e.FullName, e.ManagerId, o.Level + 1
  FROM Employee e JOIN Org o ON e.ManagerId = o.EmployeeId
)
SELECT * FROM Org ORDER BY Level, FullName;

ستون ManagerId باید NULLپذیر باشد (مدیرعامل مدیر ندارد). مدل کامل: طراح تعاملی؛ و درس CTE بازگشتی.

۴تمرین ۴ — شبکه‌ی اجتماعی: کاربران پست می‌گذارند، همدیگر را دنبال می‌کنند و پست‌ها را لایک می‌کنند. یک کاربر هر پست را حداکثر یک‌بار لایک می‌کند و نمی‌تواند خودش را دنبال کند.نمایش پاسخ
پاسخ

دو رابطه‌ی چندبه‌چند: کاربر–کاربر (دنبال‌کردن) و کاربر–پست (لایک). رابطه‌ی «دنبال‌کردن» یک M:N بازگشتی است، پس جدول واسط دو ستون کلید خارجی به همان جدول AppUser دارد که باید نام‌های متفاوت (Follower و Followee) بگیرند.

AppUserPK UserIdUserNameJoinedAt FollowPK,FK FollowerIdPK,FK FolloweeIdSince PostPK PostIdFK UserIdBodyCreatedAt PostLikePK,FK UserIdPK,FK PostIdLikedAt PostLike.UserId → AppUser.UserId
sql
CREATE TABLE AppUser (
  UserId   INT IDENTITY PRIMARY KEY,
  UserName NVARCHAR(50) NOT NULL UNIQUE,
  JoinedAt DATETIME2 NOT NULL DEFAULT SYSDATETIME()
);

CREATE TABLE Follow (
  FollowerId INT NOT NULL REFERENCES AppUser(UserId),
  FolloweeId INT NOT NULL REFERENCES AppUser(UserId),
  Since      DATETIME2 NOT NULL DEFAULT SYSDATETIME(),
  PRIMARY KEY (FollowerId, FolloweeId),
  CONSTRAINT CK_Follow_NotSelf CHECK (FollowerId <> FolloweeId)
);

CREATE TABLE Post (
  PostId    INT IDENTITY PRIMARY KEY,
  UserId    INT NOT NULL REFERENCES AppUser(UserId),
  Body      NVARCHAR(1000) NOT NULL,
  CreatedAt DATETIME2 NOT NULL DEFAULT SYSDATETIME()
);

CREATE TABLE PostLike (
  UserId  INT NOT NULL REFERENCES AppUser(UserId),
  PostId  INT NOT NULL REFERENCES Post(PostId),
  LikedAt DATETIME2 NOT NULL DEFAULT SYSDATETIME(),
  PRIMARY KEY (UserId, PostId)
);

قید CK_Follow_NotSelf جلوی دنبال‌کردن خود را می‌گیرد و کلید ترکیبی از دنبال‌کردن تکراری. اگر در طراح تعاملی چنین رابطه‌ای بسازی، ستون‌های هم‌نام خودکار پیشوند می‌گیرند؛ آن‌ها را به FollowerId و FolloweeId تغییر نام بده.