SQL MULTIPLE COUNT AND JOIN - mysql

I'm trying to do something like this:
User_guid | Post Message | Discussion board | Total |
1 | 300 | 25 | 325 |
2 | 15 | 185 | 200 |
3 | 100 | 203 | 303 |
but I don't know how.
I have the Post Message result with this Query:
SELECT COUNT( * ) as 'Quantidade' , users_entity.name , users_entity.username
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'create'
AND river.view = 'river/object/thewire/create'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC
and I have the Discussion result with this Query:
SELECT COUNT( * ) as 'Quantidade' , users_entity.name , users_entity.username
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'reply'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC
But I don't know how to join these results as rows of the users_entity table. The total interactions should be the sum of the post message and discussion board values. Someone can help me? Thanks.

It's hard to tell without table structures and sample data (which is why you're getting the down votes), but you probably need something like this:
SELECT
users_entity.name,
users_entity.username,
COUNT(CASE WHEN river.action_type = 'create' AND river.view = 'river/object/thewire/create' THEN 1 END) AS Post_Message,
COUNT(CASE WHEN river.action_type = 'reply' THEN 1 END) AS Discussion_Board
FROM river
JOIN users_entity ON users_entity.guid = river.subject_guid
GROUP BY
users_entity.name,
users_entity.username
ORDER BY ???
I'm not sure which value you want to ORDER BY with the combined query.

Maybe this works for you:
SELECT data1.username, data1.count1, data2.count2, data1.count1+data2.count2 from
(
SELECT COUNT( * ) as 'count1' , users_entity.name , users_entity.username
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'create'
AND river.view = 'river/object/thewire/create'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC
) data1,
(
SELECT COUNT( * ) as 'count2' , users_entity.name , users_entity.username
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'reply'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC) data2 where data1.username = data2.username

You could try joining the two queries like this
SELECT river_create.Quantidade AS create_q, river_reply.Quantidade AS reply_q, river_create.full_name, river_create.u_name, river_create.user_id
FROM
(SELECT COUNT( * ) as 'Quantidade' , users_entity.name AS full_name, users_entity.username AS u_name, users_entity.guid AS user_id
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'create'
AND river.view = 'river/object/thewire/create'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC) AS river_create
INNER JOIN
(SELECT COUNT( * ) as 'Quantidade' , users_entity.name , users_entity.username, users_entity.guid as reply_user_id
FROM river
JOIN users_entity
ON river.subject_guid = users_entity.guid
AND river.action_type = 'reply'
GROUP BY river.subject_guid ORDER BY 'Quantidade' DESC) AS river_reply
ON river_create.user_id = river_reply.reply_user_id

Related

Add a variable in syntax

