【问题标题】:SQL query advice (replacing exists with joins)SQL 查询建议(用连接替换存在)
【发布时间】:2013-08-15 08:05:35
【问题描述】:

我有以下字段和索引的表:
我需要做一个查询:

查表,如果有Dial事件,之后(同 unique_id) 没有 BridgeHangup 事件发生

如果有 NewCallerId 事件,之后没有 Hangup 事件(具有相同的 unique_id)或没有 Dial 事件(call_id = current unique_id)

有一个查询,但我没有让它工作,用户的回答也没有帮助。 这是在 395 秒内完成工作的查询(太长):

SELECT t1.* 
FROM   ASTERISK t1 
WHERE  EXISTS (SELECT t2.* 
               FROM   ASTERISK t2 
               WHERE  t1.OPERATOR_DIAL = '$extension' 
                      AND t1.EVENT = 'Dial' 
                      AND NOT EXISTS (SELECT t3.* 
                                      FROM   ASTERISK t3 
                                      WHERE  t3.UNIQUE_ID = t1.UNIQUE_ID 
                                             AND ( t3.EVENT = 'Hangup' 
                                                    OR t3.EVENT = 'Bridge' ))) 
        OR EXISTS (SELECT t4.* 
                   FROM   ASTERISK t4 
                   WHERE  t1.EVENT = 'NewCallerid' 
                          AND t1.OPERATOR_DIAL = '$extension' 
                          AND NOT EXISTS (SELECT t5.* 
                                          FROM   ASTERISK t5 
                                          WHERE  ( t5.UNIQUE_ID = t1.UNIQUE_ID 
                                                   AND t5.EVENT = 'Hangup' ) 
                                                  OR ( t5.CALL_ID = t1.UNIQUE_ID 
                                                       AND t5.EVENT = 'Dial' ))) 
ORDER  BY DATE DESC 
LIMIT  1 

试图用 JOINS 重写它,但得到 #1248 - 每个派生表都必须有自己的别名

这是我对 JOIN 查询的尝试:

SELECT t1.*
                    FROM asterisk t1
                    INNER JOIN (SELECT t2.* FROM asterisk t2
                                  WHERE
                                    t1.operator_dial = '$extension'
                                  AND t1.event = 'Dial'
                                  LEFT OUTER JOIN (SELECT t3.* FROM asterisk t3
                                                  WHERE t3.unique_id = t1.unique_id AND (t3.event = 'Hangup' OR t3.event = 'Bridge')))
                          OUTER JOIN (SELECT t4.* FROM asterisk t4
                                    WHERE t1.event = 'NewCallerid'
                                    AND t1.operator_dial = '$extension'
                                    LEFT OUTER JOIN (SELECT t5.* FROM asterisk t5
                                                    WHERE (t5.unique_id = t1.unique_id AND t5.event = 'Hangup') OR (t5.call_id = t1.unique_id AND t5.event = 'Dial')))
                    ORDER BY DATE DESC
                    LIMIT 1

