MySQL select Table with reviews (even with empty fields) - mysql

I need to calculate number of reviews and average grade from client_reviews table. And add these 2 fields to client_impression table. This MySQL reguest is working bu the problem that display fields only with reviews. But I need to display all fields even with no reviews. How to make it?
SELECT `impression`.*, review.count, review.grade FROM `client_impression` AS `impression`
JOIN (SELECT impression_id, count(id) AS `count`, SUM(stars) AS `grade`
FROM `client_reviews` AS rev
WHERE rev.status=1 AND (SELECT city_id FROM client_impression_offer WHERE id=rev.impression_offer_id) = 1
GROUP BY rev.impression_id ) AS review ON review.impression_id = `impression`.`id`
WHERE `impression`.`city_1`=1 AND `impression`.`id` IN (SELECT `impression` FROM `impression_to_rank` WHERE `rank`=6)

Use left join
selct `impression`.*, review.count, review.grade FROM `client_impression` AS `impression`
left join
(
SELECT impression_id, count(id) AS `count`, SUM(stars) AS `grade`
FROM `client_reviews` AS rev WHERE rev.status=1 and
(SELECT city_id FROM client_impression_offer WHERE id=rev.impression_offer_id) = 1
group by impression_id
) review ON review.impression_id = `impression`.`id` and
`impression`.`city_1`=1 AND `impression`.`id` IN (SELECT `impression` FROM `impression_to_rank` WHERE `rank`=6)

Related

sql tables to find top 5 male and top 5 female purchase using data stored in two different tables and gender in one table and purchse in another table

CREATE TABLE mutual_fund (
transaction_id INTEGER(40),
customer_id INTEGER(40),
transaction_type ENUM('Purchase','Sale'),
nav_value INTEGER(40),
no_of_units INTEGER(40),
transaction_time TIMESTAMP,
transaction_status ENUM('Success','Failed','Pending')
);
INSERT INTO mutual_fund (transaction_id,customer_id,transaction_type,nav_value,no_of_units,transaction_time,transaction_status)
VALUES (1200,11,'Sale',3000,13,'2019-04-01 12:28:05','Success'),
(1201,12,'Purchase',6000,6,'2019-04-17 14:54:10','Failed'),
(1202,13,'Sale',2000,20,'2019-01-19 16:41:12','Failed'),
(1203,14,'Purchase',3400,11,'2019-01-27 20:08:45','Success'),
(1204,15,'Sale',7000,5,'2019-04-27 14:38:45','Success'),
(1205,16,'Purchase',1000,10,'2019-04-01 09:28:55','Success'),
(1206,17,'Sale',20000,12,'2019-01-29 19:01:32','Success'),
(1207,18,'Purchase',8000,5,'2021-01-26 11:57:02','Success'),
(1208,19,'Purchase',10000,3,'2021-01-26 12:34:53','Success'),
(1209,20,'Purchase',9000,9,'2021-04-26 16:13:18','Success'),
(1210,21,'Sale',19000,9,'2021-04-26 11:03:19','Failed');
CREATE TABLE customer_details (
customer_id INTEGER(40),
customer_name CHARACTER VARYING(40),
customer_PAN CHAR(40),
banned BOOLEAN,
customer_join_time TIMESTAMP,
gender ENUM('Male','Female')
);
INSERT INTO customer_details(customer_id,customer_name,customer_PAN,banned,customer_join_time,gender)
VALUES (11,'salil','sa11',0,'2019-04-01','Male'),
(12,'puran','pu12',1,'2019-04-17','Male'),
(13,'saumya','sa12',1,'2019-01-19','Female'),
(14,'priya','pr11',0,'2019-01-27','Female'),
(15,'suresh','su15',0,'2019-04-27','Male'),
(16,'amit','am11',0,'2019-04-01','Male'),
(17,'rahul','ra45',0,'2019-01-29','Male'),
(18,'rajesh','ra21',0,'2021-01-26','Male'),
(19,'aswini','as65',0,'2021-01-26','Female'),
(20,'prabha','pr95',0,'2021-04-26','Female'),
(21,'shubham','sh01',1,'2021-04-26','Male');
SELECT c.customer_id,c.gender,m.nav_value,m.transaction_type,m.transaction_status
FROM customer_details c
INNER JOIN mutual_fund m on c.customer_id=m.customer_id
WHERE (SELECT c.gender='Male' FROM customer_details c INNER JOIN mutual_fund m on c.customer_id=m.customer_id ORDER BY m.nav_value DESC LIMIT 5) AS t1
UNION ALL
(SELECT c.gender="Female" FROM customer_details c INNER JOIN mutual_fund m on c.customer_id=m.customer_id ORDER BY m.nav_value DESC LIMIT 5) AS t2;
i am trying find out the top 5 male and top 5 female purchases(nav_value) but i am stuck here
as gender is in customer_detail table and purchase(nav_value) is on mutual_fund table so using inner join and then thought union will work but i am stuck here and got no ideas left so any ideas people can this work ??????
You could go with something like this:
SELECT *
FROM
(
SELECT c.customer_id,c.gender,m.nav_value,m.transaction_type,m.transaction_status,
RANK() OVER (PARTITION BY c.gender ORDER BY m.nav_value DESC) AS RNum
FROM customer_details c
INNER JOIN mutual_fund m on c.customer_id=m.customer_id
) t
WHERE Rnum <= 5
You can read more about window functions here. You might want to go with one of the other window functions though, like DENSE_RANK or ROW_NUMBER depending on your needs.

