Bună, Habr!
De mai bine de 3 ani, predau SQL în diferite centre de formare, iar una dintre observațiile mele este că studenții învață și înțeleg SQL mai bine atunci când li se pune o sarcină, în loc să li se vorbească doar despre posibilități și fundamente teoretice.
În acest articol, voi împărtăși cu voi lista mea de sarcini pe care le dau studenților ca temă de lucru și asupra cărora organizăm diverse sesiuni de brainstorming, ceea ce duce la o înțelegere profundă și clară a SQL.

SQL (ˈɛsˈkjuˈɛl; engl. structured query language — „limbaj de interogare structurat”) este un limbaj de programare declarativ, utilizat pentru crearea, modificarea și gestionarea datelor într-o bază de date relațională, administrată de un sistem de gestionare a bazelor de date corespunzător.
Puteți citi despre SQL din diferite surse. .
Acest articol nu are ca scop să vă învețe SQL de la zero.
Așadar, să începem.
Vom folosi binecunoscutul în Oracle cu tabelele sale ():

Menționez că vom analiza doar sarcinile pe SELECT. Nu sunt incluse sarcini pe DML și DDL.
Sarcini
Restricționarea și sortarea datelor
Tabelul Employees. Obțineți o listă cu informații despre toți angajații
Soluție
SELECT * FROM employees
Tabelul Employees. Obțineți o listă cu toți angajații cu numele ‘David’
Soluție
SELECT *
FROM employees
WHERE first_name = 'David';
Tabelul Employees. Obțineți o listă cu toți angajații cu job_id egal cu ‘IT_PROG’
Soluție
SELECT *
FROM employees
WHERE job_id = 'IT_PROG'
Tabelul Employees. Obțineți o listă cu toți angajații din departamentul 50 (department_id) cu salariul (salary) mai mare de 4000
Soluție
SELECT *
FROM employees
WHERE department_id = 50 AND salary > 4000;
Tabelul Employees. Obțineți o listă cu toți angajații din departamentele 20 și 30 (department_id)
Soluție
SELECT *
FROM employees
WHERE department_id = 20 OR department_id = 30;
Tabelul Employees. Obțineți o listă cu toți angajații ale căror nume se termină cu litera ‘a’
Soluție
SELECT *
FROM employees
WHERE first_name LIKE '%a';
Tabelul Employees. Obțineți o listă cu toți angajații din departamentele 50 și 80 (department_id) care au bonus (valoarea din coloana commission_pct nu este goală)
Soluție
SELECT *
FROM employees
WHERE (department_id = 50 OR department_id = 80)
AND commission_pct IS NOT NULL;
Tabelul Employees. Obțineți o listă cu toți angajații ale căror nume conțin cel puțin 2 litere ‘n’
Soluție
SELECT *
FROM employees
WHERE first_name LIKE '%n%n%';
Tabelul Employees. Obțineți o listă cu toți angajații ale căror nume au mai mult de 4 litere
Soluție
SELECT *
FROM employees
WHERE first_name LIKE '%_____%';
Tabelul Employees. Obțineți o listă cu toți angajații ale căror salarii se află între 8000 și 9000 (inclusiv)
Soluție
SELECT *
FROM employees
WHERE salary BETWEEN 8000 AND 9000;
Tabela Employees. Obține lista tuturor angajaților care au simbolul '%' în nume
Soluție
SELECT *
FROM employees
WHERE first_name LIKE '%%%' ESCAPE '';
Tabela Employees. Obține lista tuturor ID-urilor managerilor
Soluție
SELECT DISTINCT manager_id
FROM employees
WHERE manager_id IS NOT NULL;
Tabela Employees. Obține lista angajaților cu pozițiile lor în formatul: Donald(sh_clerk)
Soluție
SELECT first_name || '(' || LOWER (job_id) || ')' employee FROM employees;
Folosind funcții cu un singur rând pentru a personaliza ieșirea
Tabela Employees. Obține lista tuturor angajaților a căror lungime a numelui depășește 10 litere
Soluție
SELECT *
FROM employees
WHERE LENGTH (first_name) > 10;
Tabela Employees. Obține lista tuturor angajaților care au litera 'b' în nume (fără a lua în considerare majusculele și minusculele)
Soluție
SELECT *
FROM employees
WHERE INSTR (LOWER (first_name), 'b') > 0;
Tabela Employees. Obține lista tuturor angajaților care au în nume cel puțin două litere 'a'
Soluție
SELECT *
FROM employees
WHERE INSTR (LOWER (first_name),'a',1,2) > 0;
Tabela Employees. Obține lista tuturor angajaților ale căror salarii sunt multipli de 1000
Soluție
SELECT *
FROM employees
WHERE MOD (salary, 1000) = 0;
Tabela Employees. Obține primul număr de 3 cifre din numărul de telefon al angajatului, dacă numărul său este în formatul XXX.XXX.XXXX
Soluție
SELECT phone_number, SUBSTR (phone_number, 1, 3) new_phone_number
FROM employees
WHERE phone_number LIKE '___.___.____';
Tabela Departments. Obține primul cuvânt din numele departamentului pentru cei care au mai multe cuvinte în denumire
Soluție
SELECT department_name,
SUBSTR (department_name, 1, INSTR (department_name, ' ')-1)
first_word
FROM departments
WHERE INSTR (department_name, ' ') > 0;
Tabela Employees. Obține numele angajaților fără prima și ultima literă din nume
Soluție
SELECT first_name, SUBSTR (first_name, 2, LENGTH (first_name) - 2) new_name
FROM employees;
Tabela Employees. Obține lista tuturor angajaților a căror ultimă literă din nume este 'm' și lungimea numelui este mai mare de 5
Soluție
SELECT *
FROM employees
WHERE SUBSTR (first_name, -1) = 'm' AND LENGTH(first_name) > 5;
Tabela Dual. Obține data următoarei zile de vineri
Soluție
SELECT NEXT_DAY (SYSDATE, 'FRIDAY') next_friday FROM DUAL;
Tabela Employees. Obține lista tuturor angajaților care lucrează în companie de mai bine de 17 ani
Soluție
SELECT *
FROM employees
WHERE MONTHS_BETWEEN (SYSDATE, hire_date) / 12 > 17;
Tabela Employees. Obține lista tuturor angajaților ale căror ultime cifre ale numărului de telefon sunt impari și constau din 3 cifre separate prin punct
Soluție
SELECT *
FROM employees
WHERE MOD (SUBSTR (phone_number, -1), 2) != 0
AND INSTR (phone_number,'.',1,3) = 0;
Tabela Employees. Obține lista tuturor angajaților a căror valoare job_id după semnul '_' are cel puțin 3 caractere, dar acestă valoare după '_' nu este 'CLERK'
Soluție
SELECT *
FROM employees
WHERE LENGTH (SUBSTR (job_id, INSTR (job_id, '_') + 1)) > 3
AND SUBSTR (job_id, INSTR (job_id, '_') + 1) != 'CLERK';
Tabelă Employees. Obțineți lista tuturor angajaților înlocuind în valoarea PHONE_NUMBER toate ‘.’ cu ‘-‘
Soluție
SELECT phone_number, REPLACE (phone_number, '.', '-') new_phone_number
FROM employees;
Folosind funcții de conversie și expresii condiționale
Tabelă Employees. Obțineți lista tuturor angajaților care au început munca în prima zi a oricărei luni
Soluție
SELECT *
FROM employees
WHERE TO_CHAR (hire_date, 'DD') = '01';
Tabelă Employees. Obțineți lista tuturor angajaților care au început munca în anul 2008
Soluție
SELECT *
FROM employees
WHERE TO_CHAR (hire_date, 'YYYY') = '2008';
Tabelă DUAL. Afișați data de mâine în format: Mâine este a doua zi din ianuarie
Soluție
SELECT TO_CHAR (SYSDATE, 'fm""Mâine este ""Ddspth ""ziua din"" Lunar') info
FROM DUAL;
Tabelă Employees. Obțineți lista tuturor angajaților și data începerii muncii fiecăruia în format: 21st of June, 2007
Soluție
SELECT first_name, TO_CHAR (hire_date, 'fmddth ""din"" Lunar, YYYY') hire_date
FROM employees;
Tabelă Employees. Obțineți lista angajaților cu salarii mărite cu 20%. Salariul să fie afișat cu semnul dolarului
Soluție
SELECT first_name, TO_CHAR (salary + salary * 0.20, 'fm$999,999.00') new_salary
FROM employees;
Tabelă Employees. Obțineți lista tuturor angajaților care au început munca în februarie 2007.
Soluție
SELECT *
FROM employees
WHERE hire_date BETWEEN TO_DATE ('01.02.2007', 'DD.MM.YYYY')
AND LAST_DAY (TO_DATE ('01.02.2007', 'DD.MM.YYYY'));
SELECT *
FROM employees
WHERE to_char(hire_date,'MM.YYYY') = '02.2007';
Tabelă DUAL. Afișați data curentă, + secundă, + minut, + oră, + zi, + lună, + an
Soluție
SELECT SYSDATE now,
SYSDATE + 1 / (24 * 60 * 60) plus_second,
SYSDATE + 1 / (24 * 60) plus_minute,
SYSDATE + 1 / 24 plus_hour,
SYSDATE + 1 plus_day,
ADD_MONTHS (SYSDATE, 1) plus_month,
ADD_MONTHS (SYSDATE, 12) plus_year
FROM DUAL;
Tabelă Employees. Obțineți lista tuturor angajaților cu salariile complete (salary + commission_pct(%)) în format: $24,000.00
Soluție
SELECT first_name, salary, TO_CHAR (salary + salary * NVL (commission_pct, 0), 'fm$99,999.00') full_salary
FROM employees;
Tabelă Employees. Obțineți lista tuturor angajaților și informațiile despre bonusurile la salariu (Yes/No)
Soluție
SELECT first_name, commission_pct, NVL2 (commission_pct, 'Yes', 'No') has_bonus
FROM employees;
Tabelă Employees. Obțineți nivelul salarial al fiecărui angajat: Mai puțin de 5000 este considerat nivel scăzut, 5000 sau mai mult și mai puțin de 10000 este considerat nivel normal, 10000 sau mai mult este considerat nivel ridicat
Soluție
SELECT first_name,
salary,
CASE
WHEN salary = 5000 AND salary < 10000 THEN 'Normal'
ELSE 'High'
END salary_level
FROM employees;
Tabelă Countries. Pentru fiecare țară arătați regiunea în care se află: 1-Europe, 2-America, 3-Asia, 4-Africa (fără Join)
Soluție
SELECT country_name country,
DECODE (region_id,
1, 'Europa',
2, 'America',
3, 'Asia',
4, 'Africa',
'Necunoscut')
region
FROM countries;
SELECT country_name
country,
CASE region_id
WHEN 1 THEN 'Europa'
WHEN 2 THEN 'America'
WHEN 3 THEN 'Asia'
WHEN 4 THEN 'Africa'
ELSE 'Necunoscut'
END
region
FROM countries;
Raportarea datelor agregate folosind funcțiile de grup
Tabelul Employees. Obțineți un raport pe department_id cu salariul minim și maxim, cu data de angajare timpurie și târzie și cu numărul de angajați. Să fie sortat după numărul de angajați (în ordine descrescătoare)
Soluție
SELECT department_id,
MIN (salary) min_salary,
MAX (salary) max_salary,
MIN (hire_date) min_hire_date,
MAX (hire_date) max_hire_date,
COUNT (*) count
FROM employees
GROUP BY department_id
order by count(*) desc;
Tabelul Employees. Câți angajați au numele care începe cu aceeași literă? Să fie sortat după număr. Să fie afișate doar cele unde numărul este mai mare de 1
Soluție
SELECT SUBSTR (first_name, 1, 1) first_char, COUNT (*)
FROM employees
GROUP BY SUBSTR (first_name, 1, 1)
HAVING COUNT (*) > 1
ORDER BY 2 DESC;
Tabelul Employees. Câți angajați lucrează în același departament și au salarii identice?
Soluție
SELECT department_id, salary, COUNT (*)
FROM employees
GROUP BY department_id, salary
HAVING COUNT (*) > 1;
Tabelul Employees. Obțineți un raport cu câți angajați au fost angajați în fiecare zi a săptămânii. Să fie sortat după număr
Soluție
SELECT TO_CHAR (hire_date, 'Day') day, COUNT (*)
FROM employees
GROUP BY TO_CHAR (hire_date, 'Day')
ORDER BY 2 DESC;
Tabelul Employees. Obțineți un raport cu câți angajați au fost angajați de-a lungul anilor. Să fie sortat după număr
Soluție
SELECT TO_CHAR (hire_date, 'YYYY') year, COUNT (*)
FROM employees
GROUP BY TO_CHAR (hire_date, 'YYYY');
Tabelul Employees. Obțineți numărul de departamente în care există angajați
Soluție
SELECT COUNT (COUNT (*)) department_count
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id;
Tabelul Employees. Obțineți lista department_id unde lucrează mai mult de 30 de angajați
Soluție
SELECT department_id
FROM employees
GROUP BY department_id
HAVING COUNT (*) > 30;
Tabelul Employees. Obțineți lista department_id și salariul mediu rotunjit al angajaților din fiecare departament.
Soluție
SELECT department_id, ROUND (AVG (salary)) avg_salary
FROM employees
GROUP BY department_id;
Tabelul Countries. Obțineți lista region_id suma tuturor literelor din toate country_name unde este mai mare de 60
Soluție
SELECT region_id
FROM countries
GROUP BY region_id
HAVING SUM (LENGTH (country_name)) > 60;
Tabelul Employees. Obțineți lista department_id în care lucrează angajați cu mai multe job_id-uri (>1)
Soluție
SELECT department_id
FROM employees
GROUP BY department_id
HAVING COUNT (DISTINCT job_id) > 1;
Tabelul Employees. Obține lista manager_id-urilor care au mai mult de 5 subordonați și suma tuturor salariilor subordonaților săi depășește 50000
Soluție
SELECT manager_id
FROM employees
GROUP BY manager_id
HAVING COUNT (*) > 5 AND SUM (salary) > 50000;
Tabelul Employees. Obține lista manager_id-urilor ale căror salarii medii ale tuturor subordonaților se află între 6000 și 9000 și care nu primesc bonusuri (commission_pct este null)
Soluție
SELECT manager_id, AVG (salary) avg_salary
FROM employees
WHERE commission_pct IS NULL
GROUP BY manager_id
HAVING AVG (salary) BETWEEN 6000 AND 9000;
Tabelul Employees. Obține salariul maxim dintre toți angajații cu job_id care se termină cu cuvântul ‘CLERK’
Soluție
SELECT MAX (salary) max_salary
FROM employees
WHERE job_id LIKE '%CLERK';
SELECT MAX (salary) max_salary
FROM employees
WHERE SUBSTR (job_id, -5) = 'CLERK';
Tabelul Employees. Obține salariul maxim dintre toate salariile medii pe departamente
Soluție
SELECT MAX (AVG (salary))
FROM employees
GROUP BY department_id;
Tabelul Employees. Obține numărul angajaților cu același număr de litere în nume. Arată doar cei cu lungimea numelui mai mare de 5 și numărul angajaților cu acest nume mai mare de 20. Sortează după lungimea numelui
Soluție
SELECT LENGTH (first_name), COUNT (*)
FROM employees
GROUP BY LENGTH (first_name)
HAVING LENGTH (first_name) > 5 AND COUNT (*) > 20
ORDER BY LENGTH (first_name);
SELECT LENGTH (first_name), COUNT (*)
FROM employees
WHERE LENGTH (first_name) > 5
GROUP BY LENGTH (first_name)
HAVING COUNT (*) > 20
ORDER BY LENGTH (first_name);
Afișarea datelor din mai multe tabele folosind Joins
Tabelul Employees, Departaments, Locations, Countries, Regions. Obține lista regiunilor și numărul angajaților din fiecare regiune
Soluție
SELECT region_name, COUNT (*)
FROM employees e
JOIN departments d ON (e.department_id = d.department_id)
JOIN locations l ON (d.location_id = l.location_id)
JOIN countries c ON (l.country_id = c.country_id)
JOIN regions r ON (c.region_id = r.region_id)
GROUP BY region_name;
Tabelul Employees, Departaments, Locations, Countries, Regions. Obține informații detaliate despre fiecare angajat:
First_name, Last_name, Departament, Job, Street, Country, Region
Soluție
SELECT First_name,
Last_name,
Department_name,
Job_id,
street_address,
Country_name,
Region_name
FROM employees e
JOIN departments d ON (e.department_id = d.department_id)
JOIN locations l ON (d.location_id = l.location_id)
JOIN countries c ON (l.country_id = c.country_id)
JOIN regions r ON (c.region_id = r.region_id);
Tabelul Employees. Arată toți managerii care au în subordine mai mult de 6 angajați
Soluție
SELECT man.first_name, COUNT (*)
FROM employees emp JOIN employees man ON (emp.manager_id = man.employee_id)
GROUP BY man.first_name
HAVING COUNT (*) > 6;
Tabelul Employees. Arată toți angajații care nu au pe nimeni în subordine
Soluție
SELECT emp.first_name
FROM employees emp
LEFT JOIN employees man ON (emp.manager_id = man.employee_id)
WHERE man.FIRST_NAME IS NULL;
SELECT first_name
FROM employees
WHERE manager_id IS NULL;
Tabelul Employees, Job_history. Tabelul Employee conține toți angajații. Tabelul Job_history conține angajații care au părăsit compania. Obțineți un raport despre toți angajații și statutul lor în companie (Lucrează sau a părăsit compania cu data plecării)
Exemplu:
first_name | status
Jennifer | A părăsit compania pe 31 decembrie 2006
Clara | Lucrează în prezent
Soluție
SELECT first_name,
NVL2 (
end_date,
TO_CHAR (end_date, 'fm""A părăsit compania pe"" DD ""din"" Luna, YYYY'),
'Lucrează în prezent')
status
FROM employees e LEFT JOIN job_history j ON (e.employee_id = j.employee_id);
Tabelul Employees, Departaments, Locations, Countries, Regions. Obțineți lista angajaților care locuiesc în Europa (region_name)
Soluție
SELECT first_name
FROM employees
JOIN departments USING (department_id)
JOIN locations USING (location_id)
JOIN countries USING (country_id)
JOIN regions USING (region_id)
WHERE region_name = 'Europe';
SELECT first_name
FROM employees e
JOIN departments d ON (e.department_id = d.department_id)
JOIN locations l ON (d.location_id = l.location_id)
JOIN countries c ON (l.country_id = c.country_id)
JOIN regions r ON (c.region_id = r.region_id)
WHERE region_name = 'Europe';
Tabelul Employees, Departaments. Afișați toate departamentele în care lucrează mai mult de 30 de angajați
Soluție
SELECT department_name, COUNT (*)
FROM employees e JOIN departments d ON (e.department_id = d.department_id)
GROUP BY department_name
HAVING COUNT (*) > 30;
Tabelul Employees, Departaments. Afișați toți angajații care nu fac parte din niciun departament
Soluție
SELECT first_name
FROM employees e
LEFT JOIN departments d ON (e.department_id = d.department_id)
WHERE d.department_name IS NULL;
SELECT first_name
FROM employees
WHERE department_id IS NULL;
Tabelul Employees, Departaments. Afișați toate departamentele în care nu există niciun angajat
Soluție
SELECT department_name
FROM employees e
RIGHT JOIN departments d ON (e.department_id = d.department_id)
WHERE first_name IS NULL;
Tabelul Employees. Afișați toți angajații care nu au pe nimeni în subordine
Soluție
SELECT man.first_name
FROM employees emp
RIGHT JOIN employees man ON (emp.manager_id = man.employee_id)
WHERE emp.FIRST_NAME IS NULL;
Tabelul Employees, Jobs, Departaments. Afișați angajații în format: First_name, Job_title, Department_name.
Exemplu:
First_name | Job_title | Department_name
Donald | Shipping | Clerk Shipping
Soluție
SELECT first_name, job_title, department_name
FROM employees e
JOIN jobs j ON (e.job_id = j.job_id)
JOIN departments d ON (d.department_id = e.department_id);
Tabelul Employees. Obțineți lista angajaților ale căror manageri s-au angajat în 2005, dar acești angajați s-au angajat înainte de 2005
Soluție
SELECT emp.*
FROM employees emp JOIN employees man ON (emp.manager_id = man.employee_id)
WHERE TO_CHAR (man.hire_date, 'YYYY') = '2005'
AND emp.hire_date < TO_DATE ('01012005', 'DDMMYYYY');
Tabelul Angajaților. Obține lista angajaților ai căror manageri au fost angajați în luna ianuarie a oricărui an și lungimea titulului de muncă al acestor angajați este mai mare de 15 caractere.
Soluție
SELECT emp.*
FROM employees emp
JOIN employees man ON (emp.manager_id = man.employee_id)
JOIN jobs j ON (emp.job_id = j.job_id)
WHERE TO_CHAR (man.hire_date, 'MM') = '01' AND LENGTH (j.job_title) > 15;
Folosind Subinterogări pentru a Rezolva Interogările
Tabelul Angajaților. Obține lista angajaților cu cel mai lung nume.
Soluție
SELECT *
FROM employees
WHERE LENGTH (first_name) =
(SELECT MAX (LENGTH (first_name)) FROM employees);
Tabelul Angajaților. Obține lista angajaților cu un salariu mai mare decât salariul mediu al tuturor angajaților.
Soluție
SELECT *
FROM employees
WHERE salary > (SELECT AVG (salary) FROM employees);
Tabelul Angajaților, Departamentelor, Locațiilor. Obține orașul în care angajații câștigă în total cel mai puțin.
Soluție
SELECT city
FROM employees e
JOIN departments d ON (e.department_id = d.department_id)
JOIN locations l ON (d.location_id = l.location_id)
GROUP BY city
HAVING SUM (salary) =
( SELECT MIN (SUM (salary))
FROM employees e
JOIN departments d ON (e.department_id = d.department_id)
JOIN locations l ON (d.location_id = l.location_id)
GROUP BY city);
Tabelul Angajaților. Obține lista angajaților care au manageri cu salarii mai mari de 15000.
Soluție
SELECT *
FROM employees
WHERE manager_id IN (SELECT employee_id
FROM employees
WHERE salary > 15000)
Tabelul Employees, Departaments. Afișați toate departamentele în care nu există niciun angajat
Soluție
SELECT *
FROM departments
WHERE department_id NOT IN (SELECT department_id
FROM employees
WHERE department_id IS NOT NULL);
Tabelul Angajaților. Afișează toți angajații care nu sunt manageri.
Soluție
SELECT *
FROM employees
WHERE employee_id NOT IN (SELECT manager_id
FROM employees
WHERE manager_id IS NOT NULL)
Tabelul Employees. Arată toți managerii care au în subordine mai mult de 6 angajați
Soluție
SELECT *
FROM employees e
WHERE (SELECT COUNT (*)
FROM employees
WHERE manager_id = e.employee_id) > 6;
Tabelul Angajaților, Departamentelor. Afișează angajații care lucrează în departamentul IT.
Soluție
SELECT *
FROM employees
WHERE department_id = (SELECT department_id
FROM departments
WHERE department_name = 'IT');
Tabelul Employees, Jobs, Departaments. Afișați angajații în format: First_name, Job_title, Department_name.
Exemplu:
First_name | Job_title | Department_name
Donald | Shipping | Clerk Shipping
Soluție
SELECT first_name,
(SELECT job_title
FROM jobs
WHERE job_id = e.job_id)
job_title,
(SELECT department_name
FROM departments
WHERE department_id = e.department_id)
department_name
FROM employees e;
Tabelul Employees. Obțineți lista angajaților ale căror manageri s-au angajat în 2005, dar acești angajați s-au angajat înainte de 2005
Soluție
SELECT *
FROM employees
WHERE manager_id IN (SELECT employee_id
FROM employees
WHERE TO_CHAR (hire_date, 'YYYY') = '2005')
AND hire_date < TO_DATE ('01012005', 'DDMMYYYY');
Tabelul Angajaților. Obține lista angajaților ai căror manageri au fost angajați în luna ianuarie a oricărui an și lungimea titulului de muncă al acestor angajați este mai mare de 15 caractere.
Soluție
SELECT *
FROM employees e
WHERE manager_id IN (SELECT employee_id
FROM employees
WHERE TO_CHAR (hire_date, 'MM') = '01')
AND (SELECT LENGTH (job_title)
FROM jobs
WHERE job_id = e.job_id) > 15;
Asta e tot pentru acum.
Sper că sarcinile au fost interesante și captivante.
Voi completa acest listă de sarcini pe cât posibil.
De asemenea, sunt deschis la orice observații și sugestii.
P.S.: Dacă cineva are o idee interesantă pentru o problemă pe SELECT, vă rog să scrieți în comentarii, o voi adăuga pe lista.
Mulțumim.
Sursa: habr.com
