Reputation: 15
Imagine we have a table like this:
id value
1 a
2 b
3 a
4 a
5 b
Query like this
SELECT * , COUNT( * )
FROM test
GROUP BY value
gives us a table like this:
id value COUNT(*)
1 a 3
2 b 2
which tells us that there are three 'a' and two 'b' in our table.
The question is: is it possible to make a query (without nested SELECT's), which would yield a table like
id value count_in_col
1 a 3
2 b 2
3 a 3
4 a 3
5 b 2
The goal is to avoid collapsing columns and to add quantity of 'value' elements in the whole column to each row.
Upvotes: 1
Views: 2814
Reputation: 108400
Yes, it's possible to return the specified resultset using only a single SELECT keyword.
SELECT t.id
, t.value
, COUNT(DISTINCT u.id) AS count_in_col
FROM mytable t
JOIN mytable u
ON u.value = t.value
GROUP
BY t.id
to setup test case:
CREATE TABLE `mytable` (`id` INT, `value` VARCHAR(1));
INSERT INTO `mytable` VALUES (1,'a'), (2,'b'),(3,'a'),(4,'a'),(5,'b');
returns:
id value count_in_col
------ ------ --------------
1 a 3
2 b 2
3 a 3
4 a 3
5 b 2
NOTE:
This assumes that id
is unique in the table, as would be enforced by a primary key or unique key constraint.
In terms of performance, depending on cardinality, an index ... ON (value,id)
may improve performance.
This approach (using a JOIN to match rows on the value
column) does have the potential to produce a very large intermediate resultset, if there are a "lot" of rows that match on value
. For example, if there are 1,000 rows with value='a'
, the intermediate resultset for those rows will be 1,000*1,000 = 1,000,000 rows.
Adding a predicate (in the ON clause) may also improve performance, but reducing the number of rows in the intermediate result.
ON u.value = t.value
AND u.id >= t.id
(There's no real magic; the "trick" is to use COUNT(DISTINCT id)
to avoid the same id
value from being counted more than once.)
Upvotes: 2
Reputation: 10246
Here is simple (and faster) SQL, but to print exact what you want, client program should parse output of GROUP_CONCAT
SELECT GROUP_CONCAT(id), value, COUNT( * )
FROM test
GROUP BY value;
Upvotes: 0
Reputation: 21657
I don't think that is possible without a subquery to count the number of occurrences of the different values:
SELECT a.*,b.valCount
FROM test a
INNER JOIN
(
SELECT value,COUNT(*) AS valCount
FROM test
GROUP BY value
) b
ON b.value = a.value
ORDER BY a.id;
Upvotes: 1
Reputation: 8942
You need to use a subquery to group the values and do the count. Then you just have to join on that subquery on the value field.
SELECT test.id, test.value, t2.val_count
FROM test INNER JOIN
(SELECT value, COUNT(value) AS val_count FROM test GROUP BY value) AS t2
ON test.value = t2.value;
http://sqlfiddle.com/#!2/c8578/7
Edit: I just saw that you asked for an answer without using nested queries. I do not think this is possible but I will revise my answer if I find a way to do it.
Upvotes: 0