Oracle Analytic functions - resetting a windowing clause
This is a little complicated. Instead of using rank()
or the like, use lag()
to see when something changes. Then do a cumulative sum of the flag.
select dept, date1,
CASE WHEN StartFlag = 0 THEN 1
ELSE 1+StartFlag+NVL(lag(StartFlag) over (order by date1),0)
END as rnk
from (select t1.*,
(case when dept = lag(dept) over (order by date1)
then 1
else 0
end) as StartFlag
from t1
) t1
order by date1;
Here is the SQLFiddle.
EDIT:
This is Gordon editing my own answer. Oops. The original query was 90% of the way there. It identified the groups where the numbers should increase, but did not assign the numbers within the groups. I would do this with another level of row_number()
as in:
select dept, date1,
row_number() over (partition by dept, grp order by date1) as rnk
from (select dept, date1, startflag,
sum(StartFlag) over (partition by dept order by date1) as grp
from (select t1.*,
(case when dept = lag(dept) over (order by date1)
then 0
else 1
end) as StartFlag
from t1
) t1
) t1
order by date1;
So, the overall idea is the following. First use lag()
to determine where a group begins (that is, where there is a department change from one date to the next). Then, assign a "group id" to these, by doing a cumulative sum. These are the records that are to be enumerated. The final step is to enumerate them using row_number()
.
Analytic function windowing clause
The error is in val preceding
and val following
. It should be 1 preceding
and 1 following
.
The number you specify there is relative to the current record, the record corresponding to val (in the given window order), so if you specify val
there you are going back (or ahead) too far. You should need to get the min/max up to one record before (or after) the current record.
So:
WITH T AS
(SELECT CAST(LEVEL AS NUMBER) val
FROM DUAL
CONNECT BY LEVEL < 4)
SELECT val
,MIN(val) OVER(ORDER BY val RANGE BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) A
,MAX(val) OVER(ORDER BY val RANGE BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) B
,MIN(val) OVER(ORDER BY val RANGE BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) C
,MAX(val) OVER(ORDER BY val RANGE BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) D
FROM T
WHERE val IS NOT NULL
ORDER BY 1
/
Oracle Analytic functions - How to reuse a PARTITION BY clause?
If you are referring to the standard WINDOW clause like this:
SELECT col1,
MAX(col2) OVER(w),
MIN(col2) OVER(w)
FROM my_table
WINDOW w AS (PARTITION BY col3, col4, col5, col6,
CASE WHEN col7 LIKE 'foo'
THEN SUBSTR(col7,1,5)
ELSE col7
END
ORDER BY col5 ASC, col6 DESC);
then I believe the answer is no, Oracle does not support this (checked with 11gR2).
Analytic functions and means of window clause
Maybe this is what you are looking for.
Sample data:
WITH
emp (ID, EMP_NAME, HIRE_DATE, SALARY, DEPT) AS
(
Select 601, 'HILLER', To_Date('23-JAN-82', 'dd-MON-yy'), 4800, 60 From Dual Union All
Select 602, 'MILLER', To_Date('23-FEB-82', 'dd-MON-yy'), 9000, 60 From Dual Union All
Select 603, 'SMITH', To_Date('23-MAR-82', 'dd-MON-yy'), 4800, 60 From Dual Union All
Select 604, 'FORD', To_Date('23-APR-82', 'dd-MON-yy'), 4200, 60 From Dual Union All
Select 605, 'KING', To_Date('23-MAY-82', 'dd-MON-yy'), 6000, 60 From Dual Union All
Select 201, 'SCOT', To_Date('23-MAR-82', 'dd-MON-yy'), 13000, 20 From Dual Union All
Select 202, 'JONES', To_Date('23-AUG-82', 'dd-MON-yy'), 6000, 20 From Dual
),
Create CTE named grid with several analytic functions and windowing clauses. They are not all needed but the resulting dataset below shows the logic with all components included.
grid AS
(
Select
g.*, Max(GAP) OVER(PARTITION BY DEPT) "DEPT_MAX_GAP"
From
(
Select
ROWNUM "RN",
Sum(1) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN Unbounded Preceding And Current Row) "RN_DEPT",
ID, EMP_NAME, HIRE_DATE, DEPT, SALARY,
--
Nvl(Sum(SALARY) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following), 0) "SUM_SAL_LATER",
Nvl(Sum(1) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following), 0) "COUNT_EMP_LATER",
--
Nvl(Sum(SALARY) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following) /
Sum(1) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following), 0) "AVG_LATER",
--
SALARY -
Nvl((
Sum(SALARY) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following) /
Sum(1) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following)
), 0) "GAP"
from
emp
Order By
DEPT, HIRE_DATE, ID
) g
Order By
RN
)
CTE grid resultiing dataset:
RN | RN_DEPT | ID | EMP_NAME | HIRE_DATE | DEPT | SALARY | SUM_SAL_LATER | COUNT_EMP_LATER | AVG_LATER | GAP | DEPT_MAX_GAP |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 601 | HILLER | 23-JAN-82 | 60 | 4800 | 24000 | 4 | 6000 | -1200 | 6000 |
2 | 2 | 602 | MILLER | 23-FEB-82 | 60 | 9000 | 15000 | 3 | 5000 | 4000 | 6000 |
3 | 3 | 603 | SMITH | 23-MAR-82 | 60 | 4800 | 10200 | 2 | 5100 | -300 | 6000 |
4 | 4 | 604 | FORD | 23-APR-82 | 60 | 4200 | 6000 | 1 | 6000 | -1800 | 6000 |
5 | 5 | 605 | KING | 23-MAY-82 | 60 | 6000 | 0 | 0 | 0 | 6000 | 6000 |
6 | 1 | 201 | SCOT | 23-MAR-82 | 20 | 13000 | 6000 | 1 | 6000 | 7000 | 7000 |
7 | 2 | 202 | JONES | 23-AUG-82 | 20 | 6000 | 0 | 0 | 0 | 6000 | 7000 |
Analytic functions and means of window clause for calculating sum
You want aggregation rather than windowing in the outer query:
SELECT
department_id,
SUM(salary) total_sal
FROM
(
SELECT
employee_id,
first_name
|| ' '
|| last_name,
department_id,
salary,
ROW_NUMBER()
OVER(PARTITION BY department_id
ORDER BY
salary DESC
--ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING
) result
FROM
employees
) e
WHERE
result <= 3
GROUP BY department_id
I we were to do the same task with window functions only, then, starting from the existing query, we can either add another level of nesting of some sort, or use WITH TIES
. Both pursue the same effect, which is to limit the results to one row per group.
The latter would look like:
SELECT
department_id,
SUM(salary) OVER(PARTITION BY department_id) total_sal
FROM (
SELECT e.*,
ROW_NUMBER() OVER(PARTITION BY department_id ORDER BY salary DESC) result
FROM employees e
) e
WHERE result <= 3
ORDER BY result FETCH FIRST ROW WITH TIES
While the former would phrase as:
SELECT department_id, total_sal
FROM (
SELECT e.*,
SUM(salary) OVER(PARTITION BY department_id) total_sal
FROM (
SELECT e.*,
ROW_NUMBER() OVER(PARTITION BY department_id ORDER BY salary DESC) result
FROM employees e
) e
WHERE result <= 3
) e
where result = 1
Error while using Window Clause in Oracle Query
The window clause goes inside the analytic function:
select P.*,
first_value(product_name) over (
partition by product_category
order by price desc
range between unbounded preceding and unbounded following
) AS MAX_PRICE,
Last_value(product_name) over (
partition by product_category
order by price desc
range between unbounded preceding and unbounded following
) MIN_PRICE
from product p;
Or, from Oracle 21, you can use:
select P.*,
first_value(product_name) over w AS MAX_PRICE,
Last_value(product_name) over w AS MIN_PRICE
from product p
window w as (
partition by product_category
order by price desc
range between unbounded preceding and unbounded following
)
(Without the brackets around the window in the analytic function.)
db<>fiddle here
SQL Oracle - Filter on Rank/Window Partition
Rather than partitioning on the concatenation of the branch and item, you should just partition on both those columns:
WITH cte AS (
SELECT branch, item, "date", order_num,
RANK() OVER (PARTITION BY branch, item ORDER BY "date" DESC) rnk
FROM yourTable
)
SELECT branch, item, "date", order_num
FROM cte
WHERE rnk = 1;
Any difference between current row and 0 preceding/following in windowing clause of Oracle analytic functions?
It doesn't really matter which you use. They are two different ways of expressing the windowing, but the optimizer will perform the query the same way. The term "current row" is one that is common to multiple databases with analytic functions, not just Oracle. It's more of a stylistic difference, in the same way that some people prefer count(*) over count(1).
Related Topics
How to Calculate Ratios in Sql
How to Force a Column to Be Unique for an Entire Table in SQL Server 2008 R2
How to Override SQL Sanitization in Coldfusion
How to Insert into a Table with Just One Identity Column (Sql Express)
How to Execute Different Select Statements Based on a Case
Using Where Clause with Between and Null Date Parameters
Use Soundex() Word by Word on SQL Server
Confusing Error About Missing Left Parenthesis in SQL Statement
Cannot Delete and Update Records on Access Linked Table
Sql Create Statement Incorrect Syntax Near Auto Increment
Sql Server Bigint or Decimal(18,0) for Primary Key
Update Multiple Rows Using Select Statement
How to List All Stored Procedures in Informix
Database Restore Failing with Move
Insert Large Amount of Data Efficiently with Sql
Sql Server: Best Way to Concatenate Multiple Columns