I'm new here and I hope I am asking my question correctly:
I am trying to implement search on forums database.
I have 'questions' table and 'answers' table (they both related by 'id_question' field). I also have 'suggestions' table and 'comments' table that are also related.
In addition I have 'profiles' table which related to every table I've mentioned before by 'profile_id' field, this profiles table holds the information on the users.
I would like to implement wide search on specific fields ('topic', 'description'...) in all the four tables I've mentioned before, and display those fields and the information of the user who wrote the post.
I really messed with this.
right now I have 4 queries (for each table), and each query makes join with profiles table.
Do you know better way to do this?
Thanks!
You can do this with INNER JOINS. I recommend starting with some simple INNER JOINS and building up your queries to be more inclusive and join more tables. It all depends on what you're trying to get, really. Be as precise as possible in returning exactly the information you want.
Here is a simple example and the assumptions I made:
Assuming the following
table: primarykey*, foreignkey#, othercolumns
questions: id_questions*, topic, description, profile_id#
answers: id_answers*, id_questions#, topic, description, profile_id#
suggestions: id_suggestions*, topic, description, profile_id#
comments: id_comments*, topic, description, profile_id#
user: profile_id*, name, details
KEYWORD: replace with your keyword or phrase
Look for a keyword in the questions and answers
SELECT * FROM questions q
INNER JOIN answers a
ON q.profile_id = a.profile_id
WHERE topic LIKE '%KEYWORD%'
OR description LIKE '%KEYWORD%';
Get the user profile for a user who used keyword in the questions and answers tables:
SELECT u.* FROM users u
INNER JOIN answers a
ON u.profile_id = a.profile_id
INNER JOIN questions q
ON q.profile_id = u.profile_id
WHERE topic LIKE '%KEYWORD%'
OR description LIKE '%KEYWORD%';
Hope this helps.
Edit: formatting.
Yes, do it in four queries.
Otherwise you will get a Cartesian product as you try to join the tables.
I think you can try using UNION. Is not it?
Related
I have a question regarding pulling data from different tables in MYSQL. I'm sorry if this question has already been answered elsewhere, but I just can't seem to make sense of it.
I want to pull out the categories that are bound to the user's chosen newspapers.
I've got a table that tracks the user's chosen newspapers with user_id and newspaper_id.
A table with the newspapers and another table with the categories.
And at last a table which has both the newspaper_id and the category_id.
Is this possible to do with a single query? I'm really no good with sub select queries.
Thanks in advance!
Joining is probably the way to go:
SELECT c.*
FROM categories c
JOIN newspaper_categories nc ON c.category_id = nc.categroy_id
JOIN newspaper_users nu ON nu.newspaper_id = nc.newspaper_id
WHERE nu.user_id = <some id>
All,
I've got the following mySQL table structure:
Table dj_feedback_answers: answer_id, gig_id, question_id, answer_id
Table dj_feedback_summary: summary_id, user_id, gig_date, tip, summary, why_poor, why_fair
Table dj_feedback_questions: question_id, question_value, question_display
Table dj_feedback_ratings: rating_id, rating_value, rating_display
I'm basically asking users questions and the questions are stored in the dj_feedback_questions table and allowing them to give a rating for each question. The ratings are stored in the dj_feedback_ratings.
Each form can having variable questions so when I submit the questions I store the answers in the dj_feedback_answers. There are other more static fields on the form and those get svaed into the dj_feedback_summary. The summary_id and the gig_id are the keys that connect the two tables for the answers that the user submitted.
This means that there can be multiple rows in the dj_feedback_answers for a single row in the dj_feedback_summary table.
What I would like to do is basically be able to utilize the results in a PHP table. So I'd like my results to look something like this (assuming there are three questions in the dj_feedback_questions table):
summary_id, user_id, gig_date, tip, summary, why_poor, why_fair, question_1, question_2, question_3
1, 2, 07/23/2012, 5, This is a summary, It was poor, It was fair, 3, 4, 5
The values that would be stored for the questions would be the answer_id from the dj_feedback_answers.
I tried to create the following query to get my results. This works ok but it only can display a single question instead of all of the questions.
Select dj_feedback_summary.summary_id,
dj_feedback_summary.user_id,
dj_feedback_summary.gig_date,
dj_feedback_summary.tip,
dj_feedback_summary.summary,
dj_feedback_answers.question_id,
dj_feedback_answers.rating_id,
dj_feedback_questions.question_value,
users.first_name, users.last_name,
dj_feedback_ratings.rating_value
from dj_feedback_summary
join dj_feedback_answers on dj_feedback_summary.summary_id=dj_feedback_answers.gig_id
join dj_feedback_questions on dj_feedback_answers.question_id=dj_feedback_questions.question_id
join users on dj_feedback_summary.user_id=users.user_id
join dj_feedback_ratings on dj_feedback_ratings.rating_id=dj_feedback_answers.rating_id
where dj_feedback_questions.question_value='Overall Gig'
order by dj_feedback_summary.summary_id DESC
Is there a way to modify my query so it can return my results how I would like them?
Any advice is greatly appreciated!
Thanks
If you need values from multiple rows displayed, and it's not essential that they be in separate columns, then the MySQL GROUP CONCAT function would probably suit your purposes.
That would give you question_1, question_2, and question_3 values concatenated into a single column (if I understand your output intent correctly).
Update: To do a more "genuine" pivot (separating the answers to different questions into different columns), you could use one of the following approaches:
1) Use multiple table aliases: join to the dj_feedback_questions table one time for each question, e.g. something along these lines:
SELECT [...] FROM [...]
LEFT JOIN dj_feedback_questions q1 on q1.question_id = 1
LEFT JOIN dj_feedback_questions q2 on q2.question_id = 2
LEFT JOIN dj_feedback_questions q3 on q2.question_id = 3
You will then need to a) join to dj_feedback_answers similarly (multiple times with aliases), since you get to it via question_id, and you want different questions separated into different columns, and b) aggregate the results and decide how you want to handle nulls, since doing a regular JOIN instead of a LEFT JOIN will cause you to lose the row for any summary that lacks an answer to one or more questions.
2) Break into columns with an IF block: See this SO question (specifically the accepted answer) for an illustration: Transpose mysql query rows into columns
I'm having trouble with a join query, my issue is as follows.
Table: battles
Fields: id,attacker_id,defender_id
Table: users
Fields: id,profile_image
I would like to do a query to retrieve a battle and get the profile images as well from the other table.
Is there a way to do this in a single or do I have to do more than one?
Thanks in advance.
I wanted to wait a while to see if you had any attempt or if you will answer my first question to know if I understood the problem. But maybe you don't have a starting point. Try something like:
SELECT
a.profile_image as attacker_profile_image,
d.profile_image as defender_profile_image
FROM
`battles` b
LEFT JOIN
`users` a
ON
b.`attacker_id` = a.`id`
LEFT JOIN
`users` d
ON
b.`defender_id` = d.`id`
the problem here is the fact that you need to join with the users table twice, so you will need to create aliases for the columns you plan to use
This query will fetch the two images only, you will need to add the extra fields
I hate to submit a new question, but everyone else has some slight thing that is different enough to make this one seem necessary to ask.
Users are to type in a vendor name, and then see all the "kinds" of things they have bought from that company, in a list, sorted by the lowest-inventory-on-hand.
Summary:
I have three tables.
There are more fields than these, but these are the relevant ones (as far as I can tell).
stuff_table
stuff_vendor_name *(search this field with $user_input, but only one result per lookup_type)*
lookup_type
lookup_table
lookup_type
lookup_quantity (order by this)
category_type
category_table
category_type
category_location (check if this field == $this_location, which is already assigned)
Wordier Explanation:
The users are searching for a value that is contained only in the stuff_table -- distinct stuff_vendor_name values for each lookup_type. Each item can be bought from multiple sources, the idea is to see if any vendor has ever sold even one of any type of item before.
But the results need to be ORDER BY the lookup_quantity, in the lookup_table.
And importantly, I have to check to see if they are searching the correct location for these categories, located in the category_table in the category_location field.
How do I efficiently make this query?
Above, I mentioned the variables that I have:
$user_input (the value we are searching for distinct matches in the stuff_vendor_name field) and $current_location.
To understand the relationship of these tables, I will use an example.
The stuff_table would have dozens of entries with dozens of vendors, but have a lookup_type of, say, "watermelon," "apple," or "cherry."
The lookup_table would give the category_type of "Jellybean." One category type can have multiple lookup_types. But each lookup_type has exactly one category_type.
You are not sharing much about the relationships, but try this:
SELECT *
FROM stuff_table st
LEFT JOIN lookup_table lt
ON st.lookup_type = lt.lookup_type
LEFT JOIN category_table ct
ON lt.category_type = ct.category_type
AND ct.category_location = $this_location
GROUP BY st.lookup_type
ORDER BY lt.lookup_quantity
WHERE st.stuff_vendor_name = $user_input
From a first glance at it you could use foreign keys in your tables to make link between them or using the LEFT JOIN mysql command to make abstraction of another linked table.
The only example I can think of is on a Doctrine pattern, but I think you'll get what I'm saying:
$q = Doctrine_Query::create()
->from('Default_Model_DbTable_StuffTable s')
->leftJoin('s.LookupTable l')
->leftJoin('s.CategoryTable c')
->orderBy('l.lookup_quantity DESC');
$stuff= $q->execute(array(), Doctrine_Core::HYDRATE_ARRAY);
I made a nested query instead.
The final code looks like this:
$query_row=mysql_query(
"SELECT DISTINCT * FROM table_a WHERE
field_1 IN (SELECT field_1 FROM table_b WHERE field_2 = $field_2)
AND field_3 IN (SELECT field_3 FROM table_c WHERE field_4 = $field_4)
ORDER BY field_5 DESC
");
This was incredibly simple. I just didn't know you could do a nested query like that.
I read it was "bad form" because it makes some kind of search optimization not as good as it could be, so be careful using nested select statements.
However for me, it seemed to actually be significantly faster.
So I'm trying to build a simple forum. It'll be a list of topics in descending order by the date of either the topic (if no replies) or latest reply. Here's the DB structure:
forum_topic
id, name, email, body, date
forum_reply
id, email, body, date, topic_id
The forum itself will consist of an HTML table with the following headers:
Topic, Last Modified, # Replies
What would the query or queries look like to produce such a structure? I was thinking it would involve a cross join, but not sure... Thanks in advance.
Somewhat like this:
select * from forum_topic
inner join forum_reply on forum_topic.id=topc_id
However, don't use select *
That's bad practice :)
And I don't like the way you avoid normalization! Meaning I would rather have:
Users
UserID
Name
Email
Threads
ThreadID
Subject
Answered
AskedByUserID
Date
Replies
ReplyID
ThreadID
UserID
Answer
Date
Then selecting a Thread like this:
select ThreadID, Subject, Answered, AksedByUserID, Date from Threads
And selecting all replies like this
select Answer, Date, Name, Email from Threads
inner join Replies on Threads,ThreaID=Replies.ThreadID
inner join Users on AskedByUserID=UserID
where Threads.ThreadID=xxx
Now this was just written from the top of my head, but you might need to add some group by as well.
First off, it seems to me noboody is actually answering your question, which was:
What would the query or queries look like to produce such a structure?
with a requested structure of
Topic, LastModified, # Replies.
The SQL to produce a result table with that structure, given the table structures you provided, would be:
SELECT t.Id, t.Name AS Topic,
MAX(r.Date) AS LastModified,
COUNT(*) AS NumReplies
FROM Forum_Topic t
LEFT OUTER JOIN Forum_Reply r ON t.id = r.topic_id
GROUP BY t.Id, t.Name
(sorry, this is tested only on SQL Server, as I don't have access to MySql at the moment)
Also, your structure IS already normalized. Suggestions to the contrary are making assumptions about what you want to do, e.g., assuming that you are interested in tracking user names in addition to email addresses. This is quite reasonable, but is nevertheless an assumption. There is nothing wrong, from a normalization perspective, with using email address as a unique user identifier.
Now, if you are looking for general suggestions on how to set up a database, we can give you LOTS of those. Before normalization, I would start with not using potential keywords as object names (e.g., don't give columns names like 'Name' and 'Date').
Regarding the comment from Matt about the value being NULL when there are no replies: using the COALESCE() function will fix that. COALESCE() returns the first non-NULL argument (or NULL if all arguments are NULL). So replace the MAX(r.Date) with MAX(COALESCE(r.Date, t.Date)).
Yes, you should be able to get it with a query like this:
SELECT
forum_topic.id,
forum_topic.name AS Topic,
MAX(forum_reply.date) AS Last_Modified,
count(*) AS Replies
FROM forum_topic
INNER JOIN forum_reply ON (forum_topic.id=forum_reply.topic_id)
GROUP BY forum_topic.id
The "group by" is the magic that gives us one row per topic, with the MAX() and COUNT() functions giving us the aggregated data you need.
(EDIT: I missed that the body of the first post was in the topic table, so posts with no replies would get missed by the above query. Filip has the right idea suggesting you normalize your data. Once normalized, a query similar the above would get you the data you need).
By "normalized", you mean that the body column of "forum_topic" should be removed, and the actual topic body should be the first reply?