i am designing ratings table in mysql. The thing is the following:
a) The one that gives rating to other can be different type
(Admin,Client, Provider or anything else in the future)
b) The one that receives rating from other can be different
type(Admin,Client,Provider or anything else in the future)
c) Giving ratings to others happen on when There's an order(imagine
just you go to website and order food. when they get you food, you
rate them) . but orders could be different kind. In my case order
means Freight Delivery. So I can have RoadOrder, SeaOrder.
After looking at the above things, I came to conclusion to have tables like this:
1) all_ratings table
(This table doesn't calculate the current ratings for each kind of user. There are 3 morphs here)
id | from_userable_type | from_userable_id | to_userable_type | to_userable_id | orderable_type | orderable_id | rating
2) ratings table(which calculates current rating. all_ratings just saves
any kind of rating from each user, whereas ratings table has the final
rating. ratingable_id and ratingable_type could be any type of user)
id | quantity | current_rating | ratingable_id | ratingable_type
Users table
id | name | email | userable_id | userable_type
Admins,Clients,Providers(they look the same for now)
id
RoadOrder table
id | from_place | to_place | ...etc
The question: What do you think? is this the right table schema for this type of scenario?
Related
I have a database that contains a people table and another table with names for those people. For each person, there is at least one record in the names table, with one of those being set as the 'person_default_name_id' for that person, but other variations of that name in different languages. The idea is that the user who looks up the table will have a preferred language set (e.g. English, Spanish, Russian) and a preferred script set, which is based on their preferred language (e.g. if their preferred language is English or Spanish, the script would be "Latin", while if the preferred language is Russian, the script would be "Cyrillic"). It's a little complex and I'm wanting to display a list of names, but only display one name per person, and that one chosen name should be shown according to the best-fit for the user's chosen language and script.
The code below is what I'm trying:
SELECT
people.person_id,
names.name
FROM
`people`
LEFT JOIN
`names` ON names.person_id=people.person_id
LEFT JOIN
`languages` ON names.language_id = languages.language_id
LEFT JOIN
`language_scripts` ON languages.language_id = language_scripts.language_id
WHERE
(
/* 1st preference - display the default name for the person IF the default name's language writing system matches the user's writing system */
(people.person_default_name_id=names.name_id AND language_scripts.script_id = :user_script_id)
OR
/* 2nd preference - display the alternative name in the user's chosen language if an alternative name exists in that language */
names.language_id = :user_language_id
OR
/* 3rd preference - display the alternative name in the user's chosen writing system if an alternative name exists in that writing system */
language_scripts.script_id = :user_script_id
)
GROUP BY
people.person_id
ORDER BY
names.name ASC
Example data is below:
Table: people
person_id | person_default_name_id
------------------------------------
1 | 2
Table: names
name_id | name | person_id | language_id
--------------------------------------------
1 | George | 1 | 1
2 | Jorge | 1 | 2
3 | Джордж | 1 | 3
Table: languages
language_id | language
------------------------
1 | English
2 | Spanish
3 | Russian
Table: language_scripts
language_script_id | language_id | script_id
----------------------------------------------
1 | 1 | 1
2 | 2 | 1
3 | 3 | 2
Table: scripts
script_id | script
----------------------
1 | Latin
2 | Cyrillic
I'm finding that some of the expected records are not coming through. I'm guessing that there are improvements I could make to my query, but my skills are not quite advanced enough to know the best path. Can anyone see what I'm doing wrong?
I would suggest you put your where clause conditions in your select statement and return a "score" for each record. Remove it entirely from your where clause and it may give you insight into why you have missing records if they are returned with a 0 score.
Case when condition Then 5
when condition then 4
Etc...
else 0
End case
Once you have your results scored, you can order by your score descending and take the first one per person. Or add additional outer queries to only return the rows having the max score per person.
Apologies for answering from my phone.
I need to build application where users can search/filter products by multiple characteristics. There are 25 product groups. Each product group have around 10 product characteristics. I have a few data base design solutions, but none of them seems appropriate enough:
Create 25 tables per each group with column names storing product group characteristics.
Create one table with all products and as many columns as there are product characteristics (~ 200)
EAV: create 1 table for all characteristics and 1 table with all products and their attributes stored in rows, not in column names. This solution will result in writing a lot of application code, because I won't be able to select a product with all characteristics in one row. I will have to write application code to group mysql results.
I believe there are already solutions for problems like mine. Thanks for help.
EDIT:
In most cases the characteristics in groups are entirely different. These are starter/alternator components. Only around 25% of characteristics can overlap, like physical characteristics, length, diameter, etc.
I would suggest the following:
Create 3 tables; Groups, GroupCharacteristics,Products.
Groups is linked to both tables.
GroupCharacteristics will have the list of characteristics, using 3 columns, (1)GroupName,(2)CharacteristicName,(3)Mapping [Values for mapping could be C01,C02 through C10]
You will use mapping later on.
One group has many characteristics so it's a one to many link.
Products will have 12 Columns; (1)ProductName/Id,(2)GroupName,(3)C01,(4)C02 ... (12)C10.
The C** columns will be filled with the values of the related characteristics in order to keep them mapped correctly.
Groups:
[GroupName]
1-Vehicles
2-Furniture
Characteristics:
[Map][Group][Characteristic]
1-C01 | Vehicles | Length
2-C02 | Vehicles | Volume
3-C03 | Vehicles | Type
4-C01 | Furniture | Height
5-C02 | Furniture | Volume
6-C03 | Furniture | Length
Products:
[ProdName][Group][C01][C02][C03]...
1-Car | Vehicles | 2 | 50 | Hatchback
2-Jet | Vehicles | 10 | 70 | Null
3-Table| Furniture | 1 | null | 1.6
4-Cup | Furniture |0.1 | 0.12 | null
String col = Select Map from Characteristics where Group = 'Vehicles' and Characteristic = ' Type'
-- this returns the column (in this case C03) then --
String sql = "Select ProdName from Products where Group = 'Vehicles' and "+col+"='Hatchback'"
-- this will build the query in a string then you just execute it --
execute(sql)
-- in whatever language you're using this is just the basic idea behind the code you have to write.
Suppose I had these 4 tables, consisting of various foreign key relationships (eg a area must belong to a location, a shop must belong to area, an item price must belong to a shop ect..)
----------------------------------
|Location Name | Location ID |
| | |
----------------------------------
-------------------------------------------------
|Area Name | Area ID | Location ID |
| | | |
-------------------------------------------------
-------------------------------------------------
| Shop Name | Shop ID | Area ID |
| | | |
-------------------------------------------------
----------------------------------
| Item Price | Shop ID |
| | |
----------------------------------
And I wanted the sum of 'Item Price' that belonged to a specific location id. So all the areas and shops item price total for location id 'x'.
One way I found to do this is to join all the tables for one location and get the amount eg:
SELECT SUM(Item Price) FROM
items
left join shops ON (items.shop id = shops.shop id)
left join areas ON (shops.area id = areas.area id)
left join locations ON (areas.location id = location.location id)
WHERE Location Id = 4;
However is this the best way to do this since it involves retrieving the full tree of the data and filtering it out? Would there be a better way if there are a million rows or is this the best way?
You can try sub query --
SELECT SUM(Item Price) FROM
items
left join shops ON (items.shop id = shops.shop id)
left join (select area id from areas where Location Id = 4) as Ar ON (shops.area id = areas.area id)
If you define the right indexes, then the query does not read all the millions of rows for each table.
Think about a telephone book and how you look up a name. Do you read the whole book cover to cover looking for the name? No, you take advantage of the fact that the book is sorted by lastname, firstname and you go directly to the name. It takes only a few tries to find the right page. In fact, on average it takes about log2N tries for a book with N names in it.
The same kind of search happens for each join. If you have indexes, each comparison expression uses a similar lookup to find matching rows in the joined table. It's pretty fast.
But if that's not fast enough, you can also use denormalization, which in this case would be storing all the data in one table, with many columns wide.
----------------------------------------------------------------------
|Location Name | Area Name | Shop Name | Item Name | Item Price |
| | | | | |
----------------------------------------------------------------------
The advantage of denormalization is that it avoids certain joins. It stores the row just like one of the rows you'd get from the result set of your example joined SQL query. You just read one row from the table and you have all the information you need.
The disadvantage of denormalization is the redundant storage of data. Presumably each shop has many items. But each item is stored on a row of its own, which means that row has to repeat the names of the shop, area, and location.
By storing those data repeatedly, you create an opportunity for "anomalies" like if you change the name of a given shop, but you mistakenly change it only on a few rows instead of everywhere the shop name appears. Now you have two names for the same shop, and someone else looking at the database has no way of knowing which one is correct.
In general, maintaining multiple normalized tables in preferable, because each "fact" is stored exactly once, so there can be no anomalies.
Creating indexes to help your queries is sufficient for most applications.
You might like my presentation, How to Design Indexes, Really, and the video: https://www.youtube.com/watch?v=ELR7-RdU9XU
I am currently creating a web application to manage my stock portfolio, but when it comes to the transaction table, I have some problem I want to ask.
The following is my stock transaction table design:
| column name | datatype |
|----------------|----------------------|
| id | int(10) | primary key, auto increment
| portfolio_id | int(10) | reference to portfolio table primary key
| symbol | varchar(20) | stock symbol e.g: YHOO, GOOG
| type | ENUM('buy','sell') |
| tx_date | DATE |
| price | DOUBLE(15,2) |
| volume | int(20) |
| commission | DOUBLE(15,2) |
| created_at | TIMESTAMP |
| updated_at | TIMESTAMP |
In my current design, I don't have an extra table for storing the stock symbol. I generate a list of stock symbols (using some stock api) for the user to pick when they try to create a new transaction record, and I think that this approach may cause some problem when there is stock split/merge, because I may not be able to retrieve the stock price again using the same symbol.
I would like to know how I should modify my table, in order to support the stock split/merge case?
Stock splits
... symbol type shares ...
... AAPL split 100 ...
2 for 1 split; 100 shares became 200 shares.
Dividends
symbol type amount
AAPL div 20.00
Mergers
Workaround: Record a merger as a sale or the old stock and a buy of the new stock. Add appropriate notes in the 'notes' column.
A more accurate (but more complicated) strategy is to redesign the entire database so that each trade is literally a trade of one transaction for another. A 'buy' trades cash for stock. A 'sell' trades stock for cash. A merger trades stock A for stock B. A split trades 0 shares for 100 shares, etc. Cash is just another asset class.
Foreign stocks
All the major finance sites have this figured out. symbol.exchange is a unique id. No need to reinvent the wheel and create a new id column.
You will also need to add a currency column for foreign stocks.
There are less than 4000 stocks in USA. Why don't you use the stock symbol as the Primary Key. How do you plan for dividends?
I like your approach of having your own custom security (stock) ID. You can then map this to various ticker/CUSIP/ISIN changes over time from the exchange/data provider. So have a security_master table which has your security_ID, and a separate <data_provider>_security table with the one-to-many mappings. And a third security events table (splits, mergers, etc)
Your transaction, holding, and any other tables which refer to securities, will only refer to your internal security ID.
If a stock splits, you still refer to it using the same security_id, but it would map to a security events table that tracks over time, and you would query the appropriate quantity based on the split ratio for that point in time.
I'm not entirely sure how to ask this question, so I'll lead by providing an example table and an example output and then follow up with a more thorough explanation of what I'm attempting to accomplish.
Imagine that I have two tables. In the first is a list of companies. Some of these companies have duplicate entries due to being imported and continuously updated from different sources. For example, the company table may look something like this:
| rawName | strippedName |
| Kohl's | kohls |
| kohls.com | kohls |
| kohls Corporation | kohls |
So in this situation, we have information that has come in from three different sources. In an attempt to allow my program to understand that each of these sources are all the same store, I created the stripped name column (which I also use for creating URL's and whatnot).
In the second table, we have information about deals, coupons, shipping offers, etc. However, since these come in from their various sources, the end up with the three different rawNames that we identified above. For example, the second table might look something like this:
| merchantName | dealInformation |
| kohls.com | 10% off everything... |
| kohl's | Free shipping on... |
| kohls corporation | 1 Day Flash Sale! |
| kohls.com | Buy one get one... |
So here we have four entries that are all from the same company. However, when a user on the site visits the listing for Kohls, I want it to display all the entries from each source.
Here is what I currently have, but it doesn't seem to be doing the trick. This seems to only work if I set the LIMIT in that sub-query to 1 so that it only brings back one of the rawNames. I need it to match against all of the rawNames.
SELECT * FROM table2
WHERE merchantName = (SELECT rawName FROM table1 WHERE strippedName = '".$strippedName."')
The quickest fix is to replace your mercahantName = with merchantName IN
SELECT * FROM table2
WHERE merchantName IN (SELECT rawName FROM table1 WHERE strippedName = '".$strippedName."')
The = operator needs to have exactly one value on each side - the IN keyword matches a value against multiple values.