sql查询问题,想找出开始时间和结束时间

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sql查询问题,想找出开始时间和结束时间相关的知识,希望对你有一定的参考价值。

有个表,结构如下:
stime name status
2014-1-1 张三 未解决
2014-1-2 张三 解决中
2014-1-3 张三 已解决
2014-1-4 张三 未解决
2014-1-5 张三 解决中

2014-1-6 张三 解决中
2014-1-7 张三 解决中
2014-1-8 张三 已解决
2014-1-6 李四 未解决
2014-1-7 李四 解决中
2014-1-8 李四 解决中
2014-1-9 李四 已解决
大概就是这么个数据形式,我现在想要的结果是
开始时间 解决时间 name
2014-1-1 2014-1-3 张三
2014-1-4 2014-1-8 张三
2014-1-6 2014-1-9 李四
请问这个sql怎么写?建视图也可以。
刚才没有说清楚,其实想要的结果不止这些,重新写一下想要的结果
开始时间 解决时间 name 持续时间 累计时间
2014-1-1 2014-1-3 张三 2 2
2014-1-4 2014-1-8 张三 4 6
2014-1-6 2014-1-9 李四 3 3

Create Table Tb

(

stime Datetime,

name Varchar(10),

status Varchar(10)

)



Insert Into Tb Values('2014-1-1','张三', '未解决')

Insert Into Tb Values('2014-1-2','张三' , '解决中')

Insert Into Tb Values('2014-1-3', '张三' , '已解决')

Insert Into Tb Values('2014-1-4', '张三' , '未解决')

Insert Into Tb Values('2014-1-5', '张三' , '解决中')


Insert Into Tb Values('2014-1-6', '张三' , '解决中')

Insert Into Tb Values('2014-1-7', '张三' , '解决中')

Insert Into Tb Values('2014-1-8', '张三' , '已解决')

Insert Into Tb Values('2014-1-6', '李四' , '未解决')

Insert Into Tb Values('2014-1-7', '李四' , '解决中')

Insert Into Tb Values('2014-1-8', '李四' , '解决中')

Insert Into Tb Values('2014-1-9','李四'  ,'已解决')


With T

As

(

Select 开始时间,解决时间,name,datediff(dd,开始时间,解决时间) as 持续时间

From

(

Select stime as 开始时间,(Select Min(stime) From tb b Where b.name=a.name And b.stime>a.stime And b.status='已解决')

As 解决时间,Name From tb  a Where status='未解决'

) C

)

Select 开始时间,解决时间,name,持续时间,(Select sum(持续时间) From T a Where a.name=b.name And A.开始时间<=B.开始时间) As 累计时间

From T b


--SQL2005或以上版本

追问

大哥,假如我在李四这再加一个时间为2014-1-5,李四,未解决,那么结果会是四条而不是三条,这个怎么改?

追答

--不是都解决了么,还追问?

With T

As

(

Select Row_number() over(order by stime) as id,stime,name,status From tb Where status='未解决'

union All

Select Row_number() over(order by stime) as id,stime,name,status From tb Where status='已解决'

Select  stime,etime,name,datediff(dd,stime,etime) As days Into #Tmp From(

Select stime,(Select stime From T Where status='已解决' And name=A.name And id=A.id) As etime,name From T A Where status='未解决'

) m

Select  stime As 开始时间,etime As 结束时间,name ,days 持续时间,

