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.
Related
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 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
I am trying to show the results of items from one table where the count from another table equals a number from the first. I have been stuck on how to go about doing this for a couple weeks now so iv finally decided to ask for help. Im having a hard time explaining exactly what it is i need but i will try my best.
I am using PDO to interact with my database which is mysql.
For instance i have two tables:
table 1
-----------------
key | name | total
1 | item 1 | 3
2 | item 2 | 4
3 | item 3 | 2
table 2
-----------------
key | table1 key
1 | 1
2 | 2
3 | 3
4 | 1
5 | 1
6 | 3
7 | 2
8 | 2
So in this case there would be 3/3 items for item 1, 3/4 items for item 2, and 2/2 items for item 3. So it would show item 1 and item 3 as a result because the count for those two equal the total from table one.
I hope I explained this well enough.
If you want a sql query to do that, try this:
select t1.*
from table1 t1
inner join (
select table1_key, count(1) as cnt from table2 group by table1_key
) t2 on t1.key = t2.table1_key and t1.total = t2.cnt
SQLFiddel Demo
I have the following table structure:
structure_id | substructure_id | hash_id
1 1 1
1 1 2
1 2 1
2 1 3
2 1 1
2 2 2
And I want to fetch an intersection for each structure id how many hashes intersect with other structure ids, with the same hashes in different substructures being distinct. What I mean is the following output:
structure_id | structure_intersected_id | count
1 1 2
1 2 2
2 2 3
2 1 2
Is this possible to achieve with MySQL? So far I have the following query:
select t1.structure_id, t2.structure_id, count(*)
from table t1
inner join table t2 on t1.hash_id = t2.hash_id
group by t1.structure_id, t2.structure_id;
But it doesn't remove duplicated substructure hashes, and is not correct for 1-1, 2-2, 3-3, etc. structure_id pairs.
How can I solve this?
Radio_ID | Log_ID
-----------------
1 | 1
1 | 2
1 | 4
1 | 7
1 | 10
2 | 1
2 | 2
2 | 3
2 | 5
Is it possible to get the following output in a single sql statement?
Output:
Radio_ID | Log_ID
-----------------
1 | 3
1 | 5
1 | 6
1 | 8
1 | 9
2 | 4
Logic: returns missing values for each id < max value for id (ex 10 is max value for radio id 1 and missing values are 3,5,6,8,9).
I have a data parser for a radio device and when the radio is outside the coverage area it doesn't send data, so I have to send a new request for missing Log_IDs.
Thank you!
Let me assume that you have a table called numbers with integers of a sufficient range.
Then you can do:
select r.radio_id, n.n as MissingLogId
from (select radio_id, min(log_id) as minli, max(log_id) as maxli
from table t
group by radio_id
) r join
numbers n
on n.n between r.minli and r.maxli left join
table t
on t.radio_id = r.radio_id and t.log_id = n.n
where t.radio_id is null;
The idea is to generate all the numbers for each radio id (between the minimum and maximum id) using a join. Then existing values are filtered out.
If you don't have a numbers table handy, you need to create one large enough. This would probably work:
create table numbers (n int primary key);
insert into numbers(n)
select (#rn := #rn + 1) as n
from table cross join (#rn := 0);