get the total count but exclude certain condition - mysql

Hello I had this table:
id | user_id | status
1 | 34 | x
2 | 35 | x
3 | 42 | x
4 | 42 | y
My goal is to count the data with X status except if the user has a another data with Y status, it will exclude in the count. So instead of 3, it will only count 2 since the 3rd row has another data which is the 4th row with y status.
SELECT * FROM logs
AND user_id NOT IN (SELECT user_id FROM logs WHERE status = 'y')
GROUP BY user_id;

We can try the following aggregation approach:
SELECT COUNT(*) AS cnt
FROM
(
SELECT user_id
FROM logs
GROUP BY user_id
HAVING MIN(status) = MAX(status) AND
MIN(status) = 'x'
) t;
The above logic only counts a user having one or more records only having x status.

You can do it this way, I only modify a bit on your sql
SELECT COUNT(*) FROM (
SELECT u_id FROM tbl WHERE u_id NOT IN
(SELECT u_id FROM tbl WHERE status = 'y')
GROUP BY u_id
) as t

You can use inner join:
SELECT
count(t1.id) AS `cnt`
FROM
`test` AS t1,
`test` AS t2
WHERE
t2.`status`='y'
&& t1.`user_id` != t2.`user_id`;

Related

MySQL - Select results with specified ID or with null

I have one table:
| ID | ADV_ID | USER_ID |
| 1 | 22 | NULL |
| 2 | 22 | 3 |
| 5 | 44 | NULL |
and now, I want to select row where adv_id = 22 and user_id = 3. If that row doesn't exist, I want to get row where adv_id = 22 and user_id is null.
I tried in that way:
SELECT * FROM `table` WHERE adv_id = 22 AND (user_id = 3 OR user_id is null)
but this query return two rows - with user_id = NULL and with user_id = 3. I want to get one row - with user_id = 3 or (if not exist), with user_id = NULL.
How I can do it in one query?
Thanks.
Use conditional aggregation:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT
ADV_ID,
CASE WHEN COUNT(CASE WHEN USER_ID = 3 THEN 1 END) > 0 THEN 3 END USER_ID
FROM yourTable
) t2
ON t1.ADV_ID = t2.ADV_ID AND
((t1.USER_ID IS NULL AND t2.USER_ID IS NULL) OR (t1.USER_ID = t2.USER_ID))
WHERE
t1.ADV_ID = 22;
Demo
For an explanation, the subquery I have aliased as t2 aggregates over the ADV_ID, and outputs the value 3 if that value occurs in one or more records, otherwise it outputs NULL. Then, we join this subquery back to your original table on the condition that both USER_ID values are NULL, or, if not, that the two USER_ID values match.
You may modify the demo to see that it generates the output you want for other inputs.
SELECT *
FROM test
WHERE ADV_ID IS NOT NULL AND USER_ID IS NOT NULL
UNION ALL
SELECT *
FROM test
WHERE USER_ID IS NULL AND NOT EXISTS (
SELECT 1
FROM test
WHERE ADV_ID IS NOT NULL AND USER_ID IS NOT NULL
)
Select all rows with the first condition: ADV_ID IS NOT NULL AND USER_ID IS NOT NULL
and then UNION ALL with the same table if the first condition is NOT EXISTS.
So we only get results if the first condition is not returned any rows.
The MySQL UNION ALL operator is used to combine the result sets of 2 or more SELECT statements.
try like that:
SELECT * FROM `table` t1 WHERE (t1.adv_id = 44)
AND ((t1.user_id = 3) OR
(NOT EXISTS (select * from `table` t2 where t2.adv_id=t1.adv_id and t2.user_id = 3) AND t1.user_id is null ))
DEMO

MYSQL Updating row to maximum value of similar rows

