【问题标题】:Null Values during Query查询期间的空值
【发布时间】:2013-12-30 20:37:45
【问题描述】:

我有下表,很简单。

==========================================================================
attendanceID   |   agentID   |   incurredDate   |   points   |   comment
==========================================================================
    10         |  vimunson   |    2013-07-22    |     2      | Some Text
    11         |  vimunson   |    2013-07-29    |     2      | Some Text
    12         |  vimunson   |    2013-12-06    |     1      | Some Text

他的查询如下:

SELECT 
    attendanceID,
    agentID,
    incurredDate,
    leadDate,
    points,
    @1F:=IF(incurredDate <= curdate() - 90
            AND leadDate = NULL,
        points - 1,
        IF(DATEDIFF(leadDate, incurredDate) > 90,
            points - 1,
            points)) AS '1stFallOff',
    @2F:=IF(incurredDate <= curdate() - 180
            AND leadDate = NULL,
        points - 2,
        IF(DATEDIFF(leadDate, incurredDate) > 180,
            points - 2,
            @1F)) AS '2ndFallOff',
    IF(@total < 0, 0, @total:=@total + @2F) AS Total,
    comment,
    linked
FROM
    (SELECT 
        attendanceID,
            mo.agentID,
            @r AS leadDate,
            (@r:=incurredDate) AS incurredDate,
            comment,
            points,
            linked
    FROM
        (SELECT 
        m . *
    FROM
        (SELECT @_date = NULL, @total:=0) varaible, attendance m
    ORDER by agentID , incurredDate desc) mo
    where
        agentID = 'vimunson'
            AND (case
            WHEN @_date is NULL or @_date <> incurredDate THEN @r:=NULL
            ELSE NULL
        END IS NULL)
            AND (@_date:=incurredDate) IS NOT NULL) T
ORDER BY agentID , incurredDate

当我运行查询时,它返回以下内容:

========================================================================================================================================
attendanceID   |   agentID   |   incurredDate   |   leadDate   |   points   |   1stFallOff   |   2ndFallOff   |   Total   |   comment
========================================================================================================================================
   10          |  vimunson   |   2013-07-22     |     NULL     |     2      |       2        |       2        |     2     |   Some Text
   11          |  vimunson   |   2013-07-29     |     NULL     |     2      |       2        |       2        |     4     |   Some Text
   12          |  vimunson   |   2013-12-06     |     NULL     |     1      |       2        |       1        |     5     |   Some Text

我无法弄清楚为什么leadDate 列是“空”。我已将其缩小到用户会话。例如,如果我使用相同的用户会话再次运行它,它将返回我想要的内容。

【问题讨论】:

  • 对我来说只有一次为空。这是您想要的结果吗:sqlfiddle.com/#!2/6f0ac/3/0
  • 是的,leadDate 在同一个用户会话中第一次为空。但是,如果我在同一个用户中并再次运行它,它会给我我想要的结果。
  • 那么为什么当我在服务器上运行它但在 sqlfiddle 上运行良好时这不起作用?

标签: mysql sql select stored-procedures join


【解决方案1】:

变量@r@_date 的传递方式取决于对查询的某些部分求值的特定顺序。在声明式而非命令式的查询语言中,这是一个冒险的假设。查询优化器越复杂,该查询的行为就越不可预测。一个“简单”的引擎可能会遵循您的意图,另一个引擎可能会随着您的使用而调整其行为,例如因为它使用临时索引来提高查询性能。

在需要将值从一行传递到另一行的情况下,最好使用游标。 http://dev.mysql.com/doc/refman/5.0/en/cursors.html

编辑:下面的示例代码。

我专注于列“leadDate”;衰减列和总列的实现应该类似。

CREATE PROCEDURE MyProc()
BEGIN
    DECLARE done int DEFAULT FALSE;
    DECLARE currentAttendanceID int;
    DECLARE currentAgentID, previousAgentID varchar(8);
    DECLARE currentIncurredDate date;
    DECLARE currentLeadDate date;
    DECLARE currentPoints int;
    DECLARE currentComment varchar(9);
    DECLARE myCursor CURSOR FOR
        SELECT attendanceID, agentID, incurredDate, points, comment
        FROM attendance
        ORDER BY agentID, incurredDate DESC;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    CREATE TEMPORARY TABLE myTemp (
        attendanceID int,
        agentID varchar(8),
        incurredDate date,
        leadDate date,
        points int,
        comment varchar(9)
    ) ENGINE=MEMORY;

    OPEN myCursor;

    SET previousAgentID := NULL;

    read_loop: LOOP
        FETCH myCursor INTO currentAttendanceID, currentAgentID, currentIncurredDate, currentPoints, currentComment;
        IF done THEN
            LEAVE read_loop;
        END IF;
        IF previousAgentID IS NULL OR previousAgentID <> currentAgentID THEN
            SET currentLeadDate := NULL;
            SET previousAgentID := currentAgentID;
        END IF;
        INSERT INTO myTemp VALUES (currentAttendanceID, currentAgentID, currentIncurredDate, currentLeadDate, currentPoints, currentComment);
        SET currentLeadDate := currentIncurredDate;
    END LOOP;

    CLOSE myCursor;

    SELECT *
    FROM myTemp
    ORDER BY agentID, incurredDate;

    DROP TABLE myTemp;
