I'm new to MySQL. I am creating a checkout page in PHP. When the users select the items they want to buy and click "Add to Cart", a temporary table gets created which has the following fields (table name is temp):
+--------------+-----------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------+------+-----+-------------------+----------------+
| Cart_Item_ID | int(11) | NO | PRI | NULL | auto_increment |
| Item_ID | int(11) | NO | | | |
| Added_On | timestamp | YES | | CURRENT_TIMESTAMP | |
+--------------+-----------+------+-----+-------------------+----------------+
I'm only inserting to the Item_ID field which contains the ID of each item they bought (I'm populating the forms with item IDs). What I want to do is look up the item's name and price that's stored in the Inventory table. Here's how that looks:
+--------------+----------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+----------------+------+-----+-------------------+----------------+
| Inventory_ID | int(11) | NO | PRI | NULL | auto_increment |
| Item_Name | varchar(40) | NO | | | |
| Item_Price | float unsigned | NO | | 0 | |
| Added_On | timestamp | YES | | CURRENT_TIMESTAMP | |
+--------------+----------------+------+-----+-------------------+----------------+
So how would I pull out the Item_name and Item_Price fields from the Inventory table based on the Item_ID field from the temp table so I can display it on the page? I just don't understand how to formulate the query. I'd appreciate any help. Thank you.
It's called JOIN - read more here
SELECT Inventory.Item_Name, Inventory.Item_Price
FROM Inventory, temp WHERE Inventory.Inventory_ID = temp.Item_ID
what i understand is that the Item_ID in temp table is referencing to the Inventory_ID in inventory table. based on this assumption you can use the following query.
Select Item_Name, Item_Price from Inventory, Temp where Temp.Item_ID == Inventory.Inventory_ID
i guess this is what you want to do.
Thanks
As it stands, you can't (unless Inventory_ID = Item_ID)
What you need is a way of JOINing the two tables together. In this instance, if Inventory_ID = Item_ID then the following is possible:
SELECT Item_Name,
Item_Price
FROM InventoryTable
INNER JOIN TempItemTable ON (InventoryTable.Inventory_ID = ItemTable.Item_ID)
If you want to filter for a particular item you can add the constraint:
WHERE ItemTable.Item_ID = 27 --for example
That will join all the rows in your inventory table with matching rows in the Item table.
Jeff Atwood has a great (IMO) visual explanation of how JOINs work.
Related
Hello so I have an application in Laravel in which a lot of user data is stored in separate tables across several models. I now have a requirement to create an activity feed, which means ordering the various data across tables by date.
For illustrative purposes, imagine I have two models, Comment and Like.
I want a feed that combines both by date. merge() is not an option because they may have the same id.
Therefore I could UNION them, but my problem is I won't know what came from what.
My likes table looks like this:
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | | NULL | |
| asset_id | int(11) | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
My comments table looks like this:
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| asset_id | int(11) | NO | | NULL | |
| content | longtext | NO | | NULL | |
| user_id | int(11) | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
My issue is if I just union the two queries, I would only know they are different based on the presence of the content column, which could be in another model, such as Blurb or whatever.
Basically, how do I get multiple queries across models while keeping straight what belongs where, because in my activity feed I want to say, 10 minutes ago you commented, 5 minutes ago you liked, etc.
I don't want to do multiple queries because of inefficiency and I don't want to store all the activities (likes and comments, etc.) in one table either. Is there some kind of alias I can use where instead of renaming a column I insert data using the query for the purposes of the query, so for example a comment selection would add "comment" in a temporary field so that I can access it like
$data->type? I could put a type in all of the tables but then I'd have space being taken up needlessly, as obviously I know a comment is a comment if its in the comment table when that is my only query, but now I am rethinking my structure given I need one query to span multiple tables.
you can use following code to get user activity feed.
$userId = Auth::id(); //or whatever you want.
$activity = DB::table('comment as ac')
->select(DB::raw('ac.user_id , ac.asset_id , ac.comment , ac.created_at , ac.updated_at , "comment" as activity_type'))
->where("ac.user_id", $userId)
->union(
DB::table('like as al')
->select(DB::raw('al.user_id , al.asset_id , NULL as comment , al.created_at , al.updated_at , "like" as activity_type'))
->where("al.user_id", $userId)
)
->latest()
->get();
When performing your query, select an addition raw value based on the table name. For example, in raw SQL:
SELECT likes.*, '' AS content, 'like' AS type
FROM likes
WHERE likes.user_id = 1
UNION
SELECT comments.*, 'comment' AS type
FROM comments
WHERE likes.user_id = 1
ORDER BY created_at DESC
The Laravel code (untested) will look something like:
$activity = DB::table('comments')
->select("comments.*, 'comment' AS type")
->where('comments.user_id', $user->id)
->union(
DB::table('likes')
->select("likes.*, '' AS content, 'like' AS type")
->where('likes.user_id', $user->id)
)
->orderBy('created_at', 'ASC')
->get();
I have a table called ratings with the following fields:
+-----------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------+------+-----+---------+----------------+
| rating_id | bigint(20) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | MUL | NULL | |
| movie_id | int(11) | NO | | NULL | |
| rating | float | NO | | NULL | |
+-----------+------------+------+-----+---------+----------------+
Indexes on this table:
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| ratings | 0 | PRIMARY | 1 | rating_id | A | 100076 | NULL | NULL | | BTREE | | |
| ratings | 0 | user_id | 1 | user_id | A | 564 | NULL | NULL | | BTREE | | |
| ratings | 0 | user_id | 2 | movie_id | A | 100092 | NULL | NULL | | BTREE | | |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
I have another table called movie_average_ratings which has the following fields:
+----------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+---------+------+-----+---------+-------+
| movie_id | int(11) | NO | PRI | NULL | |
| average_rating | float | NO | | NULL | |
+----------------+---------+------+-----+---------+-------+
As it is obvious by this point I want to calculate the average rating of movies from ratings table and update the movie_average_ratingstable. I tried the following SQL query.
UPDATE movie_average_ratings
SET average_rating = (SELECT AVG(rating)
FROM ratings
WHERE ratings.movie_id = movie_average_ratings.movie_id);
Currently, there are around 10,000 movie records and 100,000 rating records and I get Lock wait timeout exceeded; try restarting transaction error. The number of records can grow significantly so I don't think increase timeout is a good solution.
So, how can I write 'scalable' query to acheive this? Is iterating the movie_average_ratings table records and calculate averages individually the most efficient solution to this?
Without an explain, it's hard to be clear on what's holding you up. It's also not clear that you will get a performance improvement by storing this aggregated data as a denormalized table - if the query to calculate the ratings executes in 0.04 seconds, it's unlikely querying your denormalized table will be much faster.
In general, I recommend only denormalizing if you know you have a performance problem.
But that's not the question.
I would do the following:
delete from movie_average_ratings;
insert into movie_average_ratings
Select movie_ID, avg(rating)
from ratings
group by movie_id;
I just found something in another post:
What is happening is, some other thread is holding a record lock on
some record (you're updating every record in the table!) for too long,
and your thread is being timed out.
This means that some of your records are locked you can force unlock them in the console:
1) Enter MySQL mysql -u your_user -p
2) Let's see the list of locked tables mysql> show open tables where in_use>0;
3) Let's see the list of the current processes, one of them is locking
your table(s) mysql> show processlist;
4) Kill one of these processes mysql> kill put_process_id_here;
You could redesign the movie_average_ratings table to
movie_id (int)
sum_of_ratings (int)
num_of_ratings (int)
Then, if a new rating is added you can add it to movie_average_ratings and calculate the average if needed
I am having a mysql table
content_votes_tmp
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| up | int(11) | NO | MUL | 0 | |
| down | int(11) | NO | | 0 | |
| ip | int(10) unsigned | NO | | NULL | |
| content | int(11) | NO | | NULL | |
| datetime | datetime | NO | | NULL | |
| is_updated | tinyint(2) | NO | | 0 | |
| record_num | int(11) | NO | PRI | NULL | auto_increment |
+------------+------------------+------+-----+---------+----------------+
surfers can vote up or vote down on posts i.e. content, a record gets inserted everytime a vote is given same as rating , in the table along with other data like ip , content id
Now i am trying to create cronjob script in php which will SUM(up) and SUM(down) of votes
like this,
mysqli_query($con, "SELECT SUM(up) as up_count, SUM(down) as down_count, content FROM `content_votes_tmp` WHERE is_updated = 0 GROUP by content")
and then by using while loop in php i can update the main table for the specific content id,
but i would like to set the records which are part of SUM to be marked as updated i.e. SET is_updated = 1, so the same values wont get summed again and again.
How can i achieve this ? using mysql query ? and work on same data set as , every second/milisecond the records are getting inserted in the table ,.
i can think of another way of achieving this is by getting all the non-updated records and doing sum in the php and then updating every record.
The simplest way would probably be a temporary table. Create one with the record_num values you want to select from;
CREATE TEMPORARY TABLE temp_table AS
SELECT record_num FROM `content_votes_tmp` WHERE is_updated = 0;
Then do your calculation using the temp table;
SELECT SUM(up) as up_count, SUM(down) as down_count, content
FROM `content_votes_tmp`
WHERE record_num IN (SELECT record_num FROM temp_table)
GROUP by content
Once you've received your result, you can set is_updated on the values you just calculated over;
UPDATE `content_votes_tmp`
SET is_updated = 1
WHERE record_num IN (SELECT record_num FROM temp_table)
If you want to reuse the connection to do the same thing again, you'll need to drop the temporary table before creating it again, but if you just want to do it a single time in a page, it will disappear automatically when the database is disconnected at the end of the page.
I am working on a project and I ended up with the table below:
+---------------+--------------+------+-----+--------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+--------------------+-------+
| id | int(11) | NO | MUL | NULL | A_I |
| user _id | int(11) | NO | | NULL | |
| info | varchar(255) | NO | | NULL | |
| country | tinyint(3) | NO | | NULL | |
| date_added | timestamp | NO | | 0000-00-00 00:00:00| |
+---------------+--------------+------+-----+--------------------+-------+
Because I wanted to avoid storing countries as varchar all the time I thought I should use number IDs instead. My question is, would it be better to store the country IDs in a table where I would give a name to each one of them or do that in a php file? Countries won't change or anything. It will be a list of around 100 countries.
Thanks!
Use a seperate country table.
countries table
---------------
id
name
Then you can relate to the country ID in your table. That way you make sure only countries from your list are added and you don't need to store strings everywhere and you can easily change country names or addnew ones.
I'm making a notification scheme for my social networking app. I've different kind of notification which are categorized in two groups: Friends-related and Events-related. Currently, my database schema is like this:
+---------------------+------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+------------------------+------+-----+---------+----------------+
| notification_id | int(11) | NO | PRI | NULL | auto_increment |
| notification_type | enum('event','friend') | NO | | NULL | |
| notification_date | datetime | NO | | NULL | |
| notification_viewed | bit(1) | NO | | NULL | |
| user_id | int(11) | NO | MUL | NULL | |
+---------------------+------------------------+------+-----+---------+----------------+
Now, I've two different tables fro event-related notification and friend-related notification. Below is schema for event-related notification table:
+-------------------------+----------------------------------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------------+----------------------------------------------------+------+-----+---------+-------+
| notification_id | int(11) | NO | PRI | NULL | |
| event_id | int(11) | NO | MUL | NULL | |
| event_notification_type | enum('added','kicked','new-message','info-edited') | NO | | NULL | |
+-------------------------+----------------------------------------------------+------+-----+---------+-------+
And again I've 4 more tables for each kicked, added, new-message, info-edited type of notification, since each requires to have it different kind of property (for example kicked requires a reason).
Now, I want to write a conditional SQL query such that it joins the notification with event_notification if notification_type is event otherwise different.
SELECT * FROM notification_table t WHERE t.seen = FALSE AND t.user_id = ? INNER JOIN event_notification en ON(t.notification_type='event' AND en.notification_id = t.notification_id) INNER JOIN .....
There is going to be so many inner joins is there any better way of doing it? I think my query is not very optimized either, would appreciate if any help could be provided.
You can use the joins. However, you want to create the query using left outer joins rather than inner joins:
SELECT *
FROM notification_table t
WHERE t.seen = FALSE AND t.user_id = ? left JOIN
event_notification en
ON(t.notification_type='event' AND en.notification_id = t.notification_id) left JOIN ...
Don't worry about the proliferation of joins. If your tables have proper indexing, they will perform fine.
Do consider changing the data structure so you have only one table for the different notification types. Having a few fields that are not used does not add much performance overhead, especially when you consider the complications of having so many joins and the additional management overhead of having more tables.