Home » RDBMS Server » Performance Tuning » Compare current row values with previous one until mismatch (Oracle 10g)
Compare current row values with previous one until mismatch [message #529579] Wed, 02 November 2011 01:09 Go to next message
asakchris
Messages: 4
Registered: November 2011
Junior Member
We have employee salary table which will have salary of an employee on daily basis (only working days). Below is the table structure:
emp_salary
----------
emp_id         NUMBER(15) NOT NULL
effective_date DATE NOT NULL
salary         NUMBER(15) NOT NULL

Primary key - emp_id, effective_date
This table is yearly partitioned

I have to find out how long the salary is not changed for an employee from given date, and last salary. I am using below query to do this:
WITH salary_tab AS
     (SELECT   effective_date, salary,
               (CASE
                   WHEN (LAG (salary) OVER (PARTITION BY emp_id ORDER BY effective_date ASC) =
                                                                        salary
                        )
                      THEN 0
                   ELSE 1
                END
               ) changed_ind
          FROM emp_salary
         WHERE emp_id = 12345
           AND effective_date <= TO_DATE ('07/10/2011', 'mm/dd/yyyy')
      ORDER BY effective_date DESC)
SELECT COUNT (1) not_changed_count, MIN (salary) last_salary
  FROM salary_tab
 WHERE effective_date > (SELECT MAX (effective_date)
                           FROM salary_tab
                          WHERE changed_ind = 1);

The cost of this query is 1677 and it is taking around 60 msec to complete. When I run this query for around 2000 employees in a loop it is taking around 3 minutes to complete.

Is there any way we can optimize this query?
The main bottleneck of this query is in the with clause where I am processing the entire history instead of stopping after first change.
Re: Compare current row values with previous one until mismatch [message #529595 is a reply to message #529579] Wed, 02 November 2011 02:13 Go to previous messageGo to next message
Michel Cadot
Messages: 68716
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator
Post a working Test case: create table and insert statements along with the result you want with these data then we will be able work with your table and data. Explain with words and sentences the rules that lead to this result.

Regards
Michel
Re: Compare current row values with previous one until mismatch [message #529609 is a reply to message #529595] Wed, 02 November 2011 03:03 Go to previous messageGo to next message
asakchris
Messages: 4
Registered: November 2011
Junior Member
Below are the create table and insert scripts:
CREATE TABLE emp_salary
(
  emp_id          NUMBER(15) CONSTRAINT emp_salary_01 NOT NULL,
  effective_date  DATE CONSTRAINT emp_salary_02 NOT NULL,
  salary          NUMBER(15)
);

ALTER TABLE emp_salary ADD (
  CONSTRAINT emp_salary_pk
 PRIMARY KEY
 (emp_id, effective_date));

INSERT INTO emp_salary VALUES (1, TO_DATE ('10/31/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/28/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/27/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/26/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/25/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/24/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/21/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/20/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/19/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/18/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (1, TO_DATE ('10/17/2011', 'mm/dd/yyyy'), 230);

INSERT INTO emp_salary VALUES (2, TO_DATE ('10/31/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/28/2011', 'mm/dd/yyyy'), 250);			
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/27/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/26/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/25/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/24/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/21/2011', 'mm/dd/yyyy'), 245);
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/20/2011', 'mm/dd/yyyy'), 244);
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/19/2011', 'mm/dd/yyyy'), 242);
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/18/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (2, TO_DATE ('10/17/2011', 'mm/dd/yyyy'), 240);

INSERT INTO emp_salary VALUES (3, TO_DATE ('10/31/2011', 'mm/dd/yyyy'), 250);
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/28/2011', 'mm/dd/yyyy'), 240);			
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/27/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/26/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/25/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/24/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/21/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/20/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/19/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/18/2011', 'mm/dd/yyyy'), 240);
INSERT INTO emp_salary VALUES (3, TO_DATE ('10/17/2011', 'mm/dd/yyyy'), 240);

For emp_id 1, if we ran this query for 10/31/2011, then it has to compare the 10/31 salary with 10/29 and do the same until the salary mismatches. In this case, salary salary mismatch occurs on 10/20, so the stale salary period is from 10/31 to 10/21 which is 7 days.
Below query will give that result:
WITH salary_tab AS
     (SELECT   effective_date, salary,
               (CASE
                   WHEN (LAG (salary) OVER (PARTITION BY emp_id ORDER BY effective_date ASC) =
                                                                        salary
                        )
                      THEN 0
                   ELSE 1
                END
               ) changed_ind
          FROM emp_salary
         WHERE emp_id = 1
           AND effective_date <= TO_DATE ('10/31/2011', 'mm/dd/yyyy')
      ORDER BY effective_date DESC)
SELECT COUNT (1) not_changed_count, MIN (salary) last_salary,
       MIN (effective_date) stale_start_date,
       MAX (effective_date) stale_end_date
  FROM salary_tab
 WHERE effective_date > (SELECT MAX (effective_date)
                           FROM salary_tab
                          WHERE changed_ind = 1);

NOT_CHANGED_COUNT LAST_SALARY STALE_STA STALE_END
----------------- ----------- --------- ---------
                7         250 21-OCT-11 31-OCT-11

[Updated on: Wed, 02 November 2011 03:31]

Report message to a moderator

Re: Compare current row values with previous one until mismatch [message #529627 is a reply to message #529609] Wed, 02 November 2011 04:11 Go to previous messageGo to next message
Michel Cadot
Messages: 68716
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator
SQL> def dt=10/31/2011
SQL> def emp=1
SQL> with 
  2    data as ( 
  3      select emp_id, effective_date, salary,
  4             row_number() over (order by effective_date desc) rn
  5      from emp_salary 
  6      where emp_id = &emp and effective_date <= to_date('&dt','MM/DD/YYYY')
  7    )
  8  select rn nb_rows, salary, 
  9         effective_date startdt, connect_by_root effective_date enddt
 10  from data
 11  where connect_by_isleaf = 1
 12  connect by prior rn = rn - 1 and prior salary = salary
 13  start with rn = 1
 14  /
   NB_ROWS     SALARY STARTDT     ENDDT
---------- ---------- ----------- -----------
         8        250 20-OCT-2011 31-OCT-2011

Regards
Michel
Re: Compare current row values with previous one until mismatch [message #529647 is a reply to message #529627] Wed, 02 November 2011 04:54 Go to previous messageGo to next message
asakchris
Messages: 4
Registered: November 2011
Junior Member
Hi Michel,

I already tried this one and it is also taking more than 3 minutes if we ran this query in loop for 2000 employees. With clause is having the entire history of data eventhough we can stop when there is a mismatch. Can we control this to improve the performance of this query?
Re: Compare current row values with previous one until mismatch [message #529649 is a reply to message #529647] Wed, 02 November 2011 04:57 Go to previous messageGo to next message
Michel Cadot
Messages: 68716
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator
Quote:
minutes if we ran this query in loop for 2000 employees

If you have 2000 employees to search for do it in a SINGLE query, do not loop.

Regards
Michel
Re: Compare current row values with previous one until mismatch [message #529653 is a reply to message #529649] Wed, 02 November 2011 05:18 Go to previous messageGo to next message
asakchris
Messages: 4
Registered: November 2011
Junior Member
I am using below query to get data for list of employees:
WITH salary_tab AS
     (SELECT   emp_id, effective_date, salary,
               (CASE
                   WHEN (LAG (salary) OVER (PARTITION BY emp_id ORDER BY effective_date ASC) =
                                                                        salary
                        )
                      THEN 0
                   ELSE 1
                END
               ) changed_ind
          FROM emp_salary
         WHERE emp_id IN (1, 2, 3)
           AND effective_date <= TO_DATE ('10/31/2011', 'mm/dd/yyyy')
      ORDER BY effective_date DESC)
SELECT   emp_id, COUNT (1) not_changed_count, MIN (salary) last_salary,
         MIN (effective_date) stale_start_date,
         MAX (effective_date) stale_end_date
    FROM salary_tab tab_main
   WHERE effective_date >
                 (SELECT MAX (effective_date)
                    FROM salary_tab tab_inn
                   WHERE tab_inn.emp_id = tab_main.emp_id AND changed_ind = 1)
GROUP BY emp_id;

    EMP_ID NOT_CHANGED_COUNT LAST_SALARY STALE_STA STALE_END
---------- ----------------- ----------- --------- ---------
         1                 7         250 21-OCT-11 31-OCT-11
         2                 5         250 25-OCT-11 31-OCT-11


Are there any ways to improve the performance?
Re: Compare current row values with previous one until mismatch [message #529655 is a reply to message #529653] Wed, 02 November 2011 05:25 Go to previous message
Michel Cadot
Messages: 68716
Registered: March 2007
Location: Saint-Maur, France, https...
Senior Member
Account Moderator
Either your queries are wrong, either you don't use the test case you provided.

SQL> with 
  2    data as ( 
  3      select emp_id, effective_date, salary,
  4             row_number() over (partition by emp_id order by effective_date desc) rn
  5      from emp_salary 
  6      where effective_date <= to_date('&dt','MM/DD/YYYY')
  7    )
  8  select emp_id, rn nb_rows, salary, 
  9         effective_date startdt, connect_by_root effective_date enddt
 10  from data
 11  where connect_by_isleaf = 1
 12  connect by prior rn = rn - 1 and prior salary = salary and prior emp_id = emp_id
 13  start with rn = 1
 14  order by emp_id
 15  /
    EMP_ID    NB_ROWS     SALARY STARTDT     ENDDT
---------- ---------- ---------- ----------- -----------
         1          8        250 20-OCT-2011 31-OCT-2011
         2          6        250 24-OCT-2011 31-OCT-2011
         3          1        250 31-OCT-2011 31-OCT-2011

3 rows selected.


Regards
Michel

[Updated on: Wed, 02 November 2011 05:26]

Report message to a moderator

Previous Topic: Parallelism in queries
Next Topic: sql tunning help required
Goto Forum:
  


Current Time: Sun Nov 24 12:27:11 CST 2024