Could anyone help me with the script for this - mysql

have a table like this
empid questionid options
1 1 A
2 1 A
3 1 B
4 1 C
now i need result like this
questionid responseA responseB responseC
1 50% 25% 25%

You could PIVOT;
SELECT questionid, (A / total) * 100 responseA, (B / total) * 100 responseB, (C / total) * 100 responseC FROM (
SELECT T1.questionid, T1.options, T2.total
FROM the_tbl T1
INNER JOIN (SELECT questionid, CAST(COUNT(*) AS MONEY) AS total FROM the_tbl GROUP BY questionID) T2 ON T2.questionid = T1.questionid
) T
PIVOT (
COUNT(options) FOR options IN ([A], [B], [C])
) AS pvt
ORDER BY questionid

T-SQL:
SELECT
questionid,
SUM(CASE options WHEN 'A' THEN 100.0 ELSE 0.0 END) / COUNT(options) AS responseA,
SUM(CASE options WHEN 'B' THEN 100.0 ELSE 0.0 END) / COUNT(options) AS responseB,
SUM(CASE options WHEN 'C' THEN 100.0 ELSE 0.0 END) / COUNT(options) AS responseC
FROM
answers
GROUP BY
questionid
Note: To avoid casting and multiplying 100, I used 100.0 and 0.0 values in CASE ... END expressions.

SELECT CAST(100/
( SELECT COUNT(*)
FROM your_Table as t2
WHERE t2.questionid = t1.questionid )
* COUNT(*) AS VARCHAR) + '%' AS 'Percent', OPTIONS, questionid
FROM your_Table as t1
--WHERE questionid = 2
GROUP BY OPTIONS, questionid
ORDER BY questionid;
This is one possible way you could do it
(works on SQL-Server but not sure if it does in MySql)

Related

Select rows with non matching column

I am trying to retrieve rows with same Volume value or with only 1 Volume, but could not come up with a SQL logic.
Data:
ID
Volume
A
100
A
100
B
101
B
102
B
103
B
104
C
400
Required Output:
ID
Volume
A
100
A
100
C
400
This one is achievable using a subquery.
select * from test where col1 in (
select t.col1
from(
select col1, col2,
dense_rank() over (partition by col1 order by col2) as dr
from test) t
group by t.col1
having sum(case when t.dr = 1 then 0 else t.dr end) = 0)
Try this dbfiddle.
This can be done on a more easy way:
select t1.id,
t1.volume
from tbl t1
inner join (select id
from tbl
group by id
having count(distinct volume) = 1
) as t2 on t1.id=t2.id;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=92bc234e631a1106b0e322bc4954d696
having count(distinct volume) = 1 will return only the id that have the same volume , including the id with just one volume.
I'd naturally be inclined towards Ergest Basha's pattern.
It can also be expressed using NOT EXISTS()
SELECT
t.*
FROM
tbl AS t
WHERE
NOT EXISTS (
SELECT *
FROM tbl
WHERE id = t.id
AND volume <> t.volume
)
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=243c42008f527391514d1ad124730587

how to subtract to sum(produced from same table) in MYSQL?

amount group
--------------
100 'a'
40 'b'
30 'a'
50 'b'
query output:
diff(a-b)
---------
40
how to do it in MYSQL?
You can simply:
SELECT (SELECT SUM(amount) FROM t WHERE `group` = 'a') -
(SELECT SUM(amount) FROM t WHERE `group` = 'b') AS diff
Or:
SELECT SUM(CASE
WHEN `group` = 'a' THEN amount
WHEN `group` = 'b' THEN -amount
END) AS diff
FROM t

MySql GROUP BY Max Date

