开发者

Is there a way to answer this question in one query?

I have a MySQL database of 3 tables:

I. Person (id, name, purchases)

II. Purchase(id, product, date_purchased)

III. Catalog(id, product, cost-per-unit)

Person.purchases holds Purchase.id. That is, everytime a person buys something, the order id gets recorded in Person.purchases. For eg. Person.purchases has 1, 300, 292 开发者_如何转开发stored in it.

Each Purchase entry records an instance of any item purchased. So, Purchase.id = 300 could be "foo".

And Catalog holds description about "foo".

What I want to find out is how to answer: "Who bought "foo"? I know how to answer this question in 2 steps as such:

Step 1: SELECT Purchases.id FROM Purchases INNER JOIN Catalog WHERE Purchases.product = Catalog.product;

I would store step 1's result in a variable tmp;

STEP 2: SELECT name FROM Person WHERE Person.orders LIKE "%tmp%";

I am using LIKE above because Person.orders stores multiple Purchase.id.

Is there a way to combine these two into one query?


The question can be answered using a single query:

Using EXISTS

SELECT a.name
  FROM PERSON a
 WHERE EXISTS(SELECT NULL
                FROM PURCHASE b 
                JOIN CATALOG c ON c.product = b.product
               WHERE FIND_IN_SET(b.id, a.purchases) > 0
                 AND c.product = 'foo')

Using a JOIN:

This requires DISTINCT (or GROUP BY) because duplicates are possible, if a person/customer has bought "foo" more than once.

SELECT DISTINCT a.name
  FROM PERSON a
  JOIN PURCHASE b ON FIND_IN_SET(b.id, a.purchases) > 0
  JOIN CATALOG c ON c.product = b.product
 WHERE c.product = 'foo'

Addendum

I agree with the other answers that the data model is poor - there should be a person/customer id in the PURCHASE table, not the PERSON table. But it doesn't change things drastically.


This is a poor database design and it's holding you back from answering a relatively simple question. I'd design your tables somewhat like this:

customers (id, name)
purchases (id, product_id, customer_id, date_purchased)
products (id, product_name, cost_per_unit)

Thus, your query to figure out 'Who bought foo?' is:

SELECT c.id, c.name
FROM products pr 
   LEFT JOIN purchases pu ON (pr.id = pu.product_id)
   INNER JOIN customers c ON (pu.customer_id = c.id)
WHERE product_id = foo 
-- could replace with product_name = 'foo' here, but you should know product_id

This has your database in a somewhat normal form (I don't remember which one exactly) so you can take advantage of the features that relational databases offer.

It might also be useful to make another table here, call it receipts, and rename purchases to line_items. This ensures that you can track customers who buy multiple items in one purchase, etc.


For MySQL this might be a better solutions:

SELECT person.*
FROM person
JOIN purchases
ON FIND_IN_SET(purchases.id,person.purchases) > 0
WHERE purchases.product = 'foo';


A much better structure of your tables would be:

I. Person (personid, name) ---purchases deleted from here

II. Purchase (purchaseid, buyerid, productid, date_purchased) ---buyerid added

III. Catalog (productid, product, cost-per-unit)

So, instead of storing purchaces of a person in Person table, store them in Purchase table.

This will have several benefits:

  1. You can store as many purchases as you like. The way it is now, the "purchases" field will eventually be filled with purchases and what will you do then?

  2. Easier to write your queries.


(If Person.purchases has ",1,300,292," stored in it, e.g. commas at start and end of field and no spaces), your question can be answered in one query like that:

If there are spaces and no commas at start and end the condition wili be more complex but surely it can be done.

SELECT p.id, p.name
FROM Person p
  JOIN Purchase pur
    ON p.purchases LIKE CONCAT("%,",CAST(pur.id AS CHAR),",%")
WHERE pur.product LIKE "foo"

And you don't need the join with Catalog since Product name is in Purchase table too.

If you do want to have info from Catalog, you could have the other join too:

SELECT p.id, p.name, cat.*
FROM Person p
  JOIN Purchase pur
    ON p.purchases LIKE CONCAT("%,",CAST(pur.id AS CHAR),",%")
  JOIN Catalog cat
    ON pur.product = cat.product
WHERE pur.product LIKE "foo"     ---or cat.product LIKE "foo"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