Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL: Making pairs and count samples

Tags:

sql

mysql

I have the following table (example):

ID |LOCATION|DAY           
1  | 1      |20190301   
1  | 2      |20190301  
1  | 3      |20190301  
1  | 1      |20190302   
1  | 4      |20190302  
1  | 4      |20190305     
1  | 5      |20190302   
2  | 4      |20190301       
2  | 1      |20190301   
2  | 3      |20190303   
2  | 2      |20190305  

where ID is car number, Location is location id, and time is YYYYMMDD. I would like to write a SQL query to count the number for ''pair-wise locations'' for each carID in each month(YYYYMM): how many times the car existed in location i and j. That is, the final results should look like

ID|LOCATION 1|LOCATION 2|MONTH |count1|count 2  
1 | 1        |2         |201903| 2    | 1  
1 | 1        |3         |201903| 2    | 1  
1 | 1        |4         |201903| 2    | 2  
1 | 1        |5         |201903| 2    | 1   
1 | 2        |3         |201903| 1    | 1  
1 | 2        |4         |201903| 1    | 2  

where count1 is the count for location 1 and count2 is the count for location 2, and we construct this for every pair of location1 and location2.

To construct the pairs, I tried:

Select n1.location, n2.location
From
(
  Select location
  from table
) n1,
(
  Select location
  from table
) n2
Where n1.location < n2.location
Order by n1.location, n2.location

but I would like to count the number for each location (count1, count2) instead of count for pairs.

Can I do this in sub-query in SQL? Any advice would be appreciated.

like image 901
Kohei Avatar asked Aug 22 '26 08:08

Kohei


1 Answers

This is an odd request. You are looking for independent counts of the two locations, but aligned in one row (it is odd because there is lots of repeated data).

You can do this by aggregated before joining:

with l as (
      select l.id, l.location, date_format(l.time, '%Y%m') as yyyymm,
             count(*) as cnt
      from carlocations l
      group by l.id, l.location, date_format(l.time, '%Y%m') 
     )
select l1.id, l1.location as location1, l2.location2, l1.yyyymm, l1.cnt as cnt2, l2.cnt as cnt2
from l l1 join
     l l2
     on l1.id = l2.id and l1.yyyymm = l2.yyyymm and 
        l1.location < l2.location;

The with is supported in MySQL 8+. In earlier versions, you would need to repeat the subquery in the from clause.

EDIT:

Without CTEs, this looks like:

select l1.id, l1.location as location1, l2.location2, l1.yyyymm, l1.cnt as cnt2, l2.cnt as cnt2
from (select l.id, l.location, date_format(l.time, '%Y%m') as yyyymm,
             count(*) as cnt
      from carlocations l
      group by l.id, l.location, date_format(l.time, '%Y%m') 
     ) l1 join
     (select l.id, l.location, date_format(l.time, '%Y%m') as yyyymm,
             count(*) as cnt
      from carlocations l
      group by l.id, l.location, date_format(l.time, '%Y%m') 
     ) l2
     on l1.id = l2.id and l1.yyyymm = l2.yyyymm and 
        l1.location < l2.location;
like image 188
Gordon Linoff Avatar answered Aug 24 '26 04:08

Gordon Linoff



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!