MYSQL Query - Some results don't return - mysql

I have a problem im my sql query:
SELECT table_2.id, SUM(table_2.time + table_4.time + table_6.time + table_8.time + table_10.time + table_12.time) AS total_time, SUM(table_2.connects + table_4.connects + table_6.connects + table_8.connects + table_10.connects + table_12.connects) AS total_connects FROM table_2
INNER JOIN table_4 ON table_2.id = table_4.id
INNER JOIN table_6 ON table_2.id = table_6.id
INNER JOIN table_8 ON table_2.id = table_8.id
INNER JOIN table_10 ON table_2.id = table_10.id
INNER JOIN table_12 ON table_2.id = table_12.id
GROUP BY table_2.authid ORDER BY total_time DESC
Ok, I have a script that grabs the user IDS and the time they spent and then puts it in mysql tables depending on which forum they have entered. The query above seems to be working fine but only for the users that have entered all forums, because the others that haven't entered in all forums don't return any result.
The ID is always the same, it is the users ID and connects is the number of times the user enters the specific forum.
If you understand my problem and know the answear please let me know, otherwise I'll try to explain better
Thanks

I think a good way to do this may be:
SELECT id, SUM(time), SUM(connects)
FROM (
SELECT id, time, connects FROM table_2
UNION ALL
SELECT id, time, connects FROM table_4
UNION ALL
SELECT id, time, connects FROM table_6
UNION ALL
SELECT id, time, connects FROM table_8
UNION ALL
SELECT id, time, connects FROM table_10
UNION ALL
SELECT id, time, connects FROM table_12
) uniontable
GROUP BY id
Since they all have the same fields.
EDIT: And it sounds like it may not be such a strange idea to merge them into one table, especially if you want to count the total time like this ;)
EDIT 2: I'm sorry, that wasn't valid SQL at all. This should be better.

Replace the INNER JOIN by a LEFT OUTER JOIN this will get you results over all the tables. You might need to re-define the SUM probably you'll get NULL on users who haven't visited all forums...

Replace the JOIN with the LEFT JOIN:
SELECT people.id, SUM(COALESCE(table_2.time, 0) + COALESCE(table_4.time, 0) + table_6.time, 0) + COALESCE(table_8.time, 0) + COALESCE(table_10.time, 0) + COALESCE(table_12.time, 0)) AS total_time,
SUM(COALESCE(table_2.connects, 0) + COALESCE(table_4.connects, 0) + COALESCE(table_6.connects, 0) + COALESCE(table_8.connects, 0) + COALESCE(table_10.connects, 0) + COALESCE(table_12.connects, 0)) AS total_connects
FROM people p
LEFT JOIN
table_2
ON table_2.id = people.id
LEFT JOIN
table_4
ON people.id = table_4.id
LEFT JOIN
table_6
ON people.id = table_6.id
LEFT JOIN
table_8
ON people.id = table_8.id
LEFT JOIN
table_10
ON people.id = table_10.id
LEFT JOIN
table_12
ON people.id = table_12.id
GROUP BY
people.authid
ORDER BY
total_time DESC

#afonso: I think you should populate all those time tables whenever a new user is created by inserting the user with a time of 0 (just so that they're in the table). Would this be an acceptable compromise for your application just to have those users in the tables so your initial query will work?