END

FYC:http://sqlfiddle.com/#!2/910a3/1/0

【讨论】:

  • 能否提供一个示例来说明您将如何实现这一目标?我看到光标背后的逻辑,但由于某种原因它没有点击。
  • 感谢一百万 @Ruud,经过这么长时间的努力,我能够完成项目的这一部分。
【解决方案2】:

试试这个:

SELECT a.attendanceID, a.agentID, a.incurredDate, a.leadDate, a.points, 
       @1F:= (CASE WHEN incurredDate <= CURDATE()-90 AND leadDate=NULL THEN points-1 
                   WHEN DATEDIFF(leadDate, incurredDate) > 90 THEN points-1 
                   ELSE points 
              END) AS '1stFallOff',
       @2F:= (CASE WHEN incurredDate <= CURDATE()-180 AND leadDate=NULL THEN points-2 
                   WHEN DATEDIFF(leadDate, incurredDate) > 180 THEN points-2 
                   ELSE @1F 
              END) AS '2ndFallOff',
     (@Total:=@Total + a.points) AS Total, a.comment 
FROM (SELECT a.attendanceID, a.agentID, a.incurredDate, b.incurredDate leadDate, a.points, a.comment 
      FROM attendance a 
      LEFT JOIN attendance b ON a.agentID = b.agentID AND a.incurredDate < b.incurredDate 
      GROUP BY a.agentID, a.incurredDate
     ) AS A, (SELECT @Total:=0, @1F:=0, @2F:=0) B;

查看SQL FIDDLE DEMO

输出

| ATTENDANCEID |  AGENTID |                    INCURREDDATE |                        LEADDATE | POINTS | 1STFALLOFF | 2NDFALLOFF | TOTAL |   COMMENT |
|--------------|----------|---------------------------------|---------------------------------|--------|------------|------------|-------|-----------|
|           10 | vimunson |     July, 22 2013 00:00:00+0000 |     July, 29 2013 00:00:00+0000 |      2 |          2 |          2 |     2 | Some Text |
|           11 | vimunson |     July, 29 2013 00:00:00+0000 | December, 06 2013 00:00:00+0000 |      2 |          1 |          1 |     4 | Some Text |
|           12 | vimunson | December, 06 2013 00:00:00+0000 |                          (null) |      1 |          1 |          1 |     5 | Some Text |

【讨论】:

  • 感谢您的宝贵时间,但不幸的是 JOIN 会产生不可预知的结果,我需要能够一遍又一遍地为我提供一致结果而不会出错的东西。
  • @BrandonWilson 您在哪里可以找到我的查询的不可预测行为。详细解释一下你输入日期的结果
  • 我发现了错误,我使用的是ORDER BY 而不是GROUP BY。一旦我修复了total 列,它就不会像sqlfiddle.com/#!2/b5cde5/3/0 那样计算
  • @BrandonWilson 检查我更新的答案。我根据需要添加了三列。无需使用游标。使用单个 SQL 脚本即可实现目标
【解决方案3】:

在查看了多个结果后,我得出了我所期望的结果。我输入了更多数据和以下语法。我喜欢光标的想法,但它不适合我的使用,所以我没有使用它。我不想使用CASE 或任何JOINS,因为它们可能很复杂。

http://sqlfiddle.com/#!2/2fb86/1

SELECT 
    attendanceID,
    agentID,
    incurredDate,
    @ld:=(select 
            incurredDate
        from
            attendance
        where
            incurredDate > a.incurredDate
                and agentID = a.agentID
        order by incurredDate
        limit 1) leadDate,
    points,
    @1F:=IF(incurredDate <= DATE_SUB(curdate(),
            INTERVAL IF(incurredDate < '2013-12-02', 90, 60) DAY)
            AND @ld <=> NULL,
        points - 1,
        IF(DATEDIFF(COALESCE(@ld, '1900-01-01'),
                    incurredDate) > IF(incurredDate < '2013-12-02', 90, 60),
            points - 1,
            points)) AS '1stFallOff',
    @2F:=IF(incurredDate <= DATE_SUB(curdate(),
            INTERVAL IF(incurredDate < '2013-12-02',
                180,
                120) DAY)
            AND getLeadDate(incurredDate, agentID) <=> NULL,
        points - 1,
        IF(DATEDIFF(COALESCE(@ld, '1900-01-01'),
                    incurredDate) > IF(incurredDate < '2013-12-02',
                180,
                120),
            points - 2,
            @1F)) AS '2ndFallOff',
    IF((@total + @2F) < 0,
        0,
        IF(DATE_ADD(incurredDate, INTERVAL 365 DAY) <= CURDATE(),
            @total:=0,
            @total:=@total + @2F)) AS Total,
    comment,
    linked,
    DATE_ADD(incurredDate, INTERVAL 365 DAY) AS 'fallOffDate'
FROM
    (SELECT @total:=0) v,
    attendance a
WHERE
    agentID = 'vimunson'
GROUP BY agentID , incurredDate

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-25
    • 1970-01-01
    • 2012-07-28
    • 2012-10-09
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多