Keir Simmons
Keir Simmons

Reputation: 1684

Complex multiple join query across 3 tables

I have 3 tables:

shops, PRIMARY KEY cid,zbid
shop_items, PRIMARY KEY id
shop_inventory, PRIMARY KEY id

shops a is related to shop_items b by the following: a.cid=b.cid AND a.zbid=b.szbid shops is not directly related to shop_inventory

shop_items b is related to shop_inventory c by the following: b.cid=c.cid AND b.id=c.iid

Now, I would like to run a query which returns a.* (all columns from shops). That would be:

SELECT a.* FROM shops a WHERE a.cid=1 AND a.zbid!=0

Note that the WHERE clause is necessary.

Next, I want to return the number of items in each shop:

SELECT
  a.*,
  COUNT(b.id) items
FROM shops a
LEFT JOIN shop_items b ON b.cid=a.cid AND b.szbid=a.zbid
WHERE a.cid=1
GROUP BY b.szbid,b.cid

As you can see, I have added a GROUP BY clause for this to work.

Next, I want to return the average price of each item in the shop. This isn't too hard:

SELECT
  a.*,
  COUNT(b.id) items,
  AVG(COALESCE(b.price,0)) average_price
FROM shops a
LEFT JOIN shop_items b ON b.cid=a.cid AND b.szbid=a.zbid
WHERE a.cid=1
GROUP BY b.szbid,b.cid

My next criteria is where it gets complicated. I also want to return the unique buyers for each shop. This can be done by querying shop_inventory c, getting the COUNT(DISTINCT c.zbid). Now remember how these tables are related; this should only be done for the rows in c which relate to an item in b which is owned by the respective shop, a.

I tried doing the following:

SELECT
  a.*,
  COUNT(b.id) items,
  AVG(COALESCE(b.price,0)) average_price,
  COUNT(DISTINCT c.zbid)
FROM shops a
LEFT JOIN shop_items b     ON b.cid=a.cid AND b.szbid=a.zbid
LEFT JOIN shop_inventory c ON c.cid=b.cid AND c.iid=b.id
WHERE a.cid=1
GROUP BY b.szbid,b.cid

However, this did not work as it messed up the items value. What is the proper way to achieve this result?

I also want to be able to return the total number of purchases made in each shop. This would be done by looking at shop_inventory c and adding up the c.quantity value for each shop. How would I add that in as well?

Upvotes: 0

Views: 115

Answers (2)

Zane Bien
Zane Bien

Reputation: 23125

Try this solution:

SELECT    a.*,
          COALESCE(b.item_cnt,  0) AS item_cnt,
          COALESCE(b.avg_price, 0) AS avg_price,
          COALESCE(b.buyer_cnt, 0) AS buyer_cnt
FROM      shops a
LEFT JOIN (
          SELECT    a.cid, 
                    a.szbid, 
                    COUNT(*)     AS item_cnt, 
                    AVG(a.price) AS avg_price,
                    b.buyer_cnt
          FROM      shop_items a
          LEFT JOIN (
                    SELECT   cid,
                             iid,
                             COUNT(DISTINCT zbid) AS buyer_cnt
                    FROM     shop_inventory
                    WHERE    cid = 1
                    GROUP BY cid, 
                             iid
                    ) b ON a.cid = b.cid AND a.id = b.iid
          WHERE     a.cid = 1 AND
                    a.szbid <> 0
          GROUP BY  a.cid,
                    a.szbid
          ) b ON a.cid = b.cid AND a.zbid = b.szbid
WHERE     a.cid = 1 AND 
          a.zbid <> 0

Upvotes: 2

biziclop
biziclop

Reputation: 14596

Instead of COUNT(DISTINCT c.zbid) + LEFT JOIN shop_inventory you could write a subselect:

SELECT
  a.*,
  COUNT(b.id) items,
  AVG(COALESCE(b.price,0)) average_price,

  ( SELECT COUNT(DISTINCT c.zbid)
    FROM shop_inventory c
    WHERE c.cid=b.cid AND c.iid=b.id
  )

FROM shops a
LEFT JOIN shop_items b     ON b.cid=a.cid AND b.szbid=a.zbid
WHERE a.cid=1
GROUP BY b.szbid,b.cid

Upvotes: 1

Related Questions