【问题标题】:Retrieve the last line in one to one relation with millions of rows检索具有数百万行的一对一关系的最后一行
【发布时间】:2015-02-19 06:25:56
【问题描述】:

很少发生的情况是一对一的情况,第二个表可以为第一个表提供数百万个结果。例如,我有一个包含数百万个“radacct”的“radcliente”表,但只需要使用最后一个帐户进行过滤。以下是更好解释的示例:

这是标准:

$criteria = new CDbCriteria();
$criteria->with = [
    'acct', // slow because it will take millions of lines to have only the last
];
$criteria->together = true;
$clientes = Cliente::model()->findAll($criteria);

这是 Yii 生成的查询(非常慢,超过 40 秒,它返回数百万行以在 AR 中只使用一个):

SELECT
  `t`.`id` AS `t0_c0`,
  -- ...
  `t`.`spc_serasa` AS `t0_c56`,
  `acct`.`radacctid` AS `t1_c0`,
  -- ...
  `acct`.`cliente_id` AS `t1_c27`
FROM
  `radcliente` `t`
  LEFT OUTER JOIN `radacct` `acct` ON (`acct`.`cliente_id`=`t`.`id`)
ORDER BY
  radacctid DESC

应用我的解决方案限制加入一行后(这么快!200ms-):

SELECT
  `t`.`id` AS `t0_c0`,
  ..
  `t`.`spc_serasa` AS `t0_c56`,
  `acct`.`radacctid` AS `t1_c0`,
  -- ...
  `acct`.`cliente_id` AS `t1_c27`
FROM
  `radcliente` `t`
  LEFT OUTER JOIN `radacct` `acct` ON (
    acct.radacctid = (
      SELECT   radacctid
      FROM     `radacct` `acct`
      WHERE    (acct.cliente_id = t.id)
      ORDER BY radacctid DESC
      LIMIT 1
    )
  )

这是 CActiveDataProvider 生成的总项目数查询,我的解决方案是限制连接到一个(慢,10 秒计数):

SELECT
  COUNT(*)
FROM (
  SELECT
    `t`.`id` AS `t0_c0`,
    -- ...
    `t`.`spc_serasa` AS `t0_c56`,
    `endereco_instalacao`.`id` AS `t1_c0`,
    `telefones`.`id` AS `t2_c0`,
    `telefones`.`telefone` AS `t2_c3`,
    `emails`.`id` AS `t3_c0`,
    `emails`.`email` AS `t3_c3`,
    `metodo_cobranca`.`id` AS `t4_c0`,
    `acct`.`radacctid` AS `t5_c0`,
    `acct`.`framedipaddress` AS `t5_c22`
  FROM
    `radcliente` `t`
    LEFT OUTER JOIN `radcliente_endereco_instalacao` `endereco_instalacao` ON ( 
      endereco_instalacao.id = (
        SELECT id
        FROM `radcliente_endereco_instalacao` `endereco_instalacao`
        WHERE (
          endereco_instalacao.cliente_id = t.id
        )
        LIMIT 1
      )
    )
    LEFT OUTER JOIN `radcliente_telefone` `telefones` ON (`telefones`.`cliente_id`=`t`.`id`)
    LEFT OUTER JOIN `radcliente_email` `emails` ON (`emails`.`cliente_id`=`t`.`id`)
    LEFT OUTER JOIN `radmetodo_cobranca` `metodo_cobranca` ON (
      metodo_cobranca.id = (
        SELECT id
        FROM   `radmetodo_cobranca` `metodo_cobranca`
        WHERE  (metodo_cobranca.cliente_id = t.id)
               AND (metodo_cobranca.arquivo = 'nao')
        ORDER BY metodo_cobranca.id DESC
        LIMIT 1
      )
    )
    LEFT OUTER JOIN `radacct` `acct` ON (
      acct.radacctid = (
        SELECT   radacctid
        FROM     `radacct` `acct`
        WHERE    (acct.cliente_id = t.id)
        ORDER BY radacctid DESC
        LIMIT 1
     )
  )
  GROUP BY t.id
) sq

但问题是CActiveDataProvider生成的计数(返回结果大约需要10秒)有办法优化而不必丢失关系(因为我以后需要按关系过滤)?

更新

感谢您的回复。我一直在做一些测试,并注意到在所有情况下都很慢,表'radacct'因其大小而加剧了问题,因此不应限制子查询中的 1。按照模型和链接访问系统,如果需要认证是:

访问:

http://177.86.111.30/dev2/teste

用户名:帮助

密码:1

要下载 radcliente 和 radacct 的模型和架​​构:http://177.86.111.30/files.zip

【问题讨论】:

  • 请列出问题表中定义了哪些索引。
  • 1.考虑将问题减少到证明该概念所需的最低限度。 2. 然后提供适当的 DDL(和/或 sqlfiddle)。 3.然后提供一个想要的结果集,对应2中提供的数据集。
  • 问题与'radacct'表中的数百万行有关,无法放在SQLFiddle上,我将打开我的服务器并在页面中传递一个链接进行测试。谢谢。
  • 可能你需要跳过 Active Record 并使用其他 Yii DB API 来处理你的查询yiiframework.com/forum/index.php/topic/…
  • 只加载了10条记录,这个数是统计要在CGridView中使用的总数。

标签: mysql sql database performance yii


【解决方案1】:

尝试添加另一个 JOIN(不是 LEFT JOIN),而不是 ON id = ( SELECT ... LIMIT 1 )

JOIN ( SELECT ... LIMIT 1 ) x ON ...

我担心您的代码会在需要检查 ON 子句时重复评估该子查询。我的重写将导致子查询只发生一次。

您的查询看起来像一个“相关”子查询,因此如果可能,您需要将其改写为不相关。

【讨论】:

  • 我也试过了。我试过直接放 COUNT,但是 GROUP BY 没有问题,去掉 GROUP BY 计数不正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-29
  • 2013-07-12
  • 2011-04-22
  • 2011-01-04
  • 1970-01-01
相关资源
最近更新 更多