I have a MySQL table containing login logs. Each entry contains user email, IP address, timestamp and the login result (0 fail, 1 success).
+------------+------------------+------------+--------+
| ip | email | datetime | result |
+------------+------------------+------------+--------+
| 2130706433 | user1#domain.com | 1426498362 | 0 |
| 2130706433 | user1#domain.com | 1426498363 | 1 |
| 2130706433 | user1#domain.com | 1426498364 | 0 |
| 1134706444 | user2#domain.com | 1426498365 | 0 |
+------------+------------------+------------+--------+
My goal is to create a unique query to extract the count of failed logins from a given timestamp, and the timestamp of the last login for user1#domain.com. In this case I would like to obtain (suppose for simplicity that all entries are after the required timestamp)
+--------+------------+
| count | datetime |
+--------+------------+
| 3 | 1426498364 |
+--------+------------+
Until now, I've created two separated queries to extract results separately
SELECT COUNT(result) as count FROM (SELECT result FROM accesslogs WHERE datetime>1426498360 AND result=0) as subt
SELECT MAX(datetime) as datetime FROM accesslogs WHERE email=`user1#domain.com`
Now I'm tring to combine them to get results with a single query. I was wondering about using JOIN statement, but I don't know a column where queries may join. What can I do?
You can use conditional aggregation for this, something as
select
sum(
case
when result=0 and datetime>1426498360
then 1 else 0 end
) as `count`,
max(
case
when email = 'user1#domain.com' then datetime end
) as datetime
from accesslogs ;
Here is an example
mysql> select * from test ;
+------------+------------------+------------+--------+
| ip | email | datetime | result |
+------------+------------------+------------+--------+
| 2130706433 | user1#domain.com | 1426498362 | 0 |
| 2130706433 | user1#domain.com | 1426498363 | 1 |
| 2130706433 | user1#domain.com | 1426498364 | 0 |
| 1134706444 | user2#domain.com | 1426498365 | 0 |
+------------+------------------+------------+--------+
4 rows in set (0.00 sec)
mysql> select
-> sum(
-> case
-> when result=0 and datetime>1426498360
-> then 1 else 0 end
-> ) as `count`,
-> max(
-> case
-> when email = 'user1#domain.com' then datetime end
-> ) as datetime
-> from test ;
+-------+------------+
| count | datetime |
+-------+------------+
| 3 | 1426498364 |
+-------+------------+
1 row in set (0.00 sec)
Try
select * from (selete statement a) a join (selete statement b) b
You could use UNION ALL but both queries must have same columns.
I would suggest add text column to each query descibing type of value in other column like:
SELECT COUNT(result) as count, 'count' AS TYPE FROM (SELECT result FROM accesslogs WHERE datetime>1426498360 AND result=0) as subt
UNION ALL
SELECT MAX(datetime) as datetime, 'max' AS TYPE FROM accesslogs WHERE email='user1#domain.com'
EDIT: Sorry but I realised that this is more what you are trying to do (this is t-sql code but should work)
SELECT a.email, a.count, b.datetime FROM (
SELECT COUNT(result) as count, email FROM (SELECT result, email FROM
accesslogs AND result=0) as subt
GROUP BY email) a
JOIN (
SELECT MAX(datetime) as datetime , email FROM accesslogs GROUP BY email) b
on a.email = b.email
WHERE a.email='user1#domain.com'
AND a.datetime >'2012-12-17'
Take a look at MySQLs GROUP BY and HAVING. See if this works for you:
Any email:
SELECT email, COUNT(resultSet) as loginAttempts,
COUNT(resultSet) - SUM(resultSet) as failedAttempts
FROM accesslogs
GROUP BY email;
Specific email:
SELECT COUNT(result) as count, MAX(logDate) FROM accesslogs
GROUP BY email
HAVING email='user1#example.com';
Related
I have a MySql table of users order and it has columns such as:
user_id | timestamp | is_order_Parent | Status |
1 | 10-02-2020 | N | C |
2 | 11-02-2010 | Y | D |
3 | 11-02-2020 | N | C |
1 | 12-02-2010 | N | C |
1 | 15-02-2020 | N | C |
2 | 15-02-2010 | N | C |
I want to count number of new custmer per day defined as: a customer who orders non-parent order and his order status is C AND WHEN COUNTING A USER ONCE IN A DAY WE DONT COUNT HIM FOR OTHER DAYS
An ideal resulted table will be:
Timestamp: Day | Distinct values of User ID
10-02-2020 | 1
11-02-2010 | 1
12-02-2010 | 0 <--- already counted user_id = 1 above, so no need to count it here
15-02-2010 | 1
table name is cscart_orders
If you are running MySQL 8.0, you can do this with window functions an aggregation:
select timestamp, sum(timestamp = timestamp0) new_users
from (
select
t.*,
min(case when is_order_parent = 'N' and status = 'C' then timestamp end) over(partition by user_id) timestamp0
from mytable t
) t
group by timestamp
The window min() computes the timestamp when each user became a "new user". Then, the outer query aggregates by date, and counts how many new users were found on that date.
A nice thing about this approach is that it does not require enumerating the dates separately.
You can use two levels of aggregation:
select first_timestamp, count(*)
from (select t.user_id, min(timestamp) as first_timestamp
from t
where is_order_parent = 'N' and status = 'C'
group by t.user_id
) t
group by first_timestamp;
I want to list companyIds and with the mostly occur commentable type (0,1,2).
This is subquery
select a.companyId, a.commentable, count(1) _count
from article a
group by a.companyId, a.commentable
| companyId | commentable | _count |
|-----------|-------------|--------|
| 1 | 0 | 1 |
| 1 | 1 | 1 |
| 2 | 0 | 7759 |
| 2 | 1 | 7586 |
| 2 | 2 | 7856 |
| 3 | 0 | 7828 |
| 3 | 1 | 7866 |
| 3 | 2 | 7706 |
| 4 | 0 | 7851 |
| 4 | 1 | 7901 |
| 4 | 2 | 7738 |
| 5 | 0 | 7775 |
| 5 | 1 | 7884 |
| 5 | 2 | 7602 |
| 25 | 0 | 7888 |
| 25 | 1 | 7939 |
| 25 | 2 | 7784 |
For example above
Most commentable type occur for companyId=4 is 7901 and commentable type for that is 1. In below query , i see 4-0-7901, but i expected 4-1-7901
SELECT x.companyId, x.commentable, MAX(x._count) _count
FROM
( SELECT a.companyId, a.commentable, COUNT(1) _count
FROM article a
GROUP BY a.companyId, a.commentable
) AS X
GROUP BY x.companyId;
companyId commentable _count
1 0 1
2 0 7856
3 0 7866
4 0 7901
5 0 7884
25 0 7939
Expected result
companyId commentable _count
1 0 1
2 2 7856
3 1 7866
4 1 7901
5 1 7884
25 1 7939
I dont understand 'why is all commentable column is '0' .
You need a big ugly join here. In the query below, you may view the GROUP BY query on the company and comment type the base unit of work. This query appears as itself, aliased as t1. In alias t2, we subquery and aggregate only by commentable, to find the max count for each such comment type. This, we join back to t1 to restrict only the company having the max count.
SELECT
t1.companyId,
t1.commentable,
t1.cnt
FROM
(
SELECT companyId, commentable, COUNT(*) cnt
FROM article
GROUP BY companyId, commentable
) t1
INNER JOIN
(
SELECT companyId, MAX(cnt) max_cnt
FROM
(
SELECT companyId, commentable, COUNT(*) cnt
FROM article
GROUP BY companyId, commentable
) t
GROUP BY companyId
) t2
ON t1.companyId = t2.companyId AND t1.cnt = t2.max_cnt;
By the way, things get somewhat nicer in MySQL 8+, where we can take advantage of analytic functions:
WITH cte AS (
SELECT companyId, commentable, COUNT(*) cnt,
ROW_NUMBER() OVER (PARTITION BY commentable ORDER BY COUNT(*) DESC) rn
FROM article
GROUP BY companyId, commentable
)
SELECT companyId, commentable, cnt
FROM cte
WHERE rn = 1;
You can do this using a having clause:
SELECT a.companyId, a.commentable, COUNT(*) as _count
FROM article a
GROUP BY a.companyId, a.commentable
HAVING COUNT(*) = (SELECT COUNT(*)
FROM article a2
WHERE a2.companyId = a.companyId
GROUP BY a2.commentable
ORDER BY COUNT(*) DESC
LIMIT 1
);
In the event of ties, you will get multiple rows. If you want only one row per company, you can instead use commentable for the comparison in the HAVING:
SELECT a.companyId, a.commentable, COUNT(*) as _count
FROM article a
GROUP BY a.companyId, a.commentable
HAVING a.commentable = (SELECT a2.commentable
FROM article a2
WHERE a2.companyId = a.companyId
GROUP BY a2.commentable
ORDER BY COUNT(*) DESC
LIMIT 1
);
As others have mentioned, your problem is the mis-use of GROUP BY. The unaggregated columns in the SELECT need to match the GROUP BY keys -- and vice versa.
Cause commentable is not one of group by columns. In this case, with ONLY_FULL_GROUP_BY disabled, MySQL is free to choose any one value for this column.
From MySQL doc
If ONLY_FULL_GROUP_BY is disabled, a MySQL extension to the standard SQL use of GROUP BY permits the select list, HAVING condition, or ORDER BY list to refer to nonaggregated columns even if the columns are not functionally dependent on GROUP BY columns. This causes MySQL to accept the preceding query. In this case, the server is free to choose any value from each group, so unless they are the same, the values chosen are nondeterministic, which is probably not what you want.
I have table:
+----+-------+-------------+
| id | code | value_check |
| 1 | p-01 | OK |
| 2 | p-01 | NOT OK |
| 3 | p-01 | OK |
| 4 | p-02 | OK |
| 5 | p-02 | OK |
| 6 | p-02 | OK |
+----+-------+-------------+
How can I select record which having 'OK' group by code,but if there is one or more 'NOT OK' on value_check then don't need to select
expected result:
code
p-02
i have tried my query can get the result but its very slow
this is my query :
SELECT code FROM table
WHERE code
NOT IN (SELECT code FROM table
WHERE value_check = 'NOT OK' GROUP BY code)
GROUP BY code
any other solution?
Check whether the total count is equal to the count of rows having value as OK using HAVING clause.
Query
select `code` from `your_table_name`
group by `code`
having count(*) = sum(`value_check` = 'OK');
Find a demo here
Try below with conditional aggregation
select code from table
group by code
having sum(case when value_check='NOT OK' then 1 else 0 end)=0
You can try it also with correlated subquery:
demo
SELECT distinct code FROM cte1 a
WHERE NOT exists (SELECT 1 FROM cte1 b where a.code=b.code and val = 'NOT OK')
SELECT DISTINCT x.code
FROM my_table x
LEFT
JOIN my_table y
ON y.code = x.code
AND y.value_check = 'not ok'
WHERE x.value_check = 'ok'
AND y.id IS NULL
i have a table like this
i want to get the row of each table that have min responsetime
i have tried this query :
select tablename,
index1,
index2,
min(responsetime)
from tableconf
group by tablename
order by responsetime asc
but it doesn't give what i want
the output that i want is
+------------------+------------------+--------+--------------+
| tablename | index1 | index2 | responsetime |
+------------------+------------------+--------+--------------+
| salesorderheader | TotalDue | NULL | 6.1555 |
| salesterritory | Name | NULL | 11.66667 |
| store | BusinessEntityId | Name | 3.6222 |
| previous | previous | NULL | 5.03333 |
| NONE | NONE | NULL | 5.6 |
+------------------+------------------+--------+--------------+
what query i should use for get the output that i want
Select the minimum date per table name. Use an IN clause on these to get the rows:
select *
from tableconf
where (tablename, responsetime) in
(
select tablename, min(responsetime)
from tableconf
group by tablename
);
(Edited from previous answer)
I don't know if all SQL syntax accept a comma separated where parameter. Another option building off of the highest voted answer right now utilizes a join:
select *
from tableconf t
inner join (
select tablename, min(responsetime) min_rt
from tableconf t2
group by tablename
) t3 on t.tablename = t2.tablename and t.responsetime = t2.min_rt
I have a MySQL query:
select *
from traffic
group
by type
, date
, address
having count(date and type and address) > 3
and status = 0
I want to select all details of distinct users entering the same type, date, address having count(date and type and address)>3 and status=0.
Here is the table
traffic
slno | username | lat | longi | message | dat | time | type | address | status
The above query works, but my query shows results if the same user enters the same data more than three times. I want to see results when different users enter the same traffic data.
+==========+
| traffic |
+==========+
| slno |
| username |
| lat |
| longi |
| message |
| dat |
| time |
| type |
| address |
| status |
+----------+
To select traffic records where more than 3 distinct users have the same traffic data :
SELECT *
FROM `traffic`
WHERE `status` = 0
GROUP BY `type`, `date`, `address`
HAVING COUNT(DISTINCT `username`) > 3
demo
If you want to select the corresponding users :
SELECT DISTINCT t.*
FROM `traffic` t
INNER JOIN (
SELECT *, COUNT(DISTINCT `username`)
FROM `traffic`
WHERE `status` = 0
GROUP BY `type`, `date`, `address`
HAVING COUNT(DISTINCT `username`) > 3
) x
ON t.`type` = x.`type`
AND t.`date` = x.`date`
AND t.`address` = x.`address`
demo
Did you mean like this :
select username, date, type, address
from traffic
where status = 0
group by username, date, type, address
having count(date) > 3
and count(type) > 3
and count(address) > 3