Mysql count rows and rows with null - mysql

Hy Guys,
I have a table like that:
+----+------+
| id | grade|
+----+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | NULL |
| 5 | NULL |
| 6 | NULL |
+----+------+
Where
1 Bad
2 Good
3 Very Good
Im trying to get a result something like that:
+--------------+------+
| grade | count|
+--------------+------+
| "Bad" | 2 |
| "Good" | 1 |
| "Very Good" | 0 |
| "Not Ranked" | 3 |
+--------------+------+
Im tryng with count but no success

You can use CASE to change the value of grade ibno your desired string value,
SELECT CASE WHEN grade = 1 THEN 'Bad'
WHEN grade = 2 THEN 'Good'
WHEN grade = 3 THEN 'Very Good'
ELSE 'Not Ranked'
END as grade,
COUNT(IFNULL(grade, 0)) as `count`
FROM TableName
GROUP BY CASE WHEN grade = 1 THEN 'Bad'
WHEN grade = 2 THEN 'Good'
WHEN grade = 3 THEN 'Very Good'
ELSE 'Not Ranked'
END
Since you wanted to display all values, you need to create a subquery that returns all values and join to your table using LEFT JOIN.
SELECT a.grade,
COUNT(b.id)
FROM
(
SELECT 1 id, 'Bad' grade UNION ALL
SELECT 2 id, 'Good' grade UNION ALL
SELECT 3 id, 'Very Good' grade UNION ALL
SELECT 999 id, 'Not Ranked' grade
) a
LEFT JOIN TableName b ON a.id = IFNULL(b.grade, 999)
GROUP BY a.grade
Here's a Demo.

If you can use the numbers, and match them with the string later, then this could be a solution:
SELECT grade, count(*) as id FROM tbl GROUP BY grade
source

Related

selecting only newest row with specific value

Table:
person | borrow_date | is_borrowed | SN | date | id
1 | 2019-01-10...| 1 | 20 |2019-01-10...| 6
3 | 2019-01-09...| 3 | 10 |2019-01-09...| 5
1 | 2019-01-08...| 1 | 10 |2019-01-08...| 4
2 | 2019-01-08...| 1 | 10 |2019-01-08...| 3
1 | NULL | 2 | 20 |2019-01-07...| 2
1 | NULL | 2 | 10 |2019-01-07...| 1
My wanted output is to select newest rows where "is_borrowed" equals 1 and grouped by SN, so that when the query is executed with person=2 or person=3 then it would retrieve empty set. Whereas for person=1 it would give back two rows.
Wanted output (where person=1):
person | borrow_date | is_borrowed | SN | date |id
1 | 2019-01-10...| 1 | 20 | 2019-01-10...|6
1 | 2019-01-08...| 1 | 10 | 2019-01-08...|4
Wanted output (where person=2):
EMPTY SET
Wanted output (where person=3):
EMPTY SET
This is my current query and it sadly doesn't work.
SELECT a.SN, a.is_borrowed,a.max(date) as date, a.person
FROM table a
INNER JOIN (SELECT SN, MAX(date) as date, osoba from table where person like
"2" group by SN) as b
ON a.SN=b.SN and a.date=b.date
WHERE a.person like "2" and a.is_borrowed=1
If I correctly understood you from the question and the comment you made under it, here's one way to do it without specifying the person:
select *
from TableName as p
inner join (select max(borrow_date) as borrow_date,
SN
FROM TableName
where is_borrowed = 1
group by SN) as p2
on p.borrow_date = p2.borrow_date and p.SN = p2.SN
This should give you the result you're looking for. Here's a demo.
Note that I had to change the borrowed_date values in the table since yours contain hours and minutes while I didn't add those.
You can always specify it for each person by adding a where clause after the join.
select p.person,
p.borrow_date,
p.is_borrowed,
p.SN,
p.date,
p.id
from TableName as p
inner join (select max(borrow_date) as borrow_date,
SN
FROM TableName
where is_borrowed = 1
group by SN) as p2
on p.borrow_date = p2.borrow_date and p.SN = p2.SN
where p.person = '1'
Output:
person | borrow_date | is_borrowed | SN | date | id
1 | 2019-01-10 | 1 | 20 | 2019-01-10 | 6
1 | 2019-01-08 | 1 | 10 | 2019-01-08 | 4
While where p.person = '2' and where p.person = '3' will return empty sets.