joint three tables in Mysql

I have three tables employee, promotion and punishment
Employee’s table structure something like this
Id int
Fullname varchar
...............
promotionDate date
Promotion’s table structure is like this
id int
emp_id int
directorateDate date
And punishment’s table structure is like this
id int
emp_id int
direcotorateDate date
Let’s say employee table has 200 records, each month a group of employees have promotion (after serving one year), I want to get the list of all employees in the current month that get promotion
I can easily get the list by this query
SELECT *
FROM employee
WHERE MONTH(promotionDate) = MONTH(CURRENT_DATE())
AND YEAR(promotionDate) = YEAR(CURRENT_DATE())
My question is
I want to count number of punishments and promotions each employee got in the current year from punishment and promotion table respectively
I did this query but it did not get right results
SELECT e.fullname , COUNT(punish.emp_id) as siza ,COUNT(pro.emp_id) as supas
FROM emp_employee as e
LEFT JOIN emp_punishment as punish on punish.emp_id=e.id
LEFT JOIN emp_promotion as pro on e.id=pro.emp_id
WHERE ((MONTH(e.promotionDate) = MONTH(CURRENT_DATE())
AND YEAR(e.promotionDate) = YEAR(CURRENT_DATE()))
AND ( YEAR(punish.directorate_date) = YEAR(CURRENT_DATE()) )
AND ( YEAR(pro.directorate_date) = YEAR(CURRENT_DATE()) )
GROUP BY e.fullname;
Any help please.
By joining directly the 3 tables you get duplicate rows.
Group by emp_id and aggregate separately each of the tables emp_punishment and emp_promotion and join the results to the table emp_employee.
select e.fullname, coalesce(pu.siza, 0) siza, coalesce(pr.supas, 0) supas
from emp_employee as e
left join (
select emp_id, count(*) siza
from emp_punishment
where year(directorate_date) = year(CURRENT_DATE)
group by emp_id
) pu on pu.emp_id = e.id
left join (
select emp_id, count(*) supas
from emp_promotion
where year(directorate_date) = year(CURRENT_DATE)
group by emp_id
) pr on pr.emp_id = e.id
I used only the condition:
where year(directorate_date) = year(CURRENT_DATE())
because in your question you say:
I want to count number of punishments and promotions each employee got in the current year from punishment and promotion
Removing MONTH() function, and moving each condition to their respective place, instead of within the WHERE clause should resolve the issue (Since, they're considered as if INNER JOINs with the current style ).
Only keep common column e.promotionDate within the WHERE clause :
SELECT e.fullname,
COUNT(punish.emp_id) as siza ,
COUNT(pro.emp_id) as supas
FROM emp_employee as e
LEFT JOIN emp_punishment as punish
ON punish.emp_id=e.id
AND YEAR(punish.directorate_date) = YEAR(CURRENT_DATE())
LEFT JOIN emp_promotion as pro
ON e.id=pro.emp_id
AND YEAR(pro.directorate_date) = YEAR(CURRENT_DATE()))
WHERE YEAR(e.promotionDate) = YEAR(CURRENT_DATE())
GROUP BY e.fullname;

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);

