MySQL query joining 3 tables and counting - mysql

I've been stuck on this problem for far too long.
I have to merge 3 tables and do some counting of distinct values.
I have 3 tables
1.User_me
profileId( String )
responded( int 1 or 0)
2.Profiles
profileId ( String )
idLocation ( int )
3.lookup_location
id ( int )
location (String )
I can join User_me and Profiles ON User_me.profileId = Profiles.profileId
I can join Profiles and lookup_location ON Profiles.idLocation = lookup_location.id
Under Profiles I need to count the number of distinct values for idLocation where User_me.profileId = Profiles.profileId
I also need to count the number of Profiles.idLocation that have User_me.responded = 1
I have this:
SELECT lookup.location, count(*) as total
FROM User_me user
JOIN Profiles
ON user.profileId= profiles.profileId
JOIN lookup_location lookup
ON profiles.idLocation = lookup.id
GROUP BY profiles.idLocation
but I still need to have the column giving me the count where User_me.responded = 1
Something like:
SELECT lookup.location, count(*) as total, count(*) responded

If I'm understanding your question correctly, you can you a case statement in the count aggregate:
SELECT lookup.location, count(*) as total,
count(case when user.responded = 1 then 1 end) as responded
FROM User_me user
JOIN Profiles
ON user.profileId= profiles.profileId
JOIN lookup_location lookup
ON profiles.idLocation = lookup.id
GROUP BY profiles.idLocation
Since you're using MySQL, you can also use something like sum(user.responded = 1).

Related

MySQL - Joining same table twice, parent sum column value getting duplicated

I'm just 3 months of experience in MySQL. Here I'm trying to generate a report based on log and part table. When trying to join the "Part" table twice, the "Log" table Quantity gets doubled. Kindly let me know where I'm doing wrong.
Log Table
Part Table
Expected Report
Query Used
SELECT
report.*,
(SUM(report.quantity)) AS totalQuantity,
normalPart.price AS normalPrice,
premiumPart.price AS premiumPrice
FROM
log AS report
LEFT JOIN
(SELECT
*
FROM
part AS normalPart
WHERE
normalPart.type = 'normal'
GROUP BY normalPart.partNumber , normalPart.genId) AS normalPart ON report.partNumber = normalPart.partNumber
AND report.genId = normalPart.genId
AND normalPart.cat = report.fromCat
LEFT JOIN
(SELECT
*
FROM
part AS premiumPart
WHERE
premiumPart.type = 'premium'
GROUP BY premiumPart.partNumber , premiumPart.genId) AS premiumPart ON report.partNumber = premiumPart.partNumber
AND report.genId = premiumPart.genId
AND premiumPart.cat = report.toCat;
Query Result
This answers the original version of the question.
Aggregate before you join:
select l.*, p.normal_price, p.premium_price
from (select genid, partnumber, sum(quantity) as quantity
from log
group by genid, partnumber
) l left join
(select partnumber, genid,
max(case when type = 'normal' then price end) as normal_price,
max(case when type = 'premium' then price end) as premium_price
from part
group by partnumber, genid
) p
on p.partnumber = l.partnumber and p.genid = l.genid

MySQL Multiple Join Query with Limit on One Join

