MySQL Recommend an item. - mysql

I am trying to run a query that for a selected person, it will recommend items based on their purchase history and what other people have bought who also bought that item.
Example:
Customer 1 purchases item A, and B.
Customer 2 purchases item B.
Customer 2 is recommended, item A.
I have 4 tables that are used in this I will list them with the relevant row names.
Customer
PK: Cust_ID
Session
PK: Ses_ID
FK: Cust_ID
Order
PK: Order_ID
FK: ItemRef_ID
FK: Session_ID
Item
PK: Item_ID
I'm pretty new to SQL but here's what I've got so far, following that I really have no idea how to go about doing it.
Currently it only gives the items that the selected customer has bought 'Lani Morgan', but instead I'd like it to display items that other customers have bought, who have bought the item 'Lani Morgan' has bought.
SELECT Item_Desc, Item_ID
FROM rbickers.Item
LEFT JOIN rbickers.Order
ON Item.Item_id = Order.itemref_id
LEFT join rbickers.Session
ON Order.Session_id = Session.Ses_id
left join rbickers.customer
On customer.cust_ID = session.Cust_ID
Where Cust_First = "Lani" and Cust_Last = "Morgan"
GROUP BY Item_Type
LIMIT 10
Any help would really be appreciated, thanks.

So by your basic version and with the supplied schema, the way I see it implemented is by checking for every item, if not bought by customer1, but bought by any other customer buying items customer1 has bought.
Since this schema requires you to join through all of the given tables, I'd ask you to create a view of the join you've provided:
create view CustomerItems as
SELECT *
FROM rbickers.Item
LEFT JOIN rbickers.Order
ON Item.Item_id = Order.itemref_id
LEFT join rbickers.Session
ON Order.Session_id = Session.Ses_id
left join rbickers.customer
On customer.cust_ID = session.Cust_ID
#Where Cust_First = "Lani" and Cust_Last = "Morgan"
and then:
select distinct item.* from item, CustomerItems cust1 where
cust1.Cust_First="Lani" and cust1.Cust_Last="Morgan" and
item.Item_ID not in(
select Item_ID from CustomerItems cust2 where cust1.Cust_ID=cust2.Cust_ID)
and exists(select * from CustomerItems cust3 where
cust3.Cust_ID<>cust1.cust_ID and item.Item_ID=cust3.Item_ID);

Related

MySQL - Marking duplicates from several table fields, as well as data from another table

I have two tables - one shows user purchases, and one shows a product id with it's corresponding product type.
My client wants to make duplicate users inactive based on last name and email address, but wants to run the query by product type (based on what type of product they purchased), and only wants to include user_ids who haven't purchased paint (product ids 5 and 6). So the query will be run multiple times - once for all people who have purchased lawnmowers, and then for all people who have purchased leafblowers etc (and there will be some overlap between these two). No user_id that has purchased paint should be made inactive.
In terms of who should stay active among the duplicates, the one to stay active will be the one with the highest product id purchased (as products are released annually). If they have multiple records with the same product id, the record to stay active will be the one with most recent d_modified and t_modified.
I also want to shift the current value of 'inactive' to the 'previously_inactive' column, so that this can be easily reversed if need be.
Here is some sample table data
If the query was run by leafblower purchases, rows 5, 6, and 7 would be made inactive. This is the expected output:
If the query was run by lawnmower purchases, rows 1 and 2 would be made inactive. This would be the expected output:
If row 4 was not the most recent, it would still not be made inactive, as user_id 888 had bought paint (and we want to exclude these user_ids from being made inactive).
This is an un-optimised version of the query for 'leafblower' purchases (it is working, but will probably be too slow in the interface):
UPDATE test.user_purchases
SET inactive = 1
WHERE id IN (
SELECT z.id
FROM (SELECT * FROM test.user_purchases) z
WHERE z.product_id IN (
SELECT product_id
FROM test.products
WHERE product_type IN ("leafblower")
)
AND id NOT IN (
SELECT a.id
FROM (SELECT * FROM test.user_purchases) a
INNER JOIN (
SELECT r.surname, r.email
FROM (SELECT * FROM test.user_purchases) r
JOIN test.products s on r.product_id = s.product_id
WHERE s.product_type IN ("paint")
) b
WHERE a.surname = b.surname
AND a.email = b.email
)
AND id NOT IN (
SELECT MAX(z.id)
FROM (SELECT * FROM test.user_purchases) z
WHERE z.product_id IN (
SELECT product_id
FROM test.products
WHERE product_type IN ("leafblower")
)
AND id NOT IN (
SELECT a.id
FROM (SELECT * FROM test.user_purchases) a
INNER JOIN (
SELECT r.surname, r.email
FROM (SELECT * FROM test.user_purchases) r
JOIN test.products s on r.product_id = s.product_id
WHERE s.product_type IN ("paint")
) b
WHERE a.surname = b.surname
AND a.email = b.email
)
GROUP BY surname, email
)
)
Any suggestions on how I can streamline this query and optimise the speed of it would be much appreciated.