You will need to use outer joins and you will need to use IFNULL or something similar to map the "user wasn't in forum" nulls to zeroes.
SELECT t2.id,
SUM(IFNULL(t2.time, 0) + IFNULL(t4.time, 0) +
IFNULL(t6.time, 0) + IFNULL(t8.time, 0) +
IFNULL(t10.time, 0) + IFNULL(t12.time, 0)) AS total_time,
SUM(IFNULL(t2.connects, 0) + IFNULL(t4.connects, 0) +
IFNULL(t6.connects, 0) + IFNULL(t8.connects, 0) +
IFNULL(t10.connects, 0) + IFNULL(t12.connects, 0)) AS total_connects
FROM table_2 AS t2
LEFT JOIN table_4 AS t4 ON t2.id = t4.id
LEFT JOIN table_6 AS 76 ON t2.id = t6.id
LEFT JOIN table_8 AS t8 ON t2.id = t8.id
LEFT JOIN table_10 AS t10 ON t2.id = t10.id
LEFT JOIN table_12 AS t12 ON t2.id = t12.id
GROUP BY t2.id ORDER BY total_time DESC
This almost works; it does work for all users that visit the forum for which the information is stored in table_2. To make it work for people who don't visit the 'table_2 forum', the query needs to use the table that defines users and do the outer joins with that table:
SELECT u.id,
SUM(IFNULL(t2.time, 0) + IFNULL(t4.time, 0) +
IFNULL(t6.time, 0) + IFNULL(t8.time, 0) +
IFNULL(t10.time, 0) + IFNULL(t12.time, 0)) AS total_time,
SUM(IFNULL(t2.connects, 0) + IFNULL(t4.connects, 0) +
IFNULL(t6.connects, 0) + IFNULL(t8.connects, 0) +
IFNULL(t10.connects, 0) + IFNULL(t12.connects, 0)) AS total_connects
FROM Users AS u
LEFT JOIN table_2 AS t2 ON u.id = t2.id
LEFT JOIN table_4 AS t4 ON u.id = t4.id
LEFT JOIN table_6 AS 76 ON u.id = t6.id
LEFT JOIN table_8 AS t8 ON u.id = t8.id
LEFT JOIN table_10 AS t10 ON u.id = t10.id
LEFT JOIN table_12 AS t12 ON u.id = t12.id
GROUP BY u.id ORDER BY total_time DESC
However, given the symmetry of the table_N tables, I think you should do as Spiny Norman
suggests and redesign your schema so that there is one table that stores the timing and connection values:
CREATE TABLE ForumUsage
(
ForumID INTEGER NOT NULL REFERENCES Forums,
UserID INTEGER NOT NULL REFERENCES Users,
Time INTEGER NOT NULL,
Connects INTEGER NOT NULL
);
Then you sum over the entries in that table.

Related

Why does MySQL View and the same View's underlying SELECT query return different results?

