Insert value into subquery - mysql

I'm trying to insert the value '1' into column 'isTransfer' of every result of an subquery, but it's not working. This is the query where I select the rows:
select r.*
from players r
inner join (
select name, rating, max(id) id
from players group by name, rating
having count(distinct club) > 1
)
q on r.name = q.name and r.rating = q.rating and r.id = q.id
This is what I'm trying to do:
INSERT INTO 'isTransfer' VALUES '1' WHERE
(select r.*
from players r
inner join (
select name, rating, max(id) id
from players group by name, rating
having count(distinct club) > 1
)
q on r.name = q.name and r.rating = q.rating and r.id = q.id)

For this task, you need to do an UPDATE query. Also, you cannot use the WHERE clause like that, you will get an error. Instead, change the where clause to look where the primary key is returned by the subquery. It would look something like this:
UPDATE myTable
SET isTransfer = 1
WHERE primaryKey IN [mySubquery];
You need to make sure that the only column in your SELECT of the subquery is the primary key, otherwise you will get an invalid operand count error.
In regards to your query in the comments, the JOIN is not necessary. Instead, just get the distinct id values from the subquery like this:
SELECT DISTINCT id
FROM(
SELECT name, rating, MAX(id) AS id
FROM players
GROUP BY name, rating
HAVING COUNT(DISTINCT club) > 1) q
Then, but that query as your IN operand.

Assuming the id is unique in the players table:
update players r inner join
(select name, rating, max(id) as id
from players p
group by name, rating
having count(distinct club) > 1
) nr
on r.id = nr.id
set isTransfer = 1;

Related

SELECT MAX(COUNT) MySQL

I am new to MySQL, and I have a task to do right now where I have three tables:
students(id,name)
courses(id,name)
grades(id, student_id (FK), course_id(FK), grade)
I am supposed to
fetch the name of the course that is the most registered by students and if there is any conflict or ties with other course, retrieve the course after sorting ascendingly.
I tried several queries, but they are not 'efficient enough'
SELECT course.name FROM (
SELECT CI ,MAX(Total) FROM
(
SELECT course_id as CI,COUNT(*) AS Total
FROM grades
GROUP BY course_id ASC
) AS Results
) AS x
INNER JOIN courses ON x.CI = courses.id
And
SELECT courses.name FROM (
SELECT course_id, COUNT(*) AS how_many
FROM grades
GROUP BY course_id ASC
HAVING how_many = (
SELECT COUNT(*) AS how_many
FROM grades
GROUP BY course_id
ORDER BY how_many DESC
LIMIT 1
)
LIMIT 1
) AS X
JOIN courses ON X.course_id=courses.id
Is there any more efficient query?
Both your query attempts look logically incorrect to me. You should be joining courses to grades to obtain the number of students enrolled in each course. Regarding efficiency, the RANK analytic function is one of the most performant options, assuming you are running MySQL 8+:
WITH cte AS (
SELECT c.id, c.name, RANK() OVER (ORDER BY COUNT(*) DESC, c.name) rnk
FROM courses c
INNER JOIN grades g ON g.course_id = c.id
GROUP BY c.id, c.name
)
SELECT id, name
FROM cte
WHERE rnk = 1;
On earlier versions of MySQL, we can use a LIMIT query:
SELECT c.id, c.name
FROM courses c
INNER JOIN grades g ON g.course_id = c.id
GROUP BY c.id, c.name
ORDER BY COUNT(*) DESC, c.name
LIMIT 1;
You can use the ORDER BY clause with the LIMIT clause to get what you need, without aggregating twice:
WITH enrollments AS (
SELECT course_id, COUNT(DISTINCT student_id) AS num_enrollments
FROM grades
GROUP BY course_id
)
SELECT *
FROM enrollments e
INNER JOIN courses c
ON e.course_id = c.id
ORDER BY e.num_enrollments DESC, c.name ASC
LIMIT 1
The subquery will get you the enrollments by aggregating on the students,
then it is joined with the courses to use the course name.
Data is then ordered by:
number of enrollments descendent
course name ascendent
and only the first row is considered.

Select distinct record from mapping table with condition

