【问题标题】:subquery in where clause (mysql)where子句中的子查询(mysql)
【发布时间】:2016-10-17 22:27:18
【问题描述】:
SELECT d.userID, (d.amountSuccessfulDeposits), (d.numberSuccessfulDeposits), cl.notes, d.asofdate, cl.send_date
FROM campaign_list cl
INNER JOIN deposit d
    on d.userid = cl.userid
WHERE cl.send_date > date(CURDATE()) - INTERVAL 7 DAY
    AND cl.send_date < date(CURDATE()) - INTERVAL 1 DAY
    AND d.asofdate > date(CURDATE()) - INTERVAL 7 DAY
    AND d.asofdate < date(CURDATE()) - INTERVAL 1 DAY
    and cl.notes in ('report11', 'report12', 'report13', 'report14', 'report15') 
    and d.asofdate > cl.send_date

在用户 ID 进入活动后(报告 11-15),查询会计算存款数量。 “send_date”是将用户 ID 输入广告系列的日期。但是,如果用户 ID 进一步进入另一组活动(报告 16-20),则在进入第二组活动 (16-20) 后进行的存款不应计入第一组活动 (11-15) 查询总数。

示例:

Bob 于 1/20 进入报告 11

Bob 于 1 月 21 日存入 100 美元

Bob 于 1 月 24 日进入报告 16

Bob 于 1 月 25 日存入 500 美元

因此,上述查询应该只为 Bob 返回 100 美元。

userID;  amountSuccessfulDeposits; numberSuccessfulDeposits; notes;    asofdate;     send_date
2575192;   100.00;                       1;                 report11    ;2016-01-21    ; 2016-01-20

以下查询无效

SELECT d.userID, (d.amountSuccessfulDeposits), (d.numberSuccessfulDeposits), cl.notes, d.asofdate, cl.send_date
FROM campaign_list cl
INNER JOIN deposit d
    on d.userid = cl.userid
WHERE cl.send_date > date(CURDATE()) - INTERVAL 7 DAY
    AND cl.send_date < date(CURDATE()) - INTERVAL 1 DAY
    AND d.asofdate > date(CURDATE()) - INTERVAL 7 DAY
    AND d.asofdate < date(CURDATE()) - INTERVAL 1 DAY
    and cl.notes in ('report11', 'report12', 'report13', 'report14', 'report15') 
    and d.asofdate > cl.send_date 
    AND (   
                (SELECT (ocl.send_date) 
                    FROM campaign_list ocl
                    WHERE ocl.userID = cl.userID
                        and ocl.notes in ('report16', 'report17', 'report18', 'report19', 'report20')                   
                ) > d.asofdate
        )

【问题讨论】:

  • 我们可以提供一些示例表数据和所需的输出吗?在这里获得高质量答案的最快方式。
  • 如何在表格中输入数字?当我从 mysql 客户端复制和粘贴时,它是一团糟
  • 在每行前面添加四个空格,这将强制使用等宽文本。因此,只需在记事本中创建一个漂亮的表格,在每行的开头添加四个空格,然后将其转储到您的问题中。
  • 好的。请看我的修改。

标签: mysql subquery where


【解决方案1】:

我会使用内联视图来获取后续广告系列的“最早”发送日期。 (不清楚具体的活动之间是否有任何关系。如果 Bob 进入任何活动 16-20,那么该活动的 send_date 是所有先前活动 11-15 的“截止日期”)

如果没有完全理解规范,我有点不愿意建议一个对你“有用”的查询。

但是根据给定的查询,我会做这样的事情:

SELECT d.userid
     , d.amountSuccessfulDeposits
     , d.numberSuccessfulDeposits
     , cl.notes
     , d.asofdate
     , cl.send_date
  FROM campaign_list cl