I have a table called votes with 4 columns: id, name, choice, date.
****id****name****vote******date***
****1*****sam*******A******01-01-17
****2*****sam*******B******01-05-30
****3*****jon*******A******01-01-19
My ultimate goal is to count up all the votes, but I only want to count 1 vote per person, and specifically each person's most recent vote.
In the example above, the result should be 1 vote for A, and 1 vote for B.
Here is what I currently have:
select name,
sum(case when uniques.choice = A then 1 else 0 end) votesA,
sum(case when uniques.choice = B then 1 else 0 end) votesB
FROM (
SELECT id, name, choice, max(date)
FROM votes
GROUP BY name
) uniques;
However, this doesn't work because the subquery is indeed selecting the max date, but it's not including the correct choice that is associated with that max date.
Don't think "group by" to get the most recent vote. Think of join or some other option. Here is one way:
SELECT v.name,
SUM(v.choice = 'A') as votesA,
SUM(v.choice = 'B') as votesB
FROM votes v
WHERE v.date = (SELECT MAX(v2.date) FROM votes v2 WHERE v2.name = v.name)
GROUP BY v.name;
Here is a SQL Fiddle.
Your answer are close but need to JOIN self
Subquery get Max date by name then JOIN self.
select
sum(case when T.vote = 'A' then 1 else 0 end) votesA,
sum(case when T.vote = 'B' then 1 else 0 end) votesB
FROM (
SELECT name,Max(date) as date
FROM T
GROUP BY name
) AS T1 INNER JOIN T ON T1.date = T.date
SQLFiddle
Try this
SELECT
choice,
COUNT(1)
FROM
votes v
INNER JOIN
(
SELECT
id,
max(date)
FROM
votes
GROUP BY
name
) tmp ON
v.id = tmp.id
GROUP BY
choice;
Something like this (if you really need count only last vote of person)
SELECT
sum(case when vote='A' then cnt else 0 end) voteA,
sum(case when vote='B' then cnt else 0 end) voteB
FROM
(SELECT vote,count(distinct name) cnt
FROM (
SELECT name,vote,date,max(date) over (partition by name) maxd
FROM votes
)
WHERE date=maxd
GROUP BY vote
)
PS. MySQL v 8
select
name,
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
group by name
Or output just one row for the total counts of VoteA and VoteB:
select
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
Based on #d-shish solution, and since introduction (in MySQL 5.7) of ONLY_FULL_GROUP_BY, the GROUP BY statement must be placed in subquery like this :
SELECT v.`name`,
SUM(v.`choice` = 'A') as `votesA`,
SUM(v.`choice` = 'B') as `votesB`
FROM `votes` v
WHERE (
SELECT MAX(v2.`date`)
FROM `votes` v2
WHERE v2.`name` = v.`name`
GROUP BY v.`name` # << after
) = v.`date`
# GROUP BY v.`name` << before
Otherwise, it won't work anymore !

Mysql sort by range

I have a database which looks like this :
NUM / CNT
3 / 1
5 / 0
100 / 1
300 / 0
320 / 1
And I am looking for the query that will allow me to sort them by range and make sum of their count so I will have something like this:
NUM / CNT
0-100 / 2
100-400 / 1
I am wondering if this is possible using mysql querys .
Use SUM with conditional aggregation on the value of each NUM:
SELECT '0-100' AS NUM,
SUM(CASE WHEN NUM BETWEEN 0 AND 100 THEN CNT ELSE 0 END) AS CNT
FROM yourTable
UNION
SELECT '100-400' AS NUM,
SUM(CASE WHEN NUM BETWEEN 100 AND 400 THEN CNT ELSE 0 END) AS CNT
FROM yourTable
try this it will work same as you want.
SELECT '0-100' AS NUM,
SUM(CASE WHEN NUM <= 100 THEN CNT ELSE 0 END) AS CNT
FROM tableName
UNION
SELECT '100-400' AS NUM,
SUM(CASE WHEN NUM > 100 THEN CNT ELSE 0 END) AS CNT
FROM tableName

Prioritized result set