In this example which runs here live
I would like to add the user id in the retrieved data
I use this
;with postvotes as
(
select v.creationdate,
v.userid
, p.id postid
, case v.votetypeid
when 1 then 'accepts'
when 2 then 'up-votes'
when 3 then 'down-votes'
when 9 then 'bounty_recieved'
end vote_type
, case p.posttypeid
when 1 then 'question'
when 2 then 'answer'
end post_type
, sum(case v.votetypeid
when 1 then 15
when 2 then 10
when 3 then -2
when 9 then bountyamount
end
) reputation_change
from votes v
inner join posts p on p.id = v.postid
where p.owneruserid = ##userid?1719510##
and posttypeid in (1,2)
and votetypeid in (1,2,3,9)
group by v.creationdate, p.id, votetypeid, posttypeid
),
bounties as
(
select v.creationdate
, v.postid postid
, 'bounty-offered' vote_type
, 'question' post_type
, sum(bountyamount) reputation_change
from votes v
where v.userid = ##userid?1719510##
and votetypeid = 8
group by v.creationdate, v.postid, votetypeid
),
approvededits as
(
select se.creationdate
, se.postid postid
, 'approved-edit' vote_type
, case p.posttypeid
when 1 then 'question'
when 2 then 'answer'
end post_type
, sum(2) reputation_change
from suggestededits se
inner join posts p on p.id = se.postid
where se.Owneruserid = ##userid?1719510##
and approvaldate is not null
group by se.creationdate, se.postid, posttypeid
)
select *
from postvotes
union
select *
from bounties
union
select *
from approvededits
But I receive this error:
Column 'votes.UserId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
How can I fix it?
You forgot to add userid in your group by list. So your updated query might look like -
;with postvotes as
(
select v.creationdate,
, p.id postid
, case v.votetypeid
when 1 then 'accepts'
when 2 then 'up-votes'
when 3 then 'down-votes'
when 9 then 'bounty_recieved'
end vote_type
, case p.posttypeid
when 1 then 'question'
when 2 then 'answer'
end post_type
, sum(case v.votetypeid
when 1 then 15
when 2 then 10
when 3 then -2
when 9 then bountyamount
end
) reputation_change
from votes v
inner join posts p on p.id = v.postid
where p.owneruserid = ##userid?1719510##
and posttypeid in (1,2)
and votetypeid in (1,2,3,9)
group by v.creationdate, p.id, votetypeid, posttypeid
),
bounties as
(
select v.creationdate
, v.postid postid
, 'bounty-offered' vote_type
, 'question' post_type
, sum(bountyamount) reputation_change
from votes v
where v.userid = ##userid?1719510##
and votetypeid = 8
group by v.creationdate, v.postid, votetypeid
),
approvededits as
(
select se.creationdate
, se.postid postid
, 'approved-edit' vote_type
, case p.posttypeid
when 1 then 'question'
when 2 then 'answer'
end post_type
, sum(2) reputation_change
from suggestededits se
inner join posts p on p.id = se.postid
where se.Owneruserid = ##userid?1719510##
and approvaldate is not null
group by se.creationdate, se.postid, posttypeid
)
select *
from postvotes
union
select *
from bounties
union
select *
from approvededits

Count number of ratings

