SQL Multiple Joins with Counts - mysql

I need to join 3 different MySQL tables:
The questions table (represents a questio)
The questions votes table (represents a like to the question when Sign=1 and an unlike when Sign=0)
The answers table (represents a table with the answers to an specific question).
The three tables have this structure:
Questions
--------------------------------------------
| ID | UserID | Title | Body | Date
--------------------------------------------
| 1 | 32 | Is it raining? | BodyQuestion | 01/01/2016
| 2 | 45 | Who are we? | BodyQuestion | 02/02/2016
--------------------------------------------
QuestionsVotes
--------------------------------------------
| ID | QuestionID | Sign
--------------------------------------------
| 1 | 1 | 1
| 2 | 2 | 1
| 3 | 1 | 0
| 4 | 1 | 1
| 5 | 2 | 0
Answers
--------------------------------------------
| ID | QuestionID | UserID | Body
--------------------------------------------
| 1 | 1 | 45 | Yes, it is.
| 2 | 2 | 10 | Tricky question...
| 3 | 1 | 67 | In Barcelona it is not
What I need is a SQL query that returns, given the ID of a Question, to return as much rows as number of answers the question has received each one with the Questions.ID, Questions.UserID, Questions.Title, Questions.Body, Questions.Date, the likes associated to the question, the unlikes associated with the question, Answers.ID, Answers.UserID. As you see, the only change between the rows are the Answers fields.
SQL Output (for Question with ID=1)
--------------------------------------------
| ID | UserID | Title | Body | Date | Likes | Unlikes | AnswerID | AnswerUserID
--------------------------------------------
| 1 | 32 | Is it raining? | BodyQuestion | 01/01/2016 | 2 | 1 | 1 | 45
| 1 | 32 | Is it raining? | BodyQuestion | 01/01/2016 | 2 | 1 | 3 | 67
EDIT 1: Example given.

Try this:
SELECT `ID` , `UserID` , `Title`, `Body`, `Date`, `Likes`, `Unlikes`, `AnswerID`, `AnswerUserID`
FROM QUESTIONS q
INNER JOIN ANSWERS a ON q.`ID` = a.`QuestionID`
INNER JOIN QuestionVotes v ON q.`ID` = v.`QuestionID`

Related

How to get records from below tables

I have 3 tables
QUESTION table with below 2 properties
1. ID (serial)
2. Question (varchar)
ANSWER table with below 4 properties
1. ID (serial)
2. QuestionID (foreign key to table QUESTION)
3. StudentID (foreign key to table STUDENT)
4. ANSWER (varchar)
5. SubmitDateTime (datetime)
STUDENT table with below properties
1. ID (serial)
2. Name (varchar)
I just want to show records with each student (one record for each student) with every answer. If any question's answer is not given by the student it will show blank.
For example:
QUESTION TABLE
| ID | QUESTION |
|----- | --------- |
| 1 | A FOR? |
| 2 | B FOR? |
| 3 | C FOR? |
ANSWER TABLE
| ID | QuestionID | StudentID | ANSWER | SubmitDateTime |
|----- | ----------- |------------|--------|----------------|
| 1 | 1 | 1 |Apple | something date |
| 2 | 1 | 2 |Ant | something date |
| 3 | 2 | 1 |Book | something date |
| 4 | 3 | 2 |Cat | something date |
STUDENT TABLE
| ID | NAME |
|----- | --------- |
| 1 | Jhon |
| 2 | Lily |
Expected Records
Result table
| ID | NAME | Answers |
|----- | --------- | ---------------------|
| 1 | Jhon | Apple,Book,<blank> |
| 2 | Lily | Ant,<blank>,Cat |
"blank" means no record will be shown instead of a blank space or a hyphane.
My implementation:
SELECT s.ID,s.Name,
GROUP_CONCAT(a.answer SEPARATOR ',') AS answers
FROM student AS s
LEFT JOIN answer AS a ON a.studentID=s.ID
WHERE a.submitdate BETWEEN '<somedate>' AND '<somedate>'
GROUP BY s.ID ORDER BY a.ID ASC
It does not give me a blank answer. How to get these?
Try this:
SELECT sid, sname, GROUP_CONCAT(IFNULL(a.answer,'-') ORDER BY qid)
FROM
(SELECT q.id AS qid, s.id AS sid, s.Name AS sname
FROM question q CROSS JOIN student s) qs
LEFT JOIN answer a ON qs.qid=a.QuestionID AND qs.sid=a.StudentID
GROUP BY sid, sname;
The base query is a CROSS JOIN between question and student tables that will be a subquery and give a result like this:
+-----+-----+-------+
| qid | sid | sname |
+-----+-----+-------+
| 1 | 2 | Lily |
| 1 | 1 | Jhon |
| 2 | 2 | Lily |
| 2 | 1 | Jhon |
| 3 | 2 | Lily |
| 3 | 1 | Jhon |
+-----+-----+-------+
As you can see, each of the student will be paired with all of the existing question regardless of their answer records in answer table. This will be the reference for the LEFT JOIN with answer table.
Demo fiddle

