Select different results from same table - mysql

I have a table used to gather evaluations from users, its structure is really basic and defined below along with some example rows:
id_user | id_affirmation | isCorrect
------------------------------------
1 | 10 | true
1 | 13 | false
2 | 23 | true
2 | 45 | false
3 | 31 | false
3 | 90 | true
3 | 67 | true
In the application, the users basically evaluate if the affirmations are correct or wrong, marking them as true or false. Each affirmation is evaluated only once, so users are evaluating different affirmations.
What I'm trying to do is select a resultset like the one bellow, where I can count the number of affirmations each user marked as correct and the number they marked as false.
user | correct_count | wrong_count
------------------------------------
1 | 35 | 12
2 | 76 | 22
3 | 23 | 41
I have a query to count the number of correct answers of each user, and I can simply change the expected value of ísCorrect' field to false, so I'll count the number of wrong answers each user gave. My problem is how to gather the corrcet count and the wrong count, since I can't simply use UNION.

SELECT id_user,
SUM(isCorrect),
SUM(NOT isCorrect)
FROM mytable
GROUP BY
id_user

Assuming isCorrect is being stored as VARCHAR string values true and false, one way to get the counts is to use a conditional boolean in the SELECT list, and perform use an aggregate function around that.
For example:
SELECT t.user
, SUM(t.isCorrect='true') AS correct_count
, SUM(t.isCorrect='false') AS wrong_count
FROM mytable t
GROUP BY t.user
If there are values other than true or false, would those be included in either count? (e.g. NULL, 'maybe', et al.), or if the datatype of isCorrect is other than VARCHAR, the conditional expression would need to be modified appropriately, so that each expression returns 1'if it's to be included in the count, or 0 if it's not to be included.

Related

How to select both sum value of all rows and values in some specific rows?

I have a record table and its comment table, like:
| commentId | relatedRecordId | isRead |
|-----------+-----------------+--------|
| 1 | 1 | TRUE |
| 2 | 1 | FALSE |
| 3 | 1 | FALSE |
Now I want to select newCommentCount and allCommentCount as a server response to the browser. Is there any way to select these two fields in one SQL?
I've tried this:
SELECT `isRead`, count(*) AS cnt FROM comment WHERE relatedRecordId=1 GROUP BY `isRead`
| isRead | cnt |
| FALSE | 2 |
| TRUE | 1 |
But, I have to use a special data structure to map it and sum the cnt fields in two rows to get allCommentCount by using an upper-layer programming language. I want to know if I could get the following format of data by SQL only and in one step:
| newCommentCount | allCommentCount |
|-----------------+-----------------|
| 2 | 3 |
I don't even know how to describe the question. So I got no any search result in Google and Stackoverflow. (Because of My poor English, maybe)
Use conditional aggregation:
SELECT SUM(NOT isRead) AS newCommentCount, COUNT(*) AS allCommentCount
FROM comment
WHERE relatedRecordId = 1;
if I under stand you want show sum of newComments Count and all comments so you can do it like
SELECT SUM ( CASE WHEN isRead=false THEN 1 ELSE 0 END ) AS newComment,
Count(*) AS AllComments From comments where relatedRecord=1
also you can make store procedure for it.
To place two result sets horizontally, you can as simple as use a subquery for an expression in the SELECT CLAUSE as long as the number of rows from the result sets match:
select (select count(*) from c_table where isread=false and relatedRecordId=1 ) as newCommentCount,
count(*) as allCommentCount
from c_table where relatedRecordId=1;

How can I find records in a subquery result where an item has a result which is false and has no results which are true?

