Alternatives to junction table? - mysql

I'm designing a relational database tables for storing data about eCommerce scenario where I need to store
List of Products purchased by a user
List of users who purchased a particular product.
Its a many to many relationship.
So far I could only thinking of doing this.
create a table for storing orders
table recordorders(
userID // foreign key from users table
productID, // foreign key from products table
dateofpurchase,
quantity,
price_per_unit,
total_amount
)
It will act like a junction table.
Is this a good approach and are there any other methods than junction table that are more effective and efficient for querying ?

Your bullets describe two tables, not one. Your junction table is not properly described as two lists. It is a set of order info rows. The junction table you gave holds rows where "user [userID] purchased product [productID] on ...". Ie it records order info. (Combinations of user, product, date, etc of orders.) Given a user or product, you can get the corresponding bullet table by querying the order info table.
However your table probably needs another column that is a unique order id. Otherwise it cannot record that there are two orders that are alike in all those columns. (Eg if the same person buys the same product on the same date in the same quantity, price and total.) Ie its rows probably aren't 1:1 with orders. That's why above I called it an order info table rather than an order table. It records that some order had those properties; but it doesn't record distinct orders if there can be orders with the same info. It's really a many-to-many-to-etc (for every column) association. That is why an order id gets picked as a unique name for an order as further info. This new table would be called an entity table, not a junction or association table. It holds rows where "in order [id] user [user] purchased ...".
PS An order is usually something that can be characterized as an association on/among/between an order id, user, set of order-lines (product, quantity, price & total), and other stuff (date, grand total, etc). The orders are usually relationally characterized by an order entity table on order id with its user, date etc plus an order-line association table on order ids and their order-line info.
PPS It's time for you to read a book about information modeling and database design.

You don't "store" those two things in a table (Junction, or otherwise), you discover them from the raw ("Fact") data:
Using your proposed table:
List of Products purchased by a user:
SELECT productID
FROM recordorders
WHERE userID = 123;
List of users who purchased a particular product:
SELECT userID
FROM recordorders
WHERE productID = 987;

Related

Non-unique many-to-many table design

