3

I have a table of records which are either orphaned or come in pairs. Each item has a status associated with it. I need to select a random orphaned or paired record from the table (both records if its a pair), but NOT pairs that have a status that's 0 for either one.

I have the following table:

    Table: things
+------------+---------++---------+
| id         | cluster | status   |
+------------+---------++---------+
| 1          |  1      | 0        |
| 2          |  1      | 1        |
| 3          |  3      | 1        |
| 4          |  4      | 1        |
| 5          |  4      | 0        |
| 6          |  6      | 1        |
| 7          |  6      | 1        |
| 8          |  8      | 1        |
+------------+---------++---------+

My query

SELECT 
things.id as clusterid, 
things.id as id1, 
things2.id as id2,   
(SELECT count(id) FROM things WHERE cluster= clusterid) as num_nodes
FROM things
LEFT JOIN things AS things2 ON (
    things2.status= 1  
    AND things2.cluster= things.cluster
    AND things2.id!= things.cluster)
WHERE things.status= 1 
AND things.id= things.cluster
ORDER BY RAND() LIMIT 1

This query should return any of the following combinations of records at random (listing ids):

3
6 and 7
8

My query does this, but it also returns id 4, which shouldn't be here since the num_nodes will return 2, but since the status of the 2nd thing in the in the cluster is 0 (id 5), this pair should be disregarded.

I dont know how to use the num_nodes output to properly eliminate these pairs in pure mysql.

Bonus points if the result can be 2 separate rows from the table, instead of a joined single row. So if an orphaned record that's status = 1, result is 1 row. If its a paired record with both things having status = 1, 2 rows.

4

2 回答 2

1

This bypasses the no LIMIT in subqueries limitation. Slight tweak of eggyal's original answer.

  SELECT * FROM things AS t WHERE cluster IN (
    SELECT * FROM (
      SELECT things.cluster 
        FROM
                    things
          LEFT JOIN things AS things2 ON (
                things2.cluster = things.cluster
            AND things2.id     != things.cluster
          )
        WHERE
              things.status = 1
          AND (things2.id IS NULL OR things2.status = 1)
          AND things.id = things.cluster
        ORDER BY RAND()
        LIMIT 1
    ) as cheese
  )
于 2012-05-02T14:21:15.170 回答
0

You need to allow the LEFT JOIN even when the second thing from the cluster has a 0 status, then test for records that either have both things with status 1 or no second thing:

SELECT 
  things.id  AS clusterid, 
  things.id  AS id1, 
  things2.id AS id2,   
  (SELECT count(id) FROM things WHERE cluster = clusterid) AS num_nodes
    -- num_nodes could instead be defined as: IF(things2.id IS NULL, 1, 2)
FROM
            things
  LEFT JOIN things AS things2 ON (
        things2.cluster = things.cluster
    AND things2.id     != things.cluster
  )
WHERE
      things.status = 1
  AND (things2.id IS NULL OR things2.status = 1)
  AND things.id = things.cluster
ORDER BY RAND()
LIMIT 1

To return separate rows for each thing:

SELECT * FROM things AS t WHERE cluster IN (
  SELECT things.cluster 
    FROM
                things
      LEFT JOIN things AS things2 ON (
            things2.cluster = things.cluster
        AND things2.id     != things.cluster
      )
    WHERE
          things.status = 1
      AND (things2.id IS NULL OR things2.status = 1)
      AND things.id = things.cluster
    ORDER BY RAND()
    LIMIT 1
)
于 2012-05-02T13:34:51.743 回答