I'm creating a stored procedure (MySQL) to grab some questions based on some priorities.
New questions in matching difficulty (50% of amount)
New questions (rest of the questions)
Questions that have been answered the minimum amount of times (rest...)
in matching difficulty
questions
The parameters look like this:
(IN _languageCode VARCHAR(5), IN _userId CHAR(36), IN _categoryId CHAR(36), IN _amount int, IN _accuracy DECIMAL(5,2), IN _deviation int)
First of all, all questions should match the category and language. This look something like this:
SELECT DISTINCT * FROM `QuestionTranslation` AS QT
WHERE QT.LanguageCode = _languageCode AND CASE WHEN _categoryId IS NOT NULL THEN QT.QuestionId IN (SELECT QuestionId FROM `QuestionEntities` WHERE EntityId IN (SELECT EntityId FROM `CategoryTags` WHERE CategoryId = #_categoryId)) ELSE 1 END
ORDER BY RAND() LIMIT _amount;
That works fine. This expression can check whether a question is new:
QT.QuestionId NOT IN (SELECT QuestionId FROM `Answer` WHERE UserId = _userId GROUP BY QuestionId)
Works fine as well. The next expression can check whether a question is in a desired difficulty level:
QT.QuestionId NOT IN (SELECT QuestionId FROM (SELECT QuestionId, (1 - SUM(CASE WHEN ChosenAnswer = 1 THEN 1 ELSE 0 END) / COUNT(*)) * 100 AS Level FROM `Answer` GROUP BY QuestionId HAVING Level < _accuracy - _deviation AND Level > _accuracy + _deviation) AS A)
Works great as well. The next query orders the amount of times a question has been answered by the user:
SELECT QuestionId FROM `Answer` WHERE UserId = _userId GROUP BY QuestionId ORDER BY COUNT(*)
Works fine, but how do I select only the QuestionId's that have been answered the least amount of times?
Next up, is how do I construct the full query with all these subqueries, so it performs the best?
My current approach is to just grab the questions like this:
(SELECT Priority 1 LIMIT _amount / 2)
UNION
(SELECT Priority 2 LIMIT _amount)
UNION
(SELECT Priority 3a LIMIT _amount)
UNION
(SELECT Priority 3b LIMIT _amount)
LIMIT 12;
which would look something like:
(SELECT DISTINCT * FROM `QuestionTranslation` AS QT
WHERE QT.LanguageCode = _languageCode AND CASE WHEN _categoryId IS NOT NULL THEN QT.QuestionId IN (SELECT QuestionId FROM `QuestionEntities` WHERE EntityId IN (SELECT EntityId FROM `CategoryTags` WHERE CategoryId = _categoryId)) ELSE 1 END
AND QT.QuestionId NOT IN (SELECT QuestionId FROM `Answer` WHERE UserId = _userId GROUP BY QuestionId)
AND QT.QuestionId NOT IN (SELECT QuestionId FROM (SELECT QuestionId, (1 - SUM(CASE WHEN ChosenAnswer = 1 THEN 1 ELSE 0 END) / COUNT(*)) * 100 AS Level FROM `Answer` GROUP BY QuestionId HAVING Level < _accuracy - _deviation AND Level > _accuracy + _deviation) AS A)
ORDER BY RAND() LIMIT _amount / 2)
UNION
(SELECT DISTINCT * FROM `QuestionTranslation` AS QT
WHERE QT.LanguageCode = _languageCode AND CASE WHEN _categoryId IS NOT NULL THEN QT.QuestionId IN (SELECT QuestionId FROM `QuestionEntities` WHERE EntityId IN (SELECT EntityId FROM `CategoryTags` WHERE CategoryId = _categoryId)) ELSE 1 END
AND QT.QuestionId NOT IN (SELECT QuestionId FROM `Answer` WHERE UserId = _userId GROUP BY QuestionId)
ORDER BY RAND() LIMIT _amount)
UNION
(SELECT DISTINCT * FROM `QuestionTranslation` AS QT
WHERE QT.LanguageCode = _languageCode AND CASE WHEN _categoryId IS NOT NULL THEN QT.QuestionId IN (SELECT QuestionId FROM `QuestionEntities` WHERE EntityId IN (SELECT EntityId FROM `CategoryTags` WHERE CategoryId = _categoryId)) ELSE 1 END
AND QT.QuestionId NOT IN (select least answered questions by user .... what to do here?)
AND QT.QuestionId NOT IN (SELECT QuestionId FROM (SELECT QuestionId, (1 - SUM(CASE WHEN ChosenAnswer = 1 THEN 1 ELSE 0 END) / COUNT(*)) * 100 AS Level FROM `Answer` GROUP BY QuestionId HAVING Level < _accuracy - _deviation AND Level > _accuracy + _deviation) AS A)
ORDER BY RAND() LIMIT _amount)
UNION
(SELECT DISTINCT * FROM `QuestionTranslation` AS QT
WHERE QT.LanguageCode = _languageCode AND CASE WHEN _categoryId IS NOT NULL THEN QT.QuestionId IN (SELECT QuestionId FROM `QuestionEntities` WHERE EntityId IN (SELECT EntityId FROM `CategoryTags` WHERE CategoryId = _categoryId)) ELSE 1 END
AND QT.QuestionId NOT IN (select least answered questions by user .... what to do here?)
ORDER BY RAND() LIMIT _amount)
LIMIT _amount;
I guess that this not the correct way to achieve this. Any good suggestions on how to approach this for best performance?
Best regards,
Søren