tags:

views:

104

answers:

4

I have three tables with following structure and info:

CREATE TABLE customer (
  customer_id mediumint(8) unsigned NOT NULL auto_increment,
  name varchar(50) NOT NULL,
  PRIMARY KEY (customer_id)
);

INSERT INTO customer VALUES (1, 'Dagmar');
INSERT INTO customer VALUES (2, 'Dietmar');
INSERT INTO customer VALUES (3, 'Sabine');

CREATE TABLE sales_cars (
  sale_id mediumint(8) unsigned NOT NULL auto_increment,
  customer_id mediumint(8) unsigned NOT NULL,
  sale_amount decimal(10,2) NOT NULL,
  PRIMARY KEY (sale_id)
);

INSERT INTO sales_cars VALUES (1, 3, 14.40);
INSERT INTO sales_cars VALUES (2, 1, 28.30);
INSERT INTO sales_cars VALUES (3, 2, 34.40);
INSERT INTO sales_cars VALUES (4, 2, 25.60);

CREATE TABLE sales_parts (
  sale_id mediumint(8) unsigned NOT NULL auto_increment,
  customer_id mediumint(8) unsigned NOT NULL,
  sale_amount decimal(10,2) NOT NULL,
  PRIMARY KEY (sale_id)
);

INSERT INTO sales_parts VALUES (1, 2, 68.20);
INSERT INTO sales_parts VALUES (2, 3, 21.30);
INSERT INTO sales_parts VALUES (3, 3, 54.40);
INSERT INTO sales_parts VALUES (4, 1, 35.70);

sales_car and sales_parts hold sales made by customers. The idea is to write a query that sums the "sale_amount" of both cars and parts for a particular customer and groups the result by id.

Does someone hast a suggestion how I can go about this problem?

+2  A: 

Something like this will be what you are after...

SELECT *,
       (SELECT SUM(sale_amount)
            FROM sales_cars
            WHERE sales_cars.customer_id = customer.customer_id) AS car_sales,
       (SELECT SUM(sale_amount)
            FROM sales_parts
            WHERE sales_parts.customer_id = customer.customer_id) AS part_sales
    FROM customer;
Brian Hooper
your solution gives me the sale_amount for each customer in the separate tables. The idea was to get a single value for the sale_amount, i.e. add the values from both tables. I haven't verified solution 1 above, but it seems like what i require. Thanks for your contibution
+3  A: 

You may want to try something like the following:

SELECT  c.customer_id,
        tot_cars.total + tot_parts.total AS total_sales
FROM    customer c
JOIN    (
           SELECT   customer_id, SUM(sale_amount) total
           FROM     sales_cars
           GROUP BY customer_id
        ) tot_cars ON (tot_cars.customer_id = c.customer_id)
JOIN    (
           SELECT   customer_id, SUM(sale_amount) total
           FROM     sales_parts
           GROUP BY customer_id
        ) tot_parts ON (tot_parts.customer_id = c.customer_id);

Result:

+-------------+-------------+
| customer_id | total_sales |
+-------------+-------------+
|           1 |       64.00 |
|           2 |      128.20 |
|           3 |       90.10 |
+-------------+-------------+
3 rows in set (0.03 sec)

UPDATE: Further to comments below:

Let's start with the sale_date field:

CREATE TABLE sales_cars (
  sale_id mediumint(8) unsigned NOT NULL auto_increment,
  customer_id mediumint(8) unsigned NOT NULL,
  sale_amount decimal(10,2) NOT NULL,
  sale_date datetime NOT NULL,
  PRIMARY KEY (sale_id)
);

INSERT INTO sales_cars VALUES (1, 3, 14.40, '2010-07-01 12:00:00');
INSERT INTO sales_cars VALUES (2, 1, 28.30, '2010-07-05 12:00:00');
INSERT INTO sales_cars VALUES (3, 2, 34.40, '2010-07-10 12:00:00');
INSERT INTO sales_cars VALUES (4, 2, 25.60, '2010-07-20 12:00:00');

To get the date of the latest sale of each customer, you can join the query described previously with another derived table, as follows:

SELECT  c.customer_id,
        tot_cars.total + tot_parts.total AS total_sales,
        latest_sales.date AS latest_sale
FROM    customer c
JOIN    (
           SELECT   customer_id, SUM(sale_amount) total
           FROM     sales_cars
           GROUP BY customer_id
        ) tot_cars ON (tot_cars.customer_id = c.customer_id)
JOIN    (
           SELECT   customer_id, SUM(sale_amount) total
           FROM     sales_parts
           GROUP BY customer_id
        ) tot_parts ON (tot_parts.customer_id = c.customer_id)
JOIN    (
           SELECT   customer_id, MAX(sale_date) date
           FROM     sales_cars
           GROUP BY customer_id
        ) latest_sales ON (latest_sales.customer_id = c.customer_id);

Result:

+-------------+-------------+---------------------+
| customer_id | total_sales | latest_sale         |
+-------------+-------------+---------------------+
|           1 |       64.00 | 2010-07-05 12:00:00 |
|           2 |      128.20 | 2010-07-20 12:00:00 |
|           3 |       90.10 | 2010-07-01 12:00:00 |
+-------------+-------------+---------------------+
3 rows in set (0.07 sec)

Do you see the pattern? There are other approaches to tackle the same problem, but joining with derived tables is a very easy and straightforward technique.

Then regarding the changes in the customer table, I assume you mean something like this:

CREATE TABLE customer (
  customer_id mediumint(8) unsigned NOT NULL auto_increment,
  first_name varchar(50) NOT NULL,
  last_name varchar(50) NOT NULL,
  gender char(1) NOT NULL,
  PRIMARY KEY (customer_id)
);

