I have a large table with which I need to do some analysis. The table is a log of IPs:

| Field       | Type                | Null | Key | Default | Extra |
| src         | int(10) unsigned    | NO   | PRI | 0       |       |
| dst         | int(10) unsigned    | NO   | PRI | 0       |       |
| packets     | int(10) unsigned    | YES  |     | NULL    |       |
| bytes       | int(16) unsigned    | YES  |     | NULL    |       |
| capturedate | int(10) unsigned    | NO   | PRI | 0       |       |

Every hour the past hours, IP activity is collected. The time this happens is capturedate. At that time each IP pairing (unique source to destination) is stored along with the number of packets and bytes sent from source to destination.

I've written some code for a webpage that queries the database to get a "vertex cover". It collects all the IP pairings and displays each IP and how many other unique IPs it has communicated with.

ip | verticies | 567 | 234

So, IP '' has communicated with 567 other unique IPs (as either source or destination). With millions of records in this table efficiency is an issue.

My solution is this:

First I added a view for a subquery I would reuse:

CREATE VIEW pair AS SELECT src, dst FROM iplogs GROUP BY src, dst;

Then the actual query is...

SELECT INET_NTOA(ip) AS ip, SUM(verts) as verticies
FROM (SELECT src AS ip, COUNT(src) AS verts FROM pair GROUP BY src
SELECT dst AS ip, COUNT(dst) AS verts FROM pair GROUP BY dst) B

Is this the most efficient way to get the results? Will the "pair" view update every time the query is made? Will "pair" be queried once or twice?


1 Answer 1


Since your primary key contains both src and dst you might be able to get away with something like the following:

SELECT IF (src<dst,src,dst), COUNT(*)
FROM   iplogs
SELECT IF (src<dst,dst,src), COUNT(*)
FROM iplogs
GROUP by 1

An explain suggests the query above can use the primary index (assuming the captured field is not first or that a suitable expression using the captured date is added to the where clause if it is first).

#id, select_type, table,  type,  possible_keys, key,     key_len, ref, rows, Extra
1,   PRIMARY,     iplogs, index, PRIMARY,       PRIMARY, 105,        , 6,    Using index; Using temporary; Using filesort
2,   UNION,       iplogs, index, PRIMARY,       PRIMARY, 105,        , 6,    Using index; Using temporary; Using filesort
 ,   UNION RESULT, <union1,2>,   ALL, , , , , , Using temporary

If you put EXPLAIN in front of your query you'll get details on how it will execute.

Getting down to two index scans should be as efficient as you can get and a bit simpler than using a view.

However, beware, I've only done rudimentary testing on a small data set at this point.

EDIT: With more details provided I've been able to test this and provide an improved response.

The union query given below returns two distinct portions of results. Adding these two together appears to provide the proper total. Here is a bit of PHP/MySQL code taken from some testing efforts against the supplied data.

$sql  = "SELECT IF (src<dst,src,dst) src\n";
$sql .= "      ,COUNT(DISTINCT(IF (src<dst,concat(src,'|',dst),concat(dst,'|',src)))) answer\n";
$sql .= "      ,COUNT(*) raw\n";
$sql .= "FROM   iplogs\n";
$sql .= "GROUP BY 1\n";
$sql .= "UNION\n";
$sql .= "SELECT IF (src<dst,dst,src)\n";
$sql .= "      ,COUNT(DISTINCT(IF (src<dst,concat(src,'|',dst),concat(dst,'|',src))))\n";
$sql .= "      ,COUNT(*)\n";
$sql .= "FROM iplogs\n";
$sql .= "GROUP by 1\n";
if ( !$res = mysqli_query($link,$sql) )
  die("ERROR: Unable to query vertex counts from database!\n");

However, as you can imagine, you have to post-process the query in order to add the values together to get what should be the right answer. This could be done by loading the returned records into a vertex table as is if consuming processes group by IP and sum the response.

Alternately, here is some code that does the summing and would produce records suitable for storing in a vertex table.

$qryqty = 0;
$qrylist = array();
while ( $row = mysqli_fetch_assoc($res) )
  $src = $row['src'];
  if ( !isset($qrylist[$src]) )
    $qrylist[$src] = 0;
  $qrylist[$src] += $row['answer'];

echo "$qryqty records loaded via query<br>\n";

So, I guess there is a trade off between SQL efficiency and having to do some extra processing to sum the results either before or after storing them.

NOTE: There are no IP translations as they were loaded as text for this effort.

  • \$\begingroup\$ Max, I've run this query on some test data. It gives similar but the same results as my query. I believe the reason is that it doesn't account for multiple sets of repeated src and dst pairs that were collected on different capture dates. Note that capturedate is part of the primary key. This is why I use the view. \$\endgroup\$
    – Gn13l
    Commented Jun 15, 2015 at 18:27
  • \$\begingroup\$ Gn13l, if you are able to come up with a small dataset that demonstrates a difference I'll be happy to load it up and look at it. It wasn't clear to me whether vertices are counted for some particular date range used, the whole data set or for each time period. You could try it with the capture date added to a where clause for each select? \$\endgroup\$
    – A Smith
    Commented Jun 15, 2015 at 18:44
  • \$\begingroup\$ Here is some test data. scribd.com/doc/268767282/iplogs-Test-Data \$\endgroup\$
    – Gn13l
    Commented Jun 15, 2015 at 19:32
  • \$\begingroup\$ All that matters is how many other unique IPs each IP has communicated with. Repeat entries because of different capture dates will produce erroneous data. \$\endgroup\$
    – Gn13l
    Commented Jun 15, 2015 at 19:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.