Mysql: where after having - mysql

I have the following posts table:
+-------------------+
| name | type |
+---------+---------+
| admin-1 | type1 |
| user-1 | type1 |
+-------------------+
I want to group the posts by type and return them if the group has more than 1 element, and then filter this result based on the name.
This is the query is tried:
select count(*), type from posts where name like 'admin-%' group by type having count(*) > 1
However in my query, where is executed "before" the group by clause, so I don't get type1 in my result set.
How could I achieve what I want ?
Thanks

Try nested queries
SELECT * FROM posts
WHERE type IN (
SELECT type FROM posts GROUP BY type HAVING COUNT(*) > 1
) AND name LIKE 'admin-%'

You can do it with EXISTS:
select p.*
from posts p
where p.name like 'admin-%'
and exists (select 1 from posts where type = p.type and name <> p.name)
If you expect only 1 row for each type in the results, then you can do it with conditional aggregation:
select type,
max(case when name like 'admin-%' then name end) name
from posts
group by type
having count(*) > 1 and sum(name like 'admin-%') > 0

Related

SQL count occurrence by column

After some research I haven't found what I need and I thought I'll ask here. I'm currently trying to develop a advanced search mode for a application and I'm stuck with my task. Maybe you can help me. So imagine I have the following table:
ID | Name | Surname
1 | John | Mim
2 | Johnny | Crazy
3 | Mike | Something
4 | Milk | Milk
5 | Peter | IDontknow
6 | Mitch | SomeName
Then in my frontend, there's one input field. The input of that field will go trough the query in that way:
SELECT name, surname FROM people WHERE name LIKE 'input%' OR surname LIKE 'input%'
Now lets say my input is "Mi", so I'll have 3 columns match in the "name" column, and 2 in the surname. And that's what I'm looking for.
A count which ouputs the following:
Column | Count
Name | 3
Surname | 2
Is there a way to achieve this in only one query?
What I've tried so far:
I actually created the table above on my localhost in my database and tried different queries. Tried with SELECT count(name), count(surname), but that would output 3 for both counts. So I'm not even sure if that's possible in only one query.
use union all
SELECT 'name' as col, count(name) as cnt FROM people WHERE name LIKE 'input%'
union all
SELECT 'surname', count(surname) FROM people WHERE surname LIKE 'input%'
make customize group using case when
SELECT (case when name LIKE 'input%' then 'name'
else 'surname' end) as Column, count(*) as cnt
FROM people WHERE name LIKE 'input%' OR surname LIKE 'input%'
group by Column
Try this:
SELECT "Name" as Column, count(*) as Count FROM people WHERE name LIKE 'mi%'
UNION
SELECT "Surname" as Column, count(*) as Count FROM people WHERE surname LIKE 'mi%'
In Mysql booleans are evaluated as 1 or 0, so you can do this:
select 'Name' Column, sum(name LIKE 'input%') Count from people
union all
select 'Surname', sum(surname LIKE 'input%') from people
For Mysql 8.0+ you can avoid the double scan of the table with a CTE:
with cte as (
select
sum(name LIKE 'input%') namecounter,
sum(surname LIKE 'input%') surnamecounter
from people
)
select 'Name' Column, namecounter Count from cte
union all
select 'Surname', surnamecounter from cte
The solution without UNION[ ALL] of the people table:
SELECT
CASE cj.x WHEN 1 THEN 'Name' ELSE 'Surname' END AS `Column`,
CASE cj.x
WHEN 1 THEN COUNT(CASE WHEN Name LIKE concat(#input, '%') THEN 1 end)
ELSE COUNT(CASE WHEN Surname LIKE concat(#input, '%') THEN 1 END)
END `Count`
FROM people CROSS JOIN (SELECT 1 AS x UNION ALL SELECT 2) AS cj
WHERE Name LIKE concat(#input, '%') OR Surname LIKE concat(#input, '%')
GROUP BY cj.x;
Output for the Mi input:
| Column | Count |
+---------+-------+
| Name | 3 |
| Surname | 2 |
Test it online with SQL Fiddle.

MySQL selecting missing rows

I have a table with user info:
user_id | meta_key | meta_value
-----------+----------+-------------
1 | name | John
1 | surname | Doe
2 | name | Luke
3 | name | Jane
I want to select rows with a certain key, given a list of IDs.
If I do:
SELECT meta_value FROM table WHERE meta_key = 'surname' AND user_id IN(1,2,3)
MySQL will only return the info for user 1, since the others do not have surname.
I would like to build a query to return exactly the number of rows as the IDs passed, with NULL (or an empty string) if that particular user has no surname in the table.
I have tried to use IFNULL(meta_value, "") and also using IF, but it does not seem to help.
Any suggestions? I am sure it's a trivial thing but I can't seem to get around this issue.
Here's a SQLfiddle showing the issue: http://sqlfiddle.com/#!9/86eef2/6
My expected output would be:
Doe
NULL
NULL
Try this query:
SELECT DISTINCT user_id,
(SELECT meta_value FROM mytable B WHERE B.user_id = mytable.user_id AND META_KEY = 'surname') AS 'surname_meta_value'
FROM mytable
WHERE user_id IN(1,2,3)
For study purpose, this could be a faster option, in most cases, based on rlb.usa solution:
SELECT user_id,
GROUP_CONCAT(
(CASE WHEN meta_key = "surname"
THEN meta_value
ELSE ''
END) SEPARATOR '')
AS 'surname_meta_value'
FROM mytable WHERE user_id IN(1,2,3)
GROUP BY user_id

MySQL how to count from GROUP BY

date | userid | companyid
12.8.14 | 1 | 1
12.8.14 | 2 | 2
12.8.14 | 3 | 1
I had a table like above. It is easy to count how many company 1 from table with normal query.
My question is : if my query is select * from table where companyid = '1' group by date, how can i get mysql_num_row equal to 2 for company 1 and userid is 1 and 3?
select * from table where companyid = '1' group by date will only return me
mysql_num_row equal 1 and result 12.8.14 | 1 | 1
You can nest a query to get the sum of company one entries and then join that nested query to an outer query:
SELECT ABB2.*, ABB1.mysql_num_row
FROM `table` AS ABB2
JOIN
(SELECT companyid, COUNT(userid) AS mysql_num_row
FROM `table`
GROUP BY companyid) AS ABB1 ON ABB1.companyid = ABB2.companyid
WHERE ABB2.companyid = 1;
Example
try like this also
select *,(select count(*) from table1 where companyid=a.companyid) as count
from t as a where companyid=1
You wanted:
select date,companyid,count(*)
from table
where userid = 1
group by date,companyid

MYSQL IN Clause Issue

There are a lot of questions posted on stackoverflow that are almost same like my problem but I found no working solution. I have a table message and entries are:
id | Message | Status
1 | Hello | 1
2 | Hiiii | 0
4 | Works | 1
I have another table which gives ids 1,2,3 and I want to find the status of all these entries from message table. What I want is if an id doesn't exist in message table then it should return null for that id if I use IN clause to find all status. I want following result:
id | Status
1 | 1
2 | 0
3 | null
I have been using IN clause but didn't get working output. Then I got a solution on stackoverflow and tried this query
SELECT `id`,`status` FROM ( SELECT 1 AS ID UNION ALL SELECT 2 AS ID UNION ALL SELECT 3) ids LEFT OUTER JOIN message ON ids.ID = message.id
But this query is not giving the expected results. Any help would be much appreciated.
I don't see how your query would work. The column id should be ambiguous in the select (unless your database is case sensitive). Try this:
SELECT ids.ID, m.status
FROM ( SELECT 1 AS ID UNION ALL SELECT 2 AS ID UNION ALL SELECT 3
) ids LEFT OUTER JOIN
message m
ON ids.ID = m.id;

MySQL: Finding repeated names in my User table

I want to find all users whose name appears at least twice in my User table. 'email' is a unique field, but the combination of 'firstName' and 'lastName' is not necessarily unique.
So far I have come up with the following query, which is very slow, and I am not even sure it is correct. Please let me know a better way to rewrite this.
SELECT CONCAT(u2.firstName, u2.lastName) AS fullName
FROM cpnc_User u2
WHERE CONCAT(u2.firstName, u2.lastName) IN (
SELECT CONCAT(u2.firstName, u2.lastName) AS fullNm
FROM cpnc_User u1
GROUP BY fullNm
HAVING COUNT(*) > 1
)
Also, note that the above returns the list of names that appear at least twice (I think so, anyway), but what I really want is the complete list of all user 'id' fields for these names. So each name, since it appears at least twice, will be associated with at least two primary key 'id' fields.
Thanks for any help!
Jonah
SELECT u.*
FROM cpnc_User u JOIN
(
SELECT firstName, lastName
FROM cpnc_User
GROUP BY firstName, lastName
HAVING COUNT(*) > 1
) X on X.firstName = u.firstName AND x.lastName = u.lastName
ORDER BY u.firstName, u.lastName
There is no need to make up a concatenated field, just use the 2 fields separately
SELECT u.id, u.firstName, u.lastName
FROM cpnc_User u, (
SELECT uc.firstName, uc.lastName
FROM cpnc_User uc
GROUP BY uc.firstName, uc.lastName
HAVING count(*) > 1
) u2
WHERE (
u.firstName = u2.firstName
AND u.lastName = u2.lastName
)
To experiment I created a simple table with two columns a user id, and a name. I inserted a bunch of records, including some duplicates. Then ran this query:
SELECT
count(id) AS count,
group_concat(id) as IDs
FROM
test
GROUP BY
`name`
ORDER BY
count DESC
It should give you results like this:
+-------+----------+
| count | IDs |
+-------+----------+
| 4 | 7,15,4,1 |
| 2 | 2,8 |
| 2 | 6,13 |
| 2 | 14,9 |
| 1 | 11 |
| 1 | 10 |
| 1 | 3 |
| 1 | 5 |
| 1 | 17 |
| 1 | 12 |
| 1 | 16 |
+-------+----------+
You'll need to filter out the later results using something else.
SELECT u.id
, CONCAT(u.firstName, ' ', u.lastName) AS fullname
FROM cpnc_User u
JOIN
( SELECT min(id) AS minid
, firstName
, lastName
FROM cpnc_User
GROUP BY firstName, lastName
HAVING COUNT(*) > 1
) AS grp
ON u.firstName = grp.firstName
AND u.lastName = grp.lastName
ORDER BY grp.minid
, u.id
The ORDER BY grp.minid ensures that users with same first and last name stay grouped together in the output.
OK, you are doing a concatenation, then doing a compare on this, which essentially means that the DB is going to have to do something to every single row of the database.
How about a slightly different approach, you are holding surname and first name separately. So first select all those instances where surname appears > 1 time in your database. Now this has cut your population down dramatically.
Now you can do a compare on the first name to find out where the matches are.