مدلسازی داده برای کسی که برنامهنویس نیست
هوش مصنوعی کوئری را مینویسد، اما اینکه اپ شما شش ماه بعد چه کارهایی میتواند بکند، به شکل جدولهایی بستگی دارد که امروز میسازید.
هوش مصنوعی امروز کوئری SQL را در چند ثانیه مینویسد و معمولاً هم درست مینویسد. کاری که هنوز روی دوش خودتان است مدلسازی داده است: تصمیم دربارهٔ اینکه چه چیزهایی را در چه جدولهایی نگه دارید و آن جدولها چطور به هم وصل شوند. ابزارهایی مثل Cursor و Claude Code با هر ساختاری که جلویشان بگذارید راه میآیند و برایش کد مینویسند؛ ساختار غلط را خودشان گوشزد نمیکنند، مگر صریح بپرسید.
کد بد را فردا میشود بازنویسی کرد، اما ساختار بد وقتی چند صد ردیف دادهٔ واقعی رویش نشست دیگر ارزان عوض نمیشود: باید مایگریشن بنویسید، دادهٔ قدیمی را ترمیم کنید و هر کوئری و صفحهای را که به آن ستونها وابسته بوده دوباره بسازید. این نوشته چهار ایدهٔ پایه — جدول، کلید، رابطه و شکل داده — را با یک مثال کامل جلو میبرد و همانجا تمامش میکند؛ در عمل بیشتر از این لازم ندارید.
جدول، ردیف، ستون: کل واژگانی که لازم دارید
اگر با اکسل یا گوگلشیت کار کردهاید، بخش بزرگی از مسیر را آمدهاید. جدول تقریباً همان شیت است، با دو تفاوت مهم: هر جدول فقط یک نوع چیز را نگه میدارد، و نوع هر ستون از قبل مشخص و ثابت است.
- جدول (table): فهرستی از چیزهایی که همه از یک جنساند — مشتریها، نوبتها، فاکتورها. اسمش را جمع بگذارید؛ همین عادت کوچک جلوی خیلی از سردرگمیها را میگیرد.
- ستون (column): یک واقعیت دربارهٔ هر ردیف، با نوع مشخص: متن، عدد صحیح، تاریخوزمان، بله/خیر.
- ردیف (row): یک نمونهٔ واقعی؛ یک مشتری مشخص، یک نوبت مشخص.
- کلید اصلی (primary key): ستونی که هر ردیف را یکتا میکند؛ معمولاً یک عدد که خود پایگاه داده تولید میکند.
- کلید خارجی (foreign key): ستونی که به کلید اصلیِ جدولی دیگر اشاره میکند. «رابطه» دقیقاً همین است و نه چیزی بیشتر.
سه قاعده همینجا جلوی نصف دردسرهای بعدی را میگیرد. اول، هر ستون فقط یک واقعیت را نگه دارد؛ اگر آدرس و شمارهٔ تلفن را در یک ستون بریزید، بعداً نمیتوانید روی شهر فیلتر بگذارید. دوم، کلید اصلی را از دادهٔ واقعی نسازید: کد ملی و شمارهٔ موبایل عوض میشوند و غلط وارد میشوند، اما ستون id که هیچ معنایی جز «شماره» ندارد هیچوقت به دردسر نمیافتد.
سوم، همان روز اول مشخص کنید کدام ستونها حق ندارند خالی بمانند و کدام باید یکتا باشند. این دو قید (NOT NULL و UNIQUE) کاری میکنند که پایگاه داده خودش جلوی دادهٔ ناقص و مشتری تکراری را بگیرد؛ اگر این محافظت را به کد اپلیکیشن بسپارید، اولین اسکریپت ایمپورت یا اولین فرم دومی که میسازید از کنارش رد میشود.
یک مثال واقعی: سامانهٔ نوبتدهی کلینیک
فرض کنید میخواهید برای یک کلینیک پوست و مو سامانهٔ نوبت بسازید. چیزی که تقریباً همه اول میسازند یک جدول است به اسم «نوبتها» با این ستونها: نام مشتری، موبایل، نام خدمت، قیمت، نام متخصص، تاریخ. این جدول کار میکند؛ تا وقتی چند ده ردیف بیشتر ندارید.
بعد اینها یکییکی از راه میرسند:
- شمارهٔ یک مشتری عوض میشود و باید آن را در تکتک ردیفهایی که به اسم اوست دستی اصلاح کنید؛ یکیشان جا میماند.
- «اصلاح ابرو»، «اصلاحابرو» و «ابرو» سه خدمت جدا حساب میشوند و گزارش درآمد بهازای خدمت بیمعنا میشود.
- نمیتوانید خدمتی را تعریف کنید که هنوز کسی رزروش نکرده، چون خدمت فقط وقتی وجود دارد که یک نوبت وجود داشته باشد.
- برای اینکه بفهمید یک مشتری چند بار آمده، مجبورید رشتههای متنی را با هم مقایسه کنید.
شکل درست، همان اطلاعات را در چهار جدول پخش میکند و هر چیز را فقط یکبار مینویسد:
customers(id, full_name, phone, created_at)
services(id, title, duration_min, price, is_active)
staff(id, full_name, is_active)
appointments(id, customer_id, service_id, staff_id,
starts_at, status, price_charged)
آن سه ستونی که در appointments به id جدولهای دیگر اشاره میکنند، کلید خارجیاند. در SQL اینطور نوشته میشود:
CREATE TABLE appointments (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL REFERENCES customers(id),
service_id BIGINT NOT NULL REFERENCES services(id),
staff_id BIGINT NOT NULL REFERENCES staff(id),
starts_at TIMESTAMPTZ NOT NULL,
status TEXT NOT NULL DEFAULT 'booked',
price_charged INTEGER NOT NULL
);
یک نکته که تقریباً همیشه جا میماند: در PostgreSQL کلید اصلی خودش ایندکس میگیرد، ولی ستون کلید خارجی نه. پس روی customer_id، service_id، staff_id و starts_at خودتان ایندکس بسازید؛ این ستونها همانهایی هستند که هر صفحهٔ فهرست و هر گزارش رویشان فیلتر میزند.
اگر با Supabase کار میکنید همین را کلیکی در Table Editor میسازید و اگر با Airtable، اسمش میشود فیلد Link to another record. ابزار عوض میشود، مفهوم عوض نمیشود.
رابطه: چیزی که جدولها را به هم وصل میکند
رابطه پیچیده نیست: یک ستون در یک جدول که شمارهٔ یک ردیف از جدول دیگر را نگه میدارد. تنها سؤال واقعی این است که این ستون را کدام طرف بگذاریم، و جوابش را نوع رابطه تعیین میکند.
| نوع رابطه | مثال در همین کلینیک | چطور ساخته میشود |
|---|---|---|
| یکبهچند | هر مشتری چند نوبت دارد، هر نوبت یک مشتری | ستون customer_id داخل جدول نوبتها؛ یعنی کلید خارجی همیشه سمت «چند» مینشیند |
| چندبهچند | هر متخصص چند خدمت بلد است و هر خدمت را چند متخصص انجام میدهد | یک جدول واسط staff_services(staff_id, service_id) که کلید اصلیاش ترکیب همان دو ستون است |
| یکبهیک | هر متخصص یک شمارهٔ حساب دارد | معمولاً جدول جدا لازم ندارد؛ همان ستونها را در جدول اصلی بگذارید |
قاعدهٔ عملی که تقریباً همیشه درست از آب درمیآید: اگر چیزی میتواند بیش از یک بار تکرار شود، جدول است نه ستون. لحظهای که دارید service_1 و service_2 و service_3 میسازید، یا شناسهها را با کاما در یک ستون متنی میریزید («۳,۷,۱۲»)، یعنی یک جدول واسط را جا انداختهاید. هوش مصنوعی با آن ستون متنی هم کد مینویسد، ولی از آن به بعد هیچ گزارشی از آن داده بیرون نمیآید مگر با ترفندهای شکننده.
شکل داده تعیین میکند اپ فردا چه کاری میتواند بکند
تا اینجا موضوع فقط نظم و تکرارینبودن بود. بخش مهمتر مدلسازی داده این است که هر انتخاب کوچک، در سکوت، فهرست کارهایی را که شش ماه بعد میتوانید انجام دهید تعیین میکند. سه نمونه که در پروژههای واقعی بارها به آنها برمیخورید:
قیمت را همان لحظه ثبت کنید
وسوسهبرانگیز است که قیمت فقط در جدول services بماند و گزارش درآمد با یک join خوانده شود. اما روزی که قیمت یک خدمت را بالا ببرید، گزارش ماههای گذشته هم عوض میشود، چون گذشته دارد با نرخ امروز خوانده میشود. برای همین price_charged روی خودِ نوبت نشسته است. همین منطق دربارهٔ آدرس تحویل، نرخ ارز و درصد تخفیف هم صدق میکند: هرچه «در لحظهٔ معامله» معنا دارد باید کنار همان تراکنش ذخیره شود.
بهجای حذف کردن، وضعیت بگذارید
نوبت لغوشده را پاک نکنید؛ status را روی canceled بگذارید و ستون canceled_at را پر کنید. نرخ لغو و نرخ غیبت دو عدد پرارزش برای هر کسبوکار نوبتمحورند و هر دو فقط از ردیفهایی درمیآیند که حذف نشدهاند. دادهٔ پاکشده دیگر تحلیل نمیشود.
زمان و پول را از همان اول درست ذخیره کنید
این یکی سادهتر از بقیه است و بیشتر از همه فراموش میشود. created_at و updated_at را روی همهٔ جدولها بگذارید و زمان را همراه منطقهٔ زمانی ذخیره کنید (در PostgreSQL نوع TIMESTAMPTZ)، بعد فقط موقع نمایش به وقت محلی تبدیلش کنید. هر سؤالی که با «در ماه گذشته چند تا…» شروع شود، بدون ستون تاریخ اصلاً جواب ندارد.
مبالغ را هم یا عدد صحیح نگه دارید (تومان، بدون اعشار) یا از نوع دقیق NUMERIC استفاده کنید. نوعهای اعشار شناور مثل FLOAT برای پول انتخاب بدی هستند، چون خطای گِردکردنشان در جمعهای پشتسرهم روی هم انباشته میشود و ته گزارش مالی با جمع تکتک فاکتورها نمیخواند.
پنج نشانهٔ اینکه مدل دادهتان خراب است
- ستونهایی که به عدد ختم میشوند:
option_1،phone_2،image_3. - مقادیر جداشده با کاما داخل یک ستون متنی.
- یک نام یا یک قیمت که عیناً در دهها ردیف تکرار شده و اصلاحش یعنی ویرایش دستهجمعی.
- ستونی که برای بیشتر ردیفها خالی است؛ معمولاً یعنی دو موجودیت متفاوت را در یک جدول ریختهاید.
- برای جواب دادن به یک سؤال ساده مجبور میشوید داده را در اکسل خروجی بگیرید.
هیچکدام از اینها اپ را امروز از کار نمیاندازد؛ همهشان هزینه را به شش ماه بعد منتقل میکنند، جایی که جابهجا کردن دادهٔ واقعی بهمراتب گرانتر از ساختن جدول درست در روز اول است. چند نمونهٔ دیگر از همین دست تصمیمها را در مقالههای بلاگ باز کردهایم.
چطور مدل را به هوش مصنوعی دیکته کنید
بهترین کاری که پیش از نوشتن اولین خط کد میتوانید بکنید این است که مدل را به فارسی ساده بنویسید. مدلسازی داده در این مرحله چند جملهٔ خبری است، نه SQL. بعد همان جملهها را به دستیار بدهید:
اینها قانونهای دادهٔ اپ من است:
- هر مشتری میتواند چند نوبت داشته باشد.
- هر نوبت دقیقاً یک خدمت و یک متخصص دارد.
- هر متخصص چند خدمت انجام میدهد و هر خدمت را چند متخصص.
- قیمت پرداختی روی خود نوبت ذخیره میشود، نه خواندهشده از جدول خدمات.
اول جدولها، کلیدهای خارجی و ستونهای NOT NULL را بنویس، بعد مایگریشن.
روی ستونهای کلید خارجی و starts_at ایندکس بگذار.
پیش از کد، مدل را یکبار به زبان خودت برایم خلاصه کن.
آن خط آخر بیشترین بازده را دارد: خلاصهای که دستیار مینویسد نشان میدهد کدام جمله را طور دیگری فهمیده، و اصلاح یک جمله بسیار ارزانتر از اصلاح ده فایل کد است.
یک عادت دیگر هم ارزش دارد: قبل از تأیید ساختار، ۱۰ سؤالی را که اپ باید جواب بدهد بنویسید («درآمد هر متخصص در ماه»، «مشتریهایی که سه ماه است نیامدهاند») و بپرسید آیا این ساختار به همهٔ اینها جواب میدهد. هر سؤالی که جواب ندارد، یک ستون یا جدول گمشده را لو میدهد. همین ترتیب کار را در دورهٔ ساخت سایت با هوش مصنوعی بدون کدنویسی روی یک پروژهٔ واقعی از صفر جلو میبریم.
و یک هشدار: وقتی برای قابلیت جدیدی درخواست میدهید، دستیار معمولاً کمهزینهترین کار را میکند و یک ستون به جدول موجود اضافه میکند. پیش از پذیرفتن، خودتان بپرسید «این باید ستون باشد یا جدول؟» و همان محک قبلی را بزنید: اگر میتواند بیش از یکی باشد، جدول است. مایگریشنی را هم که مینویسد یکبار بخوانید؛ فرق ALTER TABLE با دستوری که ستون یا جدول را DROP میکند در یک کلمه است و دادهٔ رفته برنمیگردد.
تمرین بیستدقیقهای برای شروع
ایدهٔ فعلیتان را بردارید و روی کاغذ این چهار قدم را بروید. مدلسازی داده در همین حد، بیشترِ ارزشش را آزاد میکند:
- اسمهای جمعِ پروژهتان را بنویسید: کاربرها، سفارشها، محصولها… هر کدام یک جدول است.
- برای هر جدول، پنج تا هشت ستون بنویسید و جلوی هرکدام نوعش را مشخص کنید؛ همانجا علامت بزنید کدامها نمیتوانند خالی بمانند.
- جدولها را دوتا دوتا کنار هم بگذارید و بپرسید «آیا یکی میتواند چندتا از آن یکی داشته باشد؟» جواب، کلید خارجی یا جدول واسط را برایتان میسازد.
- ۱۰ سؤالی را که اپ باید جواب بدهد بنویسید و تکتک روی همین کاغذ دنبالشان بگردید.
اگر از این چهار قدم بیرون آمدید و ساختارتان هنوز سرپا بود، کدنویسیاش با ابزارهای امروزی کوتاهترین بخش کار است. و اگر میخواهید همین تصمیمها را در دل یک مسیر کامل ساخت محصول ببینید، فهرست دورهها نقطهٔ بعدی است.
مهندس نرمافزار و مدرس وایب کدینگ. بیش از ۱۰ سال تجربه ساخت محصولات وب، و سه سال تمرکز روی ساخت محصول با ابزارهای هوش مصنوعی. تا امروز بیش از ۴٬۰۰۰ نفر در دورههایش شرکت کردهاند.
خواندن ادامه دهید
همه مقالههااز ایده تا انتشار: نقشه راه ۳۰ روزه ساخت محصول با هوش مصنوعی
یک برنامهی هفتهبههفته برای رساندن ایده به یک لینک واقعی در سی روز: چه چیزی را بسازید، چه چیزی را حذف کنید…
امنیت اپلیکیشن ساختهشده با هوش مصنوعی؛ شش کاری که هنوز به عهدهی توست
شش کاری که دستیار هوش مصنوعی از دوش تو برنمیدارد — کلیدها، ورود کاربر، دسترسیها، خواندن کد، بکاپ و آپدیت — بهعلاوهی…
وایب کدینگ چیست و چرا مسیر ساخت محصول را عوض کرد
وایب کدینگ نه پایان برنامهنویسی است و نه اسباببازی؛ روشی است با مرزهای مشخص: چه چیزی را به دستیار بسپاری، کجا خودت…