mysql - dynamic select row as column

Update: I want to use dynamic sql to select question as column and put answer in row, like cursor or loop, is that possible?
I want the select result like this
+--------+---------------+--------------------------------------------------------------------------+
| userid | Living Status | This is another question get from row and it's longer than 64 characters |
+--------+---------------+--------------------------------------------------------------------------+
| 19 | married | q2_opt3 |
+--------+---------------+--------------------------------------------------------------------------+
And here is my query
select
userid,
min(if(question.ordering=1,o.name,NULL )) as 'Living Status',
min(if(question.ordering=2,o.name,NULL )) as 'This is another question get from row and it's longer than 64 characters'
from answer
inner join question on question.key_value = answer.key_value
inner join q_option o on question.id = o.question_id and o.value = answer.answer
where userid in (19)
GROUP BY id
The question table is like
+----+----------+---------------------------------------------------------------------------+--------------+
| id | ordering | question | key_value |
+----+----------+---------------------------------------------------------------------------+--------------+
| 1 | 1 | Living Status | livingStatus |
| 2 | 2 | This is another question get from row and it's longer than 64 characters | question_2 |
+----+----------+---------------------------------------------------------------------------+--------------+
The answer table is like
+----+--------+--------------+--------+
| id | answer | key_value | userid |
+----+--------+--------------+--------+
| 1 | 2 | livingStatus | 19 |
| 2 | 3 | question_2 | 19 |
+----+--------+--------------+--------+
The q_option table is like
+----+----------+-------------+-------+
| id | name | question_id | value |
+----+----------+-------------+-------+
| 1 | single | 1 | 1 |
| 2 | married | 1 | 2 |
| 3 | divorced | 1 | 3 |
| 4 | q2_opt1 | 2 | 1 |
| 5 | q2_opt2 | 2 | 2 |
| 6 | q2_opt3 | 2 | 3 |
+----+----------+-------------+-------+

How can I determine which user is the top one in the specific tag?