I have a table like this in MYSQL:
ID | NAME | VALUE |
----------------------------
1 | Bob | 1 |
2 | Bob | 2 |
3 | Jack | 5 |
4 | Jack | 8 |
5 | Jack | 10 |
and I'm trying to update the VALUE column to the highest value of rows with same NAME. So the result should be:
ID | NAME | VALUE |
----------------------------
1 | Bob | 2 |
2 | Bob | 2 |
3 | Jack | 10 |
4 | Jack | 10 |
5 | Jack | 10 |
I managed to get the max value like this:
SELECT MAX(Value) max FROM `table` GROUP BY Name having count(*) >1 AND MAX(Value) != MIN(Value)
But can't figure out how to put it in my update
Update table set Value = (SELECT MAX(Value) max FROM `table` GROUP BY Name having count(*) >1 AND MAX(Value) != MIN(Value))
Doesn't work. I'd appreciate any help.
This is easier than other answers are making it.
UPDATE MyTable AS t1 INNER JOIN MyTable AS t2 USING (Name)
SET Value = GREATEST(t1.Value, t2.Value);
You don't have to find the largest value. You just have to join each row to the set of rows with the same name, and set the Value to the greater Value of the two joined rows. This is a no-op on some rows, but it will apply to every row in turn.
http://sqlfiddle.com/#!9/f79a3/1
UPDATE t1
INNER JOIN (SELECT name, MAX(`value`) max_value
FROM t1 GROUP BY name) t2
ON t1.name = t2.name
SET t1.value = t2.max_value;
Create a temporary table consisting of ID NAME and MAX VALUE as follows:
CREATE TEMP TABLE TABLE1 AS
(SELECT NAME,MAX(Value) value FROM `table` GROUP BY Name having count(*) >1
AND MAX(Value) != MIN(Value)
);
Use this temporary table to do your update as follows:
UPDATE
Table_A
SET
Table_A.value = Table_B.value
FROM
`table` AS Table_A
INNER JOIN TABLE1 AS Table_B
ON Table_A.NAME = Table_B.NAME
Also this code is somewhat of an approximation as i am not familiar with mysql but i am familiar with sql.
Let me know if this doesn't help.
Simple left join would do the trick.
Try this out and let me know in case of any queries.
select a.id,a.name,b.value
from
table a
left join
(select name,max(value) as value from table group by name) b
on a.name=b.name;
You may use this query. The table is joined with a subquery (table t2) that contains the results you want to update your table with:
UPDATE `table` t1,
(SELECT Name, MAX(Value) maxv, MIN(Value) minv
FROM `table`
GROUP BY Name
HAVING COUNT(*)>1 AND maxv != minv) t2
SET t1.Value = t2.maxv
WHERE t1.Name = t2.Name;
If you want to know how will the values be updated, you can first run an equivalent SELECT query:
SELECT t1.*, t2.maxv
FROM `table` t1,
(SELECT Name, MAX(Value) maxv, MIN(Value) minv
FROM `table`
GROUP BY Name
HAVING COUNT(*)>1 AND maxv != minv) t2
WHERE t1.Name = t2.Name;
This query will display all the fields of table, followed by the new value maxv. You can check the current value and the new value, and if it looks fine, you may run the UPDATE query.

SQL query to compare earlier values in the Table

We have a log table where user processes log entries (success/failure/timeout) each time they run. For e.g.
+----+----------+---------+
| id | username | status |
+----+----------+---------+
| 1 | saqib | success |
| 2 | mary | timeout |
| 3 | scott | timeout |
| 4 | saqib | success |
| 5 | mary | timeout |
| 6 | scott | timeout |
| 7 | saqib | timeout |
| 8 | mary | timeout |
| 9 | scott | timeout |
+----+----------+---------+
We would like to get a usernames which have had a success in the past the but the latest entry for them was a timeout. (saqib in the above example)
Is there single query that can do this? Right now we are doing this using a PHP script, but would like to use mySQL query for this.
Thanks
SQL Fiddle
SELECT DISTINCT m1.username
FROM
(
SELECT s1.username, s1.ids
FROM
(
SELECT username, MAX(id) as ids
FROM MyTable
GROUP BY username
) AS s1
INNER JOIN MyTable AS s2
ON s1.ids = s2.id
WHERE s2.status = 'timeout'
) AS m1
INNER JOIN MyTable m2 ON m1.username = m2.username
AND m2.status = 'success'
You can retrieve the latest id for each username and then JOIN it with the original table checking if there were entries for each user with status success and id less then maximum.
SELECT t.*
FROM ( SELECT username
, MAX(id) as ind
FROM tbl
GROUP BY username
) x JOIN tbl t ON t.username = x.username
AND t.id = x.ind
AND t.status IN ('timeout', 'failure')
AND EXISTS ( SELECT *
FROM tbl
WHERE username = x.username
AND id < x.ind
AND status = 'success'
)
Example
I would use exists for this problem. Exists are nice because they generally are faster than joining to the table. Unrelated, but I would recommend using a time stamp as opposed to relying on the id number.
Select username
From table t1
Where t1.status = 'timeout'
and exists (Select 1
From table t2
Where t1.username = t2.username
and t2.status = 'success'
Limit 1)
and not exists (Select 1
From table t3
Where t3.username = t1.username
and t3.id > t1.id
Limit 1);
SELECT
UserName, max(id)
FROM TABLE
WHERE
UserName IN (SELECT UserName from Table where Status = 'Success')
GROUP BY UserName
Having MAX(id) = (select max(id) from username where status = 'timeout')
You can join the table to itself:
SELECT GROUP_CONCAT(CONCAT_WS(' -> ', a.id, b.id) SEPARATOR ','), a.username
FROM t a
JOIN t b USING (username)
WHERE b.id > a.id
AND (a.status = 'success'
AND b.status = 'timeout')
GROUP BY a.username;
This shows all pairs of previous success to later timeout.
SQLFiddle
You can do so by joining 2 subqueries 1 for the maximum id per user with success status which will satisfy the condition which have had a success in the past and 2 for the max id to get users with latest timeout last comparison part t1.id < t2.id will satisfy the user should have success in past
select * from
(select `username`,max(id) id
from t
where `status` ='success'
group by `username`
) t1
join
(
select `username`,max(id) id
from t
where `status` ='timeout'
group by `username`
) t2
on(t1.username = t2.username)
where t1.id < t2.id
Demo
Another solution this will be much cleaner bu just using one query with max and case
select
username,
max(case when `status` ='success' then id else 0 end) success,
max(case when `status` ='timeout' then id else 0 end) timeout
from t
group by username
having timeout > success
and success > 0
Demo 2

