3

Consider a sales application where we have two tables SALES and INTERNAL_SALES.

  • Table "SALES" references the number of transactions made by each sales person outside the company.
  • Table "INTERNAL_SALES" references the number of transactions made by each sales person inside the company to another sales person.

SALES Table: Each date has one entry against each sales person even if transactions are zero.

id |  day          | sales_person | number_of_transactions
1  | 2011-08-01    | Tom          | 1000
2  | 2011-08-01    | Ben          | 500
3  | 2011-08-01    | Anne         | 1500
4  | 2011-08-02    | Tom          | 0
5  | 2011-08-02    | Ben          | 800
6  | 2011-08-02    | Anne         | 900
7  | 2011-08-03    | Tom          | 3000
8  | 2011-08-03    | Ben          | 0
9  | 2011-08-03    | Anne         | 40

INTERNAL_SALES Table: This table logs only the transactions that were actually made between sales persons.

id | day        | sales_person_from | sales_person_to | number_of_transactions
 0 | 2011-08-01 | Tom               | Ben             | 10
 1 | 2011-08-01 | Tom               | Anne            | 20
 2 | 2011-08-01 | Ben               | Tom             | 50
 3 | 2011-08-03 | Anne              | Tom             | 30
 4 | 2011-08-03 | Anne              | Tom             | 30

Now the problem is to come up with total transactions by each sales person on a daily basis. The way I did this is:

SELECT day, sales_person, sum(num_transactions) from
(
  SELECT day, sales_person, number_of_transactions As num_transactions FROM sales;
  UNION
  SELECT day, sales_person_from As sales_person, sum(number_of_transactions) As num_transactions FROM internal_sales GROUP BY day, sales_person_from;
)
GROUP BY day, sales_person;

This is too slow and looks ugly. I am seeking a better solution. By the way the database being used in Oracle and I have no control over database except that I can run queries against it.

1
  • 2
    This is slow? It doesn't look at all ugly to me. I don't see a more straightforward solution. There's nothing to optimize here without the ability to modify the DB. Commented Aug 11, 2011 at 6:25

1 Answer 1

7

There is no need to aggregate twice, and the union operator typically does an implicit unique sort which, again, in not necessary in your case.

SELECT day, sales_person, sum(num_transactions) from
(
    SELECT day, sales_person, number_of_transactions As num_transactions FROM sales;
    UNION ALL 
    SELECT day, sales_person_from, number_of_transactions FROM internal_sales;
)
GROUP BY day, sales_person;

Removing the intermediate aggregation and the unique sort should help a bit.

Sign up to request clarification or add additional context in comments.

3 Comments

+1 There's no need to alias SALES_PERSON_FROM as the database takes the column name from the first SELECT statement and ignores thes subsequent ones.
Also, the UNION can potentially return the wrong result. For instance, if sales rep TOM has made 10 external sales and 10 internal sales on the same day, their aggregated figure is 20; the UNION sort would treat those two rows as duplicates and so the aggregated figure would only be 10.
Thank you for your help. The explanation that goes with your solution increased my understanding of sql.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.