I need to gather sums using conditional statements as well as DISTINCT values
with a multiple GROUP BY. The example below is a simplified version of a much much more complex query.
Because the real query is very large, I need to avoid having to drastically re-write the query.
DATA
Contracts
id advertiser_id status
1 1 1
2 2 1
3 3 2
4 1 1
A Query that's close
SELECT
COUNT( DISTINCT advertiser_id ) AS advertiser_qty,
COUNT( DISTINCT id ) AS contract_qty,
SUM( IF( status = 1, 1, 0 ) ) AS current_qty,
SUM( IF( status = 2, 1, 0 ) ) AS expired_qty,
SUM( IF( status = 3, 1, 0 ) ) AS other_qty
FROM (
SELECT * FROM `contracts`
GROUP BY advertiser_id, id
) AS temp
Currently Returns
advertiser_qty contract_qty current_qty expired_qty other_qty
3 4 3 1 0
Needs to Return
advertiser_qty contract_qty current_qty expired_qty other_qty
3 4 2 1 0
Where current_qty is 2 which is the sum of records with status = 1 for only DISTINCT advertiser_ids and each sum function will need the same fix.
I hope someone has a simple solution that can plug into the SUM functions.
-Thanks!!
try this
SELECT
COUNT( DISTINCT advertiser_id ) AS advertiser_qty,
COUNT( DISTINCT id ) AS contract_qty,
(select count(distinct advertiser_id) from contracts where status =1
) AS current_qty,
SUM( IF( status = 2, 1, 0 ) ) AS expired_qty,
SUM( IF( status = 3, 1, 0 ) ) AS other_qty
FROM (
SELECT * FROM `contracts`
GROUP BY advertiser_id, id
) AS temp
DEMO HERE
EDIT:
you may look for this without subselect.
SELECT COUNT(DISTINCT advertiser_id) AS advertiser_qty,
COUNT(DISTINCT id) AS contract_qty,
COUNT(DISTINCT advertiser_id , status = 1) AS current_qty,
SUM(IF(status = 2, 1, 0)) AS expired_qty,
SUM(IF(status = 3, 1, 0)) AS other_qty
FROM (SELECT *
FROM `contracts`
GROUP BY advertiser_id, id) AS temp
DEMO HERE
Related
I have table:
ID STATUS
----------
0 0
1 1
2 0
3 1
4 2
5 2
6 0
7 3
8 2
9 0
10 1 etc.
I want to get only first occurences of 1 and 2 when they change state (I mean 0 and 3 are not important to me) - so in this case I should get ids: 1, 4, 10. I tried group by but it only groups all values by 1 or 2, and not just the cases when states has changed.
Any idea please how to specify mysql query?
So, it's a slow day...
SELECT MIN(id)
FROM
( SELECT x.*
, CASE WHEN #prev=status THEN #i:=#i ELSE #i:=#i+1 END i
, #prev:=status prev
FROM
( SELECT *
FROM my_table
WHERE status IN (1,2)
) x
JOIN
( SELECT #prev:=1,#i:=1 ) vars
ORDER
BY id
) a
GROUP
BY i;
SELECT id
, status
, if( status = #switch
and if( #switch = 1, #switch:=2, #switch:=1 ) in(1,2)
, 1
, 0
) sw
FROM status
JOIN( SELECT #switch :=1 ) t
WHERE status in (1,2)
HAVING sw=1
ORDER BY id
I have searched a lot of different posts about this and i didn't find something that works for me.
This Using LIMIT within GROUP BY to get N results per group? and http://www.sqlines.com/mysql/how-to/get_top_n_each_group didn't work for me.
I have a simple MySQL Table with the following columns
id , package, user_id, date
I want to perform a query in which i will get
X Rows / user_id WHERE date > Number Order By date ASC
In short, we want to perform a Group By user_id with LIMIT of X rows / group but have in mind the statement using the date column
Example Full Table:
id , package, user_id, date
1, full, 1 , 1447003159
2, full, 1 , 1447003055
3, full, 1 , 1447002022
4, full, 1 , 1447001013
5, full, 1 , 1447000031
6, mid, 2 , 1447003159
7, mid, 2 , 1447003055
8, mid, 2 , 1447002022
9, mid, 2 , 1447001013
10, mid, 2 , 1447000031
From the above table we want to Select only 2 rows / user_id but where date >= 1447000031 (But make ORDER BY date ASC first)
Expected Output:
4, full, 1 , 1447001013
3, full, 1 , 1447002022
9, mid, 2 , 1447001013
8, mid, 2 , 1447002022
E.g.:
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.id >= x.id
AND y.user_id = x.user_id
WHERE y.date > 1447000031
GROUP
BY x.id
HAVING COUNT(*) <= 2;
or, faster, but more typing...
SELECT id
, package
, user_id
, date
FROM
( SELECT x.*
, CASE WHEN #prev_user = user_id THEN #i:=#i+1 ELSE #i:=1 END rank
, #prev_user := user_id
FROM my_table x
, ( SELECT #prev_user = 0,#i:=1 ) vars
WHERE date > 1447000031
ORDER
BY user_id
, date
) a
WHERE rank <= 2;
Here is sqlfiddle that i made with mysql query
http://sqlfiddle.com/#!2/f2794/4
It count 10 consecutive days when present = 0, but i need to add second condition to count where present is > 10.
For example
11
22
0
0
0
0
0
0
0
0
0
0
0
0
1
should count 14
here is that query
select sum(count) total from (
SELECT COUNT(present) as count FROM (
SELECT
IF((q.present != 0), #rownum:=#rownum+1, #rownum:=#rownum) AS rownumber, #prevDate:=q.date, q.*
FROM (
SELECT
name
, date
, present
FROM
teacher, (SELECT #rownum:=0, #prevDate:='') vars
WHERE date BETWEEN '2013-07-01' AND '2013-07-31'
ORDER BY date, present
) q
) sq
GROUP BY present, rownumber
HAVING COUNT(*) >= 10
) d
So if U can help me, pls do it :)
best regards
m.
I dont really understand your query overly well, but I think simply changing (q.Present != 0) to incorporate the additional test should solve your problem:
SELECT sum(count) total from (
SELECT COUNT(present) as count FROM (
SELECT
IF((q.present != 0 AND q.present <= 10), #rownum:=#rownum+1, #rownum:=#rownum) AS rownumber, #prevDate:=q.date, q.*
FROM (
SELECT
name
, date
, present
FROM
teacher, (SELECT #rownum:=0, #prevDate:='') vars
WHERE date BETWEEN '2013-07-01' AND '2013-07-31'
ORDER BY date, present
) q
) sq
GROUP BY present, rownumber
HAVING COUNT(*) >= 10
) d
I have 3 tables in a query:
apiusers, users
these contain a user id, and a name.
download_stats:
this contains usertype (1 = users, 2 = apiusers), date, file downloaded_id.
What I want to do is get a layout of:
| user id | name | download stats for january | download stats for february
etc.
I'm currently trying with this query, which takes forever:
SELECT name,queryuserid,count(ROWA.id) ,count(ROWB.id)
FROM (SELECT IFNULL(u.name, au.companyname) AS name,IFNULL(u.id, au.id) AS queryuserid
FROM apiusers as au, users as u
GROUP BY 2 ) AS users
LEFT JOIN stats_download as ROWA ON ROWA.userid=queryuserid AND ROWA.date > date('2011-01-01') AND ROWA.date < date('2011-02-01')
LEFT JOIN stats_download as ROWB ON ROWB.userid=queryuserid AND ROWB.date > date('2011-01-01') AND ROWB.date < date('2011-02-01')
GROUP BY 2;
Is there a better way of going about this? The client wants supports to "group" the output statistics by year, month and day. So there could potentionally be 30+ LEFT JOIN's in there.
This should get you the basis, and runs an entire year sample. Look at the pattern. By doing a sum of a qualified IF() condition returning 1 or 0 you get the total count per each month. Now, if you are looking for SIZE (such as download size), instead of 1, 0, you can just substitute the fileSize instead of 1 and you'll have the total downloaded size... put them as different columns and you can have both Count of downloads and totalSize of downloads. Expand the date range over year, just keep going with the pattern...
SELECT
AllUsers.Name,
AllUsers.QueryUserID,
SUM( if( SD.Date BETWEEN '2011-01-01' and '2011-01-31', 1, 0 )) as CountJan2011,
SUM( if( SD.Date BETWEEN '2011-02-01' and '2011-02-28', 1, 0 )) as CountFeb2011,
SUM( if( SD.Date BETWEEN '2011-03-01' and '2011-03-31', 1, 0 )) as CountMar2011,
SUM( if( SD.Date BETWEEN '2011-04-01' and '2011-04-30', 1, 0 )) as CountApr2011,
SUM( if( SD.Date BETWEEN '2011-05-01' and '2011-05-31', 1, 0 )) as CountMay2011,
SUM( if( SD.Date BETWEEN '2011-06-01' and '2011-06-30', 1, 0 )) as CountJun2011,
SUM( if( SD.Date BETWEEN '2011-07-01' and '2011-07-31', 1, 0 )) as CountJul2011,
SUM( if( SD.Date BETWEEN '2011-08-01' and '2011-08-31', 1, 0 )) as CountAug2011,
SUM( if( SD.Date BETWEEN '2011-09-01' and '2011-09-30', 1, 0 )) as CountSep2011,
SUM( if( SD.Date BETWEEN '2011-10-01' and '2011-10-31', 1, 0 )) as CountOct2011,
SUM( if( SD.Date BETWEEN '2011-11-01' and '2011-11-30', 1, 0 )) as CountNov2011,
SUM( if( SD.Date BETWEEN '2011-12-01' and '2011-12-31', 1, 0 )) as CountDec2011
FROM
( select distinct
APIUsers.CompanyName as Name,
APIUsers.QueryUserID
from APIUsers
UNION
select
Users.Name,
Users.QueryUserID
from Users ) AllUsers
LEFT JOIN stats_download SD
ON AllUsers.QueryUserID = SD.UserID
AND SD.Date BETWEEN '2011-01-01' and '2011-12-31'
GROUP BY
2;
There is a better way.
You need to do just one join with the stats_download and aggregate on months, I assume the method GETMONTH extracts the month from a date (every database has its own method, you didn't specify what you are using)
(
SELECT name,queryuserid,count(stats_download.id) as cnt,GETMONTH(stats_download.date) as month
FROM (SELECT IFNULL(u.name, au.companyname) AS name,IFNULL(u.id, au.id) AS queryuserid
FROM apiusers as au, users as u
GROUP BY 2 ) AS users
LEFT JOIN stats_download ON stats_download.userid=queryuserid
GROUP BY name, queryuserid, GETMONTH(stats_download.date)
) as TableA
so now we have a table that create columns according to the different groups, so we inner join the table with itself (should be much faster since this table is smaller)
Select name, queryuserid, A.cnt as January, B.cnt as February from TableA left join TableA as A on (TableA.queryuserid = A.queryuserid and A.month=1) left join TableA as B on (TableA.queryuserid = B.queryuserid and B.month=2)
I haven't run it so I may have some typos but this is the direction.
Hope it helps...
Is it possible for me to change this query so I can do like COUNT(WHERE type_id = 2) AS success, COUNT(WHERE type_id = 1) AS error to prevent using subqueries?
SELECT
(SELECT COUNT(id) FROM log AS success_log WHERE type_id = 2 AND site_id = site.id AND DAYOFYEAR(success_log.created)) AS success,
(SELECT COUNT(id) FROM log AS success_log WHERE type_id = 1 AND site_id = site.id AND DAYOFYEAR(success_log.created)) AS error,
(SELECT COUNT(id) FROM log AS success_log WHERE site_id = site.id AND DAYOFYEAR(success_log.created)) AS total,
DATE_FORMAT(log.created, "%m-%d-%y") AS `day`
FROM log
WHERE site_id = ?
GROUP BY DAYOFYEAR(created)`
You can use single count with GroupBy TypeId and insert them in the temp table and then select the way you want it.
since they were all querying from the log table for the same site, you can just run through the table ONCE and just apply and IMMEDIATE IF() test inside a sum.... If the record is of a given type, count as 1, otherwise 0... Gets a sum of each of type 1 or type 2 and simple count(*) gets the overall count for the day in question.
SELECT
DATE_FORMAT(log.created, "%m-%d-%y") AS `day`,
sum( if( type_id = 2, 1, 0 )) as NumberSuccess,
sum( if( type_id = 1, 1, 0 )) as NumberError,
count(*) as TotalRecords
from
log
where
site_id = ?
group by
DAYOFYEAR(created)