I have a MYSQL query I'm working on that pulls data from multiple joins.
select students.studentID, students.firstName, students.lastName, userAccounts.userID, userstudentrelationship.userID, userstudentrelationship.studentID, userAccounts.getTexts, reports.pupID, contacts.pfirstName, contacts.plastName, reports.timestamp
from userstudentrelationship
join userAccounts on (userstudentrelationship.userID = userAccounts.userID)
join students on (userstudentrelationship.studentID = students.studentID)
join reports on (students.studentID = reports.studentID)
join contacts on (reports.pupID = contacts.pupID)
where userstudentrelationship.studentID = "10000005" AND userAccounts.getTexts = 1 ORDER BY reports.timestamp DESC LIMIT 1
I have a unique situation where I would like one of the joins (the reports join) to be limited to the latest result only for that table (order by reports.timestamp desc limit 1 is what I use), while not limiting the result quantities for the overall query.
By running the above query I get the data I would expect, but only one record when it should return several.
My question:
How can I modify this query to ensure that I receive all possible records available, while ensuring that only the latest record from the reports join used? I expect that each record will possibly contain different data from the other joins, but all records returned by this query will share the same report record
Provided I understand the issue; one could add a join to a set of data (aliased Z below) that has the max timestamp for each student; thereby limiting to one report record (most recent) for each student.
SELECT students.studentID
, students.firstName
, students.lastName
, userAccounts.userID
, userstudentrelationship.userID
, userstudentrelationship.studentID
, userAccounts.getTexts
, reports.pupID
, contacts.pfirstName
, contacts.plastName
, reports.timestamp
FROM userstudentrelationship
join userAccounts
on userstudentrelationship.userID = userAccounts.userID
join students
on userstudentrelationship.studentID = students.studentID
join reports
on students.studentID = reports.studentID
join contacts
on reports.pupID = contacts.pupID
join (SELECT max(timestamp) mts, studentID
FROM REPORTS
GROUP BY StudentID) Z
on reports.studentID = Z.studentID
and reports.timestamp = Z.mts
WHERE userstudentrelationship.studentID = "10000005"
AND userAccounts.getTexts = 1
ORDER BY reports.timestamp
for get all the records you should avoid limit 1 at the end of the query
for join anly one row from reports table you could use subquery as
select
students.studentID
, students.firstName
, students.lastName
, userAccounts.userID
, userstudentrelationship.userID
, userstudentrelationship.studentID
, userAccounts.getTexts
, t.pupID
, contacts.pfirstName
, contacts.plastName
, t.timestamp
from userstudentrelationship
join userAccounts on userstudentrelationship.userID = userAccounts.userID
join students on userstudentrelationship.studentID = students.studentID
join (
select * from reports
order by reports.timestamp limit 1
) t on students.studentID = t.studentID
join contacts on reports.pupID = contacts.pupID
where userstudentrelationship.studentID = "10000005"
AND userAccounts.getTexts = 1

MySql throws error Subquery returns more than 1 row

I want to retrieve values from 3 table where i am getting error "Sub query returns more than 1 row " .
My concept is to retrieve all the post where i have to calculate the sum of votes from ttpostvotes table with respect to each post and if provided userid is voted for the that post then it will shows the post count like 1 or -1.
My query is as below:
SELECT r.PostId, r.`Post`,r.PostTime, coalesce(x.Votes, 0) as Votes ,
(Select Votes From `ttpostvotes` where UserId=30 and x.PostId=r.PostId ) as IsUservoted,
(Select Count(*) From ttreply where PostId=r.PostId ) AS ReplyCount FROM `ttpost` r
left join ( SELECT PostId, sum(Votes) as Votes FROM `ttpostvotes` GROUP BY PostId ) x ON
x.PostId = r.PostId WHERE r.OffensiveCount<3 and r.SpamCount<5 and r.OtherCount<7 and r.`PeekId`=101 ORDER BY `r`.`PostTime` DESC
The 3 tables are like as below:
ttpost
ttpostvotes
ttreply
This is your select:
SELECT r.PostId, r.`Post`,r.PostTime, coalesce(x.Votes, 0) as Votes,
(Select Votes From `ttpostvotes` where UserId = 30 and x.PostId = r.PostId
) as IsUservoted,
(Select Count(*) From ttreply where PostId=r.PostId ) AS ReplyCount
The first subquery has no aggregation, so I suppose a user could vote more than once for a post. This will fix the syntax error:
SELECT r.PostId, r.`Post`,r.PostTime, coalesce(x.Votes, 0) as Votes,
(Select SUM(Votes) From `ttpostvotes` where UserId = 30 and x.PostId = r.PostId
) as IsUservoted,
(Select Count(*) From ttreply where PostId = r.PostId ) AS ReplyCount
Whether it does what you want is a different question.
Note: if you want your original query to work, you should define a unique constraint/index on ttpostvotes:
create unique index unq_ttpostvotes_userid_postid on ttpostvotes(userid, postid);

mysql SELECT EXISTS on multiple tables