MySql select next lower number without using limit

Is it possible to select the next lower number from a table without using limit.
Eg: If my table had 10, 3, 2 , 1 I'm trying to select * from table where col > 10.
The result I'm expecting is 3. I know I can use limit 1, but can it be done without that?
Try
SELECT MAX(no) no
FROM table1
WHERE no < 10
Output:
| NO |
------
| 3 |
SQLFiddle
Try this query
SELECT
*
FROM
(SELECT
#rid:=#rid+1 as rId,
a.*
FROM
tbl a
JOIN
(SELECT #rid:=0) b
ORDER BY
id DESC)tmp
WHERE rId=2;
SQL FIDDLE:
| RID | ID | TYPE | DETAILS |
------------------------------------
| 2 | 28 | Twitter | #sqlfiddle5 |
Another approach
select a.* from supportContacts a inner join
(select max(id) as id
from supportContacts
where
id in (select id from supportContacts where id not in
(select max(id) from supportContacts)))b
on a.id=b.id
SQL FIDDLE:
| ID | TYPE | DETAILS |
------------------------------
| 28 | Twitter | #sqlfiddle5 |
Alternatively, this query will always get the second highest number based on the inner where clause.
SELECT *
FROM
(
SELECT t.col,
(
SELECT COUNT(distinct t2.col)
FROM tableName t2
WHERE t2.col >= t.col
) as rank
FROM tablename t
WHERE col <= 10
) xx
WHERE rank = 2 -- <<== means second highest
SQLFiddle Demo
SQLFiddle Demo (supports duplicate values)
If you want to get next lower number from table
you can get it with this query:
SELECT distinct col FROM table1 a
WHERE 2 = (SELECT count(DISTINCT(b.col)) FROM table1 b WHERE a.col >= b.col);
later again if you want to get third lower number you can just pass 3 in place of 2 in where clause
again if you want to get second higher number, just change the condition of where clause in inner query with
a.col <= b.col

SQL get all rows sorted without duplicates

I have a table that looks something like this:
________________________
|id|value|date|approved|
-----------------------
What I need to be able to do is get each row where approved = 1. That part is obvious. For each occurrence of value, I only want the most recent row (sorted by date).
Meaning that with a table like this:
________________________
|id|value|date|approved|
-----------------------
|1 |Foo | 5 | 1 |
|2 |Bar | 6 | 1 |
|3 |Foo | 8 | 1 |
-----------------------
I only want the rows with id 2 and 3.
I assume I need to use DISTINCT somehow, but I'm not sure how. Could anyone help me out here?
SELECT m.*
FROM (
SELECT DISTINCT value
FROM mytable
) md
JOIN mytable m
ON m.id =
(
SELECT id
FROM mytable mi
WHERE mi.value = md.value
AND mi.approved = 1
ORDER BY
mi.value DESC, mi.date DESC, mi.id DESC
LIMIT 1
)
Create an index on (value, date, id) for this to work fast.
You need:
select id, value, date, approved where (value, date) in (
select value, max(date)
from your_table
group by value
);
Actually using GROUP BY will yeld better results. try something like this:
SELECT id, value, date, approved FROM table WHERE approved = 1 GROUP BY value ORDER BY date;
select id, value, date, approved
from mytable a
where approved = 1
and date =
(select max(b.date)
from mytable b
where b.approved = 1
and b.value = a.value)
select
id,
value,
date
from
( select
value, max( date ) as LastInstance
from
YourTable
where
approved = 1
group by
value ) PreQuery
join YourTable
on PreQuery.value = YourTable.value
and PreQuery.LastInstance = YourTable.LastInstance
and YourTable.approved = 1
order by
date
Try with this:
SELECT * DISTINCT FROM TABLA WHERE APPROVED = 1 ORDER BY DATE DESC
Hope this helps you.