In my MySQL database I have these tables:
I want to select count of users who only own birds and no other pet.
So far I've came up with this:
SELECT COUNT(DISTINCT(user_id)) FROM users_pets_map WHERE pet_id IN (SELECT id FROM pets WHERE animal = 'bird')
but it doesn't satisfy the requirement of not owning other animals.
You can do aggregation :
select m.user_id, count(*)
from user_pets_map m inner join
pets p
on p.id = m.pet_id
group by m.user_id
having sum( p.animal <> 'bird' ) = 0;
In other way, you can also do :
select m.user_id, count(*)
from user_pets_map m inner join
pets p
on p.id = m.pet_id
group by m.user_id
having min(p.animal) = max(p.animal) and min(p.animal) = 'bird';
EDIT : If you want only Users count then you can do :
select count(distinct m.user_id)
from user_pets_map m
where not exists (select 1 from user_pets_map m1 where m1.user_id = m.user_id and m1.pet_id <> 3);
You can modify your query as below:
SELECT COUNT(DISTINCT(user_id)) FROM users_pets_map WHERE pet_id IN (SELECT id
FROM pets WHERE animal = 'bird') AND user_id NOT IN (SELECT user_id FROM
users_pets_map WHERE pet_id IN (SELECT id FROM pets WHERE animal <> 'bird'))
The last sub-query will fetch the pet_id who are not birds, the query outside it will fetch users who have animal other than birds. Finally combined your current query it will fetch you the users who does not have any other animals as well as have bird. Although the above query is not the best possible solution in terms of time complexity, but it's one of many solutions as well as easier to understand.
You can use GROUP BY AND HAVING
SELECT COUNT(DISTINCT(user_id)) FROM users_pets_map
WHERE pet_id IN (SELECT id FROM pets WHERE animal = 'bird')
GROUP BY pet_id HAVING COUNT(distinct pet_id)=1

SQL: Conditional Insert with order by

Hi I have this item about insert if not exists here. One of the things I want to know about is if I want to get the latest items from CompResults by using order by ResultDate, to be inserted to Competitors table, how should I do it?
INSERT Competitors (cName)
SELECT DISTINCT Name
FROM CompResults cr
WHERE
NOT EXISTS (SELECT * FROM Competitors c
WHERE cr.Name = c.cName) ORDER BY cr.ResultsDate DESC
An error happens: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.
Hi you have to use order by fields in select statement I think You are using sql server
so You can use sub query
INSERT Competitors (cName)
select Name
from (
SELECT cr.Name,max(cr.ResultDate)
FROM CompResults cr
WHERE NOT EXISTS (SELECT * FROM Competitors c
WHERE cr.Name = c.cName) group BY cr.name) as t order by ResultDate
Use Row_Number to get the latest record for each Item
Insert into Competitors(col1,col2..)
Select col1,col2,..
(
Select row_number()Over(partition by Name order by ResultDate desc) Rn, *
From CompResults cr
NOT EXISTS (SELECT 1 FROM Competitors c
WHERE cr.Name = c.cName)
) a
Where Rn = 1
Also you can as the below:
INSERT Competitors (cName)
SELECT
A.Name
FROM
CompResults A INNER JOIN
(
SELECT
CR.Name,
MAX(CR.ResultsDate) MaxResultsDate
FROM
CompResults CR
) B ON A.Name = B.Name AND A.ResultsDate = B.MaxResultsDate
WHERE
NOT EXISTS (SELECT 1 FROM Competitors c
WHERE c.cName = A.Name)

Query on two tables with belongs_to/has_many relation