I want to calculate number of every rating group by given date range. I wrote the following query which is working perfect:
SELECT c.day,
(SELECT COUNT(DISTINCT user_id) FROM ratings r WHERE DATE(r.created_at) = c.day AND r.rating = 1 AND r.campaign_id = 2) AS rating1s,
(SELECT COUNT(DISTINCT user_id) FROM ratings r WHERE DATE(r.created_at) = c.day AND r.rating = 2 AND r.campaign_id = 2) AS rating2s,
(SELECT COUNT(DISTINCT user_id) FROM ratings r WHERE DATE(r.created_at) = c.day AND r.rating = 3 AND r.campaign_id = 2) AS rating3s,
(SELECT COUNT(DISTINCT user_id) FROM ratings r WHERE DATE(r.created_at) = c.day AND r.rating = 4 AND r.campaign_id = 2) AS rating4s,
(SELECT COUNT(DISTINCT user_id) FROM ratings r WHERE DATE(r.created_at) = c.day AND r.rating = 5 AND r.campaign_id = 2) AS rating5s
FROM calendar c
WHERE c.day >= '2018-08-01'
GROUP BY c.day
ORDER BY c.day
LIMIT 0, 31
But this is not an optimized way due to 5 sub queries and query is taking almost 2mins on my localhost, how can I optimize this query? The sample output is attached and I need same output.
You can rephrase this as conditional aggregation:
SELECT DATE(r.created_at),
COUNT(DISTINCT CASE WHEN r.rating = 1 THEN r.user_id END) as raging_1,
COUNT(DISTINCT CASE WHEN r.rating = 2 THEN r.user_id END) as raging_2,
COUNT(DISTINCT CASE WHEN r.rating = 3 THEN r.user_id END) as raging_3,
COUNT(DISTINCT CASE WHEN r.rating = 4 THEN r.user_id END) as raging_4,
COUNT(DISTINCT CASE WHEN r.rating = 5 THEN r.user_id END) as raging_5
FROM ratings r
WHERE r.campaign_id = 2 AND
r.created_at >= '2018-08-01'
GROUP BY DATE(r.created_at);
COUNT(DISTINCT) can be expensive. Remove it if you can.
Otherwise, it might be faster to do the DISTINCT once:
SELECT dte,
SUM( r.rating = 1 ) as raging_1,
SUM( r.rating = 2 ) as raging_2,
SUM( r.rating = 3 ) as raging_3,
SUM( r.rating = 4 ) as raging_4,
SUM( r.rating = 5 ) as raging_5
FROM (SELECT DISTINCT user_id, rating, DATE(r.created_at) as dte
FROM ratings r
WHERE r.campaign_id = 2 AND
r.created_at >= '2018-08-01'
) urd
GROUP BY dte;
This returns rows for each day that has at least one rating. If some days would have all zeroes, then you'll need an outer join of some sort. That adds almost nothing to the performance, so it can be tacked on if one of the above solutions works.
Here is a query I made using #Gordon's answer:
SELECT DATE(r.created_at),
COUNT(
DISTINCT
CASE
WHEN r.rating = 1
THEN user_id
ELSE 0
END
) as rating1s,
COUNT(
DISTINCT
CASE
WHEN r.rating = 2
THEN user_id
ELSE 0
END
) as rating2s,
COUNT(
DISTINCT
CASE
WHEN r.rating = 3
THEN user_id
ELSE 0
END
) as rating3s,
COUNT(
DISTINCT
CASE
WHEN r.rating = 4
THEN user_id
ELSE 0
END
) as rating4s,
COUNT(
DISTINCT
CASE
WHEN r.rating = 5
THEN user_id
ELSE 0
END
) as rating5s
FROM ratings r
WHERE r.campaign_id = 2 AND
DATE(r.created_at) >= '2018-08-01'
GROUP BY DATE(r.created_at)
This is still not optimized but much better than my initial solution.

MySQL query slow in Where MONTH(datetime)

