I have a vote mysql table and users (user column) can vote y or n. (option column)
My table structure is like below:
| id | option | user |
| 1 | y | jack |
| 2 | n | jack |
| 3 | n | michi|
| 4 | n | michi|
What I would like to do is, select distinct user and count option and display it in a single row like below:
| y | n |
| 1 | 2 |
I tried GROUP_CONCAT() and SUM but without luck. Can you please help me to get this sql working?
Thanks.
Group functions like GROUP_CONCAT(), SUM() and COUNT() need a GROUP BY statement to know which rows to combine.
In your query, you want to use COUNT().
Try this:
SELECT `option`, COUNT(DISTINCT `user`) AS users
FROM `table`
GROUP BY `option`
DEMO: http://sqlfiddle.com/#!9/705a9d/3
This will show you one row per option. If you want both options across one row, that's a bit trickier. You'll need to use subqueries for each option.
SELECT (
SELECT COUNT(DISTINCT `user`)
FROM `table`
WHERE `option` = 'y'
) AS y, (
SELECT COUNT(DISTINCT `user`)
FROM `table`
WHERE `option` = 'n'
) AS n
DEMO: http://sqlfiddle.com/#!9/705a9d/4
NOTE: You can use COUNT() without GROUP BY. That will make the query combine all found rows together.
Related
Is there any way to get the inverse of a group by statement in mysql? My use case is to delete all duplicates.
Say my table looks like this:
ID | columnA | ...
1 | A
2 | A
3 | A
4 | B
5 | B
6 | C
I want my result set to look like this:
ID | columnA | ...
2 | A
3 | A
5 | B
(Essentially this finds all duplicates leaving one behind. Could be used to purge all duplicate records down to 1, or to perform other analysis later).
One way is to take all but the first id for each value of ColumnA:
select t.*
from t
where t.id > (select min(t2.id) from t t2 where t2.columnA = t.columnA);
Your result seems
select max(id), columnA group by columnA
This should perform a lot better then inner select based queries.
SELECT
*
FROM
TABLE
QUALIFY
RANK() OVER (partition by columnA order by ID ASC ) = 1
EDIT : This apparently wont work in MySQL. Guess the only answer is to by a oracle license - or use another answer. ;)
I realized my own solution based on #scaisEdge response before he edited it. In need the opposite of my group by, so using a subquery:
SELECT * FROM mytable WHERE ID NOT IN (SELECT ID FROM mytable GROUP BY columnA);
I am confident this will help.
create table test.temptable select distinct * from YourTable;
truncate YourTable;
insert into YourTable select * from test.temptable ;
MODE is the value that occurs the MOST times in the data, there can be ONE MODE or MANY MODES
here's some values in two tables (sqlFiddle)
create table t100(id int auto_increment primary key, value int);
create table t200(id int auto_increment primary key, value int);
insert into t100(value) values (1),
(2),(2),(2),
(3),(3),
(4);
insert into t200(value) values (1),
(2),(2),(2),
(3),(3),
(4),(4),(4);
right now, to get the MODE(S) returned as comma separated list, I run the below query for table t100
SELECT GROUP_CONCAT(value) as modes,occurs
FROM
(SELECT value,occurs FROM
(SELECT value,count(*) as occurs
FROM
T100
GROUP BY value)T1,
(SELECT max(occurs) as maxoccurs FROM
(SELECT value,count(*) as occurs
FROM
T100
GROUP BY value)T2
)T3
WHERE T1.occurs = T3.maxoccurs)T4
GROUP BY occurs;
and the below query for table t200 (same query just with table name changed) I have 2 tables in this example because to show that it works for cases where there's 1 MODE and where there are multiple MODES.
SELECT GROUP_CONCAT(value) as modes,occurs
FROM
(SELECT value,occurs FROM
(SELECT value,count(*) as occurs
FROM
T200
GROUP BY value)T1,
(SELECT max(occurs) as maxoccurs FROM
(SELECT value,count(*) as occurs
FROM
T200
GROUP BY value)T2
)T3
WHERE T1.occurs = T3.maxoccurs)T4
GROUP BY occurs;
My question is "Is there a simpler way?"
I was thinking like using HAVING count(*) = max(count(*)) or something similar to get rid of the extra join but couldn't get HAVING to return the result i wanted.
UPDATED:
as suggested by #zneak, I can simplify T3 like below:
SELECT GROUP_CONCAT(value) as modes,occurs
FROM
(SELECT value,occurs FROM
(SELECT value,count(*) as occurs
FROM
T200
GROUP BY value)T1,
(SELECT count(*) as maxoccurs
FROM
T200
GROUP BY value
ORDER BY count(*) DESC
LIMIT 1
)T3
WHERE T1.occurs = T3.maxoccurs)T4
GROUP BY occurs;
Now is there a way to get ride of T3 altogether?
I tried this but it returns no rows for some reason
SELECT value,occurs FROM
(SELECT value,count(*) as occurs
FROM t200
GROUP BY `value`)T1
HAVING occurs=max(occurs)
basically I am wondering if there's a way to do it such that I only need to specify t100 or t200 once.
UPDATED: i found a way to specify t100 or t200 only once by adding a variable to set my own maxoccurs like below
SELECT GROUP_CONCAT(CASE WHEN occurs=#maxoccurs THEN value ELSE NULL END) as modes
FROM
(SELECT value,occurs,#maxoccurs:=GREATEST(#maxoccurs,occurs) as maxoccurs
FROM (SELECT value,count(*) as occurs
FROM t200
GROUP BY `value`)T1,(SELECT #maxoccurs:=0)mo
)T2
You are very close with the last query. The following finds one mode:
SELECT value, occurs
FROM (SELECT value,count(*) as occurs
FROM t200
GROUP BY `value`
LIMIT 1
) T1
I think your question was about multiple modes, though:
SELECT value, occurs
FROM (SELECT value, count(*) as occurs
FROM t200
GROUP BY `value`
) T1
WHERE occurs = (select max(occurs)
from (select `value`, count(*) as occurs
from t200
group by `value`
) t
);
EDIT:
This is much easier in almost any other database. MySQL supports neither with nor window/analytic functions.
Your query (shown below) does not do what you think it is doing:
SELECT value, occurs
FROM (SELECT value, count(*) as occurs
FROM t200
GROUP BY `value`
) T1
HAVING occurs = max(occurs) ;
The final having clause refers to the variable occurs but does use max(occurs). Because of the use of max(occurs) this is an aggregation query that returns one row, summarizing all rows from the subquery.
The variable occurs is not using for grouping. So, what value does MySQL use? It uses an arbitrary value from one of the rows in the subquery. This arbitrary value might match, or it might not. But, the value only comes from one row. There is no iteration over it.
I realize this is a very old question but in looking for the best way to find the MODE in a MySQL table, I came up with this:
SELECT [column name], count(*) as [ccount] FROM [table] WHERE [field] = [item] GROUP BY [column name] ORDER BY [ccount] DESC LIMIT 1 ;
In my actual situation, I had a log with recorded events in it. I wanted to know during which period (1, 2 or 3 as recorded in my log) the specific event occurred the most number of times. (Eg, the MODE of "period" column of the table for that specific event
My table looked like this (abridged):
EVENT_TYPE | PERIOD
-------------------------
1 | 3
1 | 3
1 | 3
1 | 2
2 | 1
2 | 1
2 | 1
2 | 3
Using the query:
SELECT event_type, period, count(*) as pcount FROM proto_log WHERE event_type = 1 GROUP BY period ORDER BY pcount DESC LIMIT 1 ;
I get the result:
> EVENT_TYPE | PERIOD | PCOUNT
> --------------------------------------
1 | 3 | 3
Using this result, the period column ($result['period'] for example) should contain the MODE for that query and of course pcount contains the actual count.
If you wanted to get multiple modes, I suppse you could keep adding other criteria to your WHERE clause using ORs:
SELECT event_type, period, count(*) as pcount FROM proto_log WHERE event_type = 1 ***OR event_type = 2*** GROUP BY period ORDER BY pcount DESC LIMIT 2 ;
The multiple ORs should give you the additional results and the LIMIT increase will add the additional MODES to the results. (Otherwise it will still only show the top 1 result)
Results:
EVENT_TYPE | PERIOD | PCOUNT
--------------------------------------
1 | 3 | 3
2 | 1 | 3
I am not 100% sure this is doing exactly what I think it is doing, or if it will work in all situations, so please let me know if I am on or off track here.
In the table below, how do I get just the most recent row with id=1 based on the signin column, and not all 3 rows?
+----+---------------------+---------+
| id | signin | signout |
+----+---------------------+---------+
| 1 | 2011-12-12 09:27:24 | NULL |
| 1 | 2011-12-13 09:27:31 | NULL |
| 1 | 2011-12-14 09:27:34 | NULL |
| 2 | 2011-12-14 09:28:21 | NULL |
+----+---------------------+---------+
Use the aggregate MAX(signin) grouped by id. This will list the most recent signin for each id.
SELECT
id,
MAX(signin) AS most_recent_signin
FROM tbl
GROUP BY id
To get the whole single record, perform an INNER JOIN against a subquery which returns only the MAX(signin) per id.
SELECT
tbl.id,
signin,
signout
FROM tbl
INNER JOIN (
SELECT id, MAX(signin) AS maxsign FROM tbl GROUP BY id
) ms ON tbl.id = ms.id AND signin = maxsign
WHERE tbl.id=1
SELECT *
FROM tbl
WHERE id = 1
ORDER BY signin DESC
LIMIT 1;
The obvious index would be on (id), or a multicolumn index on (id, signin DESC).
Conveniently for the case, MySQL sorts NULL values last in descending order. That's what you typically want if there can be NULL values: the row with the latest not-null signin.
To get NULL values first:
ORDER BY signin IS NOT NULL, signin DESC
You may want to append more expressions to ORDER BY to get a deterministic pick from (potentially) multiple rows with NULL.
The same applies without NULL if signin is not defined UNIQUE.
Related:
mysql order by, null first, and DESC after
The SQL standard does not explicitly define a default sort order for NULL values. The behavior varies quite a bit across different RDBMS. See:
https://docs.mendix.com/refguide/null-ordering-behavior
But there are the NULLS FIRST / NULLS LAST clauses defined in the SQL standard and supported by most major RDBMS, but not by MySQL. See:
SQL how to make null values come last when sorting ascending
Sort by column ASC, but NULL values first?
Building on #xQbert's answer's, you can avoid the subquery AND make it generic enough to filter by any ID
SELECT id, signin, signout
FROM dTable
INNER JOIN(
SELECT id, MAX(signin) AS signin
FROM dTable
GROUP BY id
) AS t1 USING(id, signin)
Select [insert your fields here]
from tablename
where signin = (select max(signin) from tablename where ID = 1)
SELECT * FROM (SELECT * FROM tb1 ORDER BY signin DESC) GROUP BY id;
I had a similar problem. I needed to get the last version of page content translation, in other words - to get that specific record which has highest number in version column. So I select all records ordered by version and then take the first row from result (by using LIMIT clause).
SELECT *
FROM `page_contents_translations`
ORDER BY version DESC
LIMIT 1
Simple Way To Achieve
I know it's an old question
You can also do something like
SELECT * FROM Table WHERE id=1 ORDER BY signin DESC
In above, query the first record will be the most recent record.
For only one record you can use something like
SELECT top(1) * FROM Table WHERE id=1 ORDER BY signin DESC
Above query will only return one latest record.
Cheers!
When I use this:
SELECT DISTINCT id FROM table
this works! but... when I want to filter only one column I try to do this:
SELECT DISTINCT prod_id, id, prod_picture FROM products
this gives me all table... I just need 1 picture for each product, like:
1 | 1 | asd.jpg
2 | 3 | weq.jph
not
1 | 1 | asd.jpg
1 | 2 | qwe.jpg
2 | 3 | weq.jpg
actually I try to use this:
SELECT DISTINCT
prod_list.id,
prod_list.prodname,
prod_pict.pict_file
FROM
prod_list
INNER JOIN urun_pict ON (prod_list.id = prod_pict_prod_id)
I have to filter just "prod_list.id"...
You should GROUP BY the product id to collapse all rows for each id into one. All columns which are not part of your GROUP BY clause should be aggregate columns. You need to tell MySQL which of the possibly multiple values for the other columns you want. If you don't care, use MIN or MAX?
SELECT
prod_list.id,
prod_list.prodname,
MAX(prod_pict.pict_file) AS `pict_file`,
FROM
prod_list
INNER JOIN
prod_pict
ON
prod_list.id = prod_pict.prod_id
GROUP BY
prod_list.id,
prod_list.prodname
SELECT prod_id, id, prod_picture
FROM products
GROUP BY prod_id
use group by prod_id,
SELECT prod_id, id, prod_picture FROM products group by prod_id
work only if in run not with this sql_mode : ONLY_FULL_GROUP_BY , The default value is empty (no modes set).
SELECT
prod_list.id,
prod_list.prodname,
prod_pict.pict_file
FROM
prod_list
INNER JOIN urun_pict ON (prod_list.id = prod_pict_prod_id)
GROUP BY prod_list.id
This should work.
Please help me figure a single query that will transform the data below...
|id |status_a |status_b |
|+++++++++++++++++++++++|
| 1|active |inactive |
...into this one.
|status_group |count|
|++++++++++++++++++++++++|
|status_a.active | 1|
|status_b.inactive | 1|
edit: If a single pass query is possible then that will be better. Also, does a query with unions does a single pass?
If status can be either only active or inactive, I'd suggest a different approach:
SELECT
sum(if(status_a='active',1,0)) AS status_a_active,
sum(if(status_a='inactive',1,0)) AS status_a_inactive,
sum(if(status_b='active',1,0)) AS status_b_active,
sum(if(status_b='inactive',1,0)) AS status_b_inactive
FROM table
Otherwise you need to use the UNION approach, but I'd do it a little differently. First, you can use UNION ALL, because you don't need to remove duplicates in the result. I'd also use GROUP BY only once like this:
SELECT status_group, count(id)
FROM (
SELECT CONCAT('status_a.', status_a) AS status_group, id FROM table
UNION ALL
SELECT CONCAT('status_b.', status_b) AS status_group, id FROM table
) a
GROUP BY status_group
I have a solution that uses UNIONs. Shown here:
SELECT 'status_a.active' AS status_group, COUNT(*) AS count FROM `test` WHERE status_a = 'active'
UNION
SELECT 'status_a.inactive' AS status_group, COUNT(*) AS count FROM `test` WHERE status_a = 'inactive'
UNION
SELECT 'status_b.active' AS status_group, COUNT(*) AS count FROM `test` WHERE status_b = 'active'
UNION
SELECT 'status_b.inactive' AS status_group, COUNT(*) AS count FROM `test` WHERE status_b = 'inactive'
Basically, it queries each condition for status_a or status_b being active or not. We get four such queries and we apply UNION to all of them.
I suppose, I've to move my comment a while ago which is also a shorter solution here than hw's.
SELECT CONCAT('status_a.', status_a) AS stat, COUNT(id) FROM base GROUP BY stat
UNION
SELECT CONCAT('status_b.', status_b) AS stat, COUNT(id) FROM base GROUP BY stat