MySQL Interview ,which made me disqualified for the job [closed] - mysql

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
I was given this MySQL Interview Question, which made me disqualified for the job.
I went to interview and was asked a question which I was not able to reply and I lost the job.
They asked.
We have two tables, the first table (master table) is CANDIDATES with fields:
candidate_id (primary_key)
candidate_name
The Second table (child table) is CANDIDATE_VOTES with fields:
v_id (primary key)
candidate_id (foreign key)
Every time a vote is given the candidate's candidate_key is put in the child.
CANDIDATE:
=================================
|candidate_id | candidate_Name |
|-------------------------------|
| 1 | abc |
|-------------------------------|
| 2 | xyz |
|-------------------------------|
| 3 | etc |
|-------------------------------|
CANDIDATE VOTES
==========================
| votes_id | candidate_id |
|-------------------------|
| 1 | 1 |
|-------------------------|
| 2 | 1 |
|-------------------------|
| 3 | 2 |
|-------------------------|
The Question was how would you declare a winner?
Please help me how to do it.
I tried a lot but could not find the logic.

You should return all candidates who have the most number of votes:
SELECT candidates.*
FROM candidates JOIN candidate_votes USING (candidate_id)
GROUP BY candidate_id
HAVING COUNT(*) = (
SELECT COUNT(*)
FROM candidate_votes
GROUP BY candidate_id
ORDER BY votes DESC
LIMIT 1
)
See it on sqlfiddle.

You can use a COUNT() to get the total number of votes associated with each candidate. By joining the two tables, you can return the candidate_name and if you use a LIMIT and ORDER BY on the query it will return only the record determined as the winner.
SELECT count(*) winner, c.candidate_Name
FROM candidates c
INNER JOIN candidate_votes cv
ON c.candidate_id = cv.candidate_id
GROUP BY c.candidate_Name
ORDER BY winner desc
LIMIT 1 -- remove the LIMIT to see all records
See SQL Fiddle with Demo
or MySQL allows to GROUP BY on fields not in the SELECT
SELECT count(*) winner, , c.candidate_Name
FROM candidates c
INNER JOIN candidate_votes cv
ON c.candidate_id = cv.candidate_id
GROUP BY cv.candidate_id
ORDER BY winner desc
LIMIT 1 -- remove the LIMIT to see all records
See SQL Fiddle with Demo

SELECT CANDIDATE_NAME, count(*)
FROM CANDIDATES, CANDIDATE_VOTES
WHERE CANDIDATES.CANDIDATE_ID = CANDIDATE_VOTES.CANDIDATE_ID
GROUP BY CANDIDATE_NAME
ORDER BY count(*) DESC
LIMIT 1
SELECT: you select the candidate names + the amount of votes
FROM: use both tables
WHERE: basicly a join to link the tables
GROUP BY: Group the votes per candidate name, the count(*) in the SELECT will show you the amount of lines that were grouped into 1 line (a.k.a. the amount of votes per candidate)
ORDER BY: order by amount of votes highest to lowest
LIMIT: Will only return 1 row, remove this line to see complete list of candidates and votes