SQL about combine row values

I have a question about a SQL statement for an existing database table:
SELECT * FROM TABLENAME
gives a result with the columns: id, lead_id, field_number and value.
ID | LEAD_ID | FIELD_NUMBER | VALUE
1 | 1 | 1 | yes
2 | 1 | 2 | a#mail.com
3 | 2 | 1 | yes
4 | 2 | 2 | b#mail.com
5 | 3 | 1 | no
6 | 3 | 2 | c#mail.com
Is it possible to split the value column in two different columns?
expected result:
LEAD_ID | YES_OR_NO | EMAIL
1 | yes | a#mail.com
2 | yes | b#mail.com
3 | no | c#mail.com
I can't use the MAX() argument right?
Use a SELF JOIN and define the FIELD_NUMBER for each self join (if using more than just the two in the example).
SELECT t.LEAD_ID, t.value AS YES_OR_NO, sub.VALUE AS EMAIL
FROM TABLENAME t
INNER JOIN TABLENAME sub ON t.LEAD_ID = sub.LEAD_ID AND sub.FIELD_NUMBER !=1
WHERE t.FIELD_NUMBER = 1
Output
LEAD_ID YES_OR_NO EMAIL
1 yes a#mail.com
2 yes b#mail.com
3 no c#mail.com
SQL Fiddle: http://sqlfiddle.com/#!9/1587e3/3/0
You could use a self join
select a.LEAD_ID, a.VALUE as YES_OR_NOT, b.VALUE as EMAIL
from TABLENAME a
inner join TABLENAME b on a.LEAD_ID = b.LEAD_ID and b.field_number = 2
where a.field_number = 1
Like:
SELECT DISTINCT LEAD_ID
, CASE WHEN FIELD_NUMBER = 1 THEN VALUE END AS 'YES_OR_NO'
, CASE WHEN FIELD_NUMBER = 2 THEN VALUE END AS 'EMAIL'
FROM TABLENAME
No Need for a self join

MySQL Crosstab aggregation with 2 conditions

I have a query which creates a crosstab. The results are a count of the txn_id for branda, and the count of txn_id for brandb.
The txn_id is NOT UNIQUE. This is an example of the transactions table.:
txn_id | nationality_id | sku | sales | units
1 | 1 | 1 | 20 | 2
1 | 1 | 2 | 15 | 1
2 | 4 | 1 | 20 | 2
3 | 2 | 1 | 10 | 1
4 | 3 | 2 | 15 | 1
5 | 4 | 1 | 10 | 1
There are 2 other tables (products) - (sku, brand, product name), and (nationalities) - (nationality_id, nationality).
I would like to add a third column which gets me the count of txn_id where BOTH brands are purchased
The output should be
nationality | branda | brandb | combined
1 | 1 | 1 | 1
2 | 1 | 0 | 0
3 | 0 | 1 | 0
4 | 2 | 0 | 0
Current query.
SELECT
nationalities.nationality,
COUNT((CASE brand WHEN 'branda' THEN txn_id ELSE NULL END)) AS branda,
COUNT((CASE brand WHEN 'brandb' THEN txn_id ELSE NULL END)) AS brandb
<I want my 3rd column here>
FROM
transaction_data
INNER JOIN
products USING (sku)
INNER JOIN
nationalities USING (nationality_id)
GROUP BY nationality
ORDER BY branda DESC
LIMIT 20;
I have tried using:
COUNT((CASE brand WHEN 'brandb' OR 'brandb' THEN txn_id ELSE NULL END)) AS combined - however this obviously returns too many (returns branda or brandb regardless of whether they were purchased together). I know I can't use AND, because obviously no single cell is going to be both branda AND brandb.
I have also tried using:
COUNT((CASE brand WHEN IN('branda', 'brandb') THEN txn_id ELSE NULL END)) AS combined - However this isn't valid syntax.
I feel that I should be using a HAVING clause, but I'm not sure how this would work in the column list.
I think you are going to need two levels of aggregation:
SELECT n.nationality,
sum(branda), sum(brandb), sum(branda * brandb)
FROM (SELECT t.txn_id, n.nationality,
MAX(CASE brand WHEN 'branda' THEN 1 ELSE 0 END) AS branda,
MAX(CASE brand WHEN 'brandb' THEN 1 ELSE 0 END) AS brandb
FROM transaction_data t INNER JOIN
products p
USING (sku) INNER JOIN
nationalities n
USING (nationality_id)
GROUP BY t.txn_id, n.nationality
) tn
GROUP BY n.nationality
ORDER BY max(txn_id) DESC
LIMIT 20;