Have tables: person,person_ip
Both tables have pid column as a primary key, in table person there is column state_id, in table person_ip there is column ip.
Want to discover if specified IP address is assigned to person with state_id is not equal to 2. But always got result 1, even if state_id is 0, 1 or 2. Always got 0 only if ip address is not listed at all. What am I doing wrong?
SELECT EXISTS (
SELECT person_ip.PID
FROM person_ip,person
WHERE person.PID=person_ip.PID
AND person.state_id NOT IN (2)
AND person_ip.ip='10.11.12.13'
)
this seems like a simple join.. unless i'm missing something
select person.*
from person
inner join person_ip
on person.pid = person_ip.pid
where person.state_id <> 2
and person_ip.ip_address = '10.0.0.1'
If you want to exclude the ip_address if it has been assigned to any user with state = 2, even if it has also been assigned to a user without state = 2, then try:
select max(i)
from (
select *
from (
select 1 as i
from dual
where not exists (
select 1
from person p
inner join person_ip pi
on p.pid = pi.pid
where p.state_id = 2
and pi.ip_address = '10.0.0.1'
)
) q
union
select 0
) qq
(dual is a system table that can be used as a sort of stub table)
here's a fiddle showing both versions
update after some actual sleep
Okay, so the above query is a little.. out there. Back in the real world, this one is probably more appropriate:
select count(case when p1.state_id = 2 then 1 end)
from person p1
inner join person_ip pi1
on p1.pid = pi1.pid
where pi1.ip_address = '10.0.0.1'
group by pi1.ip_address;
This will return 1 or more if your ip_address has been used by someone with a state_id of 2, and 0 if it has never been used by someone with a state_id of 2.
It will return nothing if the ip has never been used.
this fiddle has all three of the above queries.
SELECT IF(COUNT(*)>0,1,0)
FROM person
INNER JOIN person_ip
ON person.pid = person_ip.pid
AND person_ip.ip_address = '10.0.0.1'
WHERE person.state_id <> 2

MySQL - How to count number of rows from primary query, ignore subquery rows?

I use the following MySQL to return a list of posts and their corresponding comments.
SELECT *
FROM forum_qa
JOIN user_profiles
ON user_id = forum_qa_author_id
LEFT JOIN (SELECT forum_cm_id,
forum_cm_author_id,
forum_qa_id_fk,
forum_cm_text,
FROM forum_cm
JOIN user_profiles
ON user_id = forum_cm_author_id) AS c
ON forum_qa_id = c.forum_qa_id_fk
WHERE forum_qa_parent_id = $forum_qa_id
If I run
$data['num_answers'] = $query->num_rows();
This allows me to get the number of returned rows and pass the array to my controller and view.
But this is returning all rows (posts + comments). So if 1 post has 10 comments, it returns 10.
How could I have this query count only the number of posts (ie, returning 1) not including the subquery?
Each post has a unique id saved in forum_qa.forum_qa_id
Each comment has a unique id saved in forum_cm.forum_cm_id.
Thanks for helping -- will post more code if needed.
Not the fastest, but you are not restricted in using GROUP BY:
SELECT *,
(SELECT COUNT(*) FROM forum_qa WHERE forum_qa_parent_id = $forum_qa_id) Cnt
FROM forum_qa
JOIN user_profiles
ON user_id = forum_qa_author_id
LEFT JOIN (SELECT forum_cm_id,
forum_cm_author_id,
forum_qa_id_fk,
forum_cm_text,
FROM forum_cm
JOIN user_profiles
ON user_id = forum_cm_author_id) AS c
ON forum_qa_id = c.forum_qa_id_fk
WHERE forum_qa_parent_id = $forum_qa_id
You can run another query or add one more column (with an independent subquery) in the result set:
SELECT *
, ( SELECT COUNT(*)
FROM forum_qa
WHERE forum_qa_parent_id = $forum_qa_id
) AS cntPosts
FROM forum_qa
JOIN user_profiles
ON user_id = forum_qa_author_id
LEFT JOIN (SELECT forum_cm_id,
forum_cm_author_id,
forum_qa_id_fk,
forum_cm_text,
FROM forum_cm
JOIN user_profiles
ON user_id = forum_cm_author_id) AS c
ON forum_qa_id = c.forum_qa_id_fk
WHERE forum_qa_parent_id = $forum_qa_id
COUNT(DISTINCT forum_qa.forum_qa_id)
COUNT(DISTINCT col_name) counts the distinct post ids. This should equal the number of posts.