There are several ways to this. One would be to use a GROUP BY:
SELECT TOP 1 C.candidate_id, C.candidate_name, COUNT(1) NoOfVotes
FROM CANDIDATES C
INNER JOIN CANDIDATE_VOTES CV ON C.candidate_id = CV.candidate_id
GROUP BY C.candidate_id, , C.candidate_name
ORDER BY 3 DESC
This statement creates one row for each vote then groups these rows by candidate and counts how many rows are there for each candidate. Then it orders by the counted votes per candidate and selects the top 1 (doesn't deal with ties though).
Edit: This answer is valid for T-SQL (SQL Server). It seems from the other answers that you need to use "LIMIT 1" at the end instead of the "TOP 1" at the beginning for MySQL.

Related

Sorting rows in MySql based on algebra on two or more columns

I am designing a Test-System for a school.The table is like this
------------------------------------------------------------------------------
Student_id | Total Questions | Questions Attempted | Correct
------------------------------------------------------------------------------
36 | 60 | 42 | 20
19 | 60 | 38 | 32
119 | 60 | 37 | 31
Now, marking scheme is +3 for correct and -1 for wrong
Number of wrong questions will be calculated as wrong = attempted - correct
Questions
1) I want to give the give the student some points based on their ranks, so I want to sort the table on the decreasing order of their score i.e. score = 3 * correct - wrong.Though,I could have stored the score as well but since it is redundant data I don't want to store it into the table.How can I sort the table using SQL query.
2)When I will be updating the points of students based on their performance into the table student,I am picking student_id from result table and making updations into the student table i.e. 1 query per student.This means that if 4000 students sat for the test ,4000 queries !!! .Can I improve the situation (minimise queries)?
EDIT
Student schema for question 2
------------------------------------------------------------------------------
Student_id | fname | lname | contact | points
------------------------------------------------------------------------------
you can just specify what you want in the sort
select student_id,correct, attempted,field4,field5, (3 * correct - (attempted-correct)) as score
from students sort by score desc
yes, take a look at bulk update of sql, you can prepare the query and update 10 by 10 or 100 by 100, but not too much since sql command have limit on its length
Question 1.
Supposing the table is named Results, and that Student_id is unique, here is a possible solution to your question:
SELECT Results.*, (3*Correct-(Total_Questions-Correct)) AS score
FROM Results
ORDER BY score DESC
Question 2.
Supposing the Students are already added to the table Students, or that they already have a score, this is a possible SQL Query to update the students table without making the 4k queries:
UPDATE StudentsTable AS s
INNER JOIN PointsTable AS p
ON s.Student_id = p.Student_id
SET
s.Points = s.Points + (3 * p.Correct - (p.Questions_Attempted - p.Correct))
If you need to perform more tests in the future you can add a Test_ID column to you Points Table and then add a WHERE clause to the UPDATE query in order to just add up the score from a given test.
Optimization
You can optimize the queries a little bit by changing the way you calculate the score:
SELECT Results.*, (2*Correct-Total_Questions) AS score
FROM Results
ORDER BY score DESC
UPDATE StudentsTable AS s
INNER JOIN PointsTable AS p
ON s.Student_id = p.Student_id
SET
s.Points = s.Points + (2 * p.Correct - p.Questions_Attempted)
To rank students by score you can do
SELECT student_id,
(
SELECT 1 + COUNT(*)
FROM student_results
WHERE 3 * correct - (total - correct) >=
3 * r.correct - (r.total - r.correct)
AND student_id <> r.student_id
) rank
FROM student_results r
Output:
| STUDENT_ID | RANK |
|------------|------|
| 36 | 3 |
| 19 | 1 |
| 119 | 2 |
Now you can update student points in one go using multi-table UPDATE syntax instead of hitting the database with number of update queries.
UPDATE students s JOIN
(
SELECT student_id,
(
SELECT 1 + COUNT(*)
FROM student_results
WHERE 3 * correct - (total - correct) >=
3 * r.correct - (r.total - r.correct)
AND student_id <> r.student_id
) rank
FROM student_results r
) q
ON s.student_id = q.student_id
SET s.points = s.points +
CASE q.rank -- implement your logic of translating ranks to points here
WHEN 1 THEN 100
WHEN 2 THEN 50
WHEN 3 THEN 10
ELSE 0
END;
Here is SQLFiddle demo

MySQL select multiple values from one field

Not sure if this is possible but I have a schema like this:
id | user_id | thread_id
1 | 1 | 1
2 | 4 | 1
3 | 1 | 2
4 | 3 | 2
I am trying to retrieve the thread_id where user_id = 1 and 4. I know that in(1,4) does not fit my needs as its pretty much a OR and will pull up record 3 as well and Exists only returns a bool.
You may use JOIN (that answer already exists) or HAVING, like this:
SELECT
thread_id,
COUNT(1) AS user_count
FROM
t
WHERE
user_id IN (1,4)
GROUP BY
thread_id
HAVING
user_count=2
-check the demo. HAVING will fit better in case of many id's (because with JOIN you'll need to join as many times as many id you have). This is a bit tricky, however: you may do = comparison only if your records are unique per (user_id, thread_id); for example, your user_id can repeat, then use >=, like in this demo.
Try this with join, i guess you need to do AND operation with user_id must be 4 and 1 then
SELECT
t1.thread_id
FROM
TABLE t1
JOIN TABLE t2
ON (t1.user_id = t2.user_id)
WHERE t1.user_id = 1
AND t2.user_id = 4