I'm implementing a voting system for a php project which uses mysql. The important part is that I have to store every voting action separately for statistic reasons. The users can vote for many items multiple times, and every vote has a value (think of it like a donation kinda stuff).
So far I have a table votes in which I'm planning to store the votes with the following columns:
user_id - ID of the voting user, foreign key from users table
item_id - ID of the item which the user voted for, foreign key from items table
count - # of votes spent
created - date and time of voting
I'll need to get things out of the table like: Top X voters for an item, all the items that a user have voted for.
My questions are:
Is this table design suitable for the task? If it is, how should I index it? If not, where did I go wrong?
Would it be more rewarding to create another table beside this one, which has unique rows for the user-item relationship (not storing every vote separately, but update the count row)?
Each base table holds the rows that make a true statement from some fill-in-the-(named-)blanks statement aka predicate.
-- user [userid] has name ...
-- User(user_id, ...)
SELECT * FROM User
-- user [user_id] voted for item [item_id] spending [count] votes on [created]
-- Votes(user_id, item_id, count, created)
SELECT * FROM Votes
(Notice how the shorthand for the predicate is like an SQL declaration for its table. Notice how in the SQL query a base table predicate becomes the table's name.)
Top X voters for an item, all the items that a user have voted for.
Is this table design suitable for the task?
That query can be asked using that design. But only you can know what queries "like" that one are. You have to define sufficient tables/predicates to describe everything you care about in every situation. If Votes records the history of all relevant info about all events then it must be suitable. The query "all the items that user User has voted for" returns rows satisfying predicate
-- user User voted for item [item] spending some count on some date.
-- for some count & created,
user User voted for item [item_id] spending [count] votes on [created]
-- for some count & created, Votes(User, item_id, count, created)
-- for some user_id, count & created,
Votes(user_id, item_id, count, created) AND user_id = User
SELECT item_id FROM Votes WHERE user_id = User
(Notice how in the SQL the condition turns up in the WHERE and the columns you keep are the ones that you care about. More here and here on querying.)
If it is, how should I index it?
MySQL automatically indexes primary keys. Generally, index column sets that you JOIN ON, otherwise test, GROUP BY or ORDER BY. MySQL 5.7 Reference Manual 8.3 Optimization and Indexes
Would it be more rewarding to create another table beside this one, which has unique rows for the user-item relationship
If you mean a user-item table for some count & created, [user_id] voted for [item_id] spending [count] votes on [created] and you still want all the individual votings then you still need Votes, and that user-item table is just SELECT user_id, item_id FROM Votes. But if you want to ask about people who haven't voted, you need more.
(not storing every vote separately, but update the count row)
If you don't care about individual votings then you can have a table with user, item and the sum of count for user-item groups. But if you want Votes then that user-item-sum table is expressible in terms of Votes using GROUP BY user_id, item_id & SUM(count).

Can a table have multiple records of a single column in a single row?

There is a table 'items' with columns : item_name, i_code, items_left & price. It stores all the items that a shop sells. There is another table 'customers'. It stores record of all the customers who visited the shop. I want to keep record of all the items that a particular customer bought. I want to create a column 'items_bought' in 'customers' table, that will store item codes of all the items a particular customer bought. But having more than one item code for a particular customer row is impossible. Please help me have multiple records of items_bought in a single row of customers.
this is possible,but not suggested,
you can save your item code in customer table with or with comma or environment.newline
but it has not any use except display
my suggested solution is create a new table CustomerItem, having CustomerId,ItemId and other common attribute, that should b between Customer& Item like purchase rate, time of purchase etc(you cannot do it in above method)
The answer is, yes you can have multiple values in a single field for each row. A comma separated field could be a solution.
If you are using a relational database, your life will be easier if you create a new table, let's say items_bought, that will hold the relation between customer and item bought.
For example
create table `items_bought`
(id int not null primary key,
item_id int not null,
customer_id int not null)
Each field item_id and customer_id will have a foreign key to items.id and customers.id table fields respectively.
This way you don't need to manage strings and parse comma separated values. You can simply query your tables like:
select *
from `items` i
inner join `items_bought` ib on i.id = ib.item_id
inner join `customers` c on ib.customer_id = c.id
The above query will return all customer and item information of customers that have bought at least one item.

How to store sales in groups of orders

For a purchase-scenario:
I have a table (PRODUCTS) that contain columns like NAME, TYPE, DEFAULTPRICE, ID and a table with sales (SALES) that contain columns like DATE, ID, PRICE, AMOUNT where ID in SALES is a foreign key to primary key ID in PRODUCTS. Very simple, not much to mess up there.
This works fine as long as you insert one row of whichever product (and how many) at what total price.
BUT - let's say I want to design this system to support "basket"-kind of shopping. I.e. a user selects several items that are not the same product ID, but gets a single ORDER ID that contains all the products that were included in the order, the amount of each product, the total price, date etc.
How would one go about creating this? I was thinking maybe creating a table called "ORDERS" and link every row from SALES to an ID from ORDERS - but I'm not sure.
You would create a table called Orders for each order. It would have whatever information you want about the order.
You would then have a table for the products in each order. A common name is OrderLines. This would have an OrderId, linking it to the Orders table and a ProductId linking to the Products table.

Magento DB customer orders mapping

How are Magento orders mapped to the customer? Does the order contain the customer ID or does the customer contain all their order ids? Trying to figure out if changing order numbers will mess up customer mapping.
Let me answer your questions at first
How are Magento orders mapped to the customer?
Magento Orders are mapped to the customers using "customer_id" Foreign Key field in the order tables "sales_flat_order","sales_flat_order_grid","sales_flat_order_address". And Customer entity has no reference to the orders in turn.
Does the order contain the customer ID or does the customer contain all their order ids?
Order only contains the customer ID not the other way around. Basically Customer entity follows the EAV structure like products and categories.
Now coming to your task of changing Order Numbers (technical field name increment_id).
Yes you can change the increment numbers of the order. You need to do that in two tables
1. sales_flat_order
2. sales_flat_order_grid
Please make sure to reset the order increment last id of entity type id "5" which represents Order after completing your task of changing the order numbers. You should do this in the table "eav_entity_store"
Let me know if you need more information regarding this.

MySQL Database design and effecient query

I have the following tables:
users (id, first_name, last_name)
category (id, name)
rank(id, user_id, rank)
Each user can belong to several categories. And all users are in the rank table and have a value between 0.0 and 1.0, where 0 is the lowest rank and 1 is the highest. I’d like to setup additional tables to create the following webpage:
A visitor to the page (identified by either one of the recorded ids in the user table, or a numeric representation of their ip address) chooses a category and is presented with two randomly chosen users from the users table such that:
1) the visiting user_id has not seen this pairing in a period of 24 hours
2) the two users belong to the chosen category
3) the two users are within 1 rank value of each other. Let me explain that last criteria - if the ranks were sorted, the two chosen users would have adjacent ranks.
This is a hard one and I can’t for the life of me figure it out how to do this effeciently
I truly appreciate any help on this front.
Thanks
You just need two more tables and the rest go in your website logic.
user_category(user_id, category_id)
user_pairing(first_user_id, second_user_id, last_seen)
The first table is to represent a ManyToMany relationship between the users and the category, and the second one is for the users pairing.
I agree with #Yasel, i want to add that you properly want another table
candidate(first_user_id, second_user_id);
this table is used to pre-calculate the candidates for each user, this candidate table is prepopulated every hour/day, so when each first_user_id, second_user_id is assigned, this pair is removed from candidate table and moved into user_pairing table. so each time you only need to query candidate table which should be efficient.