I could do with some help here with this issue in which I'm trying to generate a set of results with balances brought forward and balances carried forward.
The MySQL script:
CREATE OR REPLACE VIEW vwbalances AS
SELECT th.user_id usrid
, YEAR(th.date_created) year
, IFNULL(
(SELECT SUM(tx.amount)
FROM transdetail tx
JOIN transhdr th
ON th.thdr_id = tx.thdr_id
JOIN users u
ON th.user_id = u.user_id
JOIN transtype tt
ON tt.ttype_id = tx.ttype_id
WHERE tx.ttype_id in (2,9,11)
AND u.user_id = usrid
and YEAR(tx.date_created) < year
)
,0) bal_bfwd
, SUM(td.amount) bal_ytd
, ifnull(
(SELECT SUM(ty.amount)
FROM transdetail ty
JOIN transhdr th
ON th.thdr_id = ty.thdr_id
JOIN users u
ON th.user_id = u.user_id
JOIN transtype tt
ON tt.ttype_id = ty.ttype_id
WHERE ty.ttype_id in (2,9,11)
AND u.user_id = usrid
AND YEAR(ty.date_created) <= year
)
,0) bal_cfwd
FROM transdetail td
JOIN transhdr th
ON th.thdr_id = td.thdr_id
JOIN users u
ON th.user_id = u.user_id
JOIN transtype tt
ON tt.ttype_id = td.ttype_id
WHERE td.ttype_id in (2,9,11)
GROUP
BY th.user_id
, YEAR(th.date_created)
I get this (incorrect results) when I run SELECT * FROM vwbalances:
And I get this (correct results) when I run the full SELECT statement that I used to create the VIEW:
Thanks in advance.
The query you posted shouldn't even work.
The usrid is an alias in your most outer select. It is not available in your subqueries.
Unless you have a column called year things like year(tx.date_created) < year shouldn't work either
You use the same table alias in your outer query and in your subqueries. This should also not be possible. If it is, that's probably why you get weird results.
Apart from that, you don't have to do the basically same query multiple times. You can shorten your query to something like this:
SELECT `th`.`user_id` AS usrid
, year(`th`.`date_created`) AS 'year'
sum(if(year(td.date_created`) < year, amount, 0)) as bal_ytd,
sum(if(year(td.date_created`) <= year, amount, 0)) as bal_cfwd
FROM `transdetail` `td`
JOIN `transhdr` `th` ON `th`.`thdr_id` = `td`.`thdr_id`
JOIN `users` `u` ON `th`.`user_id` = `u`.`user_id`
JOIN `transtype` `tt` ON `tt`.`ttype_id` = `td`.`ttype_id`
WHERE `td`.`ttype_id` IN (2, 9, 11)
GROUP BY `th`.`user_id`
, year(`th`.`date_created`)
(given of course, that this weird year thing works for you)
The following script has worked correctly. All help and tips appreciated:
CREATE OR REPLACE VIEW vwbalances AS
SELECT tho.user_id AS usrid, YEAR(td.date_created) AS 'year'
, ROUND(IFNULL((SELECT sum(tx.amount) FROM transdetail tx
JOIN transhdr th ON th.thdr_id = tx.thdr_id
JOIN users u ON th.user_id = u.user_id
JOIN transtype tt ON tt.ttype_id = tx.ttype_id
WHERE tx.ttype_id IN (2,9,11) AND u.user_id = tho.user_id AND
YEAR(tx.date_created) < YEAR(td.date_created) ),0),2) AS
bal_bfwd
, round(sum(td.amount),2) AS bal_ytd
, ROUND(IFNULL((select SUM(ty.amount) FROM transdetail ty
JOIN transhdr th on th.thdr_id = ty.thdr_id
JOIN users u ON th.user_id = u.user_id
JOIN transtype tt ON tt.ttype_id = ty.ttype_id
WHERE ty.ttype_id IN (2,9,11) AND u.user_id = tho.user_id AND
YEAR(ty.date_created) <= YEAR(td.date_created)),0),2) AS
bal_cfwd
FROM transdetail td JOIN transhdr tho ON tho.thdr_id =
td.thdr_id
JOIN users u ON tho.user_id = u.user_id
JOIN transtype tt on tt.ttype_id = td.ttype_id
WHERE td.ttype_id IN (2,9,11)
GROUP BY tho.user_id, YEAR(td.date_created);

Order by total points per user_id descending

select
((select
COALESCE(sum(b.points_received), 0) as badge_total_points
from
user_badges ub
join
badges b ON ub.badge_id = b.badge_id
where
ub.user_id = '$user_id') + (select
COALESCE(sum(aps.given_points), 0) as total_action_points
from
user_action_points uap
join
action_point_system aps ON uap.point_id = aps.point_id
where
uap.user_id = '$user_id')) as total_contribution_points
how do I retrieve a list getting the total points grouped by a user_id with this SQL statement? Anybody have suggestions?
EDIT:
select
ub.user_id,
COALESCE(sum(b.isGold), 0) as gold_count,
COALESCE(sum(b.isSilver), 0) as silver_count,
COALESCE(sum(b.isBronze), 0) as bronze_count
from
user_badges ub
join
badges b ON ub.badge_id = b.badge_id
group by ub.user_id
how can I add this query to the result of the first question?
Although there are ways to do what you want, you can build on your existing query using correlated subqueries:
select u.*,
((select COALESCE(sum(b.points_received), 0) as badge_total_points
from user_badges ub join
badges b
ON ub.badge_id = b.badge_id
where ub.user_id = u.user_id
) +
(select COALESCE(sum(aps.given_points), 0) as total_action_points
from user_action_points uap join
action_point_system aps
ON uap.point_id = aps.point_id
where uap.user_id = u.user_id
)
) as total_contribution_points
from users u
order by total_contribution_points desc;

multiple table join to generate report counts