(Select sum(days) From #Tmp Where name=A.name And stime<=A.stime) As 累计时间

From #Tmp A

Drop Table #Tmp


追问

大哥, 再追问最后一次,我只想要前三行结果,这个可以么?求帮忙,谢谢~

追答

--如果是所有记录只取前3条:把
Select stime As 开始时间,etime As 结束时间,name ,days 持续时间,
改成
Select Top 3 stime As 开始时间,etime As 结束时间,name ,days 持续时间,
--如果是结束时间为NULL的不要:把
From #Tmp A
改成
From #Tmp A Where etime is not null

参考技术A -- for M$SQL: 若所有的“未解决”均有“已解决”相一一对应,可以:
declare @bgnTb table (
    idx int identity(1,1) primary key,
    name varchar(16),
    bgntime datetime )
declare @endTb table (
    idx int identity(1,1) primary key,
    name varchar(16),
    endtime datetime )
insert into @bgnTb
select name, stime
from tb
where statue='未解决'
order by name, stime

insert into @endTb
select name, stime
from tb
where statue='已解决'
order by name, stime

select a.name, bgntime, endtime
    , datediff(day, bgntime, endtime) as 持续时间
from @bgnTb a
join @endTb b on a.idx = b.idx

 for SQLite3 (标准SQL)

$ sqlite3
SQLite version 3.7.13 2012-06-11 02:05:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table tb_log (
   ...>     stime datetime,
   ...>     name  varchar(16),
   ...>     status varchar(8) -- -1: 未解决, 0: 解决中, 1: 已解决
   ...> );
sqlite> 
sqlite> insert into tb_log values ('2014-1-1', '张三', '未解决');
sqlite> insert into tb_log values ('2014-1-2', '张三', '解决中');
sqlite> insert into tb_log values ('2014-1-3', '张三', '已解决');
sqlite> insert into tb_log values ('2014-1-4', '张三', '未解决');
sqlite> insert into tb_log values ('2014-1-5', '张三', '解决中');
sqlite> insert into tb_log values ('2014-1-6', '张三', '解决中');
sqlite> insert into tb_log values ('2014-1-7', '张三', '解决中');
sqlite> insert into tb_log values ('2014-1-8', '张三', '已解决');
sqlite> insert into tb_log values ('2014-1-6', '李四', '未解决');
sqlite> insert into tb_log values ('2014-1-7', '李四', '解决中');
sqlite> insert into tb_log values ('2014-1-8', '李四', '解决中');
sqlite> insert into tb_log values ('2014-1-9', '李四', '已解决');
sqlite> 
sqlite> create table tb_bgn as 
   ...> select name, stime as bgntime
   ...> from tb_log
   ...> where status = '未解决'
   ...> order by bgntime, name
   ...> ;
sqlite> 
sqlite> create table tb_end as 
   ...> select name, stime as endtime
   ...> from tb_log
   ...> where status = '已解决'
   ...> order by endtime, name
   ...> ;
sqlite> 
sqlite> select a.name, bgntime, endtime
   ...> from tb_bgn a
   ...> join tb_end b on a.name = b.name
   ...>     and a.bgntime < b.endtime
   ...> where 1=1
   ...> and not exists (
   ...>     select 1 
   ...>     from tb_bgn a0
   ...>     join tb_end b0 on a0.name = b0.name
   ...>         and a0.bgntime < b0.endtime
   ...>     where a.name = a0.name
   ...>     and a.bgntime = a0.bgntime
   ...>     and b.endtime > b0.endtime)
   ...> ;
张三|2014-1-1|2014-1-3
张三|2014-1-4|2014-1-8
李四|2014-1-6|2014-1-9
sqlite>

本回答被提问者采纳

bigquery SQL 计算项目计划的开始和结束时间

【中文标题】bigquery SQL 计算项目计划的开始和结束时间【英文标题】:bigquery SQL to calculate the Start and finish times for a project schedule 【发布时间】:2018-01-09 14:23:24 【问题描述】:
ID  NAME    DURATION    START   FINISH  P1  P2  P3
1   A         14                         1      
2   B         15                         1      
3   C         15                         1      
4   D         12                         1      
5   E         22                         2  3   
6   F         14                         4  1   
7   G         9                          5  6   

需要开发一个递归大查询 SQL 来计算计划的开始和结束时间。 请注意,活动的日程安排数量可能会有所不同 P1 、 P2 和 P3 是行活动的前身 ID 一个活动可能只有 1 个 Predecessor 或多个 Predecessor 开始和结束列的计算如下 Start 等于 "2017-01-01" 或 (Max (Finish of its Predecessors +1 )) 的较大值 完成始终是开始 + 持续时间的函数 -1 该计算预计将是递归的,直到获得答案。

我在 bigquery 中需要这个的原因是通过更改持续时间值的迭代来执行计划模拟。 上述时间表的答案如下:

ID  NAME    DURATION    START       FINISH      P1  P2  P3
1   A       14          1-Jan-17    14-Jan-17   1       
2   B       15          15-Jan-17   29-Jan-17   1       
3   C       15          15-Jan-17   29-Jan-17   1       
4   D       12          15-Jan-17   26-Jan-17   1       
5   E       22          30-Jan-17   20-Feb-17   2   3   
6   F       14          27-Jan-17   9-Feb-17    4   1   
7   G       9           21-Feb-17   1-Mar-17    5   6   

【问题讨论】:

【参考方案1】:

我希望通过一次查询进行迭代。是否可以使用内联 JS ...?

以下是 BigQuery 标准 SQL 它使用JS UDF 在一次运行中完成所有迭代 这需要将整个表的数据传递给 UDF,因此绝对是 UDF limits / limitations 的主题 我怀疑这个解决方案是否具有真正的实用价值,但绝对有趣的是锻炼和探索未来的 BigQuery 功能

还要注意这里为简化 JS 代码和关注问题根源所做的一些假设。所以假设是:所有 id 字段都连续填充,没有从值 1 开始的间隙(如果你愿意,你可以改进它:o))

#standardSQL
CREATE TEMPORARY FUNCTION y(arr ARRAY<STRING>)
RETURNS ARRAY<STRUCT<id INT64, name STRING, duration INT64, start INT64, finish INT64, p1 INT64, p2 INT64, p3 INT64>>
LANGUAGE js AS """
  var result = [], fin;
  for (i = 0; i < arr.length; i++)result.push(JSON.parse(arr[i]))
  for (w = 0; w < 40; w++) fin = true;
    for (i = 0; i < arr.length; i++) 
      if(result[i].start == null)  fin = false;
        var x1 = parseInt(result[i].p1) - 1;
        var x2 = parseInt(result[i].p2||result[i].p1) - 1;
        var x3 = parseInt(result[i].p3||result[i].p1) - 1;
        if(result[i].id == result[i].p1) 
            result[i].start = 1; 
            result[i].finish = 1 + result[i].duration - 1;
         else if (result[x1].start !== null && result[x2].start !== null && result[x3].start !== null) 
            result[i].start = Math.max(result[x1].finish, result[x2].finish, result[x3].finish) + 1;
            result[i].finish = result[i].start + result[i].duration - 1;
         
      
     if (fin) return result
   return result;
""";
SELECT 
  id, name, duration, 
  DATE_ADD(DATE '2017-01-01', INTERVAL start - 1 DAY) start, 
  DATE_ADD(DATE '2017-01-01', INTERVAL finish - 1 DAY) finish, 
  p1, p2, p3 
FROM (
  SELECT rec.* FROM (
    SELECT ARRAY_AGG(TO_JSON_STRING(t) ORDER BY id) AS data
    FROM `yourTable` t
  ), UNNEST(y(data)) AS rec
) ORDER BY id

您可以使用下面的虚拟数据(来自您的问题)测试/玩上面的内容

#standardSQL
CREATE TEMPORARY FUNCTION y(arr ARRAY<STRING>)
RETURNS ARRAY<STRUCT<id INT64, name STRING, duration INT64, start INT64, finish INT64, p1 INT64, p2 INT64, p3 INT64>>
LANGUAGE js AS """
  var result = [], fin;
  for (i = 0; i < arr.length; i++)result.push(JSON.parse(arr[i]))
  for (w = 0; w < 40; w++) fin = true;
    for (i = 0; i < arr.length; i++) 
      if(result[i].start == null)  fin = false;
        var x1 = parseInt(result[i].p1) - 1;
        var x2 = parseInt(result[i].p2||result[i].p1) - 1;
        var x3 = parseInt(result[i].p3||result[i].p1) - 1;
        if(result[i].id == result[i].p1) 
            result[i].start = 1; 
            result[i].finish = 1 + result[i].duration - 1;
         else if (result[x1].start !== null && result[x2].start !== null && result[x3].start !== null) 
            result[i].start = Math.max(result[x1].finish, result[x2].finish, result[x3].finish) + 1;
            result[i].finish = result[i].start + result[i].duration - 1;
         
      
     if (fin) return result
   return result;
""";
WITH `yourTable` AS (SELECT * FROM (
  SELECT NULL id, NULL name, NULL duration, 1 start, 1 finish, 1 p1, 1 p2, 1 p3 UNION ALL
  SELECT 1,    'A',      14,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 2,    'B',      15,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 3,    'C',      15,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 4,    'D',      12,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 5,    'E',      22,          NULL,       NULL,        2,    3,       NULL    UNION ALL   
  SELECT 6,    'F',      14,          NULL,       NULL,        4,    1,       NULL    UNION ALL   
  SELECT 7,    'G',      9,           NULL,       NULL,        5,    6,       NULL   
  ) WHERE NOT id IS NULL
)
SELECT 
  id, name, duration, 
  DATE_ADD(DATE '2017-01-01', INTERVAL start - 1 DAY) start, 
  DATE_ADD(DATE '2017-01-01', INTERVAL finish - 1 DAY) finish, 
  p1, p2, p3 
FROM (
  SELECT rec.* FROM (
    SELECT ARRAY_AGG(TO_JSON_STRING(t) ORDER BY id) AS data
    FROM `yourTable` t
  ), UNNEST(y(data)) AS rec
) ORDER BY id  

结果是