Group by in complex query with cases [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 8 years ago.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Improve this question
I have a table:
Visit (FromId, ToId, VisitTime)
where FromId and ToId are FKs to table
UserProfile (uid, name, age ...)
As a user with my UID I want to select all profiles I have visited or who visited me in one result set ordered by VisitTime and with the indication of the "direction of the visit".
I get data using this select:
SELECT CASE WHEN a.FromID = 'yourIDHere'
THEN c.Name
ELSE b.Name
END Name,
CASE WHEN a.FromID = 'yourIDHere'
THEN c.Age
ELSE b.Age
END Age,
a.VisitTime,
CASE WHEN a.FromID = 'yourIDHere'
THEN 'You'
ELSE 'Friend'
END DirectionOfVisit
FROM Visit a
INNER JOIN UserProfile b
ON a.FromID = b.Uid
INNER JOIN UserProfile c
ON a.ToID = c.Uid
WHERE 'yourIDHere' IN (a.FromID, a.ToID)
ORDER BY a.VisitTime
Now it prints (pseudo output)
Jack (id1) | IN |12.12.2012
Jack (id1) | IN |11.12.2012
Jack (id1) | IN |11.12.2012
Jack (id1) | OUT | 13.12.2012
Jack (id1) | OUT | 12.12.2012
Michael (id5) | IN | 5.12.2012
Michael (id5) | OUT | 6.12.2012
Michael (id5) | OUT | 5.12.2012
I would like the list to be like this:
Jack | IN | 12.12.2012 (the most recent)
Jack | OUT | 13.12.2012 (the most recent)
Michael (id5) | IN | 5.12.2012 (the most recent)
Michael (id5) | OUT | 6.12.2012 (the most recent)
I know the GROUP command would solve it but it's too complex for me (beginner).
You could use GROUP BY along with an aggregate function to get the result. Since you want the most recent date for each name and type (IN/OUT), then you can use the max() aggregate function on the date column. You will then use a GROUP BY on the other columns you want to return:
The basic syntax will be:
select
name,
type,
max(date) date
from yourtable
group by name, type;
See SQL Fiddle with Demo
If you want to return the max date with your existing query, you can just expand the query to use:
select name, age, max(VisitTime), DirectionOfVisit
from
(
SELECT CASE WHEN a.FromID = 'yourIDHere'
THEN c.Name
ELSE b.Name
END Name,
CASE WHEN a.FromID = 'yourIDHere'
THEN c.Age
ELSE b.Age
END Age,
a.VisitTime,
CASE WHEN a.FromID = 'yourIDHere'
THEN 'You'
ELSE 'Friend'
END DirectionOfVisit
FROM Visit a
INNER JOIN UserProfile b
ON a.FromID = b.Uid
INNER JOIN UserProfile c
ON a.ToID = c.Uid
WHERE 'yourIDHere' IN (a.FromID, a.ToID)
) d
group by name, age, DirectionOfVisit;
See SQL Fiddle with Demo

SQL: Returning the most common value for each person

EDIT: I'm using MySQL, I found another post with the same question, but it's in Postgres; I require MySQL.
Get most common value for each value of another column in SQL
I ask this question after extensive searching of this site and others but have not found a result that works as I intend it to.
I have a table of people (recordid, personid, transactionid) and a transaction table (transactionid, rating). I require a single SQL statement that can return the most common rating each person has.
I currently have this SQL statement that returns the most common rating for a specified person id. It works and perhaps it may help others.
SELECT transactionTable.rating as MostCommonRating
FROM personTable, transactionTable
WHERE personTable.transactionid = transactionTable.transactionid
AND personTable.personid = 1
GROUP BY transactionTable.rating
ORDER BY COUNT(transactionTable.rating) desc
LIMIT 1
However I require a statement that does what the above statement does for each personid in personTable.
My attempt is below; however, it times out my MySQL server.
SELECT personid AS pid,
(SELECT transactionTable.rating as MostCommonRating
FROM personTable, transactionTable
WHERE personTable.transactionid = transactionTable.transactionid
AND personTable.personid = pid
GROUP BY transactionTable.rating
ORDER BY COUNT(transactionTable.rating) desc
LIMIT 1)
FROM persontable
GROUP BY personid
Any help you can give me would be much obliged. Thanks.
PERSONTABLE:
RecordID, PersonID, TransactionID
1, Adam, 1
2, Adam, 2
3, Adam, 3
4, Ben, 1
5, Ben, 3
6, Ben, 4
7, Caitlin, 4
8, Caitlin, 5
9, Caitlin, 1
TRANSACTIONTABLE:
TransactionID, Rating
1 Good
2 Bad
3 Good
4 Average
5 Average
The output of the SQL statement I am searching for would be:
OUTPUT:
PersonID, MostCommonRating
Adam Good
Ben Good
Caitlin Average
Preliminary comment
Please learn to use the explicit JOIN notation, not the old (pre-1992) implicit join notation.
Old style:
SELECT transactionTable.rating as MostCommonRating
FROM personTable, transactionTable
WHERE personTable.transactionid = transactionTable.transactionid
AND personTable.personid = 1
GROUP BY transactionTable.rating
ORDER BY COUNT(transactionTable.rating) desc
LIMIT 1
Preferred style:
SELECT transactionTable.rating AS MostCommonRating
FROM personTable
JOIN transactionTable
ON personTable.transactionid = transactionTable.transactionid
WHERE personTable.personid = 1
GROUP BY transactionTable.rating
ORDER BY COUNT(transactionTable.rating) desc
LIMIT 1
You need an ON condition for each JOIN.
Also, the personID values in the data are strings, not numbers, so you'd need to write
WHERE personTable.personid = "Ben"
for example, to get the query to work on the tables shown.
Main answer
You're seeking to find an aggregate of an aggregate: in this case, the maximum of a count. So, any general solution is going to involve both MAX and COUNT. You can't apply MAX directly to COUNT, but you can apply MAX to a column from a sub-query where the column happens to be a COUNT.
Build the query up using Test-Driven Query Design — TDQD.
Select person and transaction rating
SELECT p.PersonID, t.Rating, t.TransactionID
FROM PersonTable AS p
JOIN TransactionTable AS t
ON p.TransactionID = t.TransactionID
Select person, rating, and number of occurrences of rating
SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
FROM PersonTable AS p
JOIN TransactionTable AS t
ON p.TransactionID = t.TransactionID
GROUP BY p.PersonID, t.Rating
This result will become a sub-query.
Find the maximum number of times the person gets any rating
SELECT s.PersonID, MAX(s.RatingCount)
FROM (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
FROM PersonTable AS p
JOIN TransactionTable AS t
ON p.TransactionID = t.TransactionID
GROUP BY p.PersonID, t.Rating
) AS s
GROUP BY s.PersonID
Now we know which is the maximum count for each person.
Required result
To get the result, we need to select the rows from the sub-query which have the maximum count. Note that if someone has 2 Good and 2 Bad ratings (and 2 is the maximum number of ratings of the same type for that person), then two records will be shown for that person.
SELECT s.PersonID, s.Rating
FROM (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
FROM PersonTable AS p
JOIN TransactionTable AS t
ON p.TransactionID = t.TransactionID
GROUP BY p.PersonID, t.Rating
) AS s
JOIN (SELECT s.PersonID, MAX(s.RatingCount) AS MaxRatingCount
FROM (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
FROM PersonTable AS p
JOIN TransactionTable AS t
ON p.TransactionID = t.TransactionID
GROUP BY p.PersonID, t.Rating
) AS s
GROUP BY s.PersonID
) AS m
ON s.PersonID = m.PersonID AND s.RatingCount = m.MaxRatingCount
If you want the actual rating count too, that's easily selected.
That's a fairly complex piece of SQL. I would hate to try writing that from scratch. Indeed, I probably wouldn't bother; I'd develop it step-by-step, more or less as shown. But because we've debugged the sub-queries before we use them in bigger expressions, we can be confident of the answer.
WITH clause
Note that Standard SQL provides a WITH clause that prefixes a SELECT statement, naming a sub-query. (It can also be used for recursive queries, but we aren't needing that here.)
WITH RatingList AS
(SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
FROM PersonTable AS p
JOIN TransactionTable AS t
ON p.TransactionID = t.TransactionID
GROUP BY p.PersonID, t.Rating
)
SELECT s.PersonID, s.Rating
FROM RatingList AS s
JOIN (SELECT s.PersonID, MAX(s.RatingCount) AS MaxRatingCount
FROM RatingList AS s
GROUP BY s.PersonID
) AS m
ON s.PersonID = m.PersonID AND s.RatingCount = m.MaxRatingCount
This is simpler to write. Unfortunately, MySQL does not yet support the WITH clause.
The SQL above has now been tested against IBM Informix Dynamic Server 11.70.FC2 running on Mac OS X 10.7.4. That test exposed the problem diagnosed in the preliminary comment. The SQL for the main answer worked correctly without needing to be changed.
Here's a somewhat hacky abuse of the fact that the max aggregate function in MySQL does lexical sorting on varchars (as well as the expected numerical sorting on integers/floats):
SELECT
PersonID,
substring(max(concat(lpad(c, 20, '0'), Rating)), 21) AS MostFrequentRating
FROM (
SELECT PersonID, Rating, count(*) c
FROM PERSONTABLE INNER JOIN TRANSACTIONTABLE USING(TransactionID)
GROUP BY PersonID, Rating
) AS grouped_ratings
GROUP BY PersonID;
Which gives the desired:
+----------+--------------------+
| PersonID | MostFrequentRating |
+----------+--------------------+
| Adam | Good |
| Ben | Good |
| Caitlin | Average |
+----------+--------------------+
(note, if there are multiple modes per person, it will pick the one with the highest alphabetic entry, so — pretty much randomly — Good over Bad and Bad over Average)
You should be able to see what the max is operating over by examining the following:
SELECT PersonID, Rating, count(*) c, concat(lpad(count(*), 20, '0'), Rating) as LexicalMaxMe
FROM PERSONTABLE INNER JOIN TRANSACTIONTABLE USING(TransactionID)
GROUP BY PersonID, Rating
ORDER BY PersonID, c DESC;
Which outputs:
+----------+---------+---+-----------------------------+
| PersonID | Rating | c | LexicalMaxMe |
+----------+---------+---+-----------------------------+
| Adam | Good | 2 | 00000000000000000002Good |
| Adam | Bad | 1 | 00000000000000000001Bad |
| Ben | Good | 2 | 00000000000000000002Good |
| Ben | Average | 1 | 00000000000000000001Average |
| Caitlin | Average | 2 | 00000000000000000002Average |
| Caitlin | Good | 1 | 00000000000000000001Good |
+----------+---------+---+-----------------------------+
For anyone using Microsoft SQL Server: You have the possibility to create a custom aggregate function to get the most common value. Example 2 of this blog post by Ahmed Tarek Hasan describes how to do it:
http://developmentsimplyput.blogspot.nl/2013/03/creating-sql-custom-user-defined.html

Find the oldest record in a join between two tables

I have two tables. "Questions": A list of questions, "Results" The users results to these questions.
Table: Questions
ID
Table: Results
ID
Created - When this record was added.
Q_ID - A FK to the Question table
Example data
Table: Questions
ID
----
1
8
15
55
Table: Results
ID | Created | Q_ID
--------------------
1 | 12:02 | 1
2 | 12:03 | 15
3 | 12:04 | 8
I am looking for a query (or two) that will get me the following information.
A question that does not have a result associated with it.
If all questions have results then find the question with the oldest result.
This query should return question.id=55, because its is the only question that does not have a result. If question.id=55 did not exist then it would return question.id=1 as it has the oldest result to the question.
If you LEFT JOIN the two tables, you can use the ORDER BY clause to do what you need:
SELECT *
FROM
questions
LEFT JOIN results
ON results.q_id = questions.id
ORDER BY
ISNULL(results.id) DESC, results.created ASC
LIMIT 1
This will place any questions that do not have results at the top of the list, followed by a list of all questions with results (in "oldest question first" order). The LIMIT 1 will have it display only the top result - which should match what you need.
1- A question that does not have a result associated with it
select q.qid from questions q where q.qid not in ( select r.qid from result r )
2- find the question with the oldest result
select min(r.createdon) from result r