/* 在这里,我将创建一个内联视图,从后续活动 16-20 中获取每个用户 ID 的“最早”send_date。然后我对该集合进行外部连接,就像这样 */

  LEFT
  JOIN ( -- inline view to get earliest send_date
         SELECT ocl.userid
              , MIN(ocl.send_date) AS min_send_date
           FROM campaign_list ocl
          WHERE ocl.notes IN ('report16'
                             ,'report17'
                             ,'report18'
                             ,'report19'
                             ,'report20'
                             )
          GROUP BY ocl.userid
       ) oc
    ON oc.userid = cl.userid

/* 这样,我们将获得来自后续活动的“最早”sent_date。现在,这与 cl 活动中的行相关联。现在,我们可以加入deposit 表。我的偏好是将d 中列的所有 条件放入连接的ON 子句中,而不是WHERE 子句*/

  JOIN deposit d
    ON d.userid     = cl.userid
   AND d.asofdate   > cl.send_date
   AND d.asofdate   > DATE(NOW()) + INTERVAL -7 DAY
   AND d.asofdate   < DATE(NOW()) + INTERVAL -1 DAY

/* 现在这里是诀窍... */

   AND d.asofdate  <= IFNULL(oc.min_send_date,d.asofdate + INTERVAL 1 DAY)

包括一个条件,该条件限制来自deposit d 的行在后续广告系列中最早的send_date 或之后具有asofdate。棘手的部分是我们需要一个 OR 条件,当没有后续活动输入时,最早的 send_date 将为 NULL。如果oc.min_send_date 的值不是NULL,那么我们就使用它。如果它为 NULL,我们替换一个不会限制从d 返回的行的值。大于d.asofdate 的值将起作用。

我正在使用&lt;=(小于或等于)测试。如果我们只使用&lt; 测试,看起来depositasofdatesend_date 的日期相同 将是无人区......它不会' 不会为之前的活动返回,因为我们已经有一个 &gt;(大于)限制 d.asofdate &gt; cl.send_date

然后是 WHERE 子句,限制来自 cl 的行 */

 WHERE cl.send_date > DATE(NOW()) + INTERVAL -7 DAY
   AND cl.send_date < DATE(NOW()) + INTERVAL -1 DAY
   AND cl.notes    IN ('report11'
                      ,'report12'
                      ,'report13'
                      ,'report14'
                      ,'report15'
                      )

这就是我会采取的方法。


或者,从属子查询在 WHERE 子句中

如果出于某种原因您需要WHERE 子句中包含依赖子查询...您可以将其写为EXISTS 谓词...检查是否有任何后续活动send_date 早于 asofdate 例如

  AND NOT EXISTS ( SELECT 1
                     FROM campaign_list ocl
                    WHERE ocl.userID    = cl.userid
                      AND ocl.send_date < d.asofdate
                      AND ocl.notes    IN ('report16'
                                          ,'report17'
                                          ,'report18'
                                          ,'report19'
                                          ,'report20'
                                          )
                 )

或者,如果出于某种原因需要对查询的返回进行标量比较(进行小于/大于比较),那么子查询的返回必须是标量...查询必须返回单列并(最多)返回一行。

要确保返回的行不超过一行,您可以使用聚合(例如MIN(),或者您可以使用LIMIT 子句。您需要准备好处理将返回的 NULL 值如果没有找到行。

 AND ( d.asofdate <= IFNULL( ( SELECT ocl.send_date 
                                 FROM campaign_list ocl
                                WHERE ocl.userID     = cl.userid
                                  AND ocl.send_date >= d.asofdate
                                  AND ocl.notes     IN ('report16'
                                                       ,'report17'
                                                       ,'report18'
                                                       ,'report19'
                                                       ,'report20'
                                                       )
                                ORDER BY ocl.send_date ASC
                                LIMIT 1
                             )
                     , d.asofdate
                     )

MySQL 中可能存在一些限制,在 WHERE 子句中可能不允许依赖子查询。你肯定可以在HAVING 子句中得到它。但是在 GROUP BY 操作之后,HAVING 子句几乎在语句执行的最后一个被评估。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-13
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    • 2015-02-18
    相关资源
    最近更新 更多