id  name    duration    start       finish      p1      p2      p3   
1   A       14          2017-01-01  2017-01-14  1       null    null     
2   B       15          2017-01-15  2017-01-29  1       null    null     
3   C       15          2017-01-15  2017-01-29  1       null    null     
4   D       12          2017-01-15  2017-01-26  1       null    null     
5   E       22          2017-01-30  2017-02-20  2       3       null     
6   F       14          2017-01-27  2017-02-09  4       1       null     
7   G       9           2017-02-21  2017-03-01  5       6       null     

【讨论】:

感谢 Mikhail Berlyant 的回答。我已尝试进行一些更改,请参阅下面的答案。我收到 SyntaxError: Unexpected token for at y(ARRAY) line 4, columns 2-5。我不确定我的回答是否会使查询更有效率。需要你的建议。 谢谢米哈伊尔。你的回答很聪明【参考方案2】:

BigQuery 不支持递归查询 因此,您需要使用您选择的client 自己编排递归

下面是如何通过运行一系列查询直到解决所有依赖项来实现这一点的演示

第 1 步:准备初始表格 - yourproject.yourdataset.yourtable(此处取自您的问题的简单示例)

第 2 步:使用与源表相同的目标表运行迭代查询 - yourproject.yourdataset.yourtable with Write Preference >> Overwrite table

#standardSQL
SELECT a.id, a.name, a.duration, a.p1, a.p2, a.p3,
  start,
  DATE_ADD(start, INTERVAL a.duration - 1 DAY) finish
FROM `yourproject.yourdataset.yourtable` a
LEFT JOIN `yourproject.yourdataset.yourtable` a1 ON a.p1 = a1.id
LEFT JOIN `yourproject.yourdataset.yourtable` a2 ON a.p2 = a2.id
LEFT JOIN `yourproject.yourdataset.yourtable` a3 ON a.p3 = a3.id
CROSS JOIN UNNEST([
  IF(a.id = a.p1, DATE '2017-01-01', ((
    SELECT DATE_ADD(MAX(finish), INTERVAL 1 DAY) 
    FROM UNNEST([a1.finish, a2.finish, a3.finish]) finish
    WHERE IF(a.p1 IS NULL, 0, 1) + IF(a.p2 IS NULL, 0, 1) + IF(a.p3 IS NULL, 0, 1) = 
    IF(a1.finish IS NULL, 0, 1) + IF(a2.finish IS NULL, 0, 1) + IF(a3.finish IS NULL, 0, 1)
  )))   
]) start
WHERE NOT a.id IS NULL
ORDER BY a.id

第 3 步:检查是否还有要计算的条目

#standardSQL
SELECT COUNT(1) still_to_iterate 
FROM `yourproject.yourdataset.yourtable`
WHERE start IS NULL   

如果此处的计数大于 0 – 继续执行步骤 2,依此类推,直到 still_to_iterate = 0

处理示例:

如果您手动执行这些步骤 - 以下是您得到的结果

迭代 1

迭代 2

迭代 3

迭代 4

当然,对于更实际的情况 - 迭代次数可能会很高,即使仍然可以手动完成,但很快就会变得不高效! 这就是您可以使用您选择的clientbq command line 以及一些 bash / awk / 等魔法来编写上述逻辑的地方

【讨论】:

感谢您的回答。但我希望的是通过一次查询进行迭代。是否可以使用内联JS创建可以自行迭代的临时表,而不是与源表同名的目标表? 正如我提到的 - BigQuery does not support recursive query,所以上面是您拥有的最佳选择 我发布了另一个答案(第一个和第二个相对较大,所以我将第二个作为单独的) - 看看 - 玩得开心 :o)【参考方案3】:
#standardSQL
CREATE TEMPORARY FUNCTION y(arr ARRAY<STRING>)
RETURNS ARRAY<STRUCT<id INT64, name STRING, duration INT64, start INT64, finish INT64, p1 INT64, p2 INT64, p3 INT64>>
LANGUAGE js AS """
  var result = [],

  for (var i = 0; i < arr.length; i++)result.push(JSON.parse(arr[i]))

  ----------
    result[1].finish= new Date(new Date('01-Jan-17').getTime()- 1 * 86400000)

  for(var i=1;i<arr.length;i++)

if (result[i].p1 === '')result[i].p1 = result[1].id
if (result[i].p2 === '')result[i].p2 = result[1].id
if (result[i].p3 === '')result[i].p3 = result[1].id
  
  for(var i=1;i<arr.length;i++)

    result[i].start= new Date(Math.max(  new Date('01-Jan-17').getTime() 
                                                ,Math.max(result[result[i][result[i].p1]].finish.getTime() + 1 * 86400000 ,
                                                          result[result[i][result[i].p2]].finish.getTime() + 1 * 86400000 ,
                                                          result[result[i][result[i].p3]].finish.getTime() + 1 * 86400000 )
                                                          )
                                                          )


   result[i].finish=  new Date( result[i].start.getTime() + result[i].duration * 86400000 - 1 * 86400000 )
    

   return result;
""";
WITH `yourTable` AS (SELECT * FROM (
  SELECT NULL id, NULL name, NULL duration, 1 start, 1 finish, 1 p1, 1 p2, 1 p3 UNION ALL
  SELECT 1,    'A',      14,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 2,    'B',      15,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 3,    'C',      15,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 4,    'D',      12,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 5,    'E',      22,          NULL,       NULL,        2,    3,       NULL    UNION ALL   
  SELECT 6,    'F',      14,          NULL,       NULL,        4,    1,       NULL    UNION ALL   
  SELECT 7,    'G',      9,           NULL,       NULL,        5,    6,       NULL   
  ) WHERE NOT id IS NULL
)
SELECT 
  id, name, duration, 
   start, 
   finish, 
  p1, p2, p3 
FROM (
  SELECT rec.* FROM (
    SELECT ARRAY_AGG(TO_JSON_STRING(t) ORDER BY id) AS data
    FROM `yourTable` t
  ), UNNEST(y(data)) AS rec
) ORDER BY id 

【讨论】:

【参考方案4】:

ScriptingStored Procedures 的支持现在处于测试阶段(截至 2019 年 10 月)

您可以提交多个用分号分隔的语句,BigQuery 现在可以运行它们。

我希望通过一次查询进行迭代

所以,现在您可以将所需的逻辑实现为一个纯 SQL 脚本(不涉及 JS UDF 且无需手动迭代),如下例所示

DECLARE cnt INT64;
CREATE TEMP TABLE temp_table AS SELECT * FROM (
  SELECT NULL id, NULL name, NULL duration, CURRENT_DATE() start, CURRENT_DATE() finish, 1 p1, 1 p2, 1 p3 UNION ALL
  SELECT 1,    'A',      14,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 2,    'B',      15,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 3,    'C',      15,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 4,    'D',      12,          NULL,       NULL,        1,    NULL,    NULL    UNION ALL
  SELECT 5,    'E',      22,          NULL,       NULL,        2,    3,       NULL    UNION ALL   
  SELECT 6,    'F',      14,          NULL,       NULL,        4,    1,       NULL    UNION ALL   
  SELECT 7,    'G',      9,           NULL,       NULL,        5,    6,       NULL   
  ) WHERE NOT id IS NULL;

LOOP
  CREATE OR REPLACE TEMP TABLE temp_table AS 
  SELECT a.id, a.name, a.duration, a.p1, a.p2, a.p3,
    start, DATE_ADD(start, INTERVAL a.duration - 1 DAY) finish
  FROM temp_table a
  LEFT JOIN temp_table a1 ON a.p1 = a1.id
  LEFT JOIN temp_table a2 ON a.p2 = a2.id
  LEFT JOIN temp_table a3 ON a.p3 = a3.id
  CROSS JOIN UNNEST([
    IF(a.id = a.p1, DATE '2017-01-01', ((
      SELECT DATE_ADD(MAX(finish), INTERVAL 1 DAY) 
      FROM UNNEST([a1.finish, a2.finish, a3.finish]) finish
      WHERE IF(a.p1 IS NULL, 0, 1) + IF(a.p2 IS NULL, 0, 1) + IF(a.p3 IS NULL, 0, 1) = 
      IF(a1.finish IS NULL, 0, 1) + IF(a2.finish IS NULL, 0, 1) + IF(a3.finish IS NULL, 0, 1)
    )))   
  ]) start
  WHERE NOT a.id IS NULL;

  SET cnt = (SELECT COUNT(1) FROM temp_table WHERE start IS NULL);
  IF cnt = 0 THEN BREAK; END IF; 
END LOOP;

SELECT * FROM temp_table ORDER BY id;  

上面的脚本最终执行了 12 个作业 - 一个父作业和 11 个子作业

如果你要检查最终工作的结果 - 你会看到结果表

【讨论】:

以上是关于sql查询问题,想找出开始时间和结束时间的主要内容,如果未能解决你的问题,请参考以下文章

SQL查询仅获取事件的开始和结束时间

sql查询开始时间和结束时间之间的数据?

SQL 查询开始和结束日期

SQL - 逐年查询

ms-sql 如何查询开始时间与结束时间之间的数据?

SQL根据开始和结束时间对满足条件的时间序列进行分组