অধ্যায় 4 · ফাংশন ও সারাংশ
অ্যাগ্রিগেট, GROUP BY ও HAVING
- পৃষ্ঠা 14 / 22
- 14 মিনিট পড়া
কেউ ডেটাবেসের কাছে "১৪টা অর্ডারের সবগুলো" চায় না। প্রশ্ন হয় "কয়টা অর্ডার পেলাম?", "প্রতিটা কাস্টমার মোট কত খরচ করেছেন?", "কোন প্রোডাক্ট সবচেয়ে বেশি আয় এনেছে?"। এর প্রতিটাই অনেক সারিকে গুটিয়ে কয়েকটা সংখ্যায় নিয়ে আসে: একটা অ্যাগ্রিগেট (aggregate)। ড্যাশবোর্ডের প্রতিটা মেট্রিক এমনই, মেশিন লার্নিংয়ের অনেক ফিচারও: কাস্টমারপ্রতি অর্ডারের সংখ্যা, গড় বাস্কেটের আকার, শেষ কেনাকাটার পর কত দিন। এই কাজ SQL-এ, ডেটাবেসের ভেতরেই করলে Python-এ আনতে হয় ছোট্ট একটা সারাংশ, লাখ লাখ কাঁচা সারি নয়।
যা শিখবেন
COUNT(*),COUNT(column)আরCOUNT(DISTINCT column)-এর পার্থক্য, সাথেSUM,AVG,MIN,MAX।- অ্যাগ্রিগেটের ভেতরে NULL আর খালি গ্রুপ কেমন আচরণ করে।
- এক বা একাধিক কলামে
GROUP BY, আর আয় মানেSUM(quantity * unit_price)। HAVINGবনামWHERE, আর কোয়েরির লজিক্যাল ক্রমে এদের জায়গা।- ক্লাসিক ভুলগুলো: গ্রুপ না করা কলাম, ভুল স্তরে অ্যাগ্রিগেট, আর গড়ের গড়।
পুরো টেবিলের ওপর অ্যাগ্রিগেট ফাংশন
GROUP BY না থাকলে অ্যাগ্রিগেট পুরো টেবিলকে (WHERE-এর পরে যা থাকে) একটা সারিতে গুটিয়ে আনে। তিন রকম COUNT তিনটা আলাদা প্রশ্নের উত্তর দেয়:
SELECT COUNT(*) AS customers, -- সারির সংখ্যা
COUNT(city) AS with_city, -- যেসব সারিতে city NULL নয়
COUNT(DISTINCT city) AS cities -- NULL বাদে আলাদা আলাদা মান
FROM customers;+-----------+-----------+--------+
| customers | with_city | cities |
+-----------+-----------+--------+
| 8 | 7 | 5 |
+-----------+-----------+--------+কাস্টমার ৮ জন, কিন্তু শহর আছে ৭ জনের (সাদিয়ার NULL), আর তাঁরা থাকেন ৫টা আলাদা শহরে। SUM, AVG, MIN আর MAX-ও NULL বাদ দেয়। গড় স্টকের ওপর এর প্রভাব দেখুন; তিনটা NULL হলো কোর্সগুলো আর গিফট কার্ড:
SELECT COUNT(*) AS products,
COUNT(stock) AS with_stock,
SUM(stock) AS total_stock,
AVG(stock) AS avg_stock,
ROUND(AVG(COALESCE(stock, 0)), 2) AS avg_if_null_were_0,
MIN(price) AS cheapest,
MAX(price) AS dearest
FROM products;+----------+------------+-------------+-----------+--------------------+----------+---------+
| products | with_stock | total_stock | avg_stock | avg_if_null_were_0 | cheapest | dearest |
+----------+------------+-------------+-----------+--------------------+----------+---------+
| 11 | 8 | 165 | 20.625 | 15.0 | 900 | 18500 |
+----------+------------+-------------+-----------+--------------------+----------+---------+AVG(stock) হলো ১৬৫ ÷ ৮ = ২০.৬২৫: যে ৮টা প্রোডাক্টের স্টকের হিসাব রাখা হয়, তাদের গড়। NULL-কে ০ ধরলে আসে ১৬৫ ÷ ১১ = ১৫.০, যা আলাদা একটা দাবি। কোনোটাই নিজে থেকে "ঠিক" নয়; আপনি কোন প্রশ্ন করছেন, তা ঠিক করুন। ML-এও ঠিক এই সিদ্ধান্তটা নিতে হয়: অনুপস্থিত মান মানে "শূন্য", নাকি "অজানা"।
কোনো সারিই না মিললে COUNT দেয় ০, কিন্তু SUM, AVG, MIN আর MAX দেয় NULL, ০ নয়। Furniture-এ কোনো প্রোডাক্ট নেই:
SELECT COUNT(*) AS products,
SUM(price) AS sum_price,
COALESCE(SUM(price), 0) AS sum_or_zero
FROM products
WHERE category_id = 5;+----------+-----------+-------------+
| products | sum_price | sum_or_zero |
+----------+-----------+-------------+
| 0 | NULL | 0 |
+----------+-----------+-------------+GROUP BY: প্রতি গ্রুপে একটা ফলাফলের সারি
GROUP BY status সারিগুলোকে প্রতিটা স্ট্যাটাসের জন্য একটা করে স্তূপে ভাগ করে, প্রতিটা স্তূপে অ্যাগ্রিগেট চালায়, আর প্রতি স্তূপে একটা সারি ফেরত দেয়:
SELECT status, COUNT(*) AS orders
FROM orders
GROUP BY status
ORDER BY orders DESC, status;+-----------+--------+
| status | orders |
+-----------+--------+
| delivered | 11 |
| cancelled | 1 |
| pending | 1 |
| shipped | 1 |
+-----------+--------+অর্ডারের id দিয়ে ছবিটা এমন:
orders (14 rows) split by status aggregate each pile
----------------- ------------------------- --------------------
1 delivered ──► delivered: 1 2 3 4 6 7 8 ──► delivered 11
2 delivered 9 10 11 14
... cancelled: 5 ──► cancelled 1
13 pending pending: 13 ──► pending 1
14 delivered shipped: 12 ──► shipped 1pandas-এ df.groupby() দিয়ে যে split-apply-combine করেছিলেন, এটা ঠিক সেটাই।
আয়: SUM(quantity * unit_price)
একটা অর্ডারের মূল্য থাকে order_items-এ: অর্ডারের প্রতিটা প্রোডাক্টের জন্য এক সারি, আসলে যে দাম নেওয়া হয়েছে তা সহ। অ্যাগ্রিগেট এক্সপ্রেশনের ওপরও কাজ করে, তাই প্রতি অর্ডারের মোট:
SELECT order_id,
COUNT(*) AS lines,
SUM(quantity) AS units,
SUM(quantity * unit_price) AS order_total
FROM order_items
GROUP BY order_id
ORDER BY order_id;+----------+-------+-------+-------------+
| order_id | lines | units | order_total |
+----------+-------+-------+-------------+
| 1 | 2 | 2 | 2100 |
| 2 | 1 | 1 | 4500 |
| 3 | 1 | 1 | 3000 |
| 4 | 2 | 2 | 4000 |
| 5 | 1 | 1 | 18500 |
| 6 | 1 | 1 | 15000 |
| 7 | 2 | 3 | 4000 |
| 8 | 1 | 2 | 2400 |
| 9 | 2 | 2 | 4950 |
| 10 | 1 | 1 | 18500 |
| 11 | 2 | 2 | 5500 |
| 12 | 1 | 2 | 3000 |
| 13 | 1 | 1 | 15000 |
| 14 | 2 | 2 | 3100 |
+----------+-------+-------+-------------+order_items-এর unit_price ব্যবহার করুন, আজকের products.price নয়: ৯ নম্বর অর্ডারে কিবোর্ডটা ডিসকাউন্টে বিক্রি হয়েছিল (৪০৫০), আর দাম সময়ের সাথে বদলায়। প্রোডাক্ট ধরে গ্রুপ করলে জানা যায় কোন প্রোডাক্ট সবচেয়ে বেশি আয় আনে:
SELECT product_id,
SUM(quantity) AS units,
SUM(quantity * unit_price) AS revenue
FROM order_items
GROUP BY product_id
ORDER BY revenue DESC, product_id
LIMIT 5;+------------+-------+---------+
| product_id | units | revenue |
+------------+-------+---------+
| 5 | 2 | 37000 |
| 7 | 2 | 30000 |
| 3 | 2 | 8550 |
| 6 | 2 | 6000 |
| 2 | 2 | 5000 |
+------------+-------+---------+এই হিসাবে অর্ডারের প্রতিটা লাইন আছে, বাতিল হওয়া ৫ নম্বর আর পেন্ডিং ১৩ নম্বর অর্ডারও। শুধু ডেলিভার হওয়া অর্ডার রাখতে লাগবে
orders-এরstatusকলাম, আর প্রোডাক্টের নাম দেখাতে লাগবেproducts: দুটোই জয়েন, যা আসছে জয়েন: INNER JOIN আর টেবিল অ্যালিয়াস-এ।
একাধিক কলামে গ্রুপ
কয়েকটা কলাম দিলে ডেটায় আসলে যে যে কম্বিনেশন আছে, তার প্রতিটার জন্য একটা সারি পাবেন। কাস্টমার আর স্ট্যাটাস ধরে অর্ডার:
SELECT customer_id, status, COUNT(*) AS orders
FROM orders
GROUP BY customer_id, status
ORDER BY customer_id, status;+-------------+-----------+--------+
| customer_id | status | orders |
+-------------+-----------+--------+
| 1 | delivered | 3 |
| 2 | delivered | 2 |
| 2 | shipped | 1 |
| 3 | delivered | 3 |
| 4 | cancelled | 1 |
| 5 | delivered | 1 |
| 6 | delivered | 1 |
| 6 | pending | 1 |
| 7 | delivered | 1 |
+-------------+-----------+--------+শুধু যে কম্বিনেশনগুলো আছে, সেগুলোই আসে: ৮ নম্বর কাস্টমার কখনো অর্ডার করেননি, তাই তাঁর কোনো সারিই নেই (এমন "হারানো" সারি খোঁজার কাজটা LEFT JOIN-এর; দেখবেন LEFT, RIGHT, FULL, CROSS আর সেলফ জয়েন-এ)।
HAVING বনাম WHERE
WHERE গ্রুপ করার আগে সারি ছাঁকে; HAVING অ্যাগ্রিগেট হিসাবের পরে গ্রুপ ছাঁকে। অন্তত ৩টা অর্ডার আছে এমন কাস্টমার:
SELECT customer_id, COUNT(*) AS orders
FROM orders
GROUP BY customer_id
HAVING COUNT(*) >= 3
ORDER BY customer_id;+-------------+--------+
| customer_id | orders |
+-------------+--------+
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
+-------------+--------+দুটো একসাথে: শুধু ডেলিভার হওয়া অর্ডার গুনুন (WHERE), তারপর রাখুন সেই কাস্টমারদের, যাঁদের এমন অর্ডার অন্তত ৩টা (HAVING)। ২ নম্বর কাস্টমার বাদ পড়েন, কারণ তাঁর তিনটা অর্ডারের একটা এখনো শুধু শিপ হয়েছে, ডেলিভার হয়নি:
SELECT customer_id, COUNT(*) AS delivered_orders
FROM orders
WHERE status = 'delivered'
GROUP BY customer_id
HAVING COUNT(*) >= 3
ORDER BY customer_id;+-------------+------------------+
| customer_id | delivered_orders |
+-------------+------------------+
| 1 | 3 |
| 3 | 3 |
+-------------+------------------+অ্যাগ্রিগেট WHERE-এ বসে না, কারণ WHERE যখন চলে, তখনো গ্রুপগুলো তৈরিই হয়নি:
SELECT customer_id, COUNT(*) AS orders
FROM orders
WHERE COUNT(*) >= 3
GROUP BY customer_id;Error: misuse of aggregate: COUNT()লজিক্যাল ক্রম দিয়েই সব বোঝা যায়: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT। SELECT যেহেতু HAVING-এর পরে আসে, orders-এর মতো কলাম অ্যালিয়াস নিয়মমতে HAVING-এ তখনো নেই। SQLite আর MySQL তবু ব্যবহার করতে দেয়; PostgreSQL দেয় না:
-- PostgreSQL
SELECT customer_id, COUNT(*) AS n
FROM orders
GROUP BY customer_id
HAVING n >= 3;Error: column "n" does not existঅ্যাগ্রিগেটটা আবার লিখুন (HAVING COUNT(*) >= 3), কোয়েরি সব জায়গায় চলবে।
গ্রুপ না করা কলাম
SELECT-এর প্রতিটা কলাম হয় GROUP BY-এ থাকতে হবে, নয়তো কোনো অ্যাগ্রিগেটের ভেতরে। নইলে প্রশ্নটার একক কোনো উত্তর নেই: ১ নম্বর কাস্টমারের অর্ডারের তারিখ তিনটা, তাঁর একমাত্র সারিতে কোনটা বসবে? তাই PostgreSQL আর MySQL 8 এমন কোয়েরি চালাতেই রাজি নয়:
-- PostgreSQL
SELECT customer_id, order_date, COUNT(*) AS orders
FROM orders
GROUP BY customer_id;Error: column "orders.order_date" must appear in the GROUP BY clause or be used in an aggregate function-- MySQL
SELECT customer_id, order_date, COUNT(*) AS orders
FROM orders
GROUP BY customer_id;Error: ERROR 1055: Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'shop.orders.order_date' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_bySQLite এটা চালায়, আর প্রতিটা গ্রুপের কোনো একটা সারি থেকে চুপচাপ একটা তারিখ তুলে নেয়:
SELECT customer_id, order_date, COUNT(*) AS orders
FROM orders
GROUP BY customer_id
ORDER BY customer_id
LIMIT 3;+-------------+------------+--------+
| customer_id | order_date | orders |
+-------------+------------+--------+
| 1 | 2026-01-12 | 3 |
| 2 | 2026-01-20 | 3 |
| 3 | 2026-02-14 | 3 |
+-------------+------------+--------+এখানে ঘটনাচক্রে প্রত্যেকের প্রথম অর্ডার এসেছে, কিন্তু SQLite এর কোনো নিশ্চয়তা দেয় না; ইনডেক্স বা নতুন ভার্সনে এটা বদলে যেতে পারে। যা বোঝাতে চান, সেটাই লিখুন: MIN(order_date) AS first_order বা MAX(order_date) AS last_order।
ঠিক স্তরে অ্যাগ্রিগেট
"আমাদের গড় অর্ডারের মূল্য কত?" একই টেবিল থেকে দুটো উত্তর:
SELECT SUM(quantity * unit_price) AS gross,
COUNT(DISTINCT order_id) AS orders,
ROUND(AVG(quantity * unit_price), 2) AS avg_line_value,
ROUND(SUM(quantity * unit_price) * 1.0
/ COUNT(DISTINCT order_id), 2) AS avg_order_value
FROM order_items;+--------+--------+----------------+-----------------+
| gross | orders | avg_line_value | avg_order_value |
+--------+--------+----------------+-----------------+
| 103550 | 14 | 5177.5 | 7396.43 |
+--------+--------+----------------+-----------------+AVG(quantity * unit_price) গড় করে ২০টা লাইনের ওপর, ১৪টা অর্ডারের ওপর নয়, তাই এটা অন্য প্রশ্নের উত্তর। গড় অর্ডার মূল্য হলো মোট ÷ অর্ডারের সংখ্যা। কোনো গড় লেখার আগে জিজ্ঞেস করুন: এক সারি মানে কী?
একই টেবিল দিয়ে আসলে যে টাকা এসেছে, তার সাথেও হিসাব মেলানো যায়। অর্ডার হওয়া ১,০৩,৫৫০ টাকার মধ্যে আছে বাতিল ৫ নম্বর অর্ডার (১৮,৫০০) আর টাকা না দেওয়া পেন্ডিং ১৩ নম্বর অর্ডার (১৫,০০০); এগুলো বাদ দিলে থাকে ৭০,০৫০, payments টেবিলে ঠিক যতটা আছে:
SELECT COUNT(*) AS payments, SUM(amount) AS received FROM payments;+----------+----------+
| payments | received |
+----------+----------+
| 13 | 70050 |
+----------+----------+আলাদা দুটো উৎসের মোট মিলছে কি না, এটা যাচাই করেই ডেটা টিমগুলো ভাঙা পাইপলাইন ধরে ফেলে।
গড়ের গড় কখনো নয়
প্রতি ক্যাটাগরির গড় দাম বের করে তারপর সেগুলোর গড় নিলে আসল গড় দাম পাওয়া যায় না:
import pandas as pd
from sqlhelp import con
by_cat = pd.read_sql_query("""
SELECT category_id, COUNT(*) AS products, AVG(price) AS avg_price
FROM products
GROUP BY category_id
ORDER BY category_id
""", con)
print(by_cat)
print("average of the averages:", by_cat["avg_price"].mean())
print("true average price: ", round(pd.read_sql_query("SELECT AVG(price) AS a FROM products", con)["a"][0], 2)) category_id products avg_price
0 NaN 1 1000.0
1 1.0 2 1850.0
2 2.0 4 7850.0
3 3.0 2 9000.0
4 4.0 2 1850.0
average of the averages: 4310.0
true average price: 5254.55গড়ের গড় একা গিফট কার্ডকে (NaN ক্যাটাগরি, SQL-এ যা NULL) চারটা ইলেকট্রনিক্সের সমান গুরুত্ব দেয়। গ্রুপের গড়গুলো ঠিকভাবে মেলাতে হলে প্রতিটা গ্রুপের সারির সংখ্যা দিয়ে ওজন দিন (weighted average), অথবা কাঁচা সারিতে ফিরে যান। আরও খেয়াল করুন, GROUP BY NULL-কে আলাদা একটা গ্রুপ হিসেবে রেখেছে।
ডায়ালেক্টের পার্থক্য
| SQLite | MySQL | PostgreSQL | |
|---|---|---|---|
পূর্ণসংখ্যার AVG (কার্ড পেমেন্টের AVG(amount)) | ফ্লোট (7491.666666666667) | DECIMAL, দশমিকের পর ৪ ঘর (7491.6667) | NUMERIC, অনেক ঘর (7491.6666666666666667) |
| গ্রুপ না করা কলাম | চলে, যেকোনো একটা সারি | এরর (ONLY_FULL_GROUP_BY, 5.7 থেকে ডিফল্ট) | এরর |
HAVING-এ অ্যালিয়াস | চলে | চলে | এরর |
| কিছুই না থাকলে যোগফল | NULL (TOTAL() দেয় 0.0) | NULL | NULL |
তাই সব জায়গায় একই আউটপুট চাইলে গড়কে ROUND(…, 2)-এ মুড়ে দিন।
বাস্তব সিস্টেমে কোথায় দেখবেন
- ML-এর জন্য কাস্টমার ফিচার: কাস্টমারপ্রতি
MAX(order_date),COUNT(*)আরSUM(amount)থেকে আসে RFM-এর (recency, frequency, monetary) R, F আর M; চার্ন (churn) আর লাইফটাইম ভ্যালু অনুমানের ক্লাসিক ফিচার। - মডেল ইভ্যালুয়েশন: ০/১ মানের
is_correctকলাম থাকলে মডেল ধরে গ্রুপ করাAVG(is_correct)-ই প্রতিটা মডেলের অ্যাকুরেসি। SQLite আর MySQL-এ তুলনার ফল এমনিতেই ০/১, তাইSELECT AVG(status = 'delivered') FROM ordersদেয় ডেলিভারির হার। - LLM খরচ নজরদারি: দিন আর মডেল ধরে
SUM(tokens)আরCOUNT(*), আর বাজেট ছাড়ানো দিন চিহ্নিত করতেHAVING।
সাধারণ ভুল
- গ্রুপ না করা কলাম সিলেক্ট করা। সার্ভারে এরর, SQLite-এ এলোমেলো মান। সমাধান: কলামটা
GROUP BY-এ যোগ করুন, অথবা মুড়ে দিন:MAX(order_date)। - WHERE-এ অ্যাগ্রিগেট।
misuse of aggregate। সমাধান: শর্তটাHAVING-এ সরান; সাধারণ সারির ফিল্টারWHERE-এই রাখুন (এতে গতিও বাড়ে, কারণ কম সারি গ্রুপ হয়)। - সারি গুনতে চেয়ে
COUNT(column)। এটা NULL বাদ দেয়:COUNT(city)৭, ৮ নয়। সমাধান: সারির জন্যCOUNT(*);COUNT(column)শুধু "যেসব সারিতে মান আছে" গুনতে। - ভুল স্তরে গড়, বা গড়ের গড়। সমাধান: এক সারি মানে কী (লাইন, অর্ডার, কাস্টমার) ঠিক করুন, তারপর সেই স্তরে
SUM(…) / COUNT(DISTINCT …)হিসাব করুন। - ভুলে যাওয়া যে কিছুই না থাকলে SUM হয় NULL। রিপোর্টে ০-এর জায়গায় ফাঁকা দেখায়। সমাধান:
COALESCE(SUM(x), 0)।
নিজে চেষ্টা করুন
- সহজ: products-এর প্রতিটা
category_id-এর জন্য প্রোডাক্টের সংখ্যা আর পূর্ণ টাকায় রাউন্ড করা গড় দাম দেখান।category_idঅনুযায়ী সাজান। - মাঝারি: অন্তত দুটো অর্ডার আছে এমন প্রতিটা কাস্টমারের অর্ডারের সংখ্যা, প্রথম অর্ডারের তারিখ আর শেষ অর্ডারের তারিখ দেখান। অর্ডারের সংখ্যা অনুযায়ী সাজান (বেশি আগে), তারপর
customer_id। - কঠিন: যেসব অর্ডারে একাধিক প্রোডাক্ট আছে এবং মূল্য অন্তত ৪০০০ টাকা, সেগুলো খুঁজুন। লাইন, ইউনিট আর অর্ডারের মোট দেখান, বড় মোট আগে (সমান হলে
order_id)। তারপর প্রতিটা পেমেন্ট মেথডে কয়টা পেমেন্ট, মোট আর ২ ঘর পর্যন্ত রাউন্ড করা গড় দেখান, শুধু যেসব মেথডের মোট ১০,০০০-এর বেশি।
উত্তর
-- সহজ
SELECT category_id, COUNT(*) AS products, ROUND(AVG(price)) AS avg_price
FROM products
GROUP BY category_id
ORDER BY category_id;+-------------+----------+-----------+
| category_id | products | avg_price |
+-------------+----------+-----------+
| NULL | 1 | 1000.0 |
| 1 | 2 | 1850.0 |
| 2 | 4 | 7850.0 |
| 3 | 2 | 9000.0 |
| 4 | 2 | 1850.0 |
+-------------+----------+-----------+-- মাঝারি
SELECT customer_id,
COUNT(*) AS orders,
MIN(order_date) AS first_order,
MAX(order_date) AS last_order
FROM orders
GROUP BY customer_id
HAVING COUNT(*) >= 2
ORDER BY orders DESC, customer_id;+-------------+--------+-------------+------------+
| customer_id | orders | first_order | last_order |
+-------------+--------+-------------+------------+
| 1 | 3 | 2026-01-12 | 2026-04-19 |
| 2 | 3 | 2026-01-20 | 2026-05-21 |
| 3 | 3 | 2026-02-14 | 2026-06-09 |
| 6 | 2 | 2026-04-02 | 2026-06-02 |
+-------------+--------+-------------+------------+-- কঠিন
SELECT order_id,
COUNT(*) AS lines,
SUM(quantity) AS units,
SUM(quantity * unit_price) AS order_total
FROM order_items
GROUP BY order_id
HAVING COUNT(*) > 1 AND SUM(quantity * unit_price) >= 4000
ORDER BY order_total DESC, order_id;
SELECT method, COUNT(*) AS payments, SUM(amount) AS total, ROUND(AVG(amount), 2) AS avg_amount
FROM payments
GROUP BY method
HAVING SUM(amount) > 10000
ORDER BY total DESC;+----------+-------+-------+-------------+
| order_id | lines | units | order_total |
+----------+-------+-------+-------------+
| 11 | 2 | 2 | 5500 |
| 9 | 2 | 2 | 4950 |
| 4 | 2 | 2 | 4000 |
| 7 | 2 | 3 | 4000 |
+----------+-------+-------+-------------+
+--------+----------+-------+------------+
| method | payments | total | avg_amount |
+--------+----------+-------+------------+
| card | 6 | 44950 | 7491.67 |
| bkash | 5 | 18000 | 3600.0 |
+--------+----------+-------+------------+সারসংক্ষেপ
COUNT(*)সারি গোনে,COUNT(col)NULL নয় এমন মান,COUNT(DISTINCT col)আলাদা মান;SUM/AVG/MIN/MAXNULL বাদ দেয়, আর কোনো সারি না থাকলে NULL দেয়।GROUP BYপ্রতি গ্রুপে (বা কলামগুলোর প্রতিটা কম্বিনেশনে) একটা সারি দেয়; সিলেক্ট করা প্রতিটা কলাম হয় গ্রুপ করা, নয়তো অ্যাগ্রিগেট হতে হবে।- আয় হলো লাইন আইটেম থেকে
SUM(quantity * unit_price), আসলে যে দাম নেওয়া হয়েছে সেই দামে। WHEREগ্রুপের আগে সারি ছাঁকে,HAVINGপরে গ্রুপ ছাঁকে; ক্রম: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT।- প্রতিটা গড়ের স্তর জানুন, কখনো গড়ের গড় করবেন না, আর আলাদা আলাদা টেবিল থেকে পাওয়া মোট মিলিয়ে দেখুন।
এরপর: CASE WHEN, শর্তসাপেক্ষ গণনা আর ব্যবসার প্রশ্ন। CASE দিয়ে কোয়েরিতে যদি-তাহলে যুক্তি যোগ করবেন, সারিগুলো বাকেটে ভাগ করবেন, একবারেই কয়েকটা শর্ত গুনবেন, মাস ধরে গ্রুপ করবেন, ডুপ্লিকেট খুঁজবেন, আর শপ নিয়ে ব্যবসার আসল প্রশ্নের উত্তর দেবেন।