SQL Left Join Multiple Tables and count values conditionally in each table

I am having some trouble putting together a SQL statement properly because I don't have much experience SQL, especially aggregate functions. Safe to say I don't really know what I'm doing outside of the basic SQL structure. I can do regular joins, but not complex ones.
I have some tables: 'Survey', 'Questions', 'Session', 'ParentSurvey', and 'ParentSurveyQuestion'. Structurally, a survey can have questions, it can have users that started the survey (a session), and it can have a parent survey whose questions get imported into the current survey.
What I want to do is get information for a each survey in the Survey table; total questions it has, how many sessions have been started (conditionally, ones that have not finished), and the number of questions in the parents survey. The three joined tables can but do not have to contain any values, and if they don't then 0 should be returned by COUNT. The common field in three of the tables is a variation of 'survey_id'
Here is my SQL so far, I put the table structure below it.
SELECT
`kp_survey_id`,
COALESCE( q.cnt, 0 ) AS questionsAmount,
COALESCE( s.cnt, 0 ) AS sessionsAmount
COALESCE( p.cnt, 0 ) AS parentQAmount,
FROM `Survey`
LEFT JOIN <-- I'd like the count of questions for this survey
( SELECT COUNT(*) AS cnt
FROM Questions
GROUP BY kf_survey_id ) q
ON Survey.kp_survey_id = Questions.kf_survey_id
LEFT JOIN
( SELECT COUNT(*) AS cnt <-- I'd like the count of started sessions for this survey
FROM Session
WHERE session_status = 'started' <-- should this be Session.session_status?
GROUP BY kf_survey_id ) s
ON Survey.kp_survey_id = Session.kf_survey_id
LEFT JOIN
( SELECT COUNT(*) AS cnt <-- I'd like the count of questions in the parent survey with this survey id
FROM ParentSurvey
GROUP BY kp_parent_survey_id ) p
ON Survey.kf_parent_survey_id = ParentSurveyQuestion.kf_parent_survey_id
'kp' prefix means primary key, while 'kf' prefix means foreign key
Structure:
Survey: 'kp_survey_id' | 'kf_parent_survey_id'
Question: 'kp_question_id' | 'kf_survey_id'
Session: 'kp_session_id' | 'kf_survey_id' | 'session_status'
ParentSurvey: 'kp_parent_survey_id' | 'survey_name'
ParentSurveyQuestion: 'kp_parent_question_id' | 'kf_parent_survey_id'
There are also other columns in each table like 'name' or 'account_id', but i don't think they matter in this case
I'd like to know if I'm doing this correctly or if I'm missing something. I'm repurposing some code I found here on stackoverflow and modifying it to meet my needs, as I haven't seen conditional aggregation for more than three tables on this site.
My expected output is something like:
kp_survey_id | questionsAmount | sessionsAmount | parentQAmount
1 | 3 | 0 | 3
2 | 0 | 5 | 3
I think you were pretty close -- just need to fix your joins and include the survey id in the subqueries to use in those joins:
SELECT
`kp_survey_id`,
COALESCE( q.cnt, 0 ) AS questionsAmount,
COALESCE( s.cnt, 0 ) AS sessionsAmount
COALESCE( p.cnt, 0 ) AS parentQAmount,
FROM `Survey`
LEFT JOIN
( SELECT COUNT(*) cnt, kf_survey_id AS cnt
FROM Questions
GROUP BY kf_survey_id ) q
ON Survey.kp_survey_id = q.kf_survey_id
LEFT JOIN
( SELECT COUNT(*) cnt, kf_survey_id
FROM Session
WHERE session_status = 'started'
GROUP BY kf_survey_id ) s
ON Survey.kp_survey_id = s.kf_survey_id
LEFT JOIN
( SELECT COUNT(*) cnt, kp_parent_survey_id
FROM ParentSurvey
GROUP BY kp_parent_survey_id ) p
ON Survey.kf_parent_survey_id = p.kp_parent_survey_id
One thing you need to do is correct your joins. When you are joining to a subquery, you need to use the alias of the subquery. In your case you are using the alias of the table being used in the subquery.
Another thing you need to change is to include the field you wish to use in your JOIN in the subquery.
Make these changes and try running. Do you get an error or the desired results?
SELECT
`kp_survey_id`,
COALESCE( q.cnt, 0 ) AS questionsAmount,
COALESCE( s.cnt, 0 ) AS sessionsAmount
COALESCE( p.cnt, 0 ) AS parentQAmount,
FROM `Survey`
LEFT JOIN <-- I'd like the count of questions for this survey
( SELECT kf_survey_id, COUNT(*) AS cnt
FROM Questions
GROUP BY kf_survey_id ) q
ON Survey.kp_survey_id = q.kf_survey_id
LEFT JOIN
( SELECT kf_survey_id, COUNT(*) AS cnt <-- I'd like the count of started sessions for this survey
FROM Session
WHERE session_status = 'started' <-- should this be Session.session_status?
GROUP BY kf_survey_id ) s
ON Survey.kp_survey_id = s.kf_survey_id
LEFT JOIN
( SELECT kp_parent_survey_id, COUNT(*) AS cnt <-- I'd like the count of questions in the parent survey with this survey id
FROM ParentSurvey
GROUP BY kp_parent_survey_id ) p
ON Survey.kf_parent_survey_id = p.kf_parent_survey_id