I am trying to generate a report that will display data from 3 tables as follows:
SELECT B.datepoll, B.clientID, B.badTerm, A.FullQuery,
B.badMessage, C.clientNAME, E.CURRENT_TERMS
FROM BadTerms AS B INNER JOIN
QueryTERMS AS A ON B.badTerm = A.QUERIES INNER JOIN
Clients AS C ON B.clientID = C.clientID INNER JOIN
(
SELECT CLIENTID,COUNT(QUERIES) AS CURRENT_TERMS FROM QueryTERMS WHERE
clientID = 'XXXXXX' GROUP BY CLIENTID) E ON
E.CLIENTID = A.CLIENTID
WHERE (CONVERT(VARCHAR(10), B.datepoll, 120) < CONVERT(VARCHAR(10), GETDATE(), 120))
AND (B.clientID = 'XXXXXX')
Basically what should be displayed is for a given datepoll, display all badTerms, fullQuery, badMessage, clientID, clientName and total terms. The clientID is common among all 3 tables and queries is common among BadTerms and QueryTerms.
I have everything fine except for the total count. It should only appear once and be the total queries in the QueryTerms table not the BadTerms table.
Any help appreciated.
Actually got it to work now:
SELECT B.datepoll, B.clientID, B.badTerm, A.FullQuery,
B.badMessage, C.clientNAME, E.CURRENT_TERMS
FROM BadTerms AS B INNER JOIN
QueryTERMS AS A ON B.badTerm = A.QUERIES INNER JOIN
Clients AS C ON B.clientID = C.clientID INNER JOIN
(
SELECT CLIENTID,COUNT(QUERIES) AS CURRENT_TERMS FROM QueryTERMS WHERE
clientID = 'XXXXXX' GROUP BY CLIENTID) E ON
E.CLIENTID = A.CLIENTID
WHERE (CONVERT(VARCHAR(10), B.datepoll, 120) < CONVERT(VARCHAR(10), GETDATE(), 120))
AND (B.clientID = 'XXXXXX')

Outer Join in mYsql Qyery

I have folowing MYsql Query And Trying to right outer join but unable to understan how to do this
here is query plase any one help
select lp_des.lpname,today.cnt_veh_tdy,todate.cnt_veh_tdate
from
(select distinct registration.lpcode,loadingpoint.lpname
from registration,loadingpoint
where registration.lpcode=loadingpoint.lpcode) lp_des,
(select lpcode,count(vehicleno) cnt_veh_tdate
from registration
where registration.companycode='01'
group by lpcode) todate,
(
select lpcode,count(vehicleno) cnt_veh_tdy
from registration
where registration.companycode='01'
and registration.date=(select max(date) from registration)
group by lpcode) today
right outer join today on lp_des.lpcode = today.lpcode
right outer join todate on lp_des.lpcode = todate.lpcode
I want to make right outer join on this part
where lp_des.lpcode=todate.lpcode
and lp_des.lpcode=today.lpcode
Please help and Thanks in advance
You asked for this:
select
lp_des.lpname,
today.cnt_veh_tdy,
todate.cnt_veh_tdate
from
(select distinct
r.lpcode,
l.lpname
from
registration r
inner join loadingpoint l on l.lpcode = r.lpcode) lp_des
right join
(select
r.lpcode,
count(r.vehicleno) cnt_veh_tdate
from
registration r
where
r.companycode='01'
group by
lpcode) todate on todate.lpcode = lp_des.lpcode
right join
(select
r.lpcode,
count(r.vehicleno) cnt_veh_tdy
from
registration r
where
r.companycode = '01'
and registration.date = (select max(date) from registration)
group by
r.lpcode) today on today.lpcode = lp_des.lpcode
But I think you mean this:
select
r.lpcode,
l.lpname,
count(r.vehicleno) cnt_veh_tdate,
count(case when r.date = md.date then r.vehicleno else null end) cnt_veh_tdy
from
registration r
inner join (select max(rm.date) maxdate from registration rm) md
left join loadingpoint l on l.lpcode = r.lpcode
where
r.companycode = '01'
group by
r.lpcode
and maybe even this:
select
r.lpcode,
l.lpname,
count(r.vehicleno) cnt_veh_tdate,
count(case when r.date = date() then r.vehicleno else null end) cnt_veh_tdy
from
registration r
left join loadingpoint l on l.lpcode = r.lpcode
where
r.companycode = '01'
group by
r.lpcode
If I read it correctly, you want a query that returns the number of vehicles for company 1 assigned to a loading point, overall as well as for today only. And you also want that count for vehicles that do not have loading point assigned yet.
Though it would help if you would have added this description. It will help the ones answering your question, but it will also help you writing the right query in the first place.
The syntax for a right outer join is:
SELECT t1.id, t2.id FROM t1 RIGHT OUTER JOIN t2 ON t1.field1 = t2.field2
If you're joining on the same field you can use USING instead of ON:
SELECT t1.id, t2.id FROM t1 RIGHT OUTER JOIN t2 USING (field)

