dongying195959 2015-10-26 16:43
浏览 139

推进ORM - UNION查询

I have looked through related questions on this on the website but none of them really answer my question. I have the following statements in a website using Propel:

$query = $query
  ->distinct()
  ->select(Request::getTransferFieldsWithRelations())
  ->leftJoinResponse("Response")
  ->joinWith("Request.SupportStatus SupportStatus")
  ->joinWith("Request.CustomerGroup CustomerGroup", Criteria::LEFT_JOIN)
  ->joinWith("Request.Customer Customer", Criteria::LEFT_JOIN)
  ->joinWith("Request.Site Site", Criteria::LEFT_JOIN)
  ->joinWith("Request.InternalUser InternalUser", Criteria::LEFT_JOIN)
  ->joinWith("Request.User User", Criteria::LEFT_JOIN)
  ->orderBy("CreatedDate", Criteria::ASC);

$conditions = array(
  "and" => array(),
  "or" => array()
);

if(isset($args["QueryText"]) && $args["QueryText"] != "") {
  $query = $query
    ->withColumn("(MATCH (Request.Subject, Request.Detail) AGAINST ('" . $args["QueryText"] . "' IN BOOLEAN MODE) + MATCH (Response.Response) AGAINST ('" . $args["QueryText"] . "' IN BOOLEAN MODE))", "RequestRelevance")
    ->condition('cond1', "(MATCH (Request.Subject, Request.Detail) AGAINST ('" . $args["QueryText"] . "' IN BOOLEAN MODE) + MATCH (Response.Response) AGAINST ('" . $args["QueryText"] . "' IN BOOLEAN MODE)) > 0.2")
    ->condition('cond2', 'Request.Id = ?', $args["QueryText"])
    ->where(array('cond1', 'cond2'), 'or')
    ->orderBy("RequestRelevance", Criteria::DESC);
}

if(isset($args["OpenCallsOnly"]) && $args["OpenCallsOnly"] == 1) {
  $query = $query
    ->useSupportStatusQuery()
      ->filterByOutstanding(1)
    ->endUse();
}

if(isset($args["ClosedCallsOnly"]) && $args["ClosedCallsOnly"] == 1) {
  $query = $query
    ->useSupportStatusQuery()
      ->filterByIsClosed(1)
    ->endUse();
}

...

foreach ($conditions as $key => $value) {
  if(!empty($value)){
    $query = $query
      ->where($value, $key);
  }
}