Need help on query sum total quantity show wrong output

Process:
When user buy item and check out then there's cart and cart items table to store the transaction.
1 cart_id have many item which stored in cart items table.
After purchase succeed, then will generate a purchase order id and stored in purchase order table .
In purchase order table, id_cart and status will be stored.
From here, i am trying to calculate quantity based on id_product or id branch or etc from the purchase made.
There is receiving and ordered quantity field, which in some cases quantity received field might be null, so i will take ordered quantity value.
This is my query
SELECT id_product,sum(DISTINCT(COALESCE(received_qty, quantity)))
FROM (SELECT C.id_cart,C.received_qty,C.quantity , P.id_product,
PO.id_purchase_order, PO.status
FROM (SELECT * FROM cart_items WHERE id_cart IN (SELECT id_cart FROM purchase_orders)) AS C
LEFT JOIN products as P on p.id_product = c.id_product
LEFT JOIN purchase_orders AS PO ON C.id_cart = PO.id_cart ) AS A
GROUP By A.id_product
Table data
The cart id in will be duplicated based on product's supplier. Because need to track and send separately to supplier.
Result
By right the product id for 1212 should be 1 and 1223 is 2, total qty =3.
What's wrong with my query ?
Your outer joins seem to cause multiplication of your data, but there are so many unnecessary layers of fluff in your query that I cant make it out exactly.
How about just this:
SELECT id_product, sum(COALESCE(received_qty, quantity)) AS Nmbr
FROM cart_items
GROUP BY id_product
If you want to make sure the cart is in your purchase_orders:
SELECT c.id_product, sum(COALESCE(c.received_qty, c.quantity)) AS Nmbr
FROM cart_items c
JOIN (SELECT DISTINCT id_cart FROM purchase_orders) p ON p.id_cart = c.id_cart
GROUP BY c.id_product

Using ORDER BY on an INNER JOIN SQL query

I have the following three tables: Product, User and Purchased.
Product contains:
A unique identifier (productID)
A name (productname)
A price
(productprice)
User contains:
A unique identifier (userID)
Purchased contains:
Two identifiers noted as private keys, productID and userID
A date of the record (creationdate)
My query should return a list of unique products that were bought on the retailer’s site since January 1st with most expensive product returned first.
My Query:
SELECT Product.productID, Product.productname, Purchased.creationdate
FROM Product
INNER JOIN Purchased
ON Product.productID = Purchased.productID;
ORDER BY Product.productprice DESC;
If you just want a list of products, I would suggest exists rather than join:
select p.*
from products p
where exists (select 1
from purchased pu
where pu.productId = p.productId and
year(pu.creationdate) = year(now())
)
order by price desc;

Inverse of Inner join (Intersect) with multiple foreign keys

