Is there any way how to create an functioning index for this query and to get rid of "filesort"?
SELECT id, title FROM recipes use index (topcat) where
(topcat='$cid' or topcat2='$cid' or topcat3='$cid')
and approved='1' ORDER BY id DESC limit 0,10;
I created index "topcat" ( columns: topcat1+topcat2+topcat3+approved+id) but still ge "Using where; Using filesort".
I can create one more column, lets say, "all_topcats" to store topcat numbers in an array - 1,5,7 and then to run query "... where $cid iIN ()...". But the probem is that in this case "all_topcats" column will be "varchar" but "approved" and "id" columns - int, and index will not be used anyway.
Any ideas? Thanks.
You might improve performance for that query if you reordered the columns in the index:
approved, topcat1, topcat2, topcat3, id
It would be useful to know what the table looks like and why you have three columns named like that. It might be easier to organise a good query if you had a subsidiary table to store the topcat values, with a link back to the main table, but without knowing why you have it set up like that it's hard to know whether that would be sensible.
Can you post the CREATE TABLE?
Edit in response to user message
Your table doesn't sound like it's well-designed. The following design would be better: Add two new tables, Category and Category_Recipe (a cross-referencing table). Category will contain a list of your categories and Category_Recipe will contain two columns, one a foreign key to Category and one a foreign key to the existing Recipe table. A row of Category_Recipe is a statement "this recipe is in this category". You will then be able to very simply write a query that will search for recipes in a given category. You also have the ability to put a recipe in arbitrarily many categories, rather than being limited to 3. Look up "database normalisation" and "foreign keys".
Related
I have two tables. The first is named master_list. It has these fields: master_id, item_id, name, img, item_code, and length. My second table is named types_join. It has these fields: master_id and type_id. (There is a third table, but it is not being used in the queries. It is more for reference.) I need to be able to combine these two tables so that I can sift the results to only show certain ones but part of the information to sift is on one table and the other part is on the other one. I don't want duplicate answers.
For example say I only want items that have a type_id of 3 and a length of 18.
When I use
SELECT * FROM master_list LEFT JOIN types_join ON master_list.master_id=types_join.master_id WHERE types_join.type_id = 3 AND master_list.length = 18"
it finds the same thing twice.
How can I query this so I won't get duplicate answers?
Here are the samples from my tables and the result I am getting.
This is what I get with an INNER JOIN:
BTW, master_id and name both only have unique information on the master_list table. However, the types_join table does use the master_id multiple times later on, but not for Lye. That is why I know it is duplicating information.
If you want unique rows from master_list, use exists:
SELECT ml.*
FROM master_list ml
WHERE ml.length = 18 AND
EXISTS (SELECT 1
FROM types_join tj
WHERE ml.master_id = tj.master_id AND tj.type_id = 3
);
Any duplicates you get will be duplicates in master_list. If you want to remove them, you need to provide more information -- I would recommend a new question.
Thank you for the data. But as you can see enter link description here, there is nothing wrong with your query.
Have you tried create an unique index over master_id, just to make sure that you do not have duplicated rows?
CREATE UNIQUE INDEX MyMasterUnique
ON master_list(master_id);
Let's say I have a table posts that contains the User_id of the user who posted and the post post_id of the post. And I have another table comments that contains only the post that it belongs to child_of_post.
The problem is here: I need to select only from comments but at the same time get the user_id of the post that the comment belongs to.
So should I use a join like:
SELECT user_id FROM comments INNER JOIN posts ON child_of_post = post_id
Reading this confused me even more, I don't really know how to explain it, but, in general if I need to use the same value like and id, should I save that value in every table that I need it ? Or should I save it only in one table and use joins to retrieve it ?
Is using a join better that adding one more column to a table ?
Is using a join better than adding one more column to a table ?
In general : Yes.
Your database design looks good. As a general principle, avoid duplicating data across tables. This is inefficient in terms of storage, and also can quickly turn into a maintenance nightmare when it comes to modifying data, which ultimately threatens the integrity of your data.
Instead of duplicating data, the usual approach is to store a reference to the table row where the original data is stored ; this is called a foreign key, and it offers various functionalities that help maintain data integrity (prevent inserts of orphan records in the child table, delete child records when the parent is deleted, ...).
In your use case, you indeed would need to JOIN to recover the user that created the original post, like :
SELECT p.user_id, c.*
FROM comments c
INNNER JOIN posts p ON c.child_of_post = p.post_id
Assuming that post_id is the primary key of table posts, such JOIN with an equality condition referencing the primary key of another table, is very efficient, especially if you create an index on referencing column comments.child_of_post.
PS : it is a good practice to give aliases to table names and use them to index the fields in the query ; it avoids subtle bugs caused by column name clashes (when both tables have fields with the same name), and makes the query easier.
I am using one table, reviews, to store reviews for multiple topics, songs, artists and albums and the reviews come from multiple users. I would like to find a way to prevent duplicate reviews from one user for a given song, artist or album. While I try to do this in PHP, I'd like MYSQL to enforce the uniqueness using a UNIQUE index on userid and songid.
However, I've run into the problem that entries show 0 for fields that are not applicable. For example, if the review is for a songid, the table shows 0 for the artistid and albumid.
Reviews
reviewid (primary key)|text|songid|albumid|artistid|userid
1|great song|222|0|0|22
2|great album|0|333|0|22
3|great singer|0|0|444|22
//I want to exclude the following entry:
4|lousy song|222|0|0|22
Can anyone suggest a way to create a Unique index that somehow works around this problem?
Thanks for any suggestions.
The only way you can use the unique indexes is if you change all those 0's to null. nulls are considered distinct from each other in most cases (group by still groups them together).
Assuming changing them to nulls will not break tons of other queries, it can be accomplished in a single query:
UPDATE reviews
SET songid = NULLIF(songid, 0)
, albumid = NULLIF(albumid, 0)
, artistid = NULLIF(artistid, 0)
;
mysql doesn't have expression indexes, but there may be an easier solution. Stop using 0s, and instead use nulls for non-applicable values.
nulls are the native way of SQL databases to signify a lack of a value, and do not participate in indexes.
I have two tables - books and images. The books table has many columns - including id (primary key), name (which is not unique), releasedate, etc. The images table have two columns - id (which is not unique, i.e one book id may have multiple images associated with it, and we need all those images. This column has a non-unique index), and poster (which is unique primary key, all images lie in the same bucket, hence cannot have duplicate names). My requirement is given a book name, find all images associated with it (along with the year of release and the bucketname for each image, the bucketname being just a number in this case).
I am running this query:
select books.id,poster,bucketname,year(releasedate) from books
inner join images where images.bookId = books.id and books.name = "<name>";
A sample result set may look like this:
As you can see there are two results matching - one with id 2 and year 1989, having 5 images, other one with id 261009, year 2013 and one image.
The problem is, the query is extremely slow. It takes around .14 seconds from MySQL console itself, under zero load (in production there may be several concurrent requests and they may be queued, leading to further delay), which is unacceptable for autocomplete. Can anyone tell me how to optimize the query by adding correct indices/keys to the tables? If it is not possible from MySQL, suggestions regarding a proper Redis schema would be useful as well.
Edit: Approx no. of rows in images - 480k, in books - 285k. In future, autocomplete will show result for book authors as well as book names, hence the query will need to expand to take into account a separate table authors where each author will have an id and name, just like a book.
For optimal performance, you want suitable covering indexes available. For example:
... on `books` (`name`,`id`,`releasedate`)
... on `images` (`bookid`,`poster`,`bucketname`)
We want name as the leading column in the index, because of the equality predicate in the WHERE clause. We want id and releasedate also included in the index to make it a "covering index", so the query can be satisfied from the index, without a need to visit pages of the underlying table to retrieve values.
We want bookid as the leading column because of the reference in the ON clause. Again, having poster and bucketname available right in the index make it a "covering" index.
Use EXPLAIN to see the query execution plan.
Also, note that the inner join operation won't return a row from books if a matching row in images is not found. If we want to return a row from books even when no image is available, we could use an outer join.
I'd write the query like this:
SELECT b.id
, i.poster
, i.bucketname
, YEAR(b.releasedate)
FROM books b
LEFT
JOIN images i
ON i.bookid = b.id
WHERE b.name = ?
I have 2 MySQL Tables: "parts_revisions" and "categories_revisions". My goal is to use the revisions data in these tables to create a log that lists out all the changes made to parts and categories. Listing the changes to "parts" in one single SQL statement has proven tricky though! Here is the situation:
All entries of each table have "timestamp" columns.
Every parts_revisions entry has a "categoryId" that basically links it to the categories_revisions table. (Every part is a child of a parent category.)
All I want to do is list out all the parts_revisions, but use the human-friendly "name" column from the categories_revisions table based on the categoryId column in parts_revisions. This will make the log more readable.
The trick is that, because there are usually multiple revisions for each category within the categories_revisions table, I cannot do just one big 'ol join on the categoryId column to get the name. The categoryId column is non-unique, and "name"s may vary. What I have to do is get the latest category_revisions entry that has a timestamp that is no later than the timestamp of the part_revisions entry. In other words, we want to get the appropriate category name that was in use AT THE TIME the part revision was made.
Not sure if this matches your table structure, but here's a go at it. It's a bit of an ugly subquery inside a subquery. Guessing it won't be terribly efficient
select part_name,
category,
(select name
from categories_revisions
where categories_revisions.match_id = parts_revisions.category
and categories_revisions.timestamp = (select MAX(categories_revisions.timestamp)
from categories_revisions
where categories_revisions.match_id = parts_revisions.category
and categories_revisions.timestamp < parts_revisions.timestamp)) as name
from parts_revisions;
http://sqlfiddle.com/#!2/da74e/1/0