Advertisement
Guest User

Untitled

a guest
Feb 25th, 2020
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.74 KB | None | 0 0
  1. /* Extensii SQL pentru procesari analitice in Oracle – ferestre de calcul */
  2.  
  3. /* EXEMPLE */
  4. -- Exemplu cu totaluri cumulative intr-o fereastra cu offset logic
  5. -- Afisati salariul angajatilor precum si suma salariilor de la primul salariu pana la salariul curent,
  6. -- ordonati crescator dupa salariu.
  7. SELECT ename, sal,
  8. SUM(sal) OVER (ORDER BY sal RANGE UNBOUNDED PRECEDING) AS SalCumul_logic
  9. FROM emp;
  10. -- fereastra de calcul este: [salariu_angajat_1, salariu_angajat_2, ..., salariu_angajat_curent]
  11.  
  12. -- Exemplu cu functii analitce de mutare a liniilor intr-o fereastra cu offset logic
  13. -- Afisati salariul angajatilor precum si media salariului angajatului curent si a celor precedenti
  14. -- care au cu cel mult 200 salariul mai mic, ordonati crescator dupa salariu.
  15. SELECT ename, sal,
  16. ROUND(AVG(sal) OVER (ORDER BY sal RANGE 200 PRECEDING), 2) AS avgsal_range
  17. FROM emp;
  18. -- fereastra de calcul este: [sal-200, sal]
  19.  
  20. -- Exemplu de calcul pentru fereastra centrata de date
  21. -- Afisati suma salariilor pentru o fereastra de plus minus 100 la salariu.
  22. SELECT ename, hiredate, sal,
  23. SUM(sal) OVER (ORDER BY sal RANGE BETWEEN 100 PRECEDING AND 100 FOLLOWING) sumsal_center
  24. FROM emp;
  25. -- fereastra de calcul este: [sal-100, sal+100]
  26.  
  27. -- Fereastra de date cu limita mobila (dinamica)
  28. -- Afisati angajatii, salariile si suma salariilor pentru un interval variabil definit de un offset logic
  29. -- dat de o functie utilizator fn
  30.  
  31. -- Functia fn intoarce numarul de angajati pentru un departament
  32. CREATE OR REPLACE FUNCTION fn(dno NUMBER) RETURN NUMBER
  33. IS
  34. Result NUMBER;
  35. res NUMBER;
  36. BEGIN
  37. SELECT COUNT(*) - 1
  38. INTO res
  39. FROM emp
  40. WHERE deptno = dno;
  41.  
  42. Result := res;
  43. RETURN(Result);
  44.  
  45. EXCEPTION
  46. WHEN OTHERS THEN
  47. Result := 0;
  48. END fn;
  49.  
  50. SELECT deptno, ename, sal, fn(deptno),
  51. SUM(sal) OVER (ORDER BY deptno RANGE fn(deptno) PRECEDING) sumsal_dept
  52. FROM emp;
  53.  
  54. -- offset fizic = inregistrari / offset logic = deplasare pe functia cumulativa
  55. -- adica daca sunt mai multi angajati cu acelasi salariu, offset-ul logic considera offset 1,
  56. -- iar cel fizic le ia pe toate in considerare ca si inregistrari separate
  57. -- Exemplu cu totaluri cumulative intr-o fereastra cu offset fizic
  58. -- Afisati salariul angajatilor precum si suma salariilor de la primul salariu pana la salariul curent,
  59. -- ordonati crescator dupa salariu.
  60. SELECT ename, sal,
  61. SUM(sal) OVER (ORDER BY sal ROWS UNBOUNDED PRECEDING) AS SalCumul_fizic
  62. FROM emp;
  63.  
  64. -- Exemplu fereastra centrata pentru offset fizic
  65. -- Afisati angajatii si suma salariilor pentru intervalul fizic de plus minus 3 angajati, ordonati
  66. -- dupa salarii.
  67. SELECT ename, sal,
  68. SUM(sal) OVER (ORDER BY sal ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING) sum3_fizic
  69. FROM emp;
  70.  
  71. -- Fereastra de date cu limita mobila (dinamica) si ofs.amount_sold, fset fizic
  72. -- Afisati angajatii, salariile si suma salariilor pentru un interval variabil, definit de un offset fizic
  73. -- dat de o functie utilizator fn.
  74. SELECT deptno, ename, sal, fn(deptno),
  75. SUM(sal) OVER (ORDER BY deptno ROWS fn(deptno) PRECEDING) sumsal_dept_fizic
  76. FROM emp;
  77. -- Fereastra este definita de la un punct de start pana la linia curenta. Punctul de start este
  78. -- variabil si este dat de numarul de linii fizice precedente calculate de functia utilizator fn.
  79.  
  80. -- Exemplu de folosire a functiilor pentru aflarea valorii minime si maxime intr-o fereastra
  81. -- de calcul
  82. -- Afisati angajatii, salariile, salariul minim si salariul maxim pentru o fereastra de calcul cu offset
  83. -- fizic de plus minus 3 angajati, ordonati dupa salariu.
  84. SELECT ename, sal,
  85. FIRST_VALUE(sal) OVER (ORDER BY sal ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING) firstval,
  86. LAST_VALUE(sal) OVER (ORDER BY SAL ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING) lastval
  87. FROM emp;
  88.  
  89. -- Exemplu de folosire a functiilor LAG/LEAD - pentru accesul la inregistrari din vecinatate
  90. -- Afisati angajatii, salariile, precum si salariile precedentilor 2 si urmatorilor 2 angajati.
  91. SELECT ename, sal,
  92. LAG(sal, 2) OVER (ORDER BY sal) lagval,
  93. LEAD(sal, 2) OVER (ORDER BY sal) leadval
  94. FROM emp;
  95.  
  96. -- Exemplu de utilizare CASE
  97. -- Afisati numele, salariul si gradul salariatului, unde gradul se calculeaza astfel: 1 pentru salariu <
  98. -- 1000, 2 pentru [1000,2000), 3 pentru [2000,3000) si 4 pentru salariu mai mare de 4000. Ordonati
  99. -- dupa grad si salariu.
  100. SELECT ename, sal,
  101. (CASE WHEN e.sal BETWEEN 0 AND 1000 THEN 1
  102. WHEN e.sal BETWEEN 1000 AND 2000 THEN 2
  103. WHEN e.sal BETWEEN 2000 AND 3000 THEN 3
  104. ELSE 4
  105. END) grad
  106. FROM emp e
  107. ORDER BY grad, sal;
  108.  
  109. /* EXERCITII */
  110. -- 1
  111. SELECT ename, hiredate, sal,
  112. SUM(sal) OVER (ORDER BY hiredate RANGE BETWEEN INTERVAL '1' MONTH PRECEDING AND INTERVAL '1' MONTH FOLLOWING) AS sumsal_1_month,
  113. -- pentru ca intervalul e lunar si nu numeric, se foloseste "RANGE BETWEEN INTERVAL x DAY / MONTH / YEAR"
  114. -- offset logic = RANGE
  115. FIRST_VALUE(sal) OVER (ORDER BY hiredate RANGE BETWEEN INTERVAL '6' MONTH PRECEDING AND INTERVAL '6' MONTH FOLLOWING) AS maxsal,
  116. -- valoarea maxima din setul de inregistrari din fereastra = FIRST_VALUE()
  117. LAST_VALUE(sal) OVER (ORDER BY hiredate RANGE BETWEEN INTERVAL '6' MONTH PRECEDING AND INTERVAL '6' MONTH FOLLOWING) AS minsal
  118. -- valoarea minima din setul de inregistrari din fereastra = LAST_VALUE()
  119. FROM emp;
  120.  
  121. -- 2
  122. SELECT deptno, ename, sal,
  123. SUM(sal) OVER(PARTITION BY deptno ORDER BY sal ROWS UNBOUNDED PRECEDING) AS sumsal_dept -- cu offset fizic = ROWS
  124. -- pana la angajatul curent = de la inceput (UNBOUNDED PRECEDING), pana la inregistrarea curent, implicit
  125. FROM emp;
  126.  
  127. SELECT deptno, ename, sal,
  128. SUM(sal) OVER(PARTITION BY deptno ORDER BY sal RANGE UNBOUNDED PRECEDING) AS sumsal_dept -- cu offset logic = RANGE
  129. FROM emp;
  130.  
  131. -- 3
  132. SELECT job, ename, sal, NVL(comm, 0) AS comision, sal + NVL(comm, 0) * sal AS venit_total,
  133. SUM(sal + NVL(comm, 0) * sal) OVER(PARTITION BY job ORDER BY sal ROWS UNBOUNDED PRECEDING) AS sumsal_job -- cu offset fizic = ROWS
  134. FROM emp;
  135.  
  136. SELECT job, ename, sal, NVL(comm, 0) AS comision, sal + NVL(comm, 0) * sal AS venit_total,
  137. SUM(sal + NVL(comm, 0) * sal) OVER(PARTITION BY job ORDER BY sal RANGE UNBOUNDED PRECEDING) AS sumsal_job -- cu offset logic = RANGE
  138. FROM emp;
  139.  
  140. -- 4
  141. CREATE OR REPLACE FUNCTION fn(dno NUMBER) RETURN NUMBER -- functia fn() din laborator
  142. IS
  143. Result NUMBER;
  144. res NUMBER;
  145. BEGIN
  146. SELECT COUNT(*) - 1
  147. INTO res
  148. FROM emp
  149. WHERE deptno = dno;
  150.  
  151. Result := res;
  152. RETURN(Result);
  153.  
  154. EXCEPTION
  155. WHEN OTHERS THEN
  156. Result := 0;
  157. END fn;
  158.  
  159. SELECT deptno, ename, sal, fn(deptno),
  160. SUM(sal) OVER (ORDER BY deptno, sal ROWS BETWEEN CURRENT ROW AND fn(deptno) FOLLOWING) AS sumsal_dept
  161. -- ROWS = offset fizic, nu folosim offset logic pentru ca in enunt se specifica "linia curenta" si nu "valoarea curenta"
  162. -- CURRENT ROW = linia curenta
  163. -- fn(deptno) FOLLOWING = urmatoarele x linii, x = valoarea intoarsa de fn()
  164. FROM emp;
  165.  
  166. -- schimbam situatia pentru partitionare pe job, modificam functia fn()
  167. CREATE OR REPLACE FUNCTION fnjob(v_job emp.job%TYPE) RETURN NUMBER
  168. IS
  169. Result NUMBER;
  170. res NUMBER;
  171. BEGIN
  172. SELECT COUNT(*) - 1
  173. INTO res
  174. FROM emp
  175. WHERE job = v_job;
  176.  
  177. Result := res;
  178. RETURN(Result);
  179.  
  180. EXCEPTION
  181. WHEN OTHERS THEN
  182. Result := 0;
  183. END fnjob;
  184.  
  185. SELECT job, ename, sal, fnjob(job),
  186. SUM(sal) OVER (ORDER BY job, sal ROWS BETWEEN CURRENT ROW AND fnjob(job) FOLLOWING) AS sumsal_job
  187. -- acelasi lucru, doar schimbam deptno cu job
  188. FROM emp;
  189.  
  190. -- 5
  191. SELECT job, ename, sal AS "Salariu curent",
  192. LAG(sal, 1) OVER (PARTITION BY job ORDER BY sal) AS "Salariu anterior",
  193. -- LAG(sal, 1) = valoarea campului "sal" de la "1" inregistrari anterioare inregistrarii curente
  194. LEAD(sal, 1) OVER (PARTITION BY job ORDER BY sal) AS "Salariu urmator"
  195. -- LEAD(sal, 1) = valoarea campului "sal" de la "1" inregistrari posterioare inregistrarii curente
  196. FROM emp;
  197.  
  198. -- 6
  199. SELECT COUNT(empno) AS "Numar total angajati",
  200. SUM(CASE WHEN sal BETWEEN 0 AND 1000 THEN 1 ELSE 0 END) AS "Numar de angajati cu salariul intre 0 si 1000",
  201. SUM(CASE WHEN sal BETWEEN 1000 AND 2000 THEN 1 ELSE 0 END) AS "Numar de angajati cu salariul intre 1000 si 2000",
  202. SUM(CASE WHEN sal BETWEEN 2000 AND 3000 THEN 1 ELSE 0 END) AS "Numar de angajati cu salariul intre 2000 si 3000",
  203. SUM(CASE WHEN sal > 3000 THEN 1 ELSE 0 END) AS "Numar de angajati cu salariul peste 3000"
  204. -- SUM(CASE...) numara cate valori ale campului "sal" se incadreaza in conditiile din CASE WHEN
  205. FROM emp;
  206.  
  207. -- 7
  208. SELECT cust.cust_first_name AS "Client", t.calendar_quarter_number AS "Trimestru",
  209. SUM(s.amount_sold) AS "Suma vanzarilor pe trimestrul curent",
  210. SUM(SUM(s.amount_sold)) OVER (PARTITION BY cust.cust_first_name ORDER BY t.calendar_quarter_number ROWS UNBOUNDED PRECEDING) AS "Suma vanzarilor anterioare"
  211. -- partitionarea se face in functie de client, deoarece clientul este folosit ca referinta in calcule: client X a vandut cantitatea Y in trimestrul Z
  212. -- pentru un client fixat, se ordoneaza inregistrarile dupa trimestru, pentru ca se calculeaza suma vanzarilor pe trimestre
  213. -- ROWS UNBOUNDED PRECEDING = inregistrarile anterioare, de la inceput pana la cea curenta (offset fizic)
  214.  
  215. -- explicatia pentru SUM(SUM(...)): ni se cere sa afisam suma vanzarilor anterioare, pentru fiecare trimestru afisat
  216. -- vanzarile pentru un anumit trimestru se obtin prin a calcula SUM(amount_sold), deci prin a suma tranzactiile efectuate de un anumit client
  217. -- deci, suma vanzarilor anterioare se calculeaza prin adunarea tuturor vanzarilor trimestrelor anterioare, deci prin a suma sumele facute anterior, deci SUM(SUM(...))
  218. FROM sales s JOIN customers cust ON (s.cust_id = cust.cust_id)
  219. JOIN times t ON (s.time_id = t.time_id)
  220. WHERE s.cust_id IN (6380, 6510) -- filtrarile cerute
  221. AND t.calendar_year = 1999
  222. GROUP BY cust.cust_first_name, t.calendar_quarter_number -- grupare si ordonare dupa cum se cere in enunt, pentru ca folosim functia agregat SUM()
  223. ORDER BY cust.cust_first_name, t.calendar_quarter_number;
  224.  
  225. -- 8
  226. SELECT cust.cust_first_name, t.calendar_month_number,
  227. SUM(s.amount_sold) AS "Suma vanzari pe luna",
  228. ROUND(AVG(SUM(s.amount_sold)) OVER (ORDER BY t.calendar_month_number RANGE 2 PRECEDING), 2) AS "Media vanzarilor anterioare"
  229. -- ordonarea se face dupa luna, iar fereastra cuprinde si 2 luni anterioare -> atentie, 2 luni, nu 2 inregistrari!
  230. -- deci se iau in calcul 2 valori anterioare, de aceea folosim offset logic (RANGE) si nu fizic (ROWS)
  231. -- din cauza offset-ului logic, pot exista 4 inregistrari care fac referire la aceleasi 2 luni si sunt luate in calcul ca facand parte din fereastra, desi sunt in numar de 4
  232.  
  233. -- AVG(SUM(...)) -> pentru ca se cere media vanzarilor anterioare, deci media unei sume calculate anterior, de aici AVG(SUM(...))
  234. FROM sales s JOIN customers cust ON (s.cust_id = cust.cust_id)
  235. JOIN times t ON (s.time_id = t.time_id)
  236. WHERE s.cust_id = 6380
  237. AND t.calendar_year = 1999
  238. GROUP BY cust.cust_first_name, t.calendar_month_number
  239. ORDER BY cust.cust_first_name, t.calendar_month_number;
  240.  
  241. -- 9
  242. SELECT cust.cust_first_name, t.time_id,
  243. SUM(s.amount_sold) AS "Suma vanzari pe zi",
  244. ROUND(AVG(SUM(s.amount_sold)) OVER (ORDER BY t.time_id RANGE BETWEEN INTERVAL '1' DAY PRECEDING AND INTERVAL '1' DAY FOLLOWING), 2) AS "Media sumei vanzarilor pe 3 zile"
  245. -- aceeasi logica precum la 7 si la 8 -> media vanzarilor pe o fereastra de 3 zile = medie de suma
  246. -- offset logic din acelasi motiv, zilele se pot repeta in inregistrari si trebuie luate in considerare
  247. -- la ferestre cu date calendaristice se foloseste BETWEEN INTERVAL
  248. FROM sales s JOIN customers cust ON (s.cust_id = cust.cust_id)
  249. JOIN times t ON (s.time_id = t.time_id)
  250. WHERE s.cust_id IN (6380, 6510)
  251. AND t.calendar_year = 1999
  252. AND t.calendar_week_number = 51
  253. GROUP BY cust.cust_first_name, t.time_id
  254. ORDER BY cust.cust_first_name, t.time_id;
  255.  
  256. -- 10
  257. SELECT t.day_number_in_month, SUM(s.amount_sold) AS "Suma vanzarilor pe ziua curenta",
  258. LAG(SUM(s.amount_sold), 1) OVER (ORDER BY SUM(s.amount_sold)) AS "Suma vanzarilor pentru linia anterioara",
  259. -- pentru valoarea de pe linia anterioara, folosim functia LAG() peste setul ordonat de inregistrari dupa suma vanzarilor
  260. LEAD(SUM(s.amount_sold), 1) OVER (ORDER BY SUM(s.amount_sold)) AS "Suma vanzarilor pentru linia urmatoare"
  261. -- pentru valoarea de pe linia anterioara, folosim functia LEAD() peste setul ordonat de inregistrari dupa suma vanzarilor
  262. FROM sales s JOIN times t ON (s.time_id = t.time_id)
  263. WHERE t.calendar_month_desc = '2000-10'
  264. AND t.day_number_in_month BETWEEN 10 AND 15 -- intre 10 si 15 octombrie 2000
  265. GROUP BY t.day_number_in_month;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement