如何从子查询中返回两个字段

Posted

技术标签:

【中文标题】如何从子查询中返回两个字段【英文标题】:How to return two fields from a subquery 【发布时间】:2017-03-13 14:52:29 【问题描述】:

下面的查询有效,但我想知道是否有更好的方法。

有一个子查询使用两个子查询。这两个子查询相同,但返回两个不同的字段。有没有办法只使用一个返回两个字段的子查询?

我检查了类似的问题(this、this 和 this),但我认为它们不适用于这种情况。

这里是查询:

SELECT *,
       time(strftime('%s', EndTime) - strftime('%s', StartTime), 'unixepoch') AS Duration
  FROM (
           SELECT (
                      SELECT Time
                        FROM Log AS LogStart
                       WHERE LogStart.User = Log.User AND 
                             LogStart.Time <= Log.Time AND 
                             LogStart.[Action] != 'done'
                       ORDER BY LogStart.Time DESC
                       LIMIT 1
                  )
                  AS StartTime,
                  Time AS EndTime,
                  User,
                  (
                      SELECT [Action]
                        FROM Log AS LogStart
                       WHERE LogStart.User = Log.User AND 
                             LogStart.Time <= Log.Time AND 
                             LogStart.[Action] != 'done'
                       ORDER BY LogStart.Time DESC
                       LIMIT 1
                  )
                  AS [Action]
             FROM Log
            WHERE [Action] = 'done'
       )
 ORDER BY duration DESC;

这是一些测试数据:

CREATE TABLE Log (
    Time     DATETIME,
    User     CHAR,
    [Action] CHAR
);

insert into Log values('2017-01-01 10:00:00', 'Joe', 'Play');
insert into Log values('2017-01-01 10:01:00', 'Joe', 'done');
insert into Log values('2017-01-01 10:02:00', 'Joe', 'Sing');
insert into Log values('2017-01-01 10:03:00', 'Joe', 'done');
insert into Log values('2017-01-01 10:04:00', 'Ann', 'Play');
insert into Log values('2017-01-01 10:04:30', 'Bob', 'Action without corresponding "done" which must be ignored');
insert into Log values('2017-01-01 10:05:00', 'Joe', 'Play');
insert into Log values('2017-01-01 10:06:00', 'Ann', 'done');
insert into Log values('2017-01-01 10:07:00', 'Joe', 'done');
insert into Log values('2017-01-01 10:08:00', 'Ann', 'Play');
insert into Log values('2017-01-01 10:09:00', 'Ann', 'done');

【问题讨论】:

只做一次 JOIN。 为什么从结束记录走到开始记录,而不是另一个方向? @CL.因为一些动作开始,但永远不会结束,我想找到结束的动作的持续时间并忽略那些没有匹配“完成”的动作。所以我从“完成”开始并返回 【参考方案1】:

使用自连接...我没有 SQLLite,所以这里的语法可能不对,但你应该明白...

Select e.*, time(strftime('%s', e.[Time]) - 
           strftime('%s', s.[Time]), 'unixepoch') AS Duration
From log e join log s  -- s is for the startevent; e for end event
    on s.[User] = e.[User]
       and s.[Action] != 'done'
       and e.[Action] = 'done'
       and s.[Time] = 
          (Select Max([time] from log
           where [User] = e.[User]
                   and [time] <= e.[Time]
                   and [Action] != 'done')

【讨论】:

谢谢,我从来没有使用过子查询的连接!你为什么用left join而不是inner join 如果有一个没有开始事件的结束事件,但是查看 cmets,我发现你只想要有开始和结束的那些,所以你应该把它变成一个内部连接。我编辑了我的答案。【参考方案2】:

我做了这个:是你要找的吗? (MSSQL,但我认为应该在 SQLLite 中工作,因为没有“非标准”SQL 命令;我应该在 [] 中写 USER)。

SELECT STARTTIME, MIN(ENDTIME) AS ENDTIME, [USER], ACTION 
FROM (
SELECT B.TIME AS STARTTIME, A.TIME AS ENDTIME, A.[USER], B.ACTION
FROM LOG A
INNER JOIN (SELECT * FROM LOG) B ON A.[USER]= B.[USER] AND B.ACTION<>'done' AND B.TIME< A.TIME
WHERE A.Action='done'
) X 
GROUP BY X.STARTTIME, [USER], ACTION ;

输出:

STARTTIME               ENDTIME                 USER       ACTION
----------------------- ----------------------- ---------- ---------------------
2017-01-01 10:00:00.000 2017-01-01 10:01:00.000 Joe        Play
2017-01-01 10:02:00.000 2017-01-01 10:03:00.000 Joe        Sing
2017-01-01 10:04:00.000 2017-01-01 10:06:00.000 Ann        Play
2017-01-01 10:05:00.000 2017-01-01 10:07:00.000 Joe        Play
2017-01-01 10:08:00.000 2017-01-01 10:09:00.000 Ann        Play

仅使用您的数据,比较执行计划在 MSSQL 中显示您的查询“成本”为 87%,而后者成本为 13%(它们的总和是 - 当然 - 100%)

【讨论】:

以上是关于如何从子查询中返回两个字段的主要内容,如果未能解决你的问题,请参考以下文章

从子查询存储过程分配变量

子查询返回的值不止一个.当子查询跟随在 =,!=,<,<=,>,>= 之后,或子查询用作

不能从子查询将行转换为列

如何从子查询中的 2 个或多个字段中获取最大值

Oracle - 如何从子查询中返回平均值?

如何从子查询中获取结果作为查询中的参数