Counting records with 2 criteria s - mysql

How do I count database records having 2 criteria.
Here is the pic:
I want to count posts which have condition 12 & 18 only. So after post 11 I should have the counter at value 1. Post 5, 6, 7, 9, 10 should not be counted since condition 12 & 18 don't show up.
To get the idea here is a query:
SELECT COUNT(post) from post_condition
where `condition`=18 AND `condition`=12
But this is wrong, I tried with some sub-queries but without success.

If you want to count the number that have both, then you can generate the matching posts using a subquery:
SELECT COUNT(*)
FROM (SELECT pc.post
FROM post_condition pc
WHERE pc.condition IN (12, 18)
GROUP BY pc.post
HAVING COUNT(pc.condition) = 2 -- assumes no duplicates
) t;
You can do this without a subquery, if you have a table of posts (which I suspect is true:
select count(*)
from posts p
where exists (select 1 from post_condition pc where pc.post = p.post and pc.condition = 12) and
exists (select 1 from post_condition pc where pc.post = p.post and pc.condition = 18);
With an index on post_condition(post, condition), this is likely to be the fastest method. This would be slower only if many, many posts had no rows at all in post_condition.

Try this:
SELECT COUNT(post)
FROM table
WHERE condition IN (12, 18)
GROUP BY post
HAVING COUNT(DISTINCT(condition)) = 2;

Related

Selecting heighest number in sql with the same id

I am trying to select max transaction_num from my table tbl_loan and group it by c_id to avoid duplicate of c_id.
here is my query
SELECT * FROM `tbl_loan` WHERE transaction_num IN (SELECT max(transaction_num) max_trans FROM tbl_loan GROUP BY c_id)
and my output is
still have duplicate c_id.
MySQL MAX with GROUP BY clause
To find the maximum value for every group, you use the MAX function with the GROUP BY clause in a SELECT statement.
You use the following query:
SELECT
*, MAX(transaction_num)
FROM
tbl_loan
GROUP BY c_id
ORDER BY MAX(transaction_num);
From the looks of it, and correct me if I'm wrong. The transaction number appears to be sequential per each C_ID whenever a new transaction happens. There is also the "I_ID" column which appears to be an auto-incrementing column which does not duplicate. It appears your transaction number is sequentially 1, 2, 3, etc per C_ID for simple counting purposes, so everyone starts with a 1, and those with more have 2nd and possibly 3rd and more...
So, if this is accurate and you want the most recent per C_ID, you really want the max "I_ID" per C_ID because multiple records will exist with a value of 2, 3, etc...
try this.
SELECT
TL.*
FROM
tbl_loan TL
JOIN ( SELECT C_ID, max(I_ID) maxI_ID
FROM tbl_loan
GROUP BY c_id) MaxPer
on TL.I_ID = MaxPer.MaxI_ID
So, from your data for C_ID = 55, you have I_ID = 61 (trans num = 1) and 62 (trans num = 2). So for ID = 55, you want the transaction I_ID = 62 which represents the second transaction.
For C_ID = 70, it has I_IDs of 77 & 78, of which will grab I_ID = 78.
The rest only have a single trans num and will get their only single entry id.
HTH
Think about it like this
Your query:
SELECT * FROM `tbl_loan` WHERE transaction_num IN (SELECT max(transaction_num) max_trans FROM tbl_loan GROUP BY c_id)
Lets say your subquery returns one transaction_num of 20. This 20 can be the same for multiple c_id's.
So your outer query is then running
SELECT * FROM `tbl_loan` WHERE transaction_num IN (20)
and returning all those results.

MySQL : Group By Clause Not Using Index when used with Case

Im using MySQL
I cant change the DB structure, so thats not an option sadly
THE ISSUE:
When i use GROUP BY with CASE (as need in my situation), MYSQL uses
file_sort and the delay is humongous (approx 2-3minutes):
http://sqlfiddle.com/#!9/f97d8/11/0
But when i dont use CASE just GROUP BY group_id , MYSQL easily uses
index and result is fast:
http://sqlfiddle.com/#!9/f97d8/12/0
Scenerio: DETAILED
Table msgs, containing records of sent messages, with fields:
id,
user_id, (the guy who sent the message)
type, (0=> means it's group msg. All the msgs sent under this are marked by group_id. So lets say group_id = 5 sent 5 msgs, the table will have 5 records with group_id =5 and type=0. For type>0, the group_id will be NULL, coz all other types have no group_id as they are individual msgs sent to single recipient)
group_id (if type=0, will contain group_id, else NULL)
Table contains approx 10 million records for user id 50001 and with different types (i.e group as well as individual msgs)
Now the QUERY:
SELECT
msgs.*
FROM
msgs
INNER JOIN accounts
ON (
msgs.user_id = accounts.id
)
WHERE 1
AND msgs.user_id IN (50111)
AND msgs.type IN (0, 1, 5, 7)
GROUP BY CASE `msgs`.`type` WHEN 0 THEN `msgs`.`group_id` ELSE `msgs`.`id` END
ORDER BY `msgs`.`group_id` DESC
LIMIT 100
I HAVE to get summary in a single QUERY,
so msgs sent to group lets say 5 (have 5 records in this table) will be shown as 1 record for summary (i may show COUNT later, but thats not an issue).
The individual msgs have NULL as group_id, so i cant just put 'GROUP BY group_id ' coz that will Group all individual msgs to single record which is not acceptable.
Sample output can be something like:
id owner_id, type group_id COUNT
1 50001 0 2 5
1 50001 1 NULL 1
1 50001 4 NULL 1
1 50001 0 7 5
1 50001 5 NULL 1
1 50001 5 NULL 1
1 50001 5 NULL 1
1 50001 0 10 5
Now the problem is that the GROUP condition after using CASE (which i currently think that i have to because i only need to group by group_id if type=0) is causing alot of delay coz it's not using indexes which it does if i dont use CASE (like just group by group_id ). Please view SQLFiddles above to see the explain results
Can anyone plz give an advice how to get it optimized
UPDATE
I tried a workaround , that does somehow works out (drops INITIAL queries to 1sec). Using union, what it does is, to minimize the resultset by union that forces SQL to write on disk for filesort (due to huge resultset), limit the resultset of group msgs, and individual msgs (view query below)
-- first part of union retrieves group msgs (that have type 0 and needs to be grouped by group_id). Applies the limit to captivate the out of control result set
-- The second query retrieves individual msgs, (those with type !=0, grouped by msgs.id - not necessary but just to be save from duplicate entries due to joins). Applies the limit to captivate the out of control result set
-- JOins the two to retrieve the desired resultset
Here's the query:
SELECT
*
FROM
(
(
SELECT
msgs.id as reference_id, user_id, type, group_id
FROM
msgs
INNER JOIN accounts
ON (msgs.user_id = accounts.id)
WHERE 1
AND accounts.id IN (50111 ) AND type = 0
GROUP BY msgs.group_id
ORDER BY msgs.id DESC
LIMIT 40
)
UNION
ALL
(
SELECT
msgs.id as reference_id, user_id, type, group_id
FROM
msgs
INNER JOIN accounts
ON (
msgs.user_id = accounts.id
)
WHERE 1
AND msgs.type != 0
AND accounts.id IN (50111)
GROUP BY msgs.id
ORDER BY msgs.id
LIMIT 40
)
) AS temp
ORDER BY reference_id
LIMIT 20,20
But has alot of caveats,
-I need to handle the limit in inner queries as well. Lets say 20recs per page, and im on page 4. For inner queries , i need to apply limit 0,80, since im not sure which of the two parts had how many records in the previous 3 pages. So, as the records per page and number of pages grow, my query grows heavier. Lets say 1k rec per page, and im on page 100 , or 1K, the load gets heavier and time exponentially increases
I need to handle ordering in inner queries and then apply on the resultset prepared by union , conditions need to be applied on both inner queries seperately(but not much of an issue)
-Cant use calc_found_rows, so will need to get count using queries seperately
The main issue is the first one. The higher i go with the pagination , the heavier it gets
Would this run faster?
SELECT id, user_id, type, group_id
FROM
( SELECT id, user_id, type, group_id, IFNULL(group_id, id) AS foo
FROM msgs
WHERE user_id IN (50111)
AND type IN (0, 1, 5, 7)
)
GROUP BY foo
ORDER BY `group_id` DESC
LIMIT 100
It needs INDEX(user_id, type).
Does this give the 'correct' answer?
SELECT DISTINCT *
FROM msgs
WHERE user_id IN (50111)
AND type IN (0, 1, 5, 7)
GROUP BY IFNULL(group_id, id)
ORDER BY `group_id` DESC
LIMIT 100
(It needs the same index)

include only the first and last groups in query results

Given the schema
The following query
SELECT a.user_id,
a.id,
a.date_created,
avg(ai.level) level
FROM assessment a
JOIN assessment_item ai ON a.id = ai.assessment_id
GROUP BY a.user_id, a.id;
Returns these results
user_id, a.id, a.date_created, level
1, 99, "2015-07-13 18:26:00", 4.0000
1, 98, "2015-07-13 19:04:58", 6.0000
13, 9, "2015-07-13 18:26:00", 2.0000
13, 11, "2015-07-13 19:04:58", 3.0000
I would like to change the query such that only the earliest results is returned for each user. In other words, the following should be returned instead
user_id, a.id, a.date_created, level
1, 99, "2015-07-13 18:26:00", 4.0000
13, 9, "2015-07-13 18:26:00", 2.0000
I think I need to add a HAVING clause, but I'm struggling to figure out the exact syntax.
I have done something like this, except for a small difference I wanted first 5 per group. The usage case was for reporting - means time for running query / creation of temp table was not a constraint.
The solution I had:
Create a new table with columns as id( a reference to original table) and id can be unique/primary
INSERT IGNORE INTO tbl1 (id) select min(id) from original_tbl where id not in (select id from tbl1) group by user_id
Repeat step 2 as many times you required( in my case it was 5 times). the new table table will have only the ids you want to show
Now run a join on tbl1 and original table will give you the required result
Note: This might not be the best solution, but this worked for me when I had to share the report in 2-3hours in a weekend. And the data size I had was around 1M records
Disclaimer: I am in a bit of a hurry, and have not tested this fully
-- Create a CTE that holds the first and last date for each user_id.
with first_and_last as (
-- Get the first date (min) for each user_id
select a.[user_id], min(a.date_created) as date_created
from assessment as a
group by a.[user_id]
-- Combine the first and last, so each user_id should have two entries, even if they are the same one.
union all
-- Get the last date (max) for each user_id
select a.[user_id], max(a.date_created)
from assessment as a
group by a.[user_id]
)
select a.[user_id],
a.id,
a.date_created,
avg(ai.[level]) as [level]
from assessment as a
inner join assessment_item as ai on a.id = ai.assessment_id
-- Join with the CTE to only keep records that have either the min or max date_created for each user_id.
inner join first_and_last as fnl on a.[user_id] = fnl.[user_id] and a.date_created = fnl.date_created
group by a.[user_id], a.id, a.date_created;

getting a row count using a subquery

im currently using a subquery of an outer query to get the number of people who ordered a book ina certain time. Im a part of the way there. My current subquery is:
select count(cust_id)
from a_bkorders.order_headers
where date_format(order_date,'%Y/%m') = Prevmonth(curdate(),1)
or date_format(order_date,'%Y/%m') = PrevMonth(curdate(),2)
group by cust_id;
which returns
count(cust_id)
1
1
1
1
1
1
1
1
2
2
2
4
14
I want a count of the rows returned which is 13 but i dont think surrounding the entire subquery in count() is going to do the trick. Im sure this is an easy fix but im not seeing it.
Thanks.
(PrevMonth is a function i have written)
I think you just want count(distinct) rather than count():
select count(distinct cust_id)
from a_bkorders.order_headers
where date_format(order_date,'%Y/%m') = Prevmonth(curdate(),1) or
date_format(order_date,'%Y/%m') = PrevMonth(curdate(),2);
you should be able to just do a count of the returned results. Not sure if this will cause problems with your outer query, but if you want to post that with some sample data I can build a fiddle and fix it up real quick.
SELECT
COUNT(first_count) -- first_count is the alias i gave the subquery count. so its a count of the number of rows returned in the subquery's count
FROM(
SELECT
COUNT(cust_id) AS first_count
FROM a_bkorders.order_headers
WHERE date_format(order_date,'%Y/%m') = Prevmonth(curdate(),1)
OR date_format(order_date,'%Y/%m') = PrevMonth(curdate(),2)
GROUP BY cust_id
) AS temp; -- all tables need to have an alias
you may not even want to do a count in your subquery... unless you need the number of books that those customers ordered as well. you could just SELECT COUNT(DISTINCT cust_id) FROM.... to get the number of unique id's.

mysql return valid nth multiples results

I know there's similar questions but none seem to apply as to what I want to do.
Given the following query that returns lets say 85 results.
SELECT * FROM tbl_name WHERE person = 'Tom';
And I have another query that's similar but returns 168 results.
SELECT * FROM tbl_name WHERE person = 'Bob';
I'm trying to get results in valid multiples of 50.
By just changing person value in the WHERE clause I want to have the expected output of Tom's 1st, 50th results. Which means 2 row results total.
Likewise Bob would have returned the 1st, 50th, 100th, 150th result. Which is 4 row results total.
Is it possible to do this with just MySQL?
Nailed it. Change the 50 for different increments. This assumes you meant that you wanted 1, 51, 101 (every 50th).
SELECT
returnTable.*
FROM (
SELECT
#rownum:=#rownum+1 AS rowNumber,
tbl_name.*
FROM forum_posts, (SELECT #rownum:=0) variableInit
WHERE tbl_name.person = 'Tom'
) AS returnTable
WHERE returnTable.rowNumber = 1
OR (returnTable.rowNumber - 1) MOD 50 = 0;
If you actually want 1, 50, 100, 150 then the following does that (removed -1 from the WHERE)
SELECT
returnTable.*
FROM (
SELECT
#rownum:=#rownum+1 AS rowNumber,
tbl_name.*
FROM forum_posts, (SELECT #rownum:=0) variableInit
WHERE tbl_name.person = 'Tom'
) AS returnTable
WHERE returnTable.rowNumber = 1
OR returnTable.rowNumber MOD 50 = 0;