Hi I want to get opposite of intersect from two tables.
I have a sale table and purchase table. What I want to do is get all purchases ids where not included in the sales table.
sale table
sale_id (pk)
product_id (fk)
purchase_id (fk)
purchase table
product_id (fk)
purchase_id (pk)
SELECT DISTINCT purchase_id
, product_id
FROM
purchase
INNER JOIN sale
USING (purchase_id, product_id);
Here is an example:
If I run the above code, this will be the result.
purchase_id product id
1 1
1 2
1 4
2 1
2 3
Now I want to get:
purchase_id product id
1 3
2 2
In short I want to get inverse of above code. Thanks in advance.
Okay, I think I understand better now.
This should return any entry in purchase that have no matching entry in sales.
SELECT
`purchase`.`purchase_id`, `purchase`.`product_id`
FROM `purchase`
LEFT JOIN `sale` ON `sale`.`purchase_id` = `purchase`.`purchase_id` AND `sale`.`product_id` = `purchase`.`product_id`
WHERE
`sale`.`sale_id` IS NULL
ORDER BY
`purchase`.`purchase_id`, `purchase`.`product_id`
If you want to get all the purchases that have no related values in the sales table, you can use a LEFT JOIN:
select
p.purchase_id
from
purchase as p
left join sale as s on p.purchase_id = s.purchase_id
where
s.purchase_id is null;
"Unilateral" joins (LEFT JOIN, RIGHT JOIN) are useful when you want to get data from a table even if data in another related table does not exist. Of course, that means that you can filter data from one table when there's no related data in a second table.
Hope this helps.
Looking at your updated question and your comment, I think that you want all the possible combinations not used.
You'll need to split this in two steps:
First you need all the possible combinations of purchase_id and sale_id values (the "cartesian product" of both the sets).
Then you need to get all the combinations already used.
Finally you need to exclude all the combinations already used.
This can be done using subqueries.
Step 1.
select distinct p.purchase_id, s.product_id from purchase as p, sale as s;
Step 2. (Your query)
select distinct
purchase_id, product_id
from
purchase as p
inner join sale as s
on (p.purchase_id = s.purchase_id and p.product_id = s.product_id);
Step 3. Put it all together
select
a.*
from
(select distinct p.purchase_id, s.product_id from purchase as p, sale as s) as a
left join (
select distinct
purchase_id, product_id
from
purchase as p
inner join sale as s
on (p.purchase_id = s.purchase_id and p.product_id = s.product_id)
) as e on (a.purchase_id = e.purchase_id and a.product_id = e.product_id)
where
e.purchase_id is null and e.product_id is null;

Count and group by join

I have a table for products and a table for users who have bought products. On the products table A there is site_name which determines where the products were bought.
Table B shows the users and what they have bought.
I am using the following to show a list of products bought, by site_name, grouping the product name together.
SELECT product FROM A JOIN B ON A.prod_id = B.prod_id WHERE A.site_name = 'ebay' group by A.product
Table A for products is:
prod_id
site_name
product
Table B for users is:
user_id
prod_id
What i can't figure out is how to get the number of products bought per line.
e.g. in table A there is
prod_id site_name product
------- --------- -------
1 ebay chair
2 amazon desk
3 ebay lamp
and on table b
user_id prod_id
------- -------
1000 1
1001 2
1002 1
1003 3
So I want to show each line where site_name is ebay and how many products were bought, order by most first like:
chair 2
lamp 1
I would use a LEFT JOIN, so that if there is a product that has never been purchased, it will show up with a count of 0.
Also, I would use COUNT(users.prod_id), instead of COUNT(*), so that it will only count rows which have satisfied the LEFT JOIN condition:
SELECT
products.product,
COUNT(users.prod_id) AS productsBought
FROM
A AS products
LEFT JOIN B AS users
ON products.prod_id = users.prod_id
WHERE products.site_name = 'ebay'
GROUP BY products.product
A minor change to Michael's query.
SELECT
products.product,
COUNT(users.prod_id) AS productsBought
FROM
A AS products
INNER JOIN B AS users
ON products.prod_id = users.prod_id
WHERE products.site_name = 'ebay'
GROUP BY products.product
The above query will not return products that haven't been bought.