{"id":73148,"date":"2015-09-17T13:47:30","date_gmt":"2015-09-17T13:47:30","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/uncategorized\/introduction-to-analytic-functions-part-2\/"},"modified":"2021-07-14T13:07:20","modified_gmt":"2021-07-14T13:07:20","slug":"introduction-to-analytic-functions-part-2","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/oracle-databases\/introduction-to-analytic-functions-part-2\/","title":{"rendered":"Introduction to Analytic Functions (Part 2)"},"content":{"rendered":"<p>In <a href=\"https:\/\/allthingsoracle.com\/introduction-to-analytic-functions-part-1-2\/\">the first part of this series<\/a> I introduced you to the analytic functions family, outlined its close relationship to aggregate functions, and illustrated my points with a few examples. I demonstrated how, by clever use of the analytic function clauses \u2013 partition by, order by, and the windowing clause \u2013 you could tune your functions to wring even more information from your data.<\/p>\n<p>That&#8217;s great and all that, but there&#8217;s more. In this second article we&#8217;ll look at the various types of analytic functions that are available to us, and we&#8217;ll get our hands dirty with a few more examples.\u00a0 After all, no one ever became a great footballer just by <em>talking<\/em> about football; you&#8217;ve got to go out there and kick a few balls.<\/p>\n<p>So let&#8217;s kick balls.<\/p>\n<h5>Types of Analytic Functions<\/h5>\n<p>You probably don&#8217;t need me to tell you this, but I&#8217;ll tell you anyway; analytic functions have the following syntax:<\/p>\n<pre>FUNCTION (params) OVER ([PARTITION BY &lt;\u2026&gt;] [ORDER BY &lt;\u2026&gt;][window_clause])\r\n<\/pre>\n<p>The clauses \u2013 partition by, order by, and the window clause \u2013 are optional; or, to put it more accurately, <em>might<\/em> be optional. It depends on the function. There are a few dozen different analytic functions in Oracle, and you could split them into two camps \u2013 those for which the ORDER BY clause is mandatory and those where it is optional.<\/p>\n<p>Alternatively, you could split them into those that have equivalent aggregate functions, and those that are purely analytic functions with no aggregate little brother.<\/p>\n<p>Or, perhaps more sensibly, you can group analytic functions by what they do.<\/p>\n<ul>\n<li>There are the Arithmetic analytic functions, such as MAX, MIN, COUNT, AVG, MEDIAN and SUM.<\/li>\n<li>There are the Statistical analytic functions: STDVAR, CORR, COVAR_POP, VARIANCE, CUME_DIST and a few others.<\/li>\n<li>There are the Ranking \u2013 or Positional \u2013 analytic functions: DENSE_RANK, FIRST_VALUE, LAST_VALUE, NTH_VALUE, NTILE, PERCENT_RANK, RANK, ROW_NUMBER and a few others.<\/li>\n<li>There are the Relative analytic functions: LEAD, LAG.<\/li>\n<li>Since 12c, there are the data mining analytic functions: CLUSTER_DETAILS, FEATURE_DETAILS, PREDICTION_DETAILS and a few others.<\/li>\n<li>And finally, there are others that do not fit neatly into a larger group, such as RATIO_TO_REPORT and LISTAGG.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/oracle\/2015\/09\/Analytic-Functions.png\" alt=\"Windowing Functions\" \/><br \/>\nObviously, we can&#8217;t discuss every single different analytic function; it&#8217;d take too long \u2013 and, if I&#8217;m being honest, I haven&#8217;t used every one of them in my career. But if we pick a representative few, it&#8217;ll give you a good sense of how and when to use analytic functions.<\/p>\n<h5>MAX, MIN, AVG<\/h5>\n<p>These functions are pretty straightforward, and don&#8217;t need much introduction, so let&#8217;s jump straight into an example.\u00a0 Say you wanted to produce a report of every employee, their salary, and a comparison of that salary to others within their departments.<\/p>\n<pre>SELECT ename, deptno, sal,\r\nAVG(sal) OVER (PARTITION BY deptno) \"Average Dept Salary\",\r\nMIN(sal) OVER (PARTITION BY deptno) \"Minimum Dept Salary\",\r\nMAX(sal) OVER (PARTITION BY deptno) \"Maximum Dept Salary\"\r\nFROM emp\r\nORDER BY ename;\r\n<\/pre>\n<table width=\"607\">\n<tbody>\n<tr>\n<td width=\"64\"><strong>ENAME<\/strong><\/td>\n<td width=\"59\"><strong>DEPTNO<\/strong><\/td>\n<td width=\"64\"><strong>SAL<\/strong><\/td>\n<td width=\"133\"><strong>Average Dept Salary<\/strong><\/td>\n<td width=\"143\"><strong>Minimum Dept Salary<\/strong><\/td>\n<td width=\"144\"><strong>Maximum Dept Salary<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"64\">ADAMS<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"64\">1100<\/td>\n<td width=\"133\">2175<\/td>\n<td width=\"143\">800<\/td>\n<td width=\"144\">3000<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">ALLEN<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"64\">1600<\/td>\n<td width=\"133\">1566.67<\/td>\n<td width=\"143\">950<\/td>\n<td width=\"144\">2850<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">BLAKE<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"64\">2850<\/td>\n<td width=\"133\">1566.67<\/td>\n<td width=\"143\">950<\/td>\n<td width=\"144\">2850<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">CLARK<\/td>\n<td width=\"59\">10<\/td>\n<td width=\"64\">2450<\/td>\n<td width=\"133\">2916.67<\/td>\n<td width=\"143\">1300<\/td>\n<td width=\"144\">5000<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">FORD<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"64\">3000<\/td>\n<td width=\"133\">2175<\/td>\n<td width=\"143\">800<\/td>\n<td width=\"144\">3000<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">JAMES<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"64\">950<\/td>\n<td width=\"133\">1566.67<\/td>\n<td width=\"143\">950<\/td>\n<td width=\"144\">2850<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">JONES<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"64\">2975<\/td>\n<td width=\"133\">2175<\/td>\n<td width=\"143\">800<\/td>\n<td width=\"144\">3000<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">KING<\/td>\n<td width=\"59\">10<\/td>\n<td width=\"64\">5000<\/td>\n<td width=\"133\">2916.67<\/td>\n<td width=\"143\">1300<\/td>\n<td width=\"144\">5000<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">MARTIN<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"64\">1250<\/td>\n<td width=\"133\">1566.67<\/td>\n<td width=\"143\">950<\/td>\n<td width=\"144\">2850<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">MILLER<\/td>\n<td width=\"59\">10<\/td>\n<td width=\"64\">1300<\/td>\n<td width=\"133\">2916.67<\/td>\n<td width=\"143\">1300<\/td>\n<td width=\"144\">5000<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">SCOTT<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"64\">3000<\/td>\n<td width=\"133\">2175<\/td>\n<td width=\"143\">800<\/td>\n<td width=\"144\">3000<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">SMITH<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"64\">800<\/td>\n<td width=\"133\">2175<\/td>\n<td width=\"143\">800<\/td>\n<td width=\"144\">3000<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">TURNER<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"64\">1500<\/td>\n<td width=\"133\">1566.67<\/td>\n<td width=\"143\">950<\/td>\n<td width=\"144\">2850<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">WARD<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"64\">1250<\/td>\n<td width=\"133\">1566.67<\/td>\n<td width=\"143\">950<\/td>\n<td width=\"144\">2850<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<h5>LISTAGG<\/h5>\n<p>Outside of the set of arithmetic analytic functions, this is probably the one that I find myself using the most.\u00a0 LISTAGG concatenates the contents of a column into a VARCHAR2 string; basically, it takes vertical data and allows you display it horizontally.<\/p>\n<p>The LISTAGG syntax is worth a look, as it is a little different.<\/p>\n<pre>LISTAGG (expr [, delimiter]) WITHIN GROUP (ORDER BY expr) [OVER (PARTITION BY)]\r\n<\/pre>\n<p>Here&#8217;s an example.\u00a0 Say we wanted to obtain a list of all our employees and all the colleagues that share a department with them, we&#8217;d turn to LISTAGG.<\/p>\n<pre>SELECT ename, deptno,\r\nLISTAGG (ename, ',') WITHIN GROUP (ORDER BY ename) OVER (PARTITION BY deptno) AS \"Colleagues\"\r\nFROM emp\r\nORDER BY ename;\r\n<\/pre>\n<table width=\"411\">\n<tbody>\n<tr>\n<td width=\"64\"><strong>ENAME<\/strong><\/td>\n<td width=\"59\"><strong>DEPTNO<\/strong><\/td>\n<td width=\"288\"><strong>Colleagues<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"64\">ADAMS<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"288\">ADAMS,FORD,JONES,SCOTT,SMITH<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">ALLEN<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"288\">ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">BLAKE<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"288\">ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">CLARK<\/td>\n<td width=\"59\">10<\/td>\n<td width=\"288\">CLARK,KING,MILLER<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">FORD<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"288\">ADAMS,FORD,JONES,SCOTT,SMITH<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">JAMES<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"288\">ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">JONES<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"288\">ADAMS,FORD,JONES,SCOTT,SMITH<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">KING<\/td>\n<td width=\"59\">10<\/td>\n<td width=\"288\">CLARK,KING,MILLER<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">MARTIN<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"288\">ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">MILLER<\/td>\n<td width=\"59\">10<\/td>\n<td width=\"288\">CLARK,KING,MILLER<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">SCOTT<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"288\">ADAMS,FORD,JONES,SCOTT,SMITH<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">SMITH<\/td>\n<td width=\"59\">20<\/td>\n<td width=\"288\">ADAMS,FORD,JONES,SCOTT,SMITH<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">TURNER<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"288\">ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">WARD<\/td>\n<td width=\"59\">30<\/td>\n<td width=\"288\">ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<h5>RANK, DENSE_RANK<\/h5>\n<p>RANK and DENSE_RANK are very closely related; they do pretty much the same thing, with one small \u2013 significant \u2013 difference. They both return the position \u2013 the <em>rank<\/em> \u2013 of the row in an ordered group. The only difference is that DENSE_RANK determines that position based on a count of the <em>unique<\/em> values in the list; RANK, on the other hand, gives rows with an equal value an equal rank, which can result in gaps in the ranking. If two rows have an equal value and rank as Position 1, the next available position using RANK will be Position 3; using DENSE_RANK, it will be Position 2.<\/p>\n<p>Here&#8217;s the syntax:<\/p>\n<pre>DENSE_RANK|RANK () OVER ([PARTITION BY &lt;\u2026&gt;] ORDER BY expr)\r\n<\/pre>\n<p>&nbsp;<\/p>\n<pre>SELECT ename, sal,\r\n  RANK () OVER (ORDER BY sal) AS \"Rank\",\r\n  DENSE_RANK () OVER (ORDER BY sal) AS \"Dense Rank\"\r\nFROM emp\r\nORDER BY sal;\r\n<\/pre>\n<table width=\"275\">\n<tbody>\n<tr>\n<td width=\"64\"><strong>ENAME<\/strong><\/td>\n<td width=\"64\"><strong>SAL<\/strong><\/td>\n<td width=\"64\"><strong>Rank<\/strong><\/td>\n<td width=\"83\"><strong>Dense_Rank<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"64\">SMITH<\/td>\n<td width=\"64\">800<\/td>\n<td width=\"64\">1<\/td>\n<td width=\"83\">1<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">JAMES<\/td>\n<td width=\"64\">950<\/td>\n<td width=\"64\">2<\/td>\n<td width=\"83\">2<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">ADAMS<\/td>\n<td width=\"64\">1100<\/td>\n<td width=\"64\">3<\/td>\n<td width=\"83\">3<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">MARTIN<\/td>\n<td width=\"64\">1250<\/td>\n<td width=\"64\">4<\/td>\n<td width=\"83\">4<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">WARD<\/td>\n<td width=\"64\">1250<\/td>\n<td width=\"64\">4<\/td>\n<td width=\"83\">4<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">MILLER<\/td>\n<td width=\"64\">1300<\/td>\n<td width=\"64\">6<\/td>\n<td width=\"83\">5<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">TURNER<\/td>\n<td width=\"64\">1500<\/td>\n<td width=\"64\">7<\/td>\n<td width=\"83\">6<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">ALLEN<\/td>\n<td width=\"64\">1600<\/td>\n<td width=\"64\">8<\/td>\n<td width=\"83\">7<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">CLARK<\/td>\n<td width=\"64\">2450<\/td>\n<td width=\"64\">9<\/td>\n<td width=\"83\">8<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">BLAKE<\/td>\n<td width=\"64\">2850<\/td>\n<td width=\"64\">10<\/td>\n<td width=\"83\">9<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">JONES<\/td>\n<td width=\"64\">2975<\/td>\n<td width=\"64\">11<\/td>\n<td width=\"83\">10<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">FORD<\/td>\n<td width=\"64\">3000<\/td>\n<td width=\"64\">12<\/td>\n<td width=\"83\">11<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">SCOTT<\/td>\n<td width=\"64\">3000<\/td>\n<td width=\"64\">12<\/td>\n<td width=\"83\">11<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">KING<\/td>\n<td width=\"64\">5000<\/td>\n<td width=\"64\">14<\/td>\n<td width=\"83\">12<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>The way that the RANK and the DENSE_RANK values fork at the Miller record demonstrates their subtle difference.<\/p>\n<p>Finally, even though this is an article on analytic functions, I think it&#8217;s worth spending a little time looking at the aggregate function equivalents for RANK and DENSE_RANK. They return a (dense) rank for a <em>hypothetical <\/em>value in the ordered list.\u00a0 Imagine you were planning to take on a new member of staff at a salary of $2650, and you wondered where that would place her within the company hierarchy.<\/p>\n<p>The syntax for the RANK and DENSE_RANK aggregate functions are a little different from that of their analytic cousins.<\/p>\n<pre>DENSE_RANK|RANK (expr) WITHIN GROUP (ORDER BY expr)\r\n<\/pre>\n<pre>SELECT DENSE_RANK (2650) WITHIN GROUP (ORDER BY sal) AS \"Dense_rank\",\r\n  RANK (2650) WITHIN GROUP (ORDER BY sal) AS \"Rank\"\r\nFROM emp;\r\n<\/pre>\n<table width=\"144\">\n<tbody>\n<tr>\n<td width=\"80\"><strong>Dense_rank<\/strong><\/td>\n<td width=\"64\"><strong>Rank<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"80\">9<\/td>\n<td width=\"64\">10<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<h5>ROW_NUMBER<\/h5>\n<p>A good corollary following on from RANK and DENSE_RANK is ROW_NUMBER. Unlike the other two, ROW_NUMBER returns a sequential ranking of a row within an ordered list, making no special allowance for rows of equal value.<\/p>\n<p>And if you&#8217;re wondering why we need ROW_NUMBER when we&#8217;ve got the perfectly good <em>rownum<\/em> pseudocolumn, then you&#8217;re forgetting the importance of the analytic function clauses. Firstly, using PARTITION BY, we can get the row number <em>within <\/em>a group.<\/p>\n<pre>SELECT ename, deptno, sal,\r\nROW_NUMBER() OVER (PARTITION BY deptno ORDER BY deptno, sal desc) AS \"Position in Dept\"\r\nFROM emp\r\nORDER BY deptno, sal desc;\r\n<\/pre>\n<table width=\"299\">\n<tbody>\n<tr>\n<td width=\"64\"><strong>ENAME<\/strong><\/td>\n<td width=\"64\"><strong>DEPTNO<\/strong><\/td>\n<td width=\"64\"><strong>SAL<\/strong><\/td>\n<td width=\"107\"><strong>Position in Dept<\/strong><\/td>\n<\/tr>\n<tr>\n<td>KING<\/td>\n<td>10<\/td>\n<td>5000<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td>CLARK<\/td>\n<td>10<\/td>\n<td>2450<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>MILLER<\/td>\n<td>10<\/td>\n<td>1300<\/td>\n<td>3<\/td>\n<\/tr>\n<tr>\n<td>SCOTT<\/td>\n<td>20<\/td>\n<td>3000<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td>FORD<\/td>\n<td>20<\/td>\n<td>3000<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>JONES<\/td>\n<td>20<\/td>\n<td>2975<\/td>\n<td>3<\/td>\n<\/tr>\n<tr>\n<td>ADAMS<\/td>\n<td>20<\/td>\n<td>1100<\/td>\n<td>4<\/td>\n<\/tr>\n<tr>\n<td>SMITH<\/td>\n<td>20<\/td>\n<td>800<\/td>\n<td>5<\/td>\n<\/tr>\n<tr>\n<td>BLAKE<\/td>\n<td>30<\/td>\n<td>2850<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td>ALLEN<\/td>\n<td>30<\/td>\n<td>1600<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>TURNER<\/td>\n<td>30<\/td>\n<td>1500<\/td>\n<td>3<\/td>\n<\/tr>\n<tr>\n<td>WARD<\/td>\n<td>30<\/td>\n<td>1250<\/td>\n<td>4<\/td>\n<\/tr>\n<tr>\n<td>MARTIN<\/td>\n<td>30<\/td>\n<td>1250<\/td>\n<td>5<\/td>\n<\/tr>\n<tr>\n<td>JAMES<\/td>\n<td>30<\/td>\n<td>950<\/td>\n<td>6<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Secondly, if you&#8217;ve ever got weird results using <em>rownum <\/em>in a query that also has an order by statement, you&#8217;ll know that rownum is determined <em>before<\/em> the order is applied to the resultset. However, using row_number along with the analytic order by clause, you can ensure that your results respect your imposed order.<\/p>\n<pre>SELECT ename, sal, rownum,\r\n  ROW_NUMBER() OVER (ORDER BY sal) AS \"Row_number\"\r\nFROM emp\r\nORDER BY sal;\r\n<\/pre>\n<table width=\"288\">\n<tbody>\n<tr>\n<td width=\"64\"><strong>ENAME<\/strong><\/td>\n<td width=\"64\"><strong>SAL<\/strong><\/td>\n<td width=\"69\"><strong>ROWNUM<\/strong><\/td>\n<td width=\"91\"><strong>Row_number<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"64\">SMITH<\/td>\n<td width=\"64\">800<\/td>\n<td width=\"69\">7<\/td>\n<td width=\"91\">1<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">JAMES<\/td>\n<td width=\"64\">950<\/td>\n<td width=\"69\">13<\/td>\n<td width=\"91\">2<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">ADAMS<\/td>\n<td width=\"64\">1100<\/td>\n<td width=\"69\">12<\/td>\n<td width=\"91\">3<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">MARTIN<\/td>\n<td width=\"64\">1250<\/td>\n<td width=\"69\">10<\/td>\n<td width=\"91\">4<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">WARD<\/td>\n<td width=\"64\">1250<\/td>\n<td width=\"69\">9<\/td>\n<td width=\"91\">5<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">MILLER<\/td>\n<td width=\"64\">1300<\/td>\n<td width=\"69\">14<\/td>\n<td width=\"91\">6<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">TURNER<\/td>\n<td width=\"64\">1500<\/td>\n<td width=\"69\">11<\/td>\n<td width=\"91\">7<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">ALLEN<\/td>\n<td width=\"64\">1600<\/td>\n<td width=\"69\">8<\/td>\n<td width=\"91\">8<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">CLARK<\/td>\n<td width=\"64\">2450<\/td>\n<td width=\"69\">3<\/td>\n<td width=\"91\">9<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">BLAKE<\/td>\n<td width=\"64\">2850<\/td>\n<td width=\"69\">2<\/td>\n<td width=\"91\">10<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">JONES<\/td>\n<td width=\"64\">2975<\/td>\n<td width=\"69\">4<\/td>\n<td width=\"91\">11<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">FORD<\/td>\n<td width=\"64\">3000<\/td>\n<td width=\"69\">6<\/td>\n<td width=\"91\">12<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">SCOTT<\/td>\n<td width=\"64\">3000<\/td>\n<td width=\"69\">5<\/td>\n<td width=\"91\">13<\/td>\n<\/tr>\n<tr>\n<td width=\"64\">KING<\/td>\n<td width=\"64\">5000<\/td>\n<td width=\"69\">1<\/td>\n<td width=\"91\">14<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<h5>Conclusion<\/h5>\n<p>There are, as I said, dozens of analytic functions and I&#8217;ve been pleased to watch Oracle add to their number with each major release.\u00a0 These articles were never going to fully dissect every possible function, but I hope that they&#8217;ve demonstrated the strengths of the functionality and its amazing flexibility.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the first part of this series I introduced you to the analytic functions family, outlined its close relationship to aggregate functions, and illustrated my points with a few examples. I demonstrated how, by clever use of the analytic function clauses \u2013 partition by, order by, and the windowing clause \u2013 you could tune your functions to wring even mor&hellip;<\/p>\n","protected":false},"author":221907,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143533],"tags":[48352,4459,4150],"coauthors":[],"class_list":["post-73148","post","type-post","status-publish","format-standard","hentry","category-oracle-databases","tag-analytic-functions","tag-oracle","tag-sql"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73148","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221907"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=73148"}],"version-history":[{"count":1,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73148\/revisions"}],"predecessor-version":[{"id":91644,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73148\/revisions\/91644"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=73148"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=73148"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=73148"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=73148"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}