One table is Users with id and email columns.
Another table is Payments with id, created_at, user_id and foo columns.
User has many Payments.
I need a query that returns each user's email, his last payment date and this last payment's foo value. How do I do that? What I have now is:
SELECT users.email, MAX(payments.created_at), payments.foo
FROM users
JOIN payments ON payments.user_id = users.id
GROUP BY users.id
This is wrong, because foo value does not necessarily belong to user's most recent payment.
Try this :
select users.email,foo,create_at
from users
left join(
select a.* from payments a
inner join (
select id,user_id,max(create_at)
from payments
group by id,user_id
)b on a.id = b.id
) payments on users.id = payments.user_id
If users has no payment yet, then foo and create_at would return NULL. if you want to exclude users who has no payment, then use INNER JOIN.
One approach would be to use a MySQL version of rank over partition and then select only those rows with rank = 1:
select tt.email,tt.created_at,tt.foo from (
select t.*,
case when #cur_id = t.id then #r:=#r+1 else #r:=1 end as rank,
#cur_id := t.id
from (
SELECT users.id,users.email, payments.created_at, payments.foo
FROM users
JOIN payments ON payments.user_id = users.id
order by users.id asc,payments.created_at desc
) t
JOIN (select #cur_id:=-1,#r:=0) r
) tt
where tt.rank =1;
This would save hitting the payments table twice. Could be slower though. Depends on your data!

Using IFNULL to set NULLs to zero

I have a table in which a field contains an integer or NULL.
parent_id
2
4
6
NULL
NULL
45
2
How would I go about adding an IFNULL statement so that ans_count will be populated with 0 instead of NULL?
Here is my SQL code:
...
(SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id) AS n
UPDATE
Full SQL below - thanks to all for your patience.
SELECT *
FROM qa
JOIN user_profiles
ON user_id = author_id
LEFT JOIN (SELECT cm_id,
cm_author_id,
id_fk,
cm_text,
cm_timestamp,
first_name AS cm_first_name,
last_name AS cm_last_name,
facebook_id AS cm_fb_id,
picture AS cm_picture
FROM cm
JOIN user_profiles
ON user_id = cm_author_id) AS c
ON id = c.id_fk
LEFT JOIN (SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id) AS n
ON id = n.pid
WHERE id LIKE '%'
ORDER BY id DESC
EDIT: NEW INFO BASED ON FULL QUERY
The reason the counts can be null in the query you specify is because a left join will return nulls on unmatched records. So the subquery itself is not returning null counts (hence all the responses and confusion). You need to specify the IFNULL in the outer-most select, as follows:
SELECT qa.*, user_profiles.*, c.*, n.pid, ifnull(n.ans_count, 0) as ans_count
FROM qa
JOIN user_profiles
ON user_id = author_id
LEFT JOIN (SELECT cm_id,
cm_author_id,
id_fk,
cm_text,
cm_timestamp,
first_name AS cm_first_name,
last_name AS cm_last_name,
facebook_id AS cm_fb_id,
picture AS cm_picture
FROM cm
JOIN user_profiles
ON user_id = cm_author_id) AS c
ON id = c.id_fk
LEFT JOIN (SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id) AS n
ON id = n.pid
WHERE id LIKE '%'
ORDER BY id DESC
OLD RESPONSE
Can you explain in more detail what you are seeing and what you expect to see? Count can't return NULLs.
Run this set of queries and you'll see that the counts are always 2. You can change the way the NULL parent_ids are displayed (as NULL or 0), but the count itself will always return.
create temporary table if not exists SO_Test(
parent_id int null);
insert into SO_Test(parent_id)
select 2 union all select 4 union all select 6 union all select null union all select null union all select 45 union all select 2;
SELECT IFNULL(parent_id, 0) AS pid, COUNT(*) AS ans_count
FROM SO_Test
GROUP BY IFNULL(parent_id, 0);
SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM SO_Test
GROUP BY parent_id;
drop table SO_Test;
I didn't test this, but I think it will work
(SELECT IF( parent_id IS NULL, 0, parent_id) AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id) AS n
Simply wrap it around your statement:
IFNULL(
(SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id)
, 0
) AS n
Have you tried just counting the parent_id's?
(SELECT parent_id AS pid, COUNT(parent_id) AS ans_count
FROM qa
GROUP BY parent_id)
SELECT IFNULL(parent_id, 0) AS pid, COUNT(IFNULL(parent_id, 0)) AS ans_count
FROM qa
GROUP BY IFNULL(parent_id, 0)
Can you post actual data and full query which exhibits the behavior you are talking about? In my experience, COUNT(*) can never be NULL.
Can Count(*) ever return null?
Does COUNT(*) always return a result?