আগের পর্বে আমরা শিখেছি GROUP BY দিয়ে কীভাবে ডেটাকে দলবদ্ধ করে প্রতিটি দলের জন্য যোগফল, গড়, গণনা ইত্যাদি বের করতে হয়। কিন্তু সেই গ্রুপগুলোর মধ্যেও আমাদের প্রায়ই ফিল্টার করার দরকার হয় – যেমন, শুধু তাদের দেখাতে চাই যাদের গ্রুপে কমপক্ষে ২টি রেকর্ড আছে, বা যাদের মোট বেতন ৫০,০০০-এর বেশি। এই কাজটির জন্য WHERE ব্যবহার করা যায় না, কারণ WHERE সারি-ভিত্তিক ফিল্টার, গ্রুপ-ভিত্তিক নয়। HAVING ক্লজ ঠিক এই চাহিদা পূরণ করে: এটি GROUP BY-র পরবর্তী গ্রুপগুলোর উপর শর্ত আরোপ করে। আজ আমরা HAVING-এর গঠন, WHERE-এর সাথে পার্থক্য, এবং বিভিন্ন অ্যাগ্রিগেট ফাংশনের সাথে এর ব্যবহার বিস্তারিত শিখব।
পূর্বপ্রস্তুতি
- SQLite ইন্সটল থাকতে হবে (সিরিজের ৩য় পর্ব দেখুন)।
company.dbডাটাবেসেCOMPANYটেবিল প্রস্তুত থাকতে হবে, যেখানে ডুপ্লিকেট নাম রয়েছে। যদি না থাকে, নিচের স্ক্রিপ্ট চালিয়ে তৈরি করুন:
bash
sqlite3 company.db
sql
CREATE TABLE COMPANY( ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, AGE INT NOT NULL, ADDRESS CHAR(50), SALARY REAL ); INSERT INTO COMPANY VALUES (1, 'Paul', 32, 'California', 20000.00); INSERT INTO COMPANY VALUES (2, 'Allen', 25, 'Texas', 15000.00); INSERT INTO COMPANY VALUES (3, 'Teddy', 23, 'Norway', 20000.00); INSERT INTO COMPANY VALUES (4, 'Mark', 25, 'Rich-Mond', 65000.00); INSERT INTO COMPANY VALUES (5, 'David', 27, 'Texas', 85000.00); INSERT INTO COMPANY VALUES (6, 'Kim', 22, 'South-Hall', 45000.00); INSERT INTO COMPANY VALUES (7, 'James', 24, 'Houston', 10000.00); INSERT INTO COMPANY VALUES (8, 'Paul', 24, 'Houston', 20000.00); INSERT INTO COMPANY VALUES (9, 'James', 44, 'Norway', 5000.00); INSERT INTO COMPANY VALUES (10, 'James', 45, 'Texas', 5000.00);
- আউটপুট পরিষ্কার করতে:
.header onএবং.mode column।
১. HAVING-এর সিনট্যাক্স ও অবস্থান
একটি SELECT কোয়েরিতে HAVING-এর সঠিক অবস্থান:
sql
SELECT column-list FROM table_name WHERE condition GROUP BY column1, column2 HAVING aggregate_condition ORDER BY column1, column2;
- WHERE: গ্রুপ করার আগে সারি-ভিত্তিক ফিল্টার। অর্থাৎ নির্দিষ্ট সারিগুলোই গ্রুপে অন্তর্ভুক্ত হবে।
- GROUP BY: সারিগুলোকে দলবদ্ধ করে।
- HAVING: গ্রুপ করার পরে অ্যাগ্রিগেট ফাংশনের উপর শর্ত প্রয়োগ করে কোন গ্রুপগুলো চূড়ান্ত ফলাফলে থাকবে তা নির্ধারণ করে।
- ORDER BY: সর্বশেষ ফলাফল সাজায়।
গুরুত্বপূর্ণ: HAVING-এ আপনি অ্যাগ্রিগেট ফাংশন (যেমন COUNT(*), SUM(SALARY), AVG(AGE)) ব্যবহার করতে পারেন, কিন্তু WHERE-এ তা সম্ভব নয়। WHERE শুধুমাত্র কলামের সাথে সরাসরি তুলনা করতে পারে। HAVING ছাড়া অ্যাগ্রিগেট কন্ডিশন দেওয়া যায় না।
২. উদাহরণ ১: COUNT-এর সাথে HAVING
আমাদের টেবিলে Paul এবং James একাধিকবার আছে। আমরা শুধু সেই নামগুলো দেখতে চাই যাদের সংখ্যা ২-এর কম (অর্থাৎ যারা একবার এসেছে)।
sql
SELECT NAME, COUNT(*) AS CNT FROM COMPANY GROUP BY NAME HAVING COUNT(*) < 2;
ফলাফল:
text
NAME CNT ---------- ---------- Allen 1 David 1 Kim 1 Mark 1 Teddy 1
Paul ও James বাদ পড়েছে কারণ তাদের সংখ্যা ২ ও ৩।
এবার যাদের সংখ্যা ২-এর বেশি (অর্থাৎ ৩ বা তার বেশি) তা দেখতে:
sql
SELECT NAME, COUNT(*) AS CNT FROM COMPANY GROUP BY NAME HAVING COUNT(*) > 2;
ফলাফল:
text
NAME CNT ---------- ---------- James 3
শুধু James গ্রুপটি টিকে আছে।
দ্রষ্টব্য: উদাহরণের জন্য আমরা
SELECT *ব্যবহার করতে পারি, কিন্তু SQLite-তেSELECT *সহGROUP BYওHAVINGব্যবহার করলে অ্যাগ্রিগেট ছাড়া কলামের মান ডিটারমিনিস্টিক নয় (কোন সারির মান দেখাবে তা অনিশ্চিত)। তাই নির্ভরযোগ্য ফলাফলের জন্য অ্যাগ্রিগেট ফাংশন ও গ্রুপ কলাম স্পষ্টভাবে উল্লেখ করাই ভালো প্র্যাকটিস। নিচের কয়েকটি প্রদর্শিত মূল টিউটোরিয়ালের মতো করে দেখানো হলেও আমরা পরবর্তীতে নির্দিষ্ট কলাম ও অ্যাগ্রিগেট ব্যবহার করব।
টিউটোরিয়ালের মতো করে যদি আমরা লিখি SELECT * FROM COMPANY GROUP BY name HAVING count(name) < 2; তাহলে SQLite নামের একেক গ্রুপ থেকে যেকোনো একটি সারির সমস্ত কলাম দেখাবে (যেমন Allen-এর জন্য তার একমাত্র সারি, ইত্যাদি)।
৩. উদাহরণ ২: SUM-এর সাথে HAVING
ধরুন, আমরা চাই শুধু সেই নামগুলো যাদের মোট বেতন ২৫,০০০-এর বেশি।
sql
SELECT NAME, SUM(SALARY) AS TOTAL_SALARY FROM COMPANY GROUP BY NAME HAVING SUM(SALARY) > 25000;
ফলাফল:
text
NAME TOTAL_SALARY ---------- ------------- David 85000.0 Mark 65000.0 Paul 40000.0 Kim 45000.0
James (20000) এবং Teddy (20000) বাদ পড়েছে। Allen (15000) বাদ পড়েছে।
৪. উদাহরণ ৩: AVG, MAX, MIN-এর সাথে HAVING
যেসব নামের কর্মীদের গড় বয়স ২৫-এর বেশি:
sql
SELECT NAME, AVG(AGE) AS AVG_AGE FROM COMPANY GROUP BY NAME HAVING AVG(AGE) > 25;
এটি James (37.67), David (27), Paul (28) ইত্যাদি দেখাবে।
৫. WHERE ও HAVING একসাথে
একটি পূর্ণাঙ্গ কোয়েরি: প্রথমে WHERE দিয়ে নির্দিষ্ট সারি বাছাই, তারপর গ্রুপ, তারপর HAVING দিয়ে গ্রুপ ফিল্টার, শেষে ORDER BY।
উদাহরণ: যাদের বেতন ১০,০০০-এর বেশি, সেই সারিগুলোকে NAME দিয়ে গ্রুপ করি, এবং শুধু সেই নামগুলো রাখি যাদের মোট বেতন ৩০,০০০-এর বেশি, এবং বেতনের যোগফল অনুযায়ী বড় থেকে ছোট সাজাই।
sql
SELECT NAME, SUM(SALARY) AS TOTAL FROM COMPANY WHERE SALARY > 10000 GROUP BY NAME HAVING SUM(SALARY) > 30000 ORDER BY TOTAL DESC;
এখানে WHERE প্রথমে বেতন > 10000 ফিল্টার করে (James-এর 10000 বাদ যায়, Paul-এর দুটো 20000 থাকে, Allen 15000, Teddy 20000 ইত্যাদি)। তারপর গ্রুপ করা হয়। James গ্রুপে SALARY>10000 কোনো সারি না থাকায় James গ্রুপই তৈরি হবে না। Paul-এর দুটি 20000 যোগ = 40000, David 85000, Mark 65000, Kim 45000 – এদের TOTAL > 30000, তাই এরা আসবে। Teddy 20000 এবং Allen 15000 বাদ পড়বে HAVING-এ। সাজানো হবে TOTAL DESC-এ: David (85000), Mark (65000), Kim (45000), Paul (40000)।
৬. WHERE বনাম HAVING সংক্ষেপে
| বৈশিষ্ট্য | WHERE | HAVING |
|---|---|---|
| প্রয়োগের সময় | গ্রুপ করার আগে (সারি-ভিত্তিক) | গ্রুপ করার পরে (গ্রুপ-ভিত্তিক) |
| অ্যাগ্রিগেট ফাংশন | ব্যবহার করা যায় না | ব্যবহার করা যায় (COUNT, SUM ইত্যাদি) |
| ব্যবহার | সাধারণ সারি ফিল্টার | গ্রুপের ফলাফল ফিল্টার |
একই কোয়েরিতে উভয়ই থাকতে পারে – WHERE আগে, HAVING পরে।
৭. ডিরেক্টরি স্ট্রাকচার ও প্যাকেজ
আমাদের অনুশীলনের জন্য শুধুমাত্র company.db ফাইলটি দরকার। কোনো অতিরিক্ত প্যাকেজ ইন্সটল করতে হয় না। HAVING সম্পূর্ণরূপে SQLite-এর মৌলিক SQL সমর্থনের অংশ।
৮. গুরুত্বপূর্ণ টিপস
- অ্যাগ্রিগেট ছাড়া HAVING অর্থহীন: HAVING-এ সাধারণ কলামের শর্ত দেওয়া যায়, কিন্তু সেটা WHERE-এই দেওয়া উচিত। HAVING মূলত অ্যাগ্রিগেটের জন্যই তৈরি।
- HAVING-এ অ্যালিয়াস: আপনি SELECT-এ যে অ্যালিয়াস দেন, HAVING-এ সরাসরি সেই অ্যালিয়াস ব্যবহার করতে পারেন (SQLite সাপোর্ট করে)।sqlSELECT NAME, SUM(SALARY) AS TOTAL FROM COMPANY GROUP BY NAME HAVING TOTAL > 25000;
- NULL গ্রুপ: GROUP BY NULL মানগুলোকে একত্রে একটি গ্রুপ হিসেবে গণ্য করে। HAVING-এ
HAVING COUNT(*) > 1ইত্যাদি দিয়ে সেই গ্রুপ ফিল্টার করা যায়। - পারফরম্যান্স: HAVING-এর শর্ত যতটা সম্ভব WHERE-এ সরিয়ে নিন, কারণ WHERE আগে কম সারি রাখলে গ্রুপিং দ্রুত হবে। তবে অ্যাগ্রিগেট কন্ডিশন HAVING-এই রাখতে হবে।
৯. সম্পূর্ণ অনুশীলন সেশন
টার্মিনালে নিচের কমান্ডগুলো চালিয়ে দক্ষতা বাড়ান:
sql
.headers on .mode column -- গ্রুপে নামের সংখ্যা 2 বা তার বেশি, তাদের গড় বেতন SELECT NAME, COUNT(*) AS CNT, AVG(SALARY) AS AVG_SAL FROM COMPANY GROUP BY NAME HAVING CNT >= 2; -- শহরভিত্তিক কর্মী সংখ্যা, যেখানে কমপক্ষে 2 জন SELECT ADDRESS, COUNT(*) AS EMPLOYEES FROM COMPANY GROUP BY ADDRESS HAVING COUNT(*) >= 2; -- বয়স 25-এর বেশি, নাম অনুযায়ী মোট বেতন, যেখানে মোট 30000-এর বেশি SELECT NAME, SUM(SALARY) AS TOTAL FROM COMPANY WHERE AGE > 25 GROUP BY NAME HAVING SUM(SALARY) > 30000;
পরবর্তী পর্বে
আমরা SQLite DISTINCT ক্লজ শিখব – কিভাবে ডুপ্লিকেট সারি বাদ দিয়ে অনন্য মান পাওয়া যায়।
লেখকের কথা: HAVING ক্লজ জটিল ডেটা বিশ্লেষণে অপরিহার্য। বাংলায় সম্পূর্ণ SQLite সিরিজটি চালিয়ে নিতে আপনার উৎসাহই বড় শক্তি। পোস্টটি ভালো লাগলে শেয়ার করুন, এবং সব আপডেট পেতে ToLearnTeam.com ব্লগে সাবস্ক্রাইব করতে ভুলবেন না। আপনার প্রশ্ন বা মতামত কমেন্টে জানান – আমরা উত্তর দিতে মুখিয়ে আছি!
SQLite Full Course
- SQLite পরিচিতি ও ইন্সটলেশন গাইড – বাংলায় প্রথম ডাটাবেস তৈরি
- SQLite কী এবং কেন শিখবেন? সম্পূর্ণ ওভারভিউ ও কমান্ড ক্লাসিফিকেশন (DDL, DML, DQL)
- SQLite ইন্সটলেশন গাইড – Windows, Linux ও Mac-এ ধাপে ধাপে সেটআপ
- SQLite ডট কমান্ড (Dot Commands) – সম্পূর্ণ গাইড, আউটপুট ফরম্যাট ও sqlite_master
- SQLite সিনট্যাক্স (Syntax) – কেস সেনসিটিভিটি, কমেন্ট, সব কমান্ডের গঠন ও উদাহরণ
- SQLite ডেটা টাইপ (Data Type) – স্টোরেজ ক্লাস, টাইপ অ্যাফিনিটি, বুলিয়ান ও ডেট-টাইম
- SQLite ডেটাবেস তৈরি (Create Database) – ধাপে ধাপে গাইড, .dump এক্সপোর্ট ও রিস্টোর
- SQLite ATTACH DATABASE – একাধিক ডাটাবেস সংযুক্ত করার সম্পূর্ণ গাইড (বাংলা)
- SQLite DETACH DATABASE – সংযুক্ত ডাটাবেস বিচ্ছিন্ন করার পূর্ণাঙ্গ গাইড
- SQLite CREATE TABLE স্টেটমেন্ট – টেবিল তৈরি করার পূর্ণাঙ্গ গাইড (বাংলা)
- SQLite DROP TABLE স্টেটমেন্ট – টেবিল ডিলিট করার সম্পূর্ণ গাইড (বাংলা)
- SQLite INSERT INTO স্টেটমেন্ট – ডেটা ইনসার্ট করার সম্পূর্ণ গাইড (বাংলা)
- SQLite SELECT স্টেটমেন্ট – টেবিল থেকে ডেটা ফেচ করার সম্পূর্ণ গাইড (বাংলা)
- SQLite অপারেটর (Operators) – Arithmetic, Comparison, Logical, Bitwise (বাংলা টিউটোরিয়াল)
- SQLite এক্সপ্রেশন (Expressions) – বুলিয়ান, নিউমেরিক ও ডেট এক্সপ্রেশন (বাংলা টিউটোরিয়াল)
- SQLite AND ও OR অপারেটর – একাধিক শর্ত দিয়ে ডেটা ফিল্টার (বাংলা টিউটোরিয়াল)
- SQLite UPDATE Query – টেবিলের ডেটা পরিবর্তন করার সম্পূর্ণ গাইড (বাংলা)
- SQLite DELETE Query – টেবিল থেকে ডেটা মুছে ফেলার সম্পূর্ণ গাইড (বাংলা)
- SQLite LIKE Operator – প্যাটার্ন মিলিয়ে ডেটা খোঁজার সম্পূর্ণ গাইড (বাংলা)
- SQLite GLOB Operator – কেস-সেনসিটিভ প্যাটার্ন মিলিয়ে ডেটা খোঁজা (বাংলা)
- SQLite LIMIT Clause – ডেটা সীমিত করে আনার সম্পূর্ণ গাইড (বাংলা)
- SQLite ORDER BY ক্লজ – ডেটা সাজানোর সম্পূর্ণ গাইড (বাংলা)
- SQLite GROUP BY ক্লজ – ডেটা গ্রুপ করে বিশ্লেষণ করার সম্পূর্ণ গাইড (বাংলা)
- SQLite HAVING ক্লজ – গ্রুপের উপর শর্ত দিয়ে ডেটা ফিল্টার (বাংলা টিউটোরিয়াল)
- SQLite DISTINCT Keyword – ডুপ্লিকেট ডেটা বাদ দিয়ে ইউনিক ভ্যালু পাওয়া (বাংলা)
- SQLite PRAGMA কমান্ড – ডাটাবেস কনফিগারেশন ও সেটিংস (বাংলা টিউটোরিয়াল)
- SQLite Constraints – NOT NULL, DEFAULT, UNIQUE, PRIMARY KEY, CHECK (বাংলা টিউটোরিয়াল)
- SQLite JOINS – CROSS JOIN, INNER JOIN, LEFT OUTER JOIN (বাংলা টিউটোরিয়াল)
- SQLite UNION ও UNION ALL – একাধিক SELECT একত্রিত করার সম্পূর্ণ গাইড (বাংলা)
- SQLite NULL Values – NULL কী, IS NULL, IS NOT NULL (বাংলা টিউটোরিয়াল)
- SQLite ALIAS Syntax – টেবিল ও কলামের অস্থায়ী নাম (বাংলা টিউটোরিয়াল)
- SQLite Triggers – স্বয়ংক্রিয় ট্রিগার তৈরি, ব্যবহার ও মুছে ফেলা (বাংলা টিউটোরিয়াল)
- SQLite Indexes – CREATE, UNIQUE, COMPOSITE, DROP INDEX (বাংলা টিউটোরিয়াল)
- SQLite INDEXED BY Clause – নির্দিষ্ট ইনডেক্স ব্যবহার ও NOT INDEXED (বাংলা)
- SQLite ALTER TABLE Command – টেবিলের গঠন পরিবর্তন (বাংলা টিউটোরিয়াল)
- SQLite TRUNCATE TABLE Command – DELETE ও DROP দিয়ে টেবিল খালি করা (বাংলা)
- SQLite Views – ভার্চুয়াল টেবিল তৈরি, ব্যবহার ও ডিলিট (বাংলা টিউটোরিয়াল)
- SQLite Transactions – BEGIN, COMMIT, ROLLBACK ও ACID প্রপার্টি (বাংলা)
- SQLite Subqueries – SELECT, INSERT, UPDATE, DELETE-এ সাবকোয়েরি ব্যবহার (বাংলা টিউটোরিয়াল)
- SQLite AUTOINCREMENT – স্বয়ংক্রিয়ভাবে প্রাইমারি কি মান বৃদ্ধি (বাংলা টিউটোরিয়াল)
- SQLite SQL Injection – কী, কেন বিপজ্জনক ও প্রতিরোধের উপায় (বাংলা টিউটোরিয়াল)
- SQLite EXPLAIN ও EXPLAIN QUERY PLAN – কোয়েরি বিশ্লেষণ (বাংলা টিউটোরিয়াল)
- SQLite VACUUM – ডাটাবেস পরিষ্কার, স্পেস রিক্লেইম ও অটো-ভ্যাকুয়াম (বাংলা টিউটোরিয়াল)
- SQLite Date & Time ফাংশন – date(), time(), datetime(), julianday(), strftime() (বাংলা টিউটোরিয়াল)
- SQLite Useful Functions – COUNT, MAX, MIN, AVG, SUM, RANDOM, ABS, UPPER, LOWER, LENGTH (বাংলা টিউটোরিয়াল)
- SQLite C/C++ প্রোগ্রামিং – sqlite3_open, exec, close, callback (বাংলা টিউটোরিয়াল)
- SQLite PHP প্রোগ্রামিং – PHP-তে SQLite ডাটাবেস ব্যবহার (বাংলা টিউটোরিয়াল)
- SQLite Perl প্রোগ্রামিং – DBI/DBD::SQLite দিয়ে ডাটাবেস অপারেশন (বাংলা)
- SQLite Python প্রোগ্রামিং – sqlite3 মডিউল দিয়ে CRUD অপারেশন (বাংলা টিউটোরিয়াল)
- SQLite Quick Guide – বাংলায় পূর্ণাঙ্গ সংক্ষিপ্ত টিউটোরিয়াল (শেখার সহজ গাইড)