为了展示查询应该如何工作,我制作了 2 个屏幕截图(顶部有一些不会出现在查询结果中的调用,底部是一个必须出现在查询结果中的调用:

【问题讨论】:

标签: mysql sql join exists


【解决方案1】:

我刚刚开始在堆栈交换上发帖。在没有深入研究的情况下,我注意到 t2 和 t4 的内部联接没有与 t1 中的任何值匹配的值(这可能是原始查询如此长的原因)。

旧查询已删除.......

感谢您的屏幕截图,这很有帮助。因此,您希望查询返回的是活动呼叫(没有“挂断”或“桥接”的呼叫)然后您希望 NewcallerID 事件没有关联的拨号事件,最后拨号事件之前有一个桥接但他们后面没有桥

SELECT * 
FROM Asterisk t1
WHERE t1.unique_id not in 
    (Select unique_id 
       from Asterisk t2
        Where event = 'Hangup', 'Bridge')
UNION
select *
from asterisk t3
Where event = 'NewCallerID'
    and t3.unique_id not in
      (Select unique_id 
       from Asterisk t4
        Where event = 'Dial')
UNION
Select distinct *
from asterisk t5
    inner join asterisk t6
        on t5.unique_ID = t6.Unique_id
            and t6.event = 'Dial'
Where t5.event = 'Bridge'
         and t6.id > t5.id

我想我已经涵盖了这里的所有案例。 (虽然闻起来有点骇人听闻)。 编辑:未覆盖拨号之前和之后存在桥的事件

【讨论】:

  • 看起来它没有在我的表中返回任何结果。我在上面添加了解释截图,请查看。
【解决方案2】:

我认为你过于复杂了。通过存在于没有连接的 t2 别名 在唯一 ID 上,您将获得笛卡尔结果。

我已对查询进行内联注释,因此请务必在运行前将其删除。

SELECT 
      t1.*
   FROM   
      ASTERISK t1
         LEFT JOIN ASTERISK t2 
            ON t1.Unique_ID = t2.Unique_ID
            AND ( t2.Event = 'Hangup' 
               OR t2.Event = 'Bridge' )

         LEFT JOIN ASTERISK t3
            ON t1.unique_id = t3.unique_id
            AND t3.EVENT = 'Hangup'

         LEFT JOIN ASTERISK t4
            ON t1.unique_id = t4.call_id
            AND t4.EVENT = 'Dial'
   WHERE
      -- THESE ARE THE PRIMARY CONDITIONS YOU WANT...
      -- a specific operator extension and looking for
      -- either a dial or newcallerid
          t1.OPERATOR_DIAL = '$extension' 
      AND (    t1.EVENT = 'Dial' 
            OR t1.EVENT = 'NewCallerID' ) 

      AND (   
              -- if an outgoing DIAL, you are concerned with 
              -- alias t2 and NOT having any hangup or bridge
              (    t1.EVENT = 'Dial' 
               AND t2.Unique_ID IS NULL )
           OR 
              -- its from the NewCallerID record via t1 alias
              -- and you do not want hangup or dial based on 
              -- the unique ID vs Call ID respectively to the
              -- t3 and t4 aliases
              (    t1.EVENT = 'NewCallerID'
               AND t3.Unique_ID IS NULL
               AND t4.Unique_ID IS NULL )
          )
  ORDER BY 
     t1.`DATE` DESC 
  LIMIT  1 

REVISION 基于示例数据,但 NewCallerID 示例数据除外...

这应该会极大地简化您所拥有的内容——至少对于出站呼叫而言。没有显示“NewCallerID”的数据,因此如果没有更好的样本数据,我无法应用答案。

无论如何,我更改为预查询给定话务员分机的所有呼叫,并根据呼叫数据记录中按每个呼叫的唯一 ID 分组的明显自动增量 ID 列来获取第一个和最后一个事件。从那里,它直接连接回每个表,因此我们可以看到调用是如何开始的以及它的最后一个活动。从那里开始,它是一个简单的 where 子句,即最后一个事件仍处于“newstate”设置中。如果还有其他设置,例如保留、电话会议等,则需要将其调整为 NOT(挂断或桥接)条件...

SELECT
      PerCallSummary.*,
      StartCall.event as BeginEvent,
      StartCall.id as BeginEventID,
      LastCall.event as EndEvent,
      LastCall.id as EndEventID
   from
      ( SELECT 
              t1.unique_id, 
              t1.id beginCall, 
              MAX(t2.id) as lastActivity
           from 
              asterisk t1
                 join asterisk t2
                   on t1.unique_id = t2.unique_id
           where 
                 t1.operator_dial = '$extension'
             and t1.event = 'Dial'
           group by
              t1.unique_id,
              t1.id ) as PerCallSummary
      JOIN Asterisk as StartCall
         ON PerCallSummary.beginCall = StartCall.id
      JOIN Asterisk as LastCall
         ON PerCallSummary.lastActivity = LastCall.id
   where
      LastCall.Event = 'Newstate'

调整内部“PerCallSummary”查询

我认为这将解决它...您没有得到结果的原因是我错误地将您的数据解释为只是其内容的简要说明,因为 operator_dial 值并未结转到每条记录。因此,内部查询现在调整为加入星号 AGAIN 但仅在呼叫的 unique_id 上(因为操作员拨号将失败,否则未填写)。因此,调用将始终以第一个 t1 记录开始,所以我只是将其作为基础,但现在只有 t2 记录 ID 的 MAX()... 现在,您应该得到正确的结果以继续此操作。

无论如何,获取一些带有“NewCallerID”外观的示例数据,我们可以对其进行调整以包含它并且它是关闭标准。

【讨论】:

  • 看起来它没有在我的表中返回任何结果。我在上面添加了解释截图,请查看。
  • @user2682945,修改答案,需要“NewCallerID”类型的呼叫样本。
  • "NewCallerID" 除了 call 本身之外还有其他 unique_id,而且它后面总是有 "Dial",所以我认为我们可以忘记 "NewCallerID"。
  • @user2682945,修改后的答案并认为我理解为什么并在答案中解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-12
  • 1970-01-01
  • 1970-01-01
  • 2019-03-02
  • 2021-12-26
  • 2017-08-21
  • 2022-01-15
相关资源
最近更新 更多