I have a question and answer website like stackoverflow. Here is the structure of some tables:
-- {superfluous} means some other columns which are not related to this question
// q&a
+----+-----------------+--------------------------+------+-----------+-----------+
| id | title | body | type | related | author_id |
+----+-----------------+--------------------------+------+-----------+-----------+
| 1 | How can I ... | I'm trying to make ... | q | NULL | 3 |
| 2 | | You can do that by ... | a | 1 | 1 |
| 3 | Why should I .. | I'm wonder, why ... | q | NULL | 1 |
| 4 | | First of all you ... | a | 1 | 2 |
| 5 | | Because that thing ... | a | 3 | 2 |
+----+-----------------+--------------------------+------+-----------+-----------+
// users
+----+--------+-----------------+
| id | name | {superfluous} |
+----+--------+-----------------+
| 1 | Jack | |
| 2 | Peter | |
| 3 | John | |
+----+--------+-----------------+
// votes
+----+----------+-----------+-------+-----------------+
| id | user_id | post_id | value | {superfluous} |
+----+----------+-----------+-------+-----------------+
| 1 | 3 | 4 | 1 | |
| 2 | 1 | 1 | -1 | |
| 3 | 2 | 1 | 1 | |
| 4 | 3 | 2 | -1 | |
| 5 | 1 | 4 | 1 | |
| 6 | 3 | 5 | -1 | |
+----+--------+-------------+-------+-----------------+
// tags
+----+------------+-----------------+
| id | name | {superfluous} |
+----+------------+-----------------+
| 1 | PHP | |
| 2 | SQL | |
| 3 | MySQL | |
| 4 | HTML | |
| 5 | CSS | |
| 6 | C# | |
+----+------------+-----------------+
// q&aTag
+-------+--------+
| q&aid | tag_id |
+-------+--------+
| 1 | 1 |
| 1 | 4 |
| 3 | 5 |
| 3 | 4 |
| 4 | 6 |
+-------+--------+
Now I need to find top users in a specific tag. For example, I need to find Peter as top user in PHP tag. Because his answer for question1 (which has PHP tag) has earned 2 upvotes. Is doing that possible?
Try this:
select q1.title, u.id, u.name, sum(v.value) total from `q&a` q1
left join `q&atag` qt ON q1.id = qt.`q&aid`
inner join tags t ON qt.tag_id = t.id
left join `q&a` q2 ON q2.related = q1.id
left join users u ON q2.author_id = u.id
left join votes v ON v.post_id = q2.id
where t.name = 'PHP'
group by q1.id, u.id
and here is a simple divided solution:
Let us divide it into sub queries:
get the id of the tag you will search for: select id from tags where name = 'PHP'
get the questions with this tag: select 'q&aid' from 'q&aTag' where tag_id = 1.
get the ids of answers for that question: select id, author_id fromq&awhere related in (2.)
get the final query: select user_id, sum(value) from votes where post_id in (3.) group by user_id
Now combining them all give the result:
select user_id, sum(`value`) total from votes
where post_id in (
select id from `q&a` where related in (
select `q&aid` from `q&aTag` where tag_id IN (
select id from tags where name = 'PHP'
)
)
)
group by user_id
you can add this at the end if you want only one record:
order by total desc limit 1

Giving weight to mysql search results

