选择1个具有复杂滤波的随机行

时间:2017-04-20 10:05:48

标签: mysql sql performance

我有2张桌子:

第一张表users

+-------------------------+---------+------+-----+---------+-------+
| Field                   | Type    | Null | Key | Default | Extra |
+-------------------------+---------+------+-----+---------+-------+
| id                      | int(11) | NO   | PRI | NULL    |       |
| first_name              | text    | NO   |     | NULL    |       |
| age                     | int(11) | YES  |     | NULL    |       |
| settings                | text    | YES  |     | NULL    |       |
+-------------------------+---------+------+-----+---------+-------+

第二张表proposals

+---------+---------+------+-----+---------+----------------+
| Field   | Type    | Null | Key | Default | Extra          |
+---------+---------+------+-----+---------+----------------+
| id      | int(11) | NO   | PRI | NULL    | auto_increment |
| from_id | int(11) | NO   |     | NULL    |                |
| to_id   | int(11) | NO   |     | NULL    |                |
| status  | int(11) | NO   |     | NULL    |                |
+---------+---------+------+-----+---------+----------------+

我需要id to_idproposals以内SELECT DISTINCT * FROM profiles WHERE profiles.first_name IS NOT NULL AND NOT EXISTS ( SELECT * FROM proposal WHERE proposal.to_id = profiles.id ) LIMIT 0 , 1 以内的用户获取1 随机

我正在使用这个sql执行它(没有rand):

1 row in set (0.00 sec)

表现很好:1 row in set (1.78 sec)

性能非常糟糕:ORDER BY RAND()当我将users.id添加到最后

我在MAX(id)中遇到了大漏洞而我无法使用limit

之类的内容

我尝试设置随机... LIMIT 1234 , 1; Empty set (2.71 sec) ,例如:

users.id

但它也需要很长时间:(

如何获得具有良好性能的proposals.to_idprofiles不存在的随机1位用户?

我认为我需要先使用rand()获取所有$('.el').click(function(){ var el = this; // assign this to el my_function(function(){ $(el).hide(); // use el here }); }); ,然后对其进行过滤,但我不知道该怎么做。

4 个答案:

答案 0 :(得分:1)

我有两个问题解决方案。

1)随机ID,来自https://stackoverflow.com/a/4329447/2051938

SELECT *
FROM profiles AS r1
JOIN
    (SELECT CEIL(RAND() *
                     (SELECT MAX(id)
                        FROM profiles)) AS id)
        AS r2
WHERE
    r1.id >= r2.id
    AND
    r1.first_name IS NOT NULL
AND
NOT EXISTS (
    SELECT *
    FROM proposal
    WHERE
        proposal.to_id = r1.id
)
LIMIT 0 , 1

2)ORDER BY RAND()

SELECT *
FROM
    (
        SELECT *
        FROM profiles
        WHERE
            profiles.first_name IS NOT NULL
        ORDER BY RAND()
    ) AS users
WHERE
    NOT EXISTS (
        SELECT *
        FROM proposal
        WHERE
            proposal.to_id = users.id
    )
LIMIT 0 , 1

第一种解决方案更快,但id""漏洞存在问题。当你从最后得到id时(用户可能会比匹配更早结束)

第二种解决方案速度较慢但没有缺陷!

答案 1 :(得分:0)

您是否尝试将not exists切换为left join

SELECT DISTINCT *
FROM   profiles t1
LEFT JOIN
       proposal t2
ON     t1.id = t2.to_id
WHERE  t1.first_name IS NOT NULL AND
       t2.to_id IS NULL
ORDER BY RAND()
LIMIT 0 , 1

这会返回profiles的所有行,并且会向提案行中不是matched的行返回,它会分配NULL值,您可以对其进行过滤。

结果应该相同,但性能可能会更好。

答案 2 :(得分:0)

由于RAND()函数为结果中的每一行指定一个随机数,因此性能将与记录数成正比。

如果您只想选择一条(随机)记录,则可以应用LIMIT <random number from 0 to record count>, 1

e.g:

SELECT u.id, count(u.id) as `count`
FROM users u
WHERE
    first_name IS NOT NULL
AND
NOT EXISTS (
    SELECT *
    FROM proposal
    WHERE
        proposal.to_id = u.id
)
LIMIT RAND(0, count-1) , 1

我还没有尝试过执行此查询,但是,MySQL抱怨在count中使用RAND,您可以单独计算计数并替换此查询中的值。

答案 3 :(得分:0)

首先,我不认为SELECT p.* FROM profiles p WHERE p.first_name IS NOT NULL AND NOT EXISTS (SELECT 1 FROM proposal pr WHERE pr.to_id = p.id ) ORDER BY rand() LIMIT 0 , 1; 是必要的。所以,尝试删除:

SELECT p.*
FROM profiles
WHERE p.first_name IS NOT NULL AND
      NOT EXISTS (SELECT 1
                  FROM proposal pr
                  WHERE pr.to_id = p.id
                 ) AND
      rand() < 0.01
ORDER BY rand()
LIMIT 0, 1;

这可能会有所帮助。然后,减少花费的时间的相对简单的方法是减少数据量。如果您知道总有数千行符合条件,那么您可以这样做:

SELECT p.*
FROM (SELECT p.*, (@rn := @rn + 1) as rn
      FROM profiles p CROSS JOIN
           (SELECT @rn := 0) params
      WHERE p.first_name IS NOT NULL AND
            NOT EXISTS (SELECT 1
                        FROM proposal pr
                        WHERE pr.to_id = p.id
                       ) 
     ) p
WHERE rand() < 100 / @rn
ORDER BY rand()
LIMIT 0, 1;

诀窍是找到确保您至少获得一行的比较值。这很棘手,因为你有另一组数据。这是一个使用子查询的方法:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:mes="http://www.orange.com/Webalc/Interfaces/ManageSupplierQuote/RequestForQuotation/v3/message">
   <soapenv:Header/>
   <soapenv:Body>
      <mes:UpdateSupplierQuote>
         <!--Zero or more repetitions:-->
         <SupplierQuote>
            <Requester>
               <local_userName>anant4</local_userName>
               <!--Optional:-->
               <local_SalesID></local_SalesID>
               <!--Optional:-->
               <local_SourcingFTID></local_SourcingFTID>
               <!--Optional:-->
               <local_AnalystBuyer>Analyst</local_AnalystBuyer>
            </Requester>
            <!--1 to 100 repetitions:-->
            <QuoteItem>
               <quoteItemID>m19</quoteItemID>
               <simulation_id>10061191</simulation_id>

这使用变量来计算行数,然后随机选择其中的100个进行处理。当随机选择100行时,非常非常非常可能至少选择一行。

这种方法的缺点是子查询需要实现,这会增加查询的成本。然而,它比完整数据的分类便宜。