selecting where in multiple join tables

I have the logic worked out, just not sure how to best write this query.
the logic is
we have a deal ID of 1
a deal is linked to multiple regions
a deal is linked to multiple interests
a user is linked to multiple regions
a user is linked to multiple interests
we want all users where....
the user is linked to the same region as a deal
userRegionLink url, dealRegionLink drl
url.regionId is in drl.regionId where drl.dealId = 1
the user is linked to the same interest as a deal
userInterestLink uil, dealInterestLink dil
uil.interestId is in dil.interestId where dil.dealId = 1
this would give us a list of the users
now we need to select distinct from the list so we only end up sending each user a single email
But I have no idea what the best way to write this query would be.
We are dealing with a few tables here
We have
users which has all the user Information in it userId and other columns not important
userInterestLink which has userId and interestId
dealInterestLink which has dealId and interestId
userRegionLink which has userId and regionId
dealRegionLink which has dealId and regionId
so what we are wanting in the end is all the user info which matches.
I take RC's answer and modify it
SELECT u.userId, uil.interestId, url.regionId FROM users u
JOIN userInterestLink uil ON (uil.userId = u.userId)
JOIN userRegionLink url ON (url.userId = u.userId)
WHERE interestId IN (
SELECT DISTINCT interestId FROM dealInterestLink WHERE dealId = 1
) AND regionId IN (
SELECT DISTINCT regionId FROM dealRegionLink WHERE dealId = 1
)
as there is no need for LEFT JOIN if I exclude the NULL rows afterwards.
A more "symmetric" version without subqueries and with USING would be
SELECT u.userId, uil.interestId, url.regionId FROM users u
JOIN userInterestLink uil USING (userId)
JOIN userRegionLink url USING (userId)
JOIN dealInterestLink dil USING (interestId)
JOIN dealRegionLink drl USING (regionId, dealId)
WHERE dealId = 1
Untested as well.
Something like:
SELECT u.userId, uil.interestId, url.regionId FROM users u
LEFT JOIN userInterestLink uil ON (uil.userId = u.userId)
LEFT JOIN userRegionLink url ON (url.userId = u.userId)
WHERE uil.interestId IS NOT NULL AND uil.interestId IN (
SELECT DISTINCT interestId FROM dealInterestLink WHERE dealId = 1
) AND url.regionId IS NOT NULL AND url.regionId IN (
SELECT DISTINCT regionId FROM dealRegionLink WHERE dealId = 1
)
? If result is OK, you can then SELECT DISTINCT u.userId FROM users u -- ...
(not tested)
SELECT `u`.*
FROM `users` AS `u`
JOIN `userRegionLink` `userReg` USING ( `userId` )
JOIN `userInterestLink` `userInt` USING ( `userId` )
JOIN `dealInterestLink` `dealInt` USING ( `interestId` )
JOIN `dealRegionLink` `dealReg` USING ( `regionId` )
JOIN `deal` `d` ON ( `dealInt`.`dealId` && `dealReg`.`dealId` && `d`.`dealId` = 1 )
GROUP BY `u`.`userId`
Tested locally using dummy data and presumed schema. Worked OK.