در این فصل چه یاد میگیری#
فصلِ قبل دو پرسش را اجرا کردیم بدونِ اینکه بگوییم چرا اینطور نوشته شدهاند. این فصل همان بدهی را میپردازد و در راه، اولین عددهای واقعی را از این جدول بیرون میکشد: ۹۴٬۱۹۱ امانت روی ۱٬۲۰۰ کتاب.
و به عددی میرسیم که کلِ ترم را شکل میدهد. از همین یک ستونِ rating دو میانگین بیرون میآید — ۳٫۷۸۶۸۹۳۷۰۴۸۵۰۳۶۱۳ و ۳٫۰۵۷۹۱۶۶۶۶۶۶۶۶۷ — و هیچکدام غلط محاسبه نشدهاند. تفاوتشان در چیزی است که هنوز اسمش را نبردهایم.

آخر این فصل میتوانی:
- یک پرسشِ
SELECTبنویسی که فقط ستونهای لازم را برمیگرداند - با
LIMITقبل از هر کارِ دیگری چند سطر نمونه ببینی - با
ASنامِ خروجی را خودت انتخاب کنی و با--توضیح بگذاری - سه عددِ کلی از یک جدول بگیری:
COUNT(*)وSUMوAVG - بگویی
AVGروی چند سطر حساب کرده — نه چند سطر در جدول هست
قبل از شروع#
از فصلِ ۱: جدول و سطر و ستون، اتصال، و تابعِ q(...). یک سطر از books یعنی یک عنوانِ کتاب که در یکی از شعبهها ثبت شده — این جمله در هر فصلِ این ترم تکرار میشود چون هر عددی که میسازیم رویش سوار است.
📓 نوتبوک: نوتبوک این فصل را در Colab باز کن — همهٔ کدهای این فصل آماده و بهترتیب داخلش هست.
| جدول | تعدادِ سطر | زمانِ ساخت |
|---|---|---|
books |
۱٬۲۰۰ | کمتر از یک ثانیه |
branches |
۶ | آنی |
۱. SELECT و FROM: کدام ستونها، از کدام جدول#
کوچکترین پرسشِ ممکن دو تکه دارد: چه چیزی میخواهی و از کجا.
SELECT فهرستِ ستونهایی است که میخواهی. FROM نامِ جدول است. همین.
یک قاعدهٔ نگارشی که از همین اولین پرسش رعایتش کن: کلیدواژهها با حروفِ بزرگ، نامِ جدول و ستون با حروفِ کوچک. در پرسشِ سهکلمهای فرقی نمیکند؛ در پرسشِ دهخطیِ ترمِ ۳ تنها چیزی است که میگذارد با یک نگاه ساختار را از داده جدا کنی.
print(q("SELECT title, author, borrowed FROM books"))
title author borrowed
0 روزهای پل مهدی سلیمی 33
1 مه در باران رویا رحیمی 20
2 روزهای خاک سعید کاشانی 1
3 مه پشت آتش سیما نیکزاد 86
4 بازگشت به باران سعید کاشانی 53
... ... ... ...
1195 خاک پشت جنگل فرهاد نیکزاد 135
1196 دریا و مه سعید توکلی 0
1197 برف پشت مه شیرین فرهنگی 2
1198 روزهای باران الهام کاشانی 0
1199 بازگشت به کویر بابک بیات 138
[1200 rows x 3 columns]
آن سه نقطهٔ وسط را pandas گذاشته، نه پایگاه داده. هر ۱٬۲۰۰ سطر واقعاً آمد؛ pandas فقط برای نمایش خلاصهشان کرد. این دقیقاً همان اشتباهی است که فصلِ ۱ اندازهاش را گرفت — هزینه پرداخت شد و بعد دور ریخته شد.
۲. LIMIT: اول نگاه کن، بعد بپرس#
LIMIT n به پایگاه داده میگوید بعد از n سطر بس کند.
print(q("SELECT title, author, borrowed FROM books LIMIT 5"))
title author borrowed
0 روزهای پل مهدی سلیمی 33
1 مه در باران رویا رحیمی 20
2 روزهای خاک سعید کاشانی 1
3 مه پشت آتش سیما نیکزاد 86
4 بازگشت به باران سعید کاشانی 53
این اولین کاری است که روی هر جدولِ ناآشنایی باید بکنی. قبل از هر عددی، قبل از هر گزارشی، پنج سطر نمونه ببین. نصفِ اشتباههایی که در این دوره جلویشان را میگیریم با همین یک نگاه پیدا میشوند.
⚠️ مواظب باش:
LIMIT 5پنج سطرِ اول را نمیدهد، پنج سطر میدهد. «اول» تا وقتی ترتیبی نگفته باشی معنایی ندارد؛ اینجا اتفاقاً همان ترتیبِ ثبت درآمده، ولی این یک تصادف است نه یک تضمین. فصلِ ۴ کلِ ماجرا را باز میکند.
۳. ستونها را اسم ببر، * ننویس#
* یعنی «همهٔ ستونها». برای یک نگاهِ اولیه خوب است و در فصلِ ۱ هم از همان استفاده کردیم. برای هر چیزِ دیگری عادتِ بدی است، و دلیلش قابلِ اندازهگیری است:
star = q("SELECT * FROM books LIMIT 5")
three = q("SELECT title, author, borrowed FROM books LIMIT 5")
print("SELECT * →", star.shape[0], "سطر ×", star.shape[1], "ستون =",
star.shape[0] * star.shape[1], "خانه")
print("SELECT سه ستون →", three.shape[0], "سطر ×", three.shape[1], "ستون =",
three.shape[0] * three.shape[1], "خانه")
SELECT * → 5 سطر × 13 ستون = 65 خانه
SELECT سه ستون → 5 سطر × 3 ستون = 15 خانه
روی پنج سطر، ۶۵ در برابرِ ۱۵. روی هزار سطر همان نسبت است و عددها هزار برابر.
و دلیلِ دومی هم دارد که از اولی مهمتر است: * یعنی پرسشِ تو به شکلِ امروزیِ جدول وابسته است. اگر فردا کسی ستونی اضافه کند، خروجیِ پرسشِ تو بیخبر عوض میشود. وقتی ستونها را اسم برده باشی، هیچ اتفاقی نمیافتد.
۴. AS و کامنتِ --#
AS نامِ ستون را در خروجی عوض میکند، نه در جدول. و هر چیزی بعد از -- تا آخرِ همان خط، توضیح است و اجرا نمیشود.
print(q("""
-- ستون borrowed اسم گویایی ندارد؛ در خروجی اسم بهتری برایش میگذاریم
SELECT title AS name,
borrowed AS times_borrowed
FROM books
LIMIT 3
"""))
name times_borrowed
0 روزهای پل 33
1 مه در باران 20
2 روزهای خاک 1
AS وقتی ضروری میشود که ستونِ خروجی اصلاً در جدول وجود نداشته باشد — مثلِ نتیجهٔ یک شمارش. بخشِ بعد را ببین.
۵. اولین عددها: COUNT(*)، SUM، AVG#
تا اینجا هر پرسشی سطر برمیگرداند. این سه تابع کارِ متفاوتی میکنند: از کلِ جدول یک عدد میسازند.
COUNT(*)سطرها را میشمارد.SUM(col)مقدارهای یک ستون را جمع میزند.AVG(col)میانگینشان را میگیرد.
print(q("""
SELECT COUNT(*) AS books,
SUM(borrowed) AS all_borrows,
AVG(borrowed) AS borrows_per_book,
AVG(pages) AS pages_per_book
FROM books
"""))
books all_borrows borrows_per_book pages_per_book
0 1200 94191 78.4925 503.798333
یک سطر، چهار عدد. و اینجا دانهبندیِ نتیجه عوض شد: ورودی ۱٬۲۰۰ سطر بود که هرکدام یک کتاب بودند، خروجی یک سطر است که یعنی «کلِ فهرست». هر بار که یک تابعِ تجمیعی مینویسی همین اتفاق میافتد، و ترمِ ۲ تمامش دربارهٔ کنترل کردنِ همین است.
✅ چک کن:
78.4925باید دقیقاً94191 / 1200باشد. حساب کن. اگر نشد، ستونی را جمع زدهای که با ستونی که شمردهای یکی نیست — و این شایعترین شکلِ عددِ غلط در گزارشهای واقعی است.
۶. دو میانگین از یک ستون، و هیچکدام غلط نیست#
حالا همان AVG را روی ستونِ rating بگیریم و کنارش، همان میانگین را از راهِ دستی حساب کنیم: جمعِ ستون تقسیم بر تعدادِ سطرهای جدول.
row = q("""
SELECT COUNT(*) AS n,
SUM(rating) AS rating_sum,
AVG(rating) AS rating_avg
FROM books
""")
print(row)
print()
print("AVG(rating) چه میگوید :", float(row["rating_avg"][0]))
print("SUM(rating) تقسیم بر COUNT(*):", float(row["rating_sum"][0]) / int(row["n"][0]))
n rating_sum rating_avg
0 1200 3669.5 3.786894
AVG(rating) چه میگوید : 3.7868937048503613
SUM(rating) تقسیم بر COUNT(*): 3.057916666666667
دو عدد، از یک ستون، در یک پرسش. اختلافشان بیش از هفتدهم است.
هیچکدام اشتباه حساب نشدهاند. SUM و COUNT(*) هر دو درستاند، و AVG هم درست است. تفاوت در مخرج است: AVG بر ۱٬۲۰۰ تقسیم نکرده.
بر چه تقسیم کرده؟ عددش را میشود از همین دو خط درآورد: 3669.5 / 3.7868937048503613 میشود ۹۶۹. یعنی AVG دویستوسیویک سطر را اصلاً به حساب نیاورده.
چرا؟ و آیا آن ۲۳۱ سطر تصادفی انتخاب شدهاند؟ جوابِ دوم «نه» است و همان چیزی است که فصلِ ۶ را به یک فصلِ ⚠️ تبدیل میکند. فعلاً همینقدر بدان که ستونِ rating در بعضی سطرها خالی است، و AVG سطرهای خالی را نمیشمارد. فصلِ ۶ کاملاً بازش میکند و نشان میدهد آن ۲۳۱ کتاب چه ویژگیِ مشترکی دارند.
پس دو عدد داری و باید انتخاب کنی، و انتخاب به پرسشت بستگی دارد: «میانگینِ امتیازِ کتابهایی که امتیاز خوردهاند» یعنی ۳٫۷۹. «میانگینِ امتیاز اگر کتابِ بیامتیاز را صفر بگیریم» یعنی ۳٫۰۶. اولی معمولاً چیزی است که میخواهی؛ دومی معمولاً چیزی است که بهاشتباه گزارش میشود.
۷. شمارشِ دوم#
قانونِ دومِ این دوره: هر عدد را با یک شمارشِ دوم بسنج. نه به این خاطر که به SQLite بیاعتمادیم — به این خاطر که به پرسشِ خودمان بیاعتمادیم.
in_sql = int(q("SELECT SUM(borrowed) FROM books").iloc[0, 0])
in_python = sum(row[0] for row in con.execute("SELECT borrowed FROM books"))
print("SUM در SQL :", in_sql)
print("جمع در پایتون :", in_python)
print("دو شمارش یکیاند؟", in_sql == in_python)
SUM در SQL : 94191
جمع در پایتون : 94191
دو شمارش یکیاند؟ True
📏 اندازه بگیر: یک سطر یعنی چه؟ نتیجهٔ بخشِ ۵ یک سطر دارد و آن سطر یعنی «کلِ فهرست» — نه یک کتاب، نه یک شعبه. با کدام شمارشِ دوم سنجیدی؟ جمعِ ستونِ
borrowedرا دو بار از دو مسیرِ مستقل گرفتیم: یک بار داخلِ پایگاه داده باSUMو یک بار در پایتون روی تکتکِ سطرها. هر دو ۹۴٬۱۹۱. چه کسی از قلم افتاد؟ دو گروه: ۲۴۰ سطرِarrivals.csvکه هنوز وارد نشدهاند، و — تازه در همین فصل کشفش کردیم — ۲۳۱ سطری کهAVG(rating)بیصدا کنار گذاشت.
🔧 اگر کار نکرد: نامِ ستون را غلط تایپ کن تا ببینی چه میشود. این خطا را در کارِ واقعی روزی چند بار میگیری:
try:
q("SELECT titel FROM books LIMIT 3")
except Exception as error:
print(type(error).__module__ + "." + type(error).__name__)
print(error)
pandas.errors.DatabaseError
Execution failed on sql 'SELECT titel FROM books LIMIT 3': no such column: titel
دو لایه در این پیام هست و هر دو را باید بخوانی. بیرونیاش pandas.errors.DatabaseError است، چون q از pandas رد میشود. جملهٔ آخر — no such column: titel — از خودِ SQLite است و همان است که به دردت میخورد. عادت کن پیام را از آخر بخوانی.
🤖 از دستیارت بپرس: «فرقِ
COUNT(*)باCOUNT(1)درSQLiteچیست؟» بعد این را بپرس: «اگر یک ستون در بعضی سطرها خالی باشد،AVGآن سطرها را چطور حساب میکند: صفر میگیرد یا نادیده میگیرد؟» — جوابِ درست «نادیده میگیرد» است، و اگر دستیارت «صفر میگیرد» گفت، خودت همین حالا با عددهای بخشِ ۶ ردش کن.
واژههای تازهٔ این فصل#
| کلمه | تلفظ به حروف فارسی | یعنی چه |
|---|---|---|
| SELECT | سلکت | بندی که میگوید چه ستونهایی میخواهی |
| FROM | فرام | بندی که میگوید از کدام جدول |
| LIMIT | لیمیت | سقفِ تعدادِ سطرهای برگشتی |
| alias | الیاس | نامِ تازهای که با AS برای ستونِ خروجی میگذاری |
| aggregate | اگریگیت | تابعی که از چند سطر یک عدد میسازد |
تمرینها
اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.
در فصل بعد#
تا اینجا هر پرسشی دربارهٔ همهٔ ۱٬۲۰۰ سطر بود. فصلِ بعد WHERE را میآورد — بندی که میگوید کدام سطرها. و با یک تلهٔ واقعی شروع میکند: دو پرسش که تنها تفاوتشان یک جفت پرانتز است، یکی ۱۸۱ سطر میدهد و دیگری ۳۵ سطر.
به آخر این فصل رسیدی!
اگر ساختی و جواب داد، این دکمه مال توست.