Skip to Main Content

SQL & PL/SQL

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Need to alter existing query to fetch correct data.

Vemula MuniAug 5 2019 — edited Aug 13 2019

Hi All,

Below is my query

WITH parameters AS

(

SELECT  TO\_DATE ('01-JUN-2019', 'DD-MON-YYYY') AS startdt  -- Always use 4 digits for years

,       TO\_DATE ('30-JUN-2019', 'DD-MON-YYYY') AS enddt

,       'CWR'                                  AS per\_org

FROM    dual

)

, got_business_unit AS

(

SELECT  p.startdt, p.enddt

,       d1.emplid, d1.per\_org, d1.effdt

,       CASE

            WHEN  b.business\_unit  IS NULL

            THEN  'I'

            ELSE  d1.hr\_status

        END         AS hr\_status

,       b.business\_unit

FROM    parameters       p

JOIN    ps\_cts\_job\_dtl1  d1   ON  d1.per\_org      = p.per\_org

                              AND d1.effdt       \<= p.enddt

OUTER APPLY (

                SELECT  MAX (b.business\_unit)  AS business\_unit

                FROM    ps\_cts\_job\_dtl  d

                JOIN    ps\_cts\_iqn\_bu   b   ON  b.business\_unit = d.business\_unit

                WHERE   d.emplid        = d1.emplid

                AND     d.effdt         = d1.effdt

            )            b

)

SELECT g.emplid

, GREATEST ( g.effdt

               , g.startdt

               )  AS startdt

, LEAST ( e.enddt

            , g.enddt

            )     AS enddt

, g.per_org

, g.hr_status

, 'Employee is active as CWR and BU is in IQN_BU setup table'

               AS reason

FROM got_business_unit g

OUTER APPLY (

            SELECT  NVL ( MIN (gs.effdt) - 1

                        , DATE '9999-12-31'

                        )  AS enddt

            FROM    got\_business\_unit  gs

            WHERE   gs.emplid    = g.emplid

            AND     gs.effdt     > g.effdt

            AND     gs.hr\_status = 'I'

        )                e

WHERE hr_status = 'A'

AND e.enddt > g.startdt

ORDER BY emplid

, startdt

;

Out Put from the query is

pastedImage_0.png

Need only one row for employee 733930 from 01-Jun-2019 to 03-Jun-2019.

need only one row for employee 733931 from 26-Jun-2019 to 30-Jun-2019.

I want out put like below based on below tables and data.

Required output:

Note : Reason column for understanding, no need to display reason column

While fetching below out put Please map EMPLID,EMPL_RCD,EFFDT,EFFSEQ Key fields in both JOB_DTL and JOB_DTL1 ( Map Key fields is very important )

  • Employee 733930 is active CWR in between '01-Jun-2019' to '03-Jun-2019' and employee have only one row with HR_STATUS ='A' ( employee have more than one row with HR_STATUS = 'A' ) and Business_unit value present in PS_CTS_IQN_BU table.
  • Employee 733930 is active CWR in between '15-Jun-2019' to '18-Jun-2019' and employee have only one row with HR_STATUS ='A' and Business_unit value present in PS_CTS_IQN_BU table.
  • Employee 733930 is active as CWR in between '19-JUN-2019' to '25-JUN-2019' in different BU which not in IQN_BU setup table, so i want to exclude that information.
  • Employee 733930 is active CWR from '26-Jun-2019' with HR_STATUS ='A' and Business_unit value present in PS_CTS_IQN_BU table.
  • Employee 733931 is active CWR from '06-Jun-2019' to '09-Jun-2019' and, employee have only one row with HR_STATUS ='A' and Business_unit value present in PS_CTS_IQN_BU table.
  • Employee 733931 is active CWR from '26-Jun-2019' to '30-Jun-2019' and he is having more than one row with HR_STATUS = 'A' and Business_unit value present in PS_CTS_IQN_BU table.

| EMPLID | FROM_DATE | To_DATE | PER_ORG | HR_STATUS | Reason |
| 733930 | 01-Jun-2019 | 03-Jun-2019 | CWR | A | Employee is active as CWR and BU is in IQN_BU setup table |
| 733930 | 15-Jun-2019 | 18-Jun-2019 | CWR | A | Employee is active as CWR and BU is in IQN_BU setup table |
| 733930 | 26-Jun-2019 | 30-Jun-2019 | CWR | A | Employee is active as CWR and BU is in IQN_BU setup table |
| 733931 | 06-Jun-2019 | 09-Jun-2019 | CWR | A | Employee is active as CWR and BU is in IQN_BU setup table |
| 733931 | 26-Jun-19 | 30-JUN-2019 | CWR | A | Employee is active as CWR and BU is in IQN_BU setup table |

