无法优化此复杂的SQL查询

时间:2013-03-20 20:13:39

标签: sql mysqli prepared-statement

由于需要使用AES_DECRYPT以及其他一些棘手的情况,我有一个非常棘手的SQL查询需要很长时间。首先,这是查询:

SELECT
CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ),
timestamp,
category,
status

FROM email_statuses

WHERE 

timestamp IN (
    SELECT MAX(timestamp)
    FROM email_statuses
    WHERE ( CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ?
        OR CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ? )
    AND category = 'EMAIL_TEMPLATE_01'
)

OR

timestamp IN (
    SELECT MAX(timestamp)
    FROM email_statuses
    WHERE ( CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ?
        OR CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ? )
    AND category = 'EMAIL_TEMPLATE_02'
) 

OR

timestamp IN (
    SELECT MAX(timestamp)
    FROM email_statuses
    WHERE ( CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ?
        OR CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ? )
    AND category = 'EMAIL_TEMPLATE_03'
);

在每个块中都显示时间戳IN(...第一个?是用户的主电子邮件,第二个?是用户的辅助电子邮件。

此查询的基本操作是返回3封电子邮件中每一封邮件的最新状态列表(category =电子邮件的名称)。对于该用户,我只需要每个LATEST状态。例如,使用“email@domain.com”的查询结果可能如下所示:

email---------------timestamp----------category-------------status---------  

email@domain.com----0000-00-00 etc-----EMAIL_TEMPLATE_01----Sent  
email@domain.com----0000-00-00 etc-----EMAIL_TEMPLATE_02----Open & click through  
email@domain.com----0000-00-00 etc-----EMAIL_TEMPLATE_03----Open

由于email_status表现在超过1000条记录,因此查询最多需要30秒,因为它需要在状态表中的每个加密电子邮件行上运行convert / AES_DECRYPT。有关优化方法的任何想法?我不是一个SQL专业人士。

3 个答案:

答案 0 :(得分:2)

您可以做的一件事就是执行以下操作:

  1. 首先AES加密提供电子邮件地址的参数
  2. 重写每个查询以根据加密参数
  3. 测试数据库表中的实际值

    因此,每个查询都会变成:

    // precompute the encrypted parameter (maybe on the PHP side, before executing the query)
    
    SELECT MAX(timestamp)
    FROM email_statuses
    WHERE email = ?              -- here pass the already encrpyed paramter
        OR email = ?             --       .. same here
    AND category = 'EMAIL_TEMPLATE_02'
    

    基本思路是避免在查询中对每个测试执行AES_ENCRYPTAES_DECRYPT。而只是加密参数,并使用它进行测试..

答案 1 :(得分:2)

将所有in语句合并到一个联接中:

SELECT distinct CONVERT( AES_DECRYPT( es.email, '$key' ) USING UTF8 ),
       es.timestamp, es.category, es.status
FROM email_statuses es join
     (select category, MAX(timestamp) as maxtimestamp
      from email_statuses
      where ( CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ? or
            CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ? ) and
            category in ('EMAIL_TEMPLATE_01', 'EMAIL_TEMPLATE_02', 'EMAIL_TEMPLATE_03')
      group by category
     ) csum
     on es.timestamp = csum.category

我还添加了distinct,以防多个类别匹配。

答案 2 :(得分:1)

由于您有三个子查询,每个子查询都必须解密所有记录。如果你可以在一个子查询中执行此操作,则应该花费三分之一时间。

SELECT CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ),
       timestamp, category, status
FROM email_statuses
WHERE 

timestamp IN (
    SELECT MAX(timestamp)
    FROM email_statuses
    WHERE ( CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ?
         OR CONVERT( AES_DECRYPT( email, '$key' ) USING UTF8 ) = ? )
        AND category IN ('EMAIL_TEMPLATE_01', 'EMAIL_TEMPLATE_02', 'EMAIL_TEMPLATE_03')
    GROUP BY category
)