Giving weight to mysql search results - mysql

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 |
+---------+

Related

SQL Query returns incorrect count [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I am trying to get the count of all the feature and the feature name against an application by using MySQL query. Here is the query:
select t2.feature,count(t2.feature) as count from judge_task t1, approve t2 where t1.application_name='Retail Bank Portal' AND t2.gap_status=1 group by feature;
Here is the description of my tables:
Table judge_task
+------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| application_id | int | NO | | NULL | |
| application_name | varchar(50) | YES | | NULL | |
| feature_id | int | NO | | NULL | |
| feature_name | varchar(50) | YES | | NULL | |
| task_type | int | NO | | NULL | |
Table approve
+------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| feature | varchar(500) | NO | | 0 | |
| gap_status | int | NO | | 3 | |
+------------------+--------------+------+-----+---------+----------------+
There is no relationship like foreign key between both the tables. The database was designed by someone else and it can not be updated now as the application is almost completed. Updating the database will disturb the whole application design.
Here is the result of the query:
+---------+-----+
| feature | app |
+---------+-----+
| S3 | 175 |
| Login | 875 |
+---------+-----+
The original count are as follows:
S3 = 1,
Login = 5
I am not getting the exact query to get my results. Would you please suggest me a correction to my query? How should I update it to get my results? Thanks a lot
Update 1
sample data for judge_task is here
| 100173 | 4 | Retail Bank Portal | 16 | Login | 1 |
| 100203 | -1 | Retail Bank Portal | 16 | Login | 2 |
| 100204 | 4 | Retail Bank Portal | 19 | Bill Pay | 2 |
| 100205 | -1 | Retail Bank Portal | 16 | Login | 2 |
| 100206 | -1 | Retail Bank Portal | 16 | Login | 2 |
| 100207 | -1 | Retail Bank Portal | 16 | Login | 2 |
| 100208 | -1 | Retail Bank Portal | 16 | Login | 2 |
| 100209 | -1 | Retail Bank Portal | 22 | S3 | 2 |
Sample data for approve
| 59 | Login | 1 |
| 60 | Login | 1 |
| 115 | Login | 1 |
| 116 | Login | 1 |
| 117 | Login | 3 |
| 118 | Login | 3 |
| 119 | Login | 3 |
| 120 | Login | 3 |
| 121 | Login | 3 |
| 122 | Login | 3 |
| 123 | Login | 3 |
| 124 | Login | 3 |
| 125 | Login | 3 |
| 126 | Login | 3 |
| 127 | Login | 3 |
| 128 | Login | 1 |
| 129 | S3 | 1 |
+-----+-------------------------+------------+
I have updated the table definitions according to sample data removing the irrelevant columns. Thanks
Update
One thing I forget to mention that the data has to be selected from approve table and just the application name is to be selected from judge_task.
Here is how I'd make the query:
SELECT DISTINCT
t.feature, t.feature_count
FROM judge_task
INNER JOIN (
SELECT
app.feature, COUNT(*) AS feature_count
FROM approve app
WHERE app.gap_status = 1
GROUP BY app.feature
) AS t ON t.feature = judge_task.feature_name
WHERE judge_task.application_name = 'Retail Bank Portal'
;
You can see the result in this SQLFiddle
With the inner select you can avoid duplication of result due to the Cartesian product with the approve table
You are performing a(n implicit) CROSS JOIN with your query, which means, all rows from table A are combined with table B in the background.
First find the way you can explicitly join your tables (feature_id = feature.id maybe?)
Use explicit join (INNER JOIN, LEFT JOIN, RIGHT JOIN, etc) instead of implicit one (listing the tables with comas in the FROM clause).
First: you do not need a foreign key to join two tables in a query. All you need is a field in both tables with the "same data", that allows to acreate this relation, for example, a "TaskID" or anything like this (the name of the field is not nesessary the same).
The only fields in your tables that allow to create this connection in your tables are: judge_task.feature_name and approve.feature. I guess we can try this query:
select t2.feature, count(t2.id) from judge_task as t1
join approve as t2 on t2.feature = t1.feature_name
where t1.application_name='Retail Bank Portal' AND t2.gap_status=1
group by feature;
If your tables have many rows I recommend that you create indexes on judge_task.feature_name and approve.feature (as well as on judge_task.application_name) to improve performance.

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 |
+----+----------+-------------+-------+

SQL Multiple Joins with Counts

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`

Counting multiple value ocurrences on multiple columns in a single query

I have a search section for looking up products which has a navigation bar for filtering purposes that shows the total results of each product feature. For example:
TOTAL RESULTS 60
New (32)
Used (28)
Particular (10)
Company (50)
In mysql I have the following queries (one per feature)
Type
SELECT a.id_type, whois.name as whoisName, COUNT(a.id_type) as countWhois
FROM (published a
INNER JOIN types whois ON whois.id = a.id_type)
GROUP BY id_type
+---------+------------+------------+
| id_type | whoisName | countWhois |
+---------+------------+------------+
| 0 | Company | 50 |
| 1 | Particular | 10 |
+---------+------------+------------+
Condition
SELECT a.id_condition, cond.name as condName, COUNT(a.id_condition) as countCondition
FROM (published a
INNER JOIN conditions cond ON cond.id = a.id_condition)
GROUP BY id_condition
+--------------+---------------+----------------+
| id_condition | conditionName | countCondition |
+--------------+---------------+----------------+
| 0 | New | 32 |
| 1 | Used | 28 |
+--------------+---------------+----------------+
I want to summarize the two queries in a single one but canĀ“t figure out how. I was thinking something like this:
+---------+------------+------------+--------------+---------------+----------------+
| id_type | whoisName | countWhois | id_condition | conditionName | countCondition |
+---------+------------+------------+--------------+---------------+----------------+
| 0 | Company | 50 | NULL | NULL | NULL |
| 1 | Particular | 10 | NULL | NULL | NULL |
| NULL | NULL | NULL | 0 | New | 32 |
| NULL | NULL | NULL | 1 | Used | 28 |
+---------+------------+------------+--------------+---------------+----------------+
Is this possible?
Thanks and sorry if my English is bad, it's not my native language.

MySQL get linked results from the same table

I can't wrap my head around a small (hopefully) MySQL question. I have a table called links. It contains a customer_id field and a linked_id field and basically links customer accounts to each other where customer_id is in the lead. The newly created accounts can spawn accounts themselves and I would like to see all accounts that were created by the logged on user + all the accounts created by subaccounts.
Table looks like this:
+----+-------------+-----------+
| id | customer_id | linked_id |
+----+-------------+-----------+
| 1 | 1 | 5 |
| 2 | 1 | 2 |
| 3 | 1 | 11 |
| 4 | 1 | 13 |
| 5 | 13 | 14 |
| 6 | 3 | 4 |
| 7 | 7 | 8 |
+----+-------------+-----------+
So if I am logged in as user with customer_id 1 then I would like to get the userlist with linked_id 5,2,11,13 (because they are a direct connection) and linked_id 14 (because this user was created by a user who is directly connected to 1).
The query needs to be a subquery to get all user details. I currently have:
SELECT username, firstname, lastname, email, active, level FROM customers WHERE id
IN (SELECT linked_id FROM links WHERE customer_id=1) or id=1;
This obviously only returns the direct connections and the user with id=1 directly.
Thanks to eggyal for putting me on the right track. Seeing the relative complexity I do not feel so ashamed anymore that I could not crack it in the first go.
I ended up doing some research and found some nice setups to used closure tables in mysql. I ended up creating a stored procedure to populate my closure table and of course the new table cust_closure. I renamed by links table to cust_links.
cust_links:
+-------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| customer_id | int(11) | YES | | NULL | |
| linked_id | int(11) | YES | | NULL | |
+-------------+---------+------+-----+---------+----------------+
cust_closure:
+-------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+-------+
| customer_id | int(11) | YES | | NULL | |
| linked_id | int(11) | YES | | NULL | |
| distance | int(11) | YES | | NULL | |
+-------------+---------+------+-----+---------+-------+
And then added the stored procedure:
CREATE PROCEDURE populate_cust_closure()
BEGIN
DECLARE distance int;
TRUNCATE TABLE cust_closure;
SET distance = 0;
-- seed closure with self-pairs (distance 0)
INSERT INTO cust_closure (customer_id, linked_id, distance)
SELECT customer_id, customer_id, distance
FROM cust_links GROUP BY customer_id;
-- for each pair (root, leaf) in the closure,
-- add (root, leaf->child) from the base table
REPEAT
SET distance = distance + 1;
INSERT INTO cust_closure (customer_id, linked_id, distance)
SELECT cust_closure.customer_id, cust_links.linked_id, distance
FROM cust_closure, cust_links
WHERE cust_closure.linked_id = cust_links.customer_id
AND cust_closure.distance = distance - 1;
UNTIL ROW_COUNT()=0
END REPEAT;
END //
When I then called the stored procedure it produced:
mysql> select * from cust_closure;
+-------------+-----------+----------+
| customer_id | linked_id | distance |
+-------------+-----------+----------+
| 1 | 1 | 0 |
| 3 | 3 | 0 |
| 7 | 7 | 0 |
| 13 | 13 | 0 |
| 1 | 5 | 0 |
| 1 | 2 | 0 |
| 1 | 11 | 0 |
| 1 | 13 | 0 |
| 13 | 14 | 0 |
| 1 | 14 | 1 |
| 3 | 4 | 0 |
| 7 | 8 | 0 |
+-------------+-----------+----------+
So now my original query becomes:
SELECT username, firstname, lastname, email, active, level FROM customers WHERE id
IN (SELECT linked_id FROM cust_closure WHERE customer_id=1);
Thanks again for eggyal and hope this helps someone in the future.