I'm pretty deep into a combination of joins and subqueries which have ended up having a query result which looks like this: I'm not dealing with marbles at all, but I guess a way of explaining this would be to imagine a bag with blue and red marbles. Bag 1 contains both blue and red, (hence a result for true and false), Bag 2 also the same. Bag 3 only only contains blue marbles and no red ones, so there is only one entry which is true. And bag 4 contains only red ones so the value is false.
What I'm trying to do is select all the IDs where they don't have a 'true' value. So in this case below, IDs 1,2 and 3 all have a row which has the value 'true', whereas IDs 4 and 5 only contains false(s) and no trues. So I want the query to return ID 4 and 5. These results are grouped by their IDs so I've been trying different HAVING clauses, trying to count where COUNT of trues = 0 for a specific idea but haven't had any success.
Input: this table (which is a result of a bigger query)
| ID | hasBlueMarbles |
| -- | ------- |
| 1 | true |
| 1 | false |
| 2 | true |
| 2 | false |
| 3 | true |
| 4 | false |
| 5 | false |
| 5 | false |
expected output: IDs which only have a record for the value 'false' and not 'true'
| ID |
| -- |
| 4 |
| 5 |
This is probably quite a weird dataset to have in the first place, probably meaning I did something quite wrong with my initial queries that got me here. If needed I can add the main query but it may be a little confusing out of context. Thank you!
1. BOOL_OR()
The function you are looking for is BOOL_OR().
Here explains as:
If any value in a set is true, the BOOL_OR function returns true (t). If no value in a set is true, the function returns false (f).
Here goes:
SELECT ID,
BOOL_OR(hasBlueMarbles) AS HasBlueMarbles
FROM table
GROUP BY ID
HAVING BOOL_OR(hasBlueMarbles) = FALSE
2. MAX()
Or you can simply use MAX() function to take the biggest of booleans and filter out the result.
SELECT ID,
MAX(hasBlueMarbles) AS HasBlueMarbles
FROM table
GROUP BY ID
HAVING MAX(hasBlueMarbles) = 0

Stop MySQL from selecting empty rows in query

I am trying to write a script that will allow me to query my database and show the user even if the user has numerous profiles.
+----+-------+---------+--------------------+
| id | name | phone | email |
+----+-------+---------+--------------------+
| 1 | bob | 5555555 | bob#gmail.com |
| 2 | John | 6666666 | john#gmail.com |
| 3 | robert| 5555555 | bobswork#gmail.com |
+----+-------+---------+--------------------+
I am doing this currently with this query:
SELECT DISTINCT a.* FROM entires a INNER JOIN
(SELECT * FROM entires WHERE id='1') b
ON (a.phone=b.phone
OR a.email=b.email
);
Which works really well, however it breaks the whole system if two fields are blank. Then it just matches everything and I get the results of every entry with missing details. How can I prevent this from showing blank matches?
For mySql use IFNULL() function in join condition.
Ex: IFNULL(a.phone, '1111') = IFNULL(b.phone, '1111') or IFNULL(a.email, 'a#a.com') = IFNULL(b.email, 'a#a.com').
IFNULL() function takes the second argument value if the first argument is NULL. In above case condition will be true if both the records having NULL value or same value. In case you want the condition to fail if both values are NULL, then take different value in second argument for IFNULL() function.

MySQL rollup when some columns include NULL values

I have a dataset where it is somewhat common for fields to have NULL as a valid value. This causes an issue when I want to use the ROLLUP operator in MySQL, as I can't distinguish between the NULL values it generates as part of its subtotals/totals and the actual NULL values in the data.
My current query is as follows:
SELECT
COALESCE(car_score, "Total") AS car_score,
COUNT(DISTINCT id) AS volume
FROM cars_table
GROUP BY
car_score ASC WITH ROLLUP;
This provides me with the following table:
cars_score | volume
---------------------------
Total | 500
1 | 100
2 | 200
3 | 300
4 | 400
5 | 500
Total | 2000
when I'd like it to be:
cars_score | volume
---------------------------
NULL | 500
1 | 100
2 | 200
3 | 300
4 | 400
5 | 500
Total | 2000
This is a simple example, and it becomes more frustrating once I have multiple dimensions for the ROLLUP. The reason I can't just change the NULL value before to something else is that I also need to be able to aggregate the data in other parts of the application, so having a proper NULL is important to me.
One option would be to wrap with a subquery which first replaces the actual NULL values which indicate missing data. Then, use COALESCE() as you were to replace the NULL from the rollup with the string "Total":
SELECT
COALESCE(t.car_score, 'Total') AS car_score,
COUNT(DISTINCT t.id) AS volume
FROM
(
SELECT COALESCE(cars_score, 99) AS car_score, id
FROM cars_table
) t
GROUP BY t.car_score WITH ROLLUP
Here I have used 99 as a placeholder to indicate car scores which were missing. You can use any placeholder you want, other than NULL.