How to retrieve the most present pair in groups of three columns, with MySQL?

I am working on ice hockey software: trying to find out who in your team has collected the most points with a specific player (in this example user_id = 1).
Data structure:
goal_user_id | assist_user_id | second_assist_user_id
-----------------------------------
1 | 13856 | null
1 | 15157 | null
1 | 15157 | null
1 | 15157 | 18733
345 | 1 | 28703
18733 | 1 | null
36014 | 34867 | 1
Desired output:
user_id | partner_id | total_points
-----------------------------------
1 | 15157 | 3
1 | 18733 | 2
1 | 13856 | 1
1 | 345 | 1
1 | 28703 | 1
1 | 34867 | 1
1 | 36014 | 1
SQL Fiddle:
http://sqlfiddle.com/#!9/b1587/4/0 (Note: SQLFiddle has been acting weird today).
I managed to get it done on the across two columns but couldn't figure out for three:
SELECT
COUNT(goal_user_id) as total_points,
CASE WHEN goal_user_id <> 1
THEN goal_user_id
ELSE assist_one_user_id
END as partner_id,
CASE WHEN goal_user_id < assist_one_user_id
THEN CONCAT(goal_user_id,'-',assist_one_user_id)
ELSE CONCAT(assist_one_user_id,'-',goal_user_id)
END as player_pair
FROM goals
WHERE
assist_one_user_id IS NOT NULL AND (goals.goal_user_id = 1 OR goals.assist_one_user_id = 1)
GROUP BY player_pair
ORDER BY total_points DESC
My inclination is to break all the rows out into pairs of users. Then do the aggregation based on that:
select u2, count(*) as total_points
from ((select goal_user_id as u1, assist_one_user_id as u2 from goals) union all
(select assist_one_user_id, goal_user_id from goals) union all
(select goal_user_id, assist_two_user_id from goals) union all
(select assist_two_user_id, goal_user_id from goals) union all
(select assist_one_user_id, assist_two_user_id from goals) union all
(select assist_two_user_id, assist_one_user_id from goals)
) uu
where uu.u1 = 1 and uu.u2 is not null
group by u2
order by total_points desc;
Here is a SQL Fiddle.

SQL COUNT on GROUP BY

This is the code i got so far
SELECT users_ID,Problem_ID
FROM 'submission'
WHERE Status = "AC"
GROUP BY users_ID,Problem_ID
I am getting these results
+----------+------------+
| Users_ID | Problem_ID |
+----------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 3 |
+----------+------------+
I only want to get
+----------+------------+
| Users_ID | Problem_ID |
+----------+------------+
| 1 | 3 | -- so because there are 3 results for user_ID 1
| 2 | 2 | -- and there are 2 results for user_ID 2
+----------+------------+
So the Problem_ID is how many rows I am getting from my query for each user.
But how do I accomplish this?
Edit:
I forgot mention that the table contains duplicates of the same problem for example.
I got a Problem with the ID of 1 and then in the database there could be two rows with the same user and with status as "AC" but I want to only get one of them.
SELECT users_ID, count(Problem_ID) as `problem_count`
FROM `submission`
WHERE Status = 'AC'
GROUP BY users_ID;
This should work:
SELECT users_ID, COUNT(DISTINCT Problem_ID)
FROM `submission`
WHERE Status = 'AC'
GROUP BY users_ID
You can do something like this :
SELECT
s.users_ID
,count(s.Problem_ID)+CASE WHEN IFNULL(userDupli.nbrUserAC, 0) > 0 THEN 1 ELSE 0 END as `problem_count`
FROM
`submission` s
left join (SELECT
users_ID
,count(*) as nbrUserAC
FROM `submission`
WHERE Status = 'AC'
GROUP BY users_ID) userDupli
on userDupli.users_ID = s.users_ID
WHERE
Status <> 'AC'
GROUP BY
users_ID
,userDupli.nbrUserAC