I have a mapping table like below
c1 c2
-- --
1 1
1 1
1 2
1 3
1 3
2 1
2 2
2 3
3 1
and so on. The table has a separate id column (not showed here).
Here is my query so far :
SELECT `c1`, `c2`, COUNT(*) AS `count_of_uniques` FROM `map_table`
GROUP BY `c1`, `c2`
I have also tried with distinct query like this.
SELECT `c1`, `c2`, COUNT(DISTINCT `c1`, `c2`) AS `count_of_uniques` FROM `map_table`
The expected result is
c1 c2 count_of_uniques
-- -- ----------------
1 1 2
1 2 2
1 3 3
2 2 1
2 3 1
My current query shows the correct output when the combination is made of two similar numbers, but when we have something like 1-2 and 2-1, the query does not have the correct output.
distinct shows still fewer results.
Any help is greatly appreciated. Thank you.
I think you might be after something that deals with combinations rather than permutations i.e 1, 2 and 2, 1 should be treated as the same combination. If this is the case you can use a case expression to make sure that c1 is always the lower of the two values, and c2 is the higher of the two. This will group similar pairs together (so 2, 1 will first be reversed to become 1, 2 then grouped with all similar results):
SELECT c1, c2, COUNT(*) AS `count_of_uniques`
FROM ( SELECT CASE WHEN c1 > c2 THEN c2 ELSE c1 END AS c1,
CASE WHEN c1 > c2 THEN c1 ELSE c2 END AS c2
FROM map_table) AS t
GROUP BY c1, c2
Output
c1 c2 count_of_uniques
-------------------------------
1 1 2
1 2 2
1 3 3
2 2 1
2 3 1
Example on DB Fiddle
You can use the functions least() and greatest() to get the pairs on which you will group:
select
least(c1, c2) c1,
greatest(c1, c2) c2,
count(*) count_of_uniques
from map_table
group by
least(c1, c2),
greatest(c1, c2)
See the demo.
Results:
| c1 | c2 | count_of_uniques |
| --- | --- | ---------------- |
| 1 | 1 | 2 |
| 1 | 2 | 2 |
| 1 | 3 | 3 |
| 2 | 2 | 1 |
| 2 | 3 | 1 |
Select C1,C2,Count(*) from map_table Group By C1,C2 Order by C1
Related
I have a table with the following structure:
IdM|IdS
-------
1 | 2
1 | 3
1 | 4
2 | 1
2 | 3
2 | 4
3 | 1
3 | 2
3 | 3
3 | 4
How could I make a select statement on this table, which will return some rows of this table, where in each row, a specific id appears only one, indifferent on which column it is specified?
For the above result set, I would like a query that would return:
-------
1 | 2
3 | 4
-------
To give another example, if you would omit the first row in the original dataset:
IdM|IdS
-------
1 | 3
1 | 4
2 | 1
2 | 3
2 | 4
3 | 1
3 | 2
3 | 3
3 | 4
the result set should be:
-------
1 | 3
2 | 4
-------
That's actually an interesting problem. If I follow you correctly, you want to iterate through the dataset and only retain rows where both values were never seen before. You could use a recursive query:
with recursive
data as (
select idm, ids, row_number() over(order by idm, ids) rn
from mytable
where idm <> ids
),
cte as (
select idm, ids, rn, 1 as to_keep , concat(idm, ',', ids) visited from data where rn = 1
union all
select d.idm, d.ids, d.rn,
(not find_in_set(d.idm, c.visited) and not find_in_set(d.ids, c.visited)),
case when (not find_in_set(d.idm, c.visited) and not find_in_set(d.ids, c.visited))
then concat_ws(',', c.visited, d.idm, d.ids)
else c.visited
end
from cte c
inner join data d on d.rn = c.rn + 1
)
select idm, ids from cte where to_keep
The first CTE enumerates the rows ordered by both columns. Then the recursive query walks the resultset, checks if both values are new, and sets a flag accordingly of the columns. Flagged numbers are retained to be used for filtering in the following iteration.
Demo on DB Fiddle
Note that, given your requirement, not all values may appear in the resultset. Consider the following dataset:
idm ids
+-----+---
1 2
1 3
1 4
Your logic will only return the first row.
I have a table with these columns:
s, s2, s3
1, 2, 3
4
1, 3
4, 2,
2, 1
3, 4
4
I want to know how many times the unique values in column s appears in the columns s, s2 and s3.
So far I have:
$query = "SELECT s, COUNT(*) as count FROM table GROUP BY s";
This will give me:
1 - count 2
2 - count 1
3 - count 1
4 - count 3
But I want to count the column s2 and s3 also so the outcome will be:
1 - count 3
2 - count 3
3 - count 3
4 - count 4
Any idea how I must edit the query so I can count the columns s, s2 and s3 group by the values of column s?
Kind regards,
Arie
You need a UNION ALL for all the columns and then count them:
select
t.s, count(*) counter
from (
select s from tablename union all
select s2 from tablename union all
select s3 from tablename
) t
where t.s is not null
group by t.s
See the demo.
Results:
| s | counter |
| --- | ------- |
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
| 4 | 4 |
If in the columns s2 and s3 there are values that do not exist in the column s and you want them excluded, then instead of:
where t.s is not null
use
where t.s in (select s from tablename)
#forpas answer is a good one. However, two things you should consider.
Due to the use of union the query will become slower as the data size increases.
If the input is as following:
s, s2, s3
1, 2, 3
4
1, 3
4, 2,
2, 1
3, 4
4 5
The result of the provided query will be:
| s | counter |
| --- | ------- |
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
| 4 | 4 |
| 5 | 1 |
whereas it should remain the same as 5 is not present into the s column.
In order to resolve both of the above issues, I propose the approach to use JOIN instead of UNION:
SELECT t3.s, IF(t3.s = t4.s3, cnt1 + 2, cnt1 + 1) as counter FROM
(SELECT *, count(*) AS cnt1 FROM
(SELECT s from table) AS t1
LEFT JOIN
(SELECT s2 FROM table) AS t2
ON t1.s = t2.s2 GROUP BY t1.s
) AS t3
LEFT JOIN
(SELECT s3 FROM table) AS t4
ON t3.s = t4.s3
ORDER BY t3.s
The query might look a bit lengthy and complicated but it is really simple when you look into the logic.
Step 1
What I have done here is to make a left join from s column to s2 and counted results for that so it will give you 1 lesser number than how many numbers are present in total as it will make relation left to right.
Step 2
Then I have made a left join from s to s3, and only increase the count of step 1 by 1 if the relation is found.
Step 3
Ultimately I have increased the count by 1 so that we can convert the number of relations to the number of the enities.
I hope it makes sense
I have a little advise from SQL. I need to select a two groups (WHERE) in two columns. Some working 'like':
SELECT COUNT(WHERE Draw=1) as D1, COUNT(WHERE Draw=2) as D2 FROM SampleData
Exemple data table:
SampleData
--------------------
Id | Draw | Element
--------------------
1 | 1 | 13
2 | 1 | 15
3 | 1 | 22
4 | 1 | 36
5 | 1 | 45
6 | 2 | 11
7 | 2 | 15
8 | 2 | 22
And output like this:
Output:
--------
D1 | D2
--------
5 | 3
You can use CASE expression for this:
SELECT
COUNT(CASE WHEN Draw=1 THEN 1 END) as D1,
COUNT(CASE WHEN Draw=2 THEN 1 END) as D2
FROM SampleData
mysql also supports If() so you could also do something like the following. Just keep in mind this isn't portable to other RDBMS's but the CASE version is:
SELECT
SUM(IF(Draw=1, 1, 0)) as D1,
SUM(IF(Draw=2, 1, 0)) as D2
FROM SampleData;
Also... mysql supports math on Boolean expressions so you could get really terse here:
SELECT SUM(Draw=1) as D1, Sum(Draw=2) as D2 FROM SampleData;
Again though this is not portable to other RDBMSs like the CASE expression is.
Maybe something like this? (untested)
SELECT COUNT(*) as D1 FROM SampleData WHERE Draw = 1 UNION SELECT COUNT(*) as D2 FROM SampleData WHERE Draw = 2
I have mysql table with hospitals and treatments(associated with sub treatments) that they provide. I need to make mysql query on the table which returns hospitals providing all treatment/sub_treatment given in a list. For example:
From table below I need hospitals providing all treatments in list: (tretament_id, sub_treatment_id) = (1-1, 1-2). So result must be hospitals with id 1 and 8.
hospital_id | treatment_id | sub_treatment_id
-------------------------------------------------
1 | 1 | 1
1 | 1 | 2
1 | 1 | 3
_________________________________________________
4 | 1 | 1
4 | 2 | 1
_________________________________________________
8 | 1 | 1
8 | 1 | 2
_________________________________________________
7 | 2 | 1
I tried WHERE IN but it works like OR so returns hospital 4 which satisfies only (1,1). How can I write an sql query like WHERE IN but which works like AND?
Try this:
SELECT hospital_id
FROM mytable
WHERE (treatment_id, sub_treatment_id) IN ((1, 1), (1, 2))
GROUP BY hospital_id
HAVING COUNT(CASE
WHEN (treatment_id, sub_treatment_id) IN ((1, 1), (1, 2))
THEN 1
END) = 2
Demo here
You can do this using group by and having:
select hospital_id
from t
where treatment_id = 1 and sub_treatment_id in (1, 2)
group by hospital_id
having count(*) = 2;
Note: This assumes that there are no duplicates in the table. That is easy enough to fix using count(distinct), but probably not necessary.
Here is a solution using GROUP_CONCAT and JOIN:
select distinct t.hospital_id
from hospitals h and treatments t ON h.id = t.hospital_id
having GROUP_CONCAT(CONCAT(t.treatment_id, '-', t.sub_treatment_id)
ORDER BY t.treatment_id, t.sub_treatment_id)
= '1-1,1-2';
TABLE A
Row IdA ValueA
1 1 ABCD
2 2 EFGH
3 3 IJKL
TABLE B
Row IdB ValueB
1 1 QWER
2 2 TYUI
3 3 OPAS
CONNECTOR X
Row IdA IdB
1 1 1
2 1 2
3 2 3
I want the output to display:
OUTPUT
Value A --- ValueB(1), ValueB(2)
ABCD --- QWER, TYUI
So, basically, every time there's a doublet in the connector table's IdA column, those two (or more) entries merge the strings in the Value field for my output.
Is this even doable with a MySQL query, or do I -have- to resort to sorting through the whole database with a PHP array? I'd rather like to avoid that, if at all possible!
I've looked at the various JOINs to no avail and thought about using a GROUP BY and COUNT(DISTINCT ...) query, but it just seems a very inelegant way to go about it. Suggestions are welcome!
SELECT a.ValueA, GROUP_CONCAT( b.ValueB SEPARATOR ', ' ) AS ValuesB
FROM connector c
JOIN tblA a ON c.IdA = a.IdA
JOIN tblB b ON c.IdB = b.IdB
GROUP BY a.IdA
Will give you results:
+--------+------------+
| ValueA | ValuesB |
+--------+------------+
| ABCD | QWER, TYUI |
| EFGH | OPAS |
+--------+------------+