mysql returns wrong results with random duplicate values

i need to return the best 5 scores in each category from a table.so far i have tried query below following an example from this site: selecting top n records per group
query:
select
subject_name,substring_index(substring_index
(group_concat(exams_scores.admission_no order by exams_scores.score desc),',',value),',',-1) as names,
substring_index(substring_index(group_concat(score order by score desc),',',value),',',-1)
as orderedscore
from exams_scores,students,subjects,tinyint_asc
where tinyint_asc.value >=1 and tinyint_asc.value <=5 and exam_id=2
and exams_scores.admission_no=students.admission_no and students.form_id=1 and
exams_scores.subject_code=subjects.subject_code group by exams_scores.subject_code,value;
i get the top n as i need but my problem is that its returning duplicates at random which i dont know where they are coming from
As you can see English and Math have duplicates which should not be there
+------------------+-------+--------------+
| subject_name | names | orderedscore |
+------------------+-------+--------------+
| English | 1500 | 100 |
| English | 1500 | 100 |
| English | 2491 | 100 |
| English | 1501 | 99 |
| English | 1111 | 99 |
|Mathematics | 1004 | 100 |
| Mathematics | 1004 | 100 |
| Mathematics | 2722 | 99 |
| Mathematics | 2734 | 99 |
| Mathematics | 2712 | 99 |
+-----------------------------------------+
I have checked table and no duplicates exist
to confirm there are no duplicates in the table:
select * from exams_scores
having(exam_id=2) and (subject_code=121) and (admission_no=1004);
result :
+------+--------------+---------+--------------+-------+
| id | admission_no | exam_id | subject_code | score |
+------+--------------+---------+--------------+-------+
| 4919 | 1004 | 2 | 121 | 100 |
+------+--------------+---------+--------------+-------+
1 row in set (0.00 sec)
same result for English.
If i run the query like 5 times i sometimes end up with another field having duplicate values.
can anyone tell me why my query is behaving this way..i tried adding distinct inside
group_concat(ditinct(exams_scores.admission_no))
but that didnt work ??
You're grouping by exams_scores.subject_code, value. If you add them to your selected columns (...as orderedscore, exams_scores.subject_code, value from...), you should see that all rows are distinct with respect to these two columns you grouped by. Which is the correct semantics of GROUP BY.
Edit, to clarify:
First, the SQL server removes some rows according to your WHERE clause.
Afterwards, it groups the remaining rows according to your GROUP BY clause.
Finally, it selects the colums you specified, either by directly returning a column's value or performing a GROUP_CONCAT on some of the columns and returning their accumulated value.
If you select columns not included in the GROUP BY clause, the returned results for these columns are arbitrary, since the SQL server reduces all rows equal with respect to the columns specified in the GROUP BY clause to one single row - as for the remaining columns, the results are pretty much undefined (hence the "randomness" you're experiencing), because - what should the server choose as a value for this column? It can only pick one randomly from all the reduced rows.
In fact, some SQL servers won't perform such a query and return an SQL error, since the result for those columns would be undefined, which is something you don't want to have in general. With these servers (I believe MSSQL is one of them), you more or less can only have columns in you SELECT clause which are part of your GROUP BY clause.
Edit 2: Which, finally, means that you have to refine your GROUP BY clause to obtain the grouping that you want.