shakur007
shakur007

Reputation: 23

Oracle PL/SQL Query

The code is working but the top (X) TOP PRICE are not fetched accordingly. How do I use Ties with my code ?
Some of the top (x) are ignored.

This is my table

    STAFF_NUMBER STAFF_NAME                     PRICE
    ------------ ----------------------------------------- ----------
          36 Helen Hilfg                   330066
          52 Octavia Chan                  356885
          36 Helen Hilfg                   176088
          4 Heidi Lee                      231046
          6 Jill Murphy                    469844
          32 Lily Roy                      247549
          58 John Roy                      309299

This is what I am getting

   SQL> SQL> SQL> exec p4(3)
    PERSON#    NAME
     36 Helen Hilfg
     52 Octavia Chan
     4 Heidi Lee

This is what I suppose to get

    SQL> exec p4(3);
    PERSON#  NAME
     --------------------
    36       Helen Hilfg
    6        Jill Murphy
    52       Octavia Chan

Here is my procedure

CREATE OR REPLACE PROCEDURE p3( X NUMBER ) 
AS
  staff_number  NUMBER ;
  staff_name VARCHAR2(30) ;

  CURSOR c1 IS
    SELECT staff_number, 
           staff_name  
     FROM (SELECT P.peid staff_number, 
                  P.firstname || ' ' || P.lastname  staff_name , 
                  D.price  PRICE
             FROM Contact C, 
                  Deal D, 
                  Person P, 
                  Staff S
            WHERE S.peid = C.peid 
              AND C.pno = D.pno 
              AND P.peid = C.peid 
              AND (SYSDATE - D.day < 365))
    WHERE ROWNUM  <= X
 GROUP BY staff_name , staff_number
 ORDER BY SUM( price ) DESC ; 

BEGIN

   dbms_output.put_line( ' PERSON# ' ||'  '||' NAME ' );
   FOR R IN c1
   LOOP
     staff_number := R.staff_number ;
     staff_name := R.staff_name ;
     dbms_output.put_line( staff_number ||' '||  staff_name ) ;  
   END LOOP ;
END;
/

The code is working but the top (X) TOP PRICE are not fetched accordingly. How do I use Ties with my code ?
Some of the top (x) are ignored.

Upvotes: 0

Views: 487

Answers (3)

APC
APC

Reputation: 146239

Please, look at your query and try to understand what it's doing.

You have an inner query which filters on some criteria. You haven't given us the data, so we can't predict what that result will look like. Let's assume it returns the records you think it's going to.

Then in your outer query you have a WHERE clause which filters on ROWNUM <= X. This means the query will return three rows, essentially at random, which will then be grouped and sorted on the basis of SUM(PRICE).

This is obviously not what you're expecting. But that's because you don't understand how ROWNUM works. It is easy enough to fix: you just need to apply the ROWNUM filter to the grouped and sorted results. Like this:

SELECT * FROM (
    SELECT staff_number, 
           staff_name  
     FROM (SELECT P.peid staff_number, 
                  P.firstname || ' ' || P.lastname  staff_name , 
                  D.price  PRICE
             FROM Contact C, 
                  Deal D, 
                  Person P, 
                  Staff S
            WHERE S.peid = C.peid 
              AND C.pno = D.pno 
              AND P.peid = C.peid 
              AND (SYSDATE - D.day < 365)
              )
 GROUP BY staff_name , staff_number
 ORDER BY SUM( price ) DESC
)
WHERE ROWNUM  <= X
; 

Upvotes: 1

Trevor North
Trevor North

Reputation: 2296

First as DCookie suggested, ensure you have enabled output

 set serveroutput on

If you dont get the result you are expecting, try breaking down the query until you find where the data is missing e.g. if there are no deals in the last year, you wont get anything back so select just one deal record which meets the criteria and then check the other tables for matching records. Also confirm there is data in each of the tables as if any of them is empty, again the result set will be empty.

e.g.

select pno  from deal where (sysdate - day) < 365;

(The assumption is that day is a date field), then select this pno from the contact table

select peid from contact where pno=&pno;

select this peid from person table and if any of those selects come up empty, check example values for the field to confirm the id numbers are actually matching between the tables e.g.

select count(*) 
  from deal d 
 where exists (select 1 from contact c where c.pno=d.pno)

select count(*) 
  from contact c 
  where exists (select 1 from person p where p.peid=c.peid)

You are joining to the STAFF table but both STAFF and CONTACT have the same peid field and you arent selecting any columns from the STAFF table but you get firstname, lastname and staff number from the person table.

Are there other rows in the person table for people who might be contacts on deals but are not staff (meaning the join to staff is to validate the type) ? if not then maybe the staff table is redundant here.

When you have a complicated query (and they can get really complicated, like a couple of pages of A4 printed out), you have to have a method to breakdown the query to simpler queries that you can validate each step and this leads you to where the error occurs.

Hope that helps.


probably you should use the analytic functions so a query something like

       select staff_number, staff_name from (
       SELECT P.peid staff_number, 
              P.firstname || ' ' || P.lastname  staff_name , 
              D.price  PRICE
              RANK() OVER (ORDER BY d.price) AS position
         FROM Contact C, 
              Deal D, 
              Person P, 
              Staff S
        WHERE S.peid = C.peid 
          AND C.pno = D.pno 
          AND P.peid = C.peid 
          AND (SYSDATE - D.day < 365)
 ) where position < x

Use rank() if you want to skip positions where there are several people with a tied position or dense_rank() for having several records with the same position.


just saw that you need it to sum deals for the same person so might be more like this (untested)

   select staff_number, staff_name from (
       SELECT P.peid staff_number, 
              P.firstname || ' ' || P.lastname  staff_name , 
              sum(D.price)  PRICE
              RANK() OVER (ORDER BY sum(d.price)) AS position
         FROM Contact C, 
              Deal D, 
              Person P, 
              Staff S
        WHERE S.peid = C.peid 
          AND C.pno = D.pno 
          AND P.peid = C.peid 
          AND (SYSDATE - D.day < 365)
         group by p.peid, p.first_name|' '||p.lastname
 ) where position < x

Definitely have a look at the analytic functions as rownum just returns the first rows in the query, it doesnt return the full result set and then give you the top N records. You may need to use a partition clause but give it a try and see here for examples of rank.

http://psoug.org/reference/analytic_functions.html

Upvotes: 1

DCookie
DCookie

Reputation: 43533

When you say "nothing was output", how did you run the procedure? Did you enable dbms_output? For example, if you ran it in SQL*Plus, did you SET SERVEROUTPUT ON? You should have at least got your header line, assuming the procedure has no compile errors.

Does the query itself run outside of the procedure, in SQL*Plus?

Upvotes: 1

Related Questions