INSERT INTO customer VALUES (1, 'Joe', 'Doe', 'M');
INSERT INTO customer VALUES (2, 'Jane', 'Smith', 'F');
INSERT INTO customer VALUES (3, 'Peter', 'Brown', 'M');

To concatenate string fields in MySQL, you can simply use the CONCAT() function:

SELECT CONCAT(c.first_name, ' ', c.last_name) as full_name
FROM   customer c;

Returns:

+-------------+
| full_name   |
+-------------+
| Jane Smith  |
| Peter Brown |
| Joe Doe     |
+-------------+
3 rows in set (0.01 sec)

To apply the 'Mr' or 'Ms' conditionally, you can then use the CASE statement:

SELECT (CASE c.gender WHEN 'M' THEN 'Mr' WHEN 'F' THEN 'Ms' END) salutaiton,
       CONCAT(c.first_name, ' ', c.last_name) as full_name
FROM   customer c;

Returns:

+------------+-------------+
| salutaiton | full_name   |
+------------+-------------+
| Ms         | Jane Smith  |
| Mr         | Peter Brown |
| Mr         | Joe Doe     |
+------------+-------------+
3 rows in set (0.01 sec)

You can also concatenate the two fields together:

SELECT CONCAT((CASE c.gender WHEN 'M' THEN 'Mr' WHEN 'F' THEN 'Ms' END), ' ',
               c.first_name, ' ', c.last_name) as full_name
FROM   customer c;

Returns:

+----------------+
| full_name      |
+----------------+
| Ms Jane Smith  |
| Mr Peter Brown |
| Mr Joe Doe     |
+----------------+
3 rows in set (0.00 sec)

Finally, we can attach this to our main query, as follows:

SELECT  c.customer_id,
        CONCAT((CASE c.gender WHEN 'M' THEN 'Mr' WHEN 'F' THEN 'Ms' END), ' ',
                   c.first_name, ' ', c.last_name) as full_name,
        tot_cars.total + tot_parts.total AS total_sales,
        latest_sales.date AS latest_sale
FROM    customer c
JOIN    (
           SELECT   customer_id, SUM(sale_amount) total
           FROM     sales_cars
           GROUP BY customer_id
        ) tot_cars ON (tot_cars.customer_id = c.customer_id)
JOIN    (
           SELECT   customer_id, SUM(sale_amount) total
           FROM     sales_parts
           GROUP BY customer_id
        ) tot_parts ON (tot_parts.customer_id = c.customer_id)
JOIN    (
           SELECT   customer_id, MAX(sale_date) date
           FROM     sales_cars
           GROUP BY customer_id
        ) latest_sales ON (latest_sales.customer_id = c.customer_id);

Returns:

+-------------+----------------+-------------+---------------------+
| customer_id | full_name      | total_sales | latest_sale         |
+-------------+----------------+-------------+---------------------+
|           1 | Mr Joe Doe     |       64.00 | 2010-07-05 12:00:00 |
|           2 | Ms Jane Smith  |      128.20 | 2010-07-20 12:00:00 |
|           3 | Mr Peter Brown |       90.10 | 2010-07-01 12:00:00 |
+-------------+----------------+-------------+---------------------+
3 rows in set (0.02 sec)
Daniel Vassallo
this is exactly what i wanted thanks. Imagine i now delete the name column in the customer table to add 3 new columns: "gender", "lastname" and "firstname". The idea is to have the total_sales displayed with name of the customer in the form Mr. Dietmar Peter for example. So the complication here is turning the gender into either Mr of Mrs and combining first and lastname. Secondly if a new column "sale_date" were added to the sales_cars and sales_parts, how would you output the date of the most recent sale along side the total_sales say in a new column. Thanks alot
@vibanty: Check the updated answer :)
Daniel Vassallo
+1  A: 
  select customer.customer_id,(totalcar + totalparts) as total from customer  
inner join
(select customer_id ,sum(sale_amount) as totalcar 
        from sales_cars group by customer_id) d
on customer_id = d.customer_id
inner join 
(select customer_id , sum(sale_amount) as totalparts  
        from sales_parts group by customer_id) d1

on customer_id = d1. customer_id
Pranay Rana
I have verified your result and the following points are to note:1- customer_id after the "on" part of the join is ambiguos since customer_id appears on all three tables2- your solution will issue a separate some for the cars and parts sales of each customer. The requirement is to add up the sum. See the first solution which i have verified to be correct.Thanks for your contribution
see the answer its updated now
Pranay Rana
A: 

i dont think aggregated subqueries is good idea

select customer_id, sum(sale_amount) from (
select customer.customer_id, sale_amount from customer
join sales_cars on sales_cars.customer_id = customer.customer_id
union all
select customer.customer_id, sale_amount from customer
join sales_parts on sales_parts.customer_id = customer.customer_id
) totals group by customer_id
Whenever you catch yourself write `UNION` check if `UNION ALL` will work, as the performance delta is ~O(n log n). Actually here `UNION` without `ALL` is a bug (even though usually you will not have a part that costs exactly as much as a whole car sold to the same customer).
Unreason
Granted, either synthetic key or union all is missing here.
thanks alot for your contribution. Imagine i now delete the name column in the customer table to add 3 new columns: "gender", "lastname" and "firstname". The idea is to have the total_sales displayed with name of the customer in the form Mr. Dietmar Peter for example. So the complication here is turning the gender into either Mr of Mrs and combining first and lastname. Secondly if a new column "sale_date" were added to the sales_cars and sales_parts, how would you output the date of the most recent sale along side the total_sales say in a new column. Thanks alot