Below is the requirement details.

  1. I want to fetch the contract employee information with HR_Status = 'A'. ( per_org = 'CWR' in PS_CTS_JOB_DTL1 table ).
  2. And active contract employee Business_Unit value of 'PS_CTS_JOB_DTL' table should be present in PS_CTS_IQN_BU table.
  3. there is a chance to have an employee multiple consecutive rows with hr_status='A'

DBA Version : Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production

Below is my "PS_CTS_JOB_DTL" table structure and data. in this table (EMPLID,EMPL_RCD,EFFDT,EFFSEQ) are key fields.

EMPL_RCD : when ever employee converted from contract to full time and from full time to contract this value will increase. (0,1,2,...)

EFFDT : this filed use to track when change happens.

EFFSEQ : this field will increase from 0 to 1,2 .. when there is more than one transaction in same date.( with same effdt )

ACTION : Will determine which action they taken on employee ( please don't consider action field to achieve output as below. because action codes may different in each time)

Note : Data Volume of this table is more than 50 million rows

| PS_CTS_JOB_DTL Table |
| EMPLID | BUSINESS_UNIT | EMPL_RCD | EFFDT | EFFSEQ | ACTION |
| 733930 | US410 | 0 | 27-May-2019 | 0 | ADD |
| 733930 | US410 | 0 | 02-Jun-2019 | 0 | DTA |
| 733930 | US410 | 0 | 04-Jun-2019 | 0 | TER |
| 733930 | US410 | 1 | 06-Jun-2019 | 0 | HIR |
| 733930 | US410 | 1 | 15-Jun-2019 | 0 | TER |
| 733930 | US410 | 2 | 15-Jun-2019 | 1 | ADD |
| 733930 | US608 | 2 | 19-Jun-2019 | 0 | ASC |
| 733930 | CN512 | 2 | 26-Jun-2019 | 0 | ASG |
| 733931 | US410 | 0 | 26-Apr-2019 | 0 | HIR |
| 733931 | US410 | 0 | 05-Jun-2019 | 0 | TER |
| 733931 | IN208 | 1 | 06-Jun-2019 | 0 | ADD |
| 733931 | IN208 | 1 | 10-Jun-2019 | 0 | TER |
| 733931 | IN208 | 2 | 12-Jun-2019 | 0 | HIR |
| 733931 | IN408 | 2 | 18-Jun-2019 | 0 | ASC |
| 733931 | IN408 | 2 | 19-Jun-2019 | 0 | TER |
| 733931 | IN208 | 3 | 26-Jun-2019 | 0 | ADD |
| 733931 | IN208 | 3 | 28-Jun-2019 | 0 | DTA |

PS_CTS_JOB_DTL1 Table :

Note : Data Volume of this table is more than 50 million rows

Below is my "PS_CTS_JOB_DTL1" table structure and data. in this table (EMPLID,EMPL_RCD,EFFDT,EFFSEQ) are key fields.

EMPL_RCD : when ever employee converted from contract to full time and from full time to contract this value will increase. (0,1,2,...)

EFFDT : this filed use to track when change happens.

EFFSEQ : this field will increase from 0 to 1,2 .. when there is more than one transaction in same date.( with same effdt )

HR_STATUS : To identify employee status ( A = Active, I = Inactive ).

PER_ORG : to identify employee type ( CWR = Contract, EMP = Employee )

Note : employee may contains more then one row with HR_STATUS = 'A' before he is going to inactive ( HR_STSTUS = 'I' )

| EMPL_ID | EMPL_RCD | EFFDT | EFFSEQ | HR_STATUS | PER_ORG |
| 733930 | 0 | 27-May-2019 | 0 | A | CWR |
| 733930 | 0 | 02-Jun-2019 | 0 | A | CWR |
| 733930 | 0 | 04-Jun-2019 | 0 | I | CWR |
| 733930 | 1 | 06-Jun-2019 | 0 | A | EMP |
| 733930 | 1 | 15-Jun-2019 | 0 | I | EMP |
| 733930 | 2 | 15-Jun-2019 | 1 | A | CWR |
| 733930 | 2 | 19-Jun-2019 | 0 | A | CWR |
| 733930 | 2 | 26-Jun-2019 | 0 | A | CWR |
| 733931 | 0 | 26-Apr-2019 | 0 | A | EMP |
| 733931 | 0 | 05-Jun-2019 | 0 | I | EMP |
| 733931 | 1 | 06-Jun-2019 | 0 | A | CWR |
| 733931 | 1 | 10-Jun-2019 | 0 | I | CWR |
| 733931 | 2 | 12-Jun-2019 | 0 | |

This post has been answered by Frank Kulash on Aug 7 2019
Jump to Answer

Comments

Frank Kulash

Hi,

This looks almost identical to reply #12 in your earlier thread: Fetching Contract Employee information by Join the JOB_DTL,JOB_DTL1,IQN_BU Tables.

Please don't post the same question in multiple threads.  It's confusing for people trying to answer questions, and it's confusing for you, too, since you have to look in multiple places for answers.  Mark one of these threads as "Answered" before we go any farther.

The query you posted meets the requirements you posted.

  1. It fetches contract employee information with HR_Status = 'A'. ( per_org = 'CWR' in PS_CTS_JOB_DTL1 table ).
  2. It only returns rows where active contract employee Business_Unit value of 'PS_CTS_JOB_DTL' table is present in PS_CTS_IQN_BU table.
  3. It handles the case were an employee has multiple consecutive rows with hr_status='A'.   (It may not handle them the way you want, but you haven't said what that way is.)

You apparently have other requirements you haven't posted yet.

Vemula Muni

Hi @"Frank Kulash",

EMPL_IDEMPL_RCDEFFDTEFFSEQHR_STATUSPER_ORG
733930027-May-20190ACWR
733930002-Jun-20190ACWR
733930004-Jun-20190ICWR

Employee 733930 is active as CWR from 27-May-2019 to 03-Jun-2019.  and he is having one extra row with HR_STATUS = 'A' and effdt 02-Jun-2019. (there is a chances employee may have more than one row with HR_STATUS = 'A' between active and inactive.). And for each effdt row employee Business_unit also available in IQN BU setup table.

When i run the query with start date 01-JUN-2019 and end date 30-JUN-2019 Query is fetching two rows. 01-JUN-2019 to 03-JUN-2019 one row and 02-JUN-2019 to 03-JUN-2019 one row.

instead of fetching two rows i want to fetch only one row from 01-JUN-2019 to 03-JUN-2019.  Because he is active CWR from 01-JUN-2019 to 03-JUN-2019 .

Note :

  1. when ever there is more than one row with HR_STATUS = 'A' between start and end dates, we need to check for each row business unit is available or not in the IQN BU table. If business unit is available then we need to skip that row.

in this case 27-MAY-2019 effdt row business unit valu is present in IQN BU table so we are fetching greatest value 01-JUN-2019 as start date.

For 02-JUN-2019 effdt row business unit also available in IQN BU table so we need to skip this row. ( but query is fetching this row )

2. If Business unit value is not available in IQN BU table then we need to treat row as HR_STATUS is inactive. ( This one is working fine in the Query)

similarly for Employee 733931 also it is fetching two rows for below data.

EMPL_IDEMPL_RCDEFFDTEFFSEQHR_STATUSPER_ORG
733931326-Jun-20190ACWR
733931328-Jun-20190ADTA

Query is fetching 26-JUN-2019 to 30-JUN-2019 one row and 28-JUN-2019 to 30-JUN-2019 second row.    For 28-JUN-2019 effdt row business unit value is available in IQN BU table so  i want to fetch only one from 26-JUN-2019 to 30-JUN-2019.

Vemula Muni

Hi @"Frank Kulash" , Could you please help me out on this.

Frank Kulash

Hi,

Sorry, I still don't understand why you want the results you posted.  The specific examples are good, but I need a general rule, such as "When there are two (or more) consecutive rows (in order by effdt) for the same emplid that all have hr_status='A', then only the first one counts.  The second (and later) consecutive 'A' rows can be ignored."

Vemula Muni

Hi @"Frank Kulash", Yes correct When there are two (or more) consecutive rows (in order by effdt) for the same emplid that all have hr_status='A',  and business unit also should be present in IQN BU table for all consecutive rows then only the first one counts.  The second (and later) consecutive 'A' rows can be ignored."   { If business unit is not available in IQN BU table for consecutive rows then we need to treat that row as inactive. Currently we are checking this thing we are validating in query }

Frank Kulash
Answer

Hi,

Here's one way to do it:

WITH    parameters    AS

(

    SELECT  TO_DATE ('01-JUN-2019', 'DD-MON-YYYY') AS pstartdt  -- Always use 4 digits for years

    ,       TO_DATE ('30-JUN-2019', 'DD-MON-YYYY') AS penddt

    ,       'CWR'                                  AS per_org

    FROM    dual

)

,    got_business_unit   AS

(

    SELECT  p.pstartdt, p.penddt

    ,       d1.emplid, d1.per_org, d1.effdt

    ,       CASE

                WHEN  b.business_unit  IS NULL

                THEN  'I'

                ELSE  d1.hr_status

            END         AS hr_status

    ,       b.business_unit

    FROM    parameters       p

    JOIN    ps_cts_job_dtl1  d1   ON  d1.per_org      = p.per_org

                                  AND d1.effdt       <= p.penddt

    OUTER APPLY (

                    SELECT  MAX (b.business_unit)  AS business_unit

                    FROM    ps_cts_job_dtl  d

      JOIN    ps_cts_iqn_bu   b   ON  b.business_unit = d.business_unit

                    WHERE   d.emplid        = d1.emplid

                    AND     d.effdt         = d1.effdt

                )            b

    WHERE   d1.hr_status  IN ('A', 'I')  -- if necessary

)

SELECT    emplid

,         GREATEST (sdt, pstartdt)  AS startdt

,         LEAST  ( NVL (edt - 1, penddt)

                 , penddt

                 )                 AS enddt

,         per_org

,         'A'                      AS hr_status

,         'Employee is active as CWR and BU is in IQN_BU setup table'

                                   AS reason

FROM      got_business_unit

MATCH_RECOGNIZE

          (

              PARTITION BY  emplid, pstartdt, penddt, per_org

              ORDER BY      effdt

              MEASURES      MIN (effdt)    AS sdt

              ,             i.effdt        AS edt

              ONE ROW PER MATCH

              PATTERN       (a+ i?)

              DEFINE        a  AS   hr_status  = 'A'

              ,             i  AS   hr_status  = 'I'

   )

WHERE     NVL (edt - 1, penddt)  > pstartdt

ORDER BY  emplid

,         sdt

;

The sub-queries are almost the same as the corresponding sub-queries you posted.

In parameters, I changed the column names to pstartdt and penddt, to avoid confusing them with startdt and enddt elsewhere.

In got_business_unit, I added a condition "WHERE   d1.hr_status  IN ('A', 'I')".  If hr_status is always 'A' or 'I', then you don't need it.

Marked as Answer by Vemula Muni · Sep 27 2020
Vemula Muni

Hi @"Frank Kulash", Thanks for your support, when i execute the above SQL i am getting below error. Please help me on this.

pastedImage_0.png

Frank Kulash

HI,

Vemula Muni wrote:

Hi [Frank Kulash](/people/Frank Kulash?customTheme=otn), Thanks for your support, when i execute the above SQL i am getting below error. Please help me on this.

pastedImage_0.png

That doesn't look like an Oracle error message.

IN and OUT parameters occur in PL/SQL procedures. Nothing in this thread before this has even mentioned PL/SQL. Are you doing something other than what is posted here?

I don't get any error when I run the query above in SQL*Plus (version 12.2.0.1.0). Do you?

What front end are you using when you get the error you posted? If it's detecting an error on line 1, then maybe that front end doesn't recognize WITH clauses (which have been part of Oracle since version 9.1, released in 2001).

The query you posted in your original message also started with WITH. Did it raise an error?

Vemula Muni

Hi @"Frank Kulash", I am using the below version SQL developer . till i executed all your quires in this and it is accepted WITH clauses. And i am not changing the query ( Executing as it is).

pastedImage_0.png

Frank Kulash

Hi,

Vemula Muni wrote:

Hi [Frank Kulash](/people/Frank Kulash?customTheme=otn), I am using the below version SQL developer . till i executed all your quires in this and it is accepted WITH clauses. And i am not changing the query ( Executing as it is).

pastedImage_0.png

So you're using SQL Developer version 1.5.5? That sounds kind of old.

The query in reply #6 runs (and returns the results you requested) when I try it in SQL Developer 4.1.4.21. It looks like you have a separate problem that involves SQL Developer, not SQL or PL/SQL. Start a new thread in the SQL Developer forum. When posting there, simplify the query as much as possible, so that it still causes the same error. That will also help isolate exactly what couases the problem, which will help if you can't solve the SQL Developer issue and need to find a different way to run the query

Ranagal

Or how about downloading a latest version from the internet and installing the same that is compatible with 12c ?

Regards,

Ranagal

Glen Conway

Your screen shot of SQL Developer 1.5.5 running on Java JDK 1.8 is pretty stunning.  However you got it to work, you should at least have had to click through some dialog saying 1.5.5 does not support that version of Java.

And even getting that far would be pretty surprising.  Ditto for achieving a connection to Oracle 12c.  For me, this is a definite +1 on upgrading.

Vemula Muni

Hi @"Frank Kulash", Thanks for your kind support. Now this is working fine in SQL Developer 4.1.4.21. Once again Thanks a lot.

L. Fernigrini

Just an additional comment, when using hard-coded dates, try to avoid using month names, use numbers instead.

This piece of code from your query would work in my environment:

TO_DATE ('01-JUN-2019', 'DD-MON-YYYY')

But if the month was August, you would have used AUG, and that's not a valid abbreviation for months in Spanish :-).

Since the conversions depends on the NLS settings of the sessions, you may find issues if any user uses another language... That may not be possible on some systems, but better prevent the issue than complain later!

You should use:

TO_DATE ('01-06-2019', 'DD-MM-YYYY')
L. Fernigrini

And completely OUT OF TOPIC, but regarding the error you had on the older SQL Developer, I found a similar error, this time running the 19.2 version, with the following query:

CREATE TABLE Producto (

  ID NUMBER(10) NOT NULL,

  Marca VARCHAR2(100) NOT NULL,

  Nombre VARCHAR2(100) NOT NULL,

  Precio NUMBER(10,4) NOT NULL,

  CONSTRAINT PK\_Producto PRIMARY KEY (ID)

);

INSERT INTO Producto VALUES (1, 'Sony', 'TV LED 40"', 20000);

INSERT INTO Producto VALUES (2, 'Philips', 'TV LED 32"', 15000 );

INSERT INTO Producto VALUES (3, 'Motorola', 'Moto Z4', 25000);

SELECT JSON_OBJECT('ID':id, 'Producto':nombre) AS json_data_new

FROM Producto p;

This works perfectly on Live SQL

pastedImage_0.png

pastedImage_1.png

But fails on SQL Developer 19.2!

pastedImage_2.png

Ir's the same error message (in Spanish since I had the UI configured in Spanish for a lecture). I will follow this issue up on the SQL Developer forum :-)

Vemula Muni

Hi @"Frank Kulash", Could you please Explain how this query will work.

Frank Kulash

Hi,

Vemula Muni wrote:

Hi Frank Kulash, Could you please Explain how this query will work.

Can you be more specific?  What, in particular, do you have trouble understanding?  I could waste a lot of time (both mine and yours) by explaining things you already know.

To understand a query that uses WITH sub-queries, start by looking at what each sub-query does.  For example, in the query from reply #6, you might run just the first sub-query, like this:

WITH    parameters    AS

(

    SELECT  TO_DATE ('01-JUN-2019', 'DD-MON-YYYY') AS pstartdt  -- Always use 4 digits for years

    ,       TO_DATE ('30-JUN-2019', 'DD-MON-YYYY') AS penddt

    ,       'CWR'                                  AS per_org

    FROM    dual

)

SELECT  *

FROM    parameters;

In this case, the first sub-query is so simple you probably won't need to do it, but this gives you the idea.

When you understand what this does, then try running the next sub-query, got_business_unit, the same way.  (Since it references the first sub-query, you'll need to include that, too.)  If you can't understand what  a sub-query does, then ask a specific question about it.  For example:

"When I run … I get the results ….  Why don't I get … instead?  The SQL manual, under … says …; doesn't that mean …?"

If there are unfamiliar features (maybe OUTER APPLY or MATCH_RECOGNIZE) or functions, search the documentation and sites like this for simple examples of how they work.  If you see examples of specific features that you can't understand, then you might want to start a separate thread asking about that example works.

In this problem, we want to know if the period given in the parameters overlaps with another period (the period between an 'A' row and an 'I' row).  How can you tell if two periods overlap?  They DO NOT overlap if (and only if) one period ends before the other begins, so they DO overlap if (and only if) each period begins before the other ends, so you need to test two conditions.  In reply #6, this is tricky, because one of those tests

d1.effdt       <= p.enddt

is done in the got_business_unit sub-query, but the other test

NVL (edt - 1, penddt)  > pstartdt

is done much later, at the very end of the main query.

1 - 17

Post Details

Added on Aug 5 2019
17 comments
597 views