I am trying to add index in datetime, but the result still same.
SELECT s.id, s.player,
COUNT(case when dg.winner = 1 AND dp.colour <= 5 then 1 when dg.winner = 2 AND dp.colour > 5 then 1 else null end) as totalwin,
COUNT(case when dg.winner = 2 AND dp.colour <= 5 then 1 when dg.winner = 1 AND dp.colour > 5 then 1 else null end) as totallose,
COUNT(dg.winner) as totalgames
FROM dotaplayers AS dp
LEFT JOIN gameplayers AS gp ON gp.gameid = dp.gameid and dp.colour = gp.colour
LEFT JOIN stats AS s ON s.player_lower = gp.name
LEFT JOIN dotagames AS dg ON dg.gameid = dp.gameid
LEFT JOIN games AS g ON g.id = dp.gameid
LEFT JOIN bans as b ON b.name=gp.name
WHERE MONTH(g.datetime) = 4
GROUP by gp.name
ORDER BY totalwin DESC LIMIT 0,10
Showing rows 0 - 9 (10 total, Query took 7.7552 seconds.)
I want order the most winner in 4th month (April). Then it shows id, username, totalwins, totallose, totaldraw, totalgames. The case in my query is the how to get that. The result is correct, but slow.
Assuming g.datetime is indexed, try this instead:
WHERE g.`datetime` BETWEEN 20150401 AND 20150430`
Using the MONTH function, or any other function, on the field data in the WHERE eliminates the benefits of any indexes you might have on those fields; this results in the query requiring a full scan of the values in the table.
Rearranging the order of JOINs will probably help as well:
SELECT s.id, s.player
, SUM(case
when dg.winner = 1 AND dp.colour <= 5 then 1
when dg.winner = 2 AND dp.colour > 5 then 1
else 0
end
) as totalwin
, SUM(case
when dg.winner = 2 AND dp.colour <= 5 then 1
when dg.winner = 1 AND dp.colour > 5 then 1
else 0
end
) as totallose
, COUNT(dg.winner) as totalgames -- Not, sure of the nature of dg.`winner`, a SUM might be more appropriate here as well.
FROM games AS g
INNER JOIN dotaplayers AS dp ON g.id = dp.gameid
LEFT JOIN gameplayers AS gp ON gp.gameid = dp.gameid and dp.colour = gp.colour
LEFT JOIN stats AS s ON s.player_lower = gp.name
LEFT JOIN dotagames AS dg ON dg.gameid = dp.gameid
LEFT JOIN bans as b ON b.name=gp.name
WHERE g.`datetime` BETWEEN 20150401000000 AND 20150430235959
GROUP by gp.name
ORDER BY totalwin DESC
LIMIT 0,10
;
Another thing to note: Depending on the relationship between tables, some of the intermediate joins may result in effectively multiplying the resulting totals; this can be resolved by doing the sums in subqueries and joining those instead.

how to calculate from each row value in sql

I have a table named general_ledger from which I need to show dr_amount, cr_amount and the balance between them as running_balance. That's why I have written a query that is given below. But I am getting the result of each query like the balance only of current row. But I need to produce the result with the remaining balance. Suppose First row dr_balance is 20000 and cr_balance is 5000 and remaining balance is 15000. In second row only cr_balance is 5000. Now the result should be 10000 with the deduction but my result is -5000. I have no idea how to fix this. Can anyone please help me on this? I need your help very much. Here is my query given below :
SELECT '' AS cost_center_id
, '' AS cost_center_name
, '' AS office_code
, CONVERT('2013-02-01',DATETIME) AS transaction_date
, '' AS accounts_head_id
, '' AS account_name
, '' AS opposite_accounts_head_id
, '' AS opposite_account_name
, 'Opening Balance' AS particulars
, tempOpeningBalance.dr_amount
, tempOpeningBalance.cr_amount
, '' AS voucher_no
, '' AS vin
FROM (SELECT IFNULL(mcoa.account_code,'1101010101100321') AS account_code
, IFNULL(mcoa.account_name,'Cash') AS account_name
, IFNULL(mcoa.account_type,'ASSET') AS accountType
, CAST(IFNULL(SUM(IFNULL(maingl.dr_balance,0)),0) AS DECIMAL(27,5)) AS dr_amount
, CAST(IFNULL(SUM(IFNULL(maingl.cr_balance,0)),0) AS DECIMAL(27,5)) AS cr_amount
FROM master_chart_of_accounts AS mcoa
INNER JOIN chart_of_accounts AS coa ON (mcoa.id = coa.master_chart_of_accounts_id AND mcoa.id = 80)
LEFT JOIN general_ledger AS maingl ON (coa.id = maingl.accounts_head_id AND coa.account_code='1101010101100321')
INNER JOIN
( SELECT gl.accounts_head_id, MAX(gl.gl_id) AS max_gl_id, gl.office_code, gl.office_type, gl.country_id,gl.cost_center_id
FROM general_ledger AS gl
-- INNER JOIN voucher_info AS vi ON (gl.voucher_info_id = vi.id)
-- WHERE vi.posting_date < '2013-02-01' AND
WHERE gl.transaction_date < '2013-02-01' AND
gl.cost_center_id IN ('BI0000000000000000000001') AND
gl.country_id IN (1) AND
gl.office_code IN ('UG500013') AND
1=1
GROUP BY gl.accounts_head_id, gl.office_code, gl.office_type, gl.country_id,gl.cost_center_id
ORDER BY gl.accounts_head_id
) AS tmpgl
ON ( maingl.office_code = tmpgl.office_code
AND maingl.office_type = tmpgl.office_type
AND maingl.accounts_head_id = tmpgl.accounts_head_id
AND maingl.country_id = tmpgl.country_id
AND maingl.cost_center_id = tmpgl.cost_center_id
AND maingl.gl_id = tmpgl.max_gl_id
)
WHERE mcoa.account_status_id = 1 AND
coa.account_status_id = 1
) AS tempOpeningBalance
UNION
SELECT vi.cost_center_id
, cc.center_name AS cost_center_name
, gl.office_code
, vi.posting_date AS transaction_date
, vd.accounts_head_id
, (SELECT chart_of_accounts.account_name FROM chart_of_accounts WHERE chart_of_accounts.id = vd.accounts_head_id) AS account_name
, vd.opposite_accounts_head_id
, (SELECT chart_of_accounts.account_name FROM chart_of_accounts WHERE chart_of_accounts.id = vd.opposite_accounts_head_id) AS opposite_account_name
, vd.particulars
, gl.dr_amount AS dr_amount -- here to check
, gl.cr_amount AS cr_amount
, vi.voucher_no
, vi.vin
FROM general_ledger AS gl
INNER JOIN voucher_info AS vi
ON (gl.voucher_info_id = vi.id)
INNER JOIN cost_center AS cc
ON (vi.cost_center_id = cc.id)
INNER JOIN voucher_details AS vd
ON (vi.id = vd.voucher_info_id)
INNER JOIN chart_of_accounts AS coa
ON (vd.accounts_head_id = coa.id)
WHERE vi.posting_date BETWEEN '2013-02-01' AND'2013-02-28'
AND vi.voucher_status_id = 3
AND vd.status_id = 1
AND vi.office_code = 'UG500063'
AND coa.account_code='1101010101100321'
AND coa.cost_center_id = 'BI0000000000000000000001'
ORDER BY cost_center_name
, office_code
, transaction_date;
Use a variable like this
SET #running_balance=0;
SELECT dr_amount AS dr_amount
, cr_amount AS cr_amount
, #running_balance := (#running_balance + dr_amount - cr_amount)
FROM general_ledger

mySQL - Limit the number of rows returned in one side of JOIN statement?

Everyone,
I am just curious if there is a way to do this sort of limiting with a query on a mySQL database:
Here are my tables:
Events
event_id event_title creation_time
Images
image_id src event_id
Comments
event_comment_id event_comment event_id
I would like to fetch events sorted by creation time, and get only 3 images and 3 comments for each event.
Any help, resources, or criticism is welcome. Thank you
Here's one approach. Basically, get the rownumber associated with each group of comments/images and only display up to 3:
SELECT E.*,
MAX(CASE WHEN I.rn = 1 THEN I.Image_Id END) Image1,
MAX(CASE WHEN I.rn = 2 THEN I.Image_Id END) Image2,
MAX(CASE WHEN I.rn = 3 THEN I.Image_Id END) Image3,
MAX(CASE WHEN C.rn = 1 THEN C.event_comment_id END) Comment1,
MAX(CASE WHEN C.rn = 2 THEN C.event_comment_id END) Comment2,
MAX(CASE WHEN C.rn = 3 THEN C.event_comment_id END) Comment3
FROM Events E
LEFT JOIN (SELECT #curRow:=IF(#prevRow = event_id, #curRow + 1, 1) rn,
Image_Id, src, event_id, #prevRow:= event_id
FROM Images
JOIN (SELECT #curRow := 0) r
) I ON E.event_id = I.Event_id
LEFT JOIN (SELECT #curRow2:=IF(#prevRow2 = event_id, #curRow2 + 1, 1) rn,
event_comment_id, event_comment, event_id, #prevRow2:= event_id
FROM Comments
JOIN (SELECT #curRow2 := 0) r
) C ON E.event_id = C.Event_id
GROUP BY E.Event_Id
ORDER BY E.Event_Id, E.creation_time DESC
And here is the SQL Fiddle.