How to group summed up columns from different tables?

I have a user table (User) and 3 tutorial tables (Text, Video and Other).
Each tutorial has the columns rating_positive and rating_negative
and is linked to a user (id).
I want to select the 10 users with the most tutorials and the sum
of positive/negative ratings of their tutorials.
I tried the following query but it does not work. It returns way too many results for tutorials_count/pos/neg. How can I do it correctly?
SELECT
u.id AS user_id,
(COUNT(t.id) + COUNT(v.id) + COUNT(o.id)) AS tutorials_count,
(SUM(t.rating_positive) + SUM(v.rating_positive) + SUM(o.rating_positive)) AS pos,
(SUM(t.rating_negative) + SUM(v.rating_negative) + SUM(o.rating_negative)) AS neg
FROM
user u LEFT JOIN trick t ON u.id = t.submitter_id
LEFT JOIN video v ON u.id = v.submitter_id
LEFT JOIN other o ON u.id = o.submitter_id
GROUP BY u.id
ORDER BY tutorials_count DESC
LIMIT 10
Try making a subquery with a UNION ALL of the three tables you are interested in and then join with that:
SELECT
u.id AS user_id,
COUNT(submitter_id) AS tutorials_count,
IFNULL(SUM(rating_positive), 0) AS pos,
IFNULL(SUM(rating_negative), 0) AS neg
FROM user u
LEFT JOIN (
SELECT submitter_id, rating_positive, rating_negative FROM trick
UNION ALL
SELECT submitter_id, rating_positive, rating_negative FROM video
UNION ALL
SELECT submitter_id, rating_positive, rating_negative FROM other
) T1
ON u.id = T1.submitter_id
GROUP BY u.id
ORDER BY tutorials_count DESC
LIMIT 10
The LEFT JOINs are fine, and will perform better than unioning all three tables before performing aggregation.
The issue is that SUMmation on a LEFT JOIN means the result could be NULL, which you can't add in conjunction with the sum from the other columns. IE:
... SUM(t.rating_positive) + 1
...will return NULL if SUM(t.rating_positive) there are no supporting records, because NULL + 1 equals NULL.
You need to use COALESCE to convert these to zero for the math to work - IFNULL is an acceptable alternative, but it's MySQL specific so not likely to be portable:
SELECT u.id AS user_id,
COALESCE(COUNT(t.id), 0) + COALESCE(COUNT(v.id), 0) + COALESCE(COUNT(o.id), 0) AS tutorials_count,
COALESCE(SUM(t.rating_positive), 0) + COALESCE(SUM(v.rating_positive), 0) + COALESCE(SUM(o.rating_positive), 0) AS pos,
COALESCE(SUM(t.rating_negative), 0) + COALESCE(SUM(v.rating_negative), 0) + COALESCE(SUM(o.rating_negative), 0) AS neg
FROM USER u
LEFT JOIN trick t ON u.id = t.submitter_id
LEFT JOIN video v ON u.id = v.submitter_id
LEFT JOIN other o ON u.id = o.submitter_id
GROUP BY u.id
ORDER BY tutorials_count DESC
LIMIT 10