However, this query takes a good 20 seconds to execute on the website if sorting by ClosedCallsOnly (so nearly 50000 results) and over 8 seconds if using raw SQL. I have optimised it to the following query, using UNION statements:

    (SELECT DISTINCT
requests.requestID AS "Id", requests.subject AS "Subject", requests.detail AS "Detail", requests.created AS "CreatedDate", requests.lastresponsedate AS "LastResponseDate", SupportStatus.supportstatusID AS "SupportStatus.Id", SupportStatus.supportstatus AS "SupportStatus.Name", SupportStatus.isnew AS "SupportStatus.IsNew", SupportStatus.isclosed AS "SupportStatus.IsClosed", CustomerGroup.customergroupID AS "CustomerGroup.Id", CustomerGroup.customergroup AS "CustomerGroup.Name", Site.siteID AS "Site.Id", Site.site AS "Site.Name", InternalUser.userID AS "InternalUser.Id", InternalUser.username AS "InternalUser.Username", User.userID AS "User.Id", User.username AS "User.Username", Customer.customerID AS "Customer.Id", Customer.customer AS "Customer.Name", Customer.customergroupID AS "Customer.CustomerGroupId", Customer.rate AS "Customer.Rate"
FROM requests
  LEFT JOIN responses Response ON (requests.requestID=Response.requestID)
  INNER JOIN supportstatus SupportStatus ON (requests.supportstatusID=SupportStatus.supportstatusID)
  INNER JOIN customergroups CustomerGroup ON (requests.customergroupID=CustomerGroup.customergroupID)
  INNER JOIN customers Customer ON (requests.customerID=Customer.customerID)
  INNER JOIN sites Site ON (requests.siteID=Site.siteID)
  LEFT JOIN users InternalUser ON (requests.internal_userID=InternalUser.userID)
  LEFT JOIN users User ON (requests.userID=User.userID)
WHERE ((MATCH (requests.subject, requests.detail) AGAINST ('slow pc' IN BOOLEAN MODE)
  ))
ORDER BY requests.created ASC)
UNION
(SELECT DISTINCT
requests.requestID AS "Id", requests.subject AS "Subject", requests.detail AS "Detail", requests.created AS "CreatedDate", requests.lastresponsedate AS "LastResponseDate", SupportStatus.supportstatusID AS "SupportStatus.Id", SupportStatus.supportstatus AS "SupportStatus.Name", SupportStatus.isnew AS "SupportStatus.IsNew", SupportStatus.isclosed AS "SupportStatus.IsClosed", CustomerGroup.customergroupID AS "CustomerGroup.Id", CustomerGroup.customergroup AS "CustomerGroup.Name", Site.siteID AS "Site.Id", Site.site AS "Site.Name", InternalUser.userID AS "InternalUser.Id", InternalUser.username AS "InternalUser.Username", User.userID AS "User.Id", User.username AS "User.Username", Customer.customerID AS "Customer.Id", Customer.customer AS "Customer.Name", Customer.customergroupID AS "Customer.CustomerGroupId", Customer.rate AS "Customer.Rate"
FROM requests
  LEFT JOIN responses Response ON (requests.requestID=Response.requestID)
  INNER JOIN supportstatus SupportStatus ON (requests.supportstatusID=SupportStatus.supportstatusID)
  INNER JOIN customergroups CustomerGroup ON (requests.customergroupID=CustomerGroup.customergroupID)
  INNER JOIN customers Customer ON (requests.customerID=Customer.customerID)
  INNER JOIN sites Site ON (requests.siteID=Site.siteID)
  LEFT JOIN users InternalUser ON (requests.internal_userID=InternalUser.userID)
  LEFT JOIN users User ON (requests.userID=User.userID)
WHERE (requests.requestID = 'slow pc')
ORDER BY requests.created ASC)
UNION
(SELECT DISTINCT
Request.requestID AS "Id", Request.subject AS "Subject", Request.detail AS "Detail", Request.created AS "CreatedDate", Request.lastresponsedate AS "LastResponseDate", SupportStatus.supportstatusID AS "SupportStatus.Id", SupportStatus.supportstatus AS "SupportStatus.Name", SupportStatus.isnew AS "SupportStatus.IsNew", SupportStatus.isclosed AS "SupportStatus.IsClosed", CustomerGroup.customergroupID AS "CustomerGroup.Id", CustomerGroup.customergroup AS "CustomerGroup.Name", Site.siteID AS "Site.Id", Site.site AS "Site.Name", InternalUser.userID AS "InternalUser.Id", InternalUser.username AS "InternalUser.Username", User.userID AS "User.Id", User.username AS "User.Username", Customer.customerID AS "Customer.Id", Customer.customer AS "Customer.Name", Customer.customergroupID AS "Customer.CustomerGroupId", Customer.rate AS "Customer.Rate"
FROM responses
  LEFT JOIN requests Request ON (Request.requestID=responses.requestID)
  INNER JOIN supportstatus SupportStatus ON (Request.supportstatusID=SupportStatus.supportstatusID)
  INNER JOIN customergroups CustomerGroup ON (Request.customergroupID=CustomerGroup.customergroupID)
  INNER JOIN customers Customer ON (Request.customerID=Customer.customerID)
  INNER JOIN sites Site ON (Request.siteID=Site.siteID)
  LEFT JOIN users InternalUser ON (Request.internal_userID=InternalUser.userID)
  LEFT JOIN users User ON (Request.userID=User.userID)
WHERE ((
  MATCH (responses.response) AGAINST ('slow pc' IN BOOLEAN MODE)))
ORDER BY Request.created ASC)

Execution time of this statement improves roughly 8x which is really good but unfortunately, I am not sure how to translate it to a Propel query. From looking at other questions, it seems that using UNION in Propel isn't possible. I know that using SQL statements is possible in Propel but as Propel queries are being used everywhere else in this class, I am not sure how it would be possible? How could I implement this query into my website? If needed, I can provide more code for this class.

  • 写回答

2条回答 默认 最新

  • dongqing904999 2015-10-27 14:41
    关注

    There is an article about it on the propel blog, explaining when it is more interesting to use raw sql rather than query API, and your case seem to definitely fit the bill (lot of joins). http://propelorm.org/blog/2011/02/02/how-can-i-write-this-query-using-an-orm-.html

    Here is one of the presented use case:

    "This query is not object-oriented, it’s purely relational, so it doesn’t need an Object-Relational Mapping. The best way to execute this query inside an ORM is to skip the ORM and use PDO directly :"

    $con = Propel::getConnection();
    $query = 'SELECT COUNT(t1.user) AS users, t1.choice AS lft, t2.choice AS rgt
      FROM choice t1 iNNER JOIN choice t2 ON (t1.user = t2.user)
      WHERE t1.choice IN (?, ?) AND t2.choice IN (?, ?)
      GROUP BY t1.choice, t2.choice';
    $stmt = $con->prepare($query);
    $stmt->bindValue(1, 'foo');
    $stmt->bindValue(2, 'bar');
    $stmt->bindValue(3, 'baz');
    $stmt->bindValue(4, 'foz');
    $res = $stmt->execute();
    
    评论

报告相同问题?

悬赏问题

  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计