I'm trying to improve my search functionality in one of my sites by ordering the records by those with the most matches.
I have the following mysql tables.
SurveyResponses
+--------+-------------+------------------+
| sID | Title | Description |
+--------+-------------+------------------+
| 1 | Set 1 | Some txt here |
| 2 | Set 2 | Other text here |
+--------+-------------+------------------+
Answers:
+------+---------+------------+---------+
| aID | Parent | Question | Answer |
+------+---------+------------+---------+
| 1 | 1 | 1 | yes |
| 2 | 1 | 2 | yes |
| 3 | 1 | 3 | yes |
| 4 | 2 | 1 | yes |
| 5 | 2 | 2 | no |
| 6 | 2 | 3 | no |
+------+---------+------------+---------+
I want to get ALL the records from the SurveyResponses table and order them in order of the questions that were matched as correct in the answers table. The correct answers are as follows:
Q1 = yes
Q2 = no
Q3 = no
So based on this, the query should return Set 2 at the top of the results because the answers were all correct in the answers table for that question, whereas q2 and q3 were wrong in Set 1.
I imagine I need some kind of scoring system so I can do something like
IF q1 = yes THEN score = score + 1
Obviously I can do this in PHP, but not sure what method to use in mysql.
A answer to the question would be appreciated, but even a hint or link to the best approach would be greatly appreciated.
I don't really understand your design, or what you're trying to do so instead I'll offer up an alternative example that may or may not be helpful...
CREATE TABLE survey
(question_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,question VARCHAR(100) NOT NULL UNIQUE
,correct_answer VARCHAR(3) NOT NULL
);
INSERT INTO survey VALUES
(1,'Is Rome the capital of Italy?','Yes'),
(2,'Is Paris the capital of England?','No'),
(3,'Is London the capital of Spain?','No');
CREATE TABLE survey_response
(user_id INT NOT NULL
,question_id INT NOT NULL
,response VARCHAR(3) NOT NULL
,PRIMARY KEY(user_id,question_id)
);
INSERT INTO survey_response VALUES
(101,1,'Yes'),
(101,2,'Yes'),
(101,3,'Yes'),
(102,1,'Yes'),
(102,2,'No'),
(102,3,'No');
SELECT * FROM survey;
+-------------+----------------------------------+----------------+
| question_id | question | correct_answer |
+-------------+----------------------------------+----------------+
| 1 | Is Rome the capital of Italy? | Yes |
| 2 | Is Paris the capital of England? | No |
| 3 | Is London the capital of Spain? | No |
+-------------+----------------------------------+----------------+
SELECT * FROM survey_response;
+---------+-------------+----------+
| user_id | question_id | response |
+---------+-------------+----------+
| 101 | 1 | Yes |
| 101 | 2 | Yes |
| 101 | 3 | Yes |
| 102 | 1 | Yes |
| 102 | 2 | No |
| 102 | 3 | No |
+---------+-------------+----------+
So let's say I want to sort respondents (user_ids) according to who got the most correct answers. i could do that this way...
SELECT sr.user_id
FROM survey s
JOIN survey_response sr
ON sr.question_id = s.question_id
GROUP
BY user_id
ORDER
BY SUM(sr.response = s.correct_answer) DESC;
+---------+
| user_id |
+---------+
| 102 |
| 101 |
+---------+

MySQL - Use Header Name as Part of Query Filter

I'm relatively new to MySQL and have come across a problem to which I cannot seem to find a solution. I have searched but could not find an answer. I'm open to the possibility that I'm not asking the question correctly. Here goes:
I'm trying to use the name of a given column and the values within that column from one table to pull values from another table. The first table contains 3 columns with the response codified. The second table contains the definitions for each code for each item. The same number code is associated with different meanings depending on the item. For example:
table1 (this table cannot change):
--------------------------------------------------------------
|result_id | f_initial | l_name | item_A | item_B | item_C |
--------------------------------------------------------------
| 1 | j | doe | 1 | 3 | 2 |
| 2 | k | smith | 3 | 1 | 2 |
| 3 | l | williams | 2 | 2 | 1 |
--------------------------------------------------------------
table2 (this table can be modified, split, or whatever needs to be done):
-------------------------------------------
|item_id | item_name | score | definition |
-------------------------------------------
| 1 | item_A | 1 | agree |
| 2 | item_A | 2 | neutral |
| 3 | item_A | 3 | disagree |
| 4 | item_B | 1 | likely |
| 5 | item_B | 2 | not likely |
| 6 | item_B | 3 | no reply |
| 7 | item_C | 1 | yes |
| 8 | item_C | 2 | no |
-------------------------------------------
My goal is for the query to output the following:
--------------------------------------------------------------------
|result_id | f_initial | l_name | item_A | item_B | item_C |
--------------------------------------------------------------------
| 1 | j | doe | agree | no reply | no |
| 2 | k | smith | disagree | likely | no |
| 3 | l | williams | neutral | not likely | yes |
--------------------------------------------------------------------
Any assistance or guidance is greatly appreciated. Thank you in advance.
You must join the two tables on the item_A/B/C and score columns
select t1.result_id, t1.f_initial, t1.l_name,
t2a.definition as item_a,
t2b.definition as item_b,
t2c.definition as item_c
from table1 t1
join table2 t2a on t2a.score = t1.item_a
join table2 t2b on t2b.score = t1.item_b
join table2 t2c on t2c.score = t1.item_c
where t2a.item_name = 'item_A'
and t2b.item_name = 'item_B'
and t2c.item_name = 'item_C'