T-SQL利用笛卡尔积/窗口函数_分析函数/表连接累计、累加

T-SQL利用笛卡尔积/窗口函数/表连接累计、累加

【1】 笛卡尔积与子查询解决累计

方法1:笛卡尔积

--原始数据

select templateid,needitem1Count from db_tank..TS_CardMain

--累计数据

select t1.templateId,t1.needitem1Count,sum(t2.needitem1count) sum_num from db_tank..TS_CardMain t1 
cross join db_tank..TS_CardMain t2 
where t2.templateid <= t1.templateid
group by t1.templateid,t1.needitem1Count

 

 

 

方法2:子查询

select templateid,needitem1Count, 
(select sum(needitem1Count) from db_tank..TS_CardMain t2 where t2.templateid<=t1.templateid ) as sum_num
from db_tank..TS_CardMain t1

 

 

 

【2】解决分组累加问题:利用表连接、笛卡尔积、子查询

基于多个分组的分别累加

方法1:笛卡尔积

;with temp1 as (
select 1 as id ,1 as num
union all
select 1 as id ,2 as num
union all
select 1 as id ,3 as num
union all
select 2 as id ,4 as num
union all
select 2 as id ,5 as num
union all
select 2 as id ,6 as num
)
select  t1.id,t1.num,sum(t2.num) sum_num from temp1 t1  join temp1 t2 on  t2.id =t1.id AND t2.num <= t1.num
group by t1.id,t1.num
order by id

  

 解法2:利用子查询

;with temp1 as (
select 1 as id ,1 as num
union all
select 1 as id ,2 as num
union all
select 1 as id ,3 as num
union all
select 2 as id ,4 as num
union all
select 2 as id ,5 as num
union all
select 2 as id ,6 as num
)
select *,(select sum(num) from temp1 where id=t.id and num <= t.num) sum_num from temp1 t

 

 

 

【3】窗口函数_分析函数(sum over)

sql server 2012及以上可用 

rows between unbounded preceding and current row

--【3.1】利用sum() over()嵌套使用
;with temp1 as (
select 1 as id ,1 as num union all
select 1 as id ,2 as num union all
select 1 as id ,3 as num union all
select 2 as id ,4 as num union all
select 2 as id ,5 as num union all
select 2 as id ,6 as num
)
select *,sum(num) over(order by num) as sum_num from temp1 
--select *,sum(num) over(partition by id order by num asc rows between unbounded preceding and current row) from temp1

   

【4】笛卡尔积的其他妙用

【4.1】自连接

需求:

  

 

 

 解决:

  

 

 

【4.2】笛卡尔积+递归构造日期

需求:

 CREATE TABLE testtable(ID int identity(1,1), NNAME VARCHAR(20), begintime DATEtime, endtime DATEtime);
 
 INSERT INTO testtable 
 VALUES('bm1','2021-01-10 20:57:02','2021-01-11 10:08:29')
 ,('bm2','2021-01-08 17:23:17','2021-01-10 11:54:23')

 select * from testtable

ID NNAME begintime endtime
1 bm1 2021-01-10 20:57:02.000 2021-01-11 10:08:29.000
2 bm2 2021-01-08 17:23:17.000 2021-01-10 11:54:23.000

按照7:3019:30 为白班 ,19:30 到7:30为晚班拆分为:

ID NNAME begintime endtime remark
1 bm1 2021-01-10 20:57:02.000 2021-01-11 06:59:59.000 2021-01-10 晚班
2 bm1 2021-01-11 07:30:00.000 2021-01-11 10:08:29.000 2021-01-11 白班
3 bm2 2021-01-08 17:23:17.000 2021-01-08 18:59:59.000 2021-01-08 白班
4 bm2 2021-01-08 19:30:00.000 2021-01-09 06:59:59.000 2021-01-08 晚班
5 bm2 2021-01-09 07:30:00.000 2021-01-09 18:59:59.000 2021-01-09 白班
6 bm2 2021-01-09 19:30:00.000 2021-01-10 06:59:59.000 2021-01-09 晚班
7 bm2 2021-01-10 07:30:00.000 2021-01-10 11:54:23.000 2021-01-10 白班

解决:

思路,代码见下面
(1)(即递归CTE,t1表)根据表中最小时间、和最大时间,构造出这段时间所有的上下班时间点行,包含最小时间前一天 和 最大时间后一天;
(2)把构造的每一行都与实际表中的行笛卡尔积,这样我们的每一行都对应上区间的所有白班夜班情况时间段;在大表上建议修改表连接,已亲测没有问题。这样可以及大幅度减少无效行结果集数量;
      from t1
      cross join #a t2
优化改成
      from t1
      join #a t2 on (t2.begintime-t1.endtime<0.5) and (t1.endtime - t2.endtime<0.5)
(3) where 条件,二度精准筛选,提取我们想要的行
(4)select 中的 case when,根据你的需求,做出判断什么情况,应该拿什么值;

--
 
use tempdb;
if object_id('A') is null
    drop table #a
CREATE TABLE #A(ID int identity(1,1), NNAME VARCHAR(20), begintime DATEtime, endtime DATEtime);
   
INSERT INTO #A 
VALUES('bm1','2021-01-10 20:57:00','2021-01-11 10:08:29')
 ,('bm2','2021-01-08 17:23:17','2021-01-10 11:54:23')
 ,('bm3','2021-01-08 07:23:17','2021-01-08 07:54:23')
 ,('bm4','2021-01-09 19:23:17','2021-01-09 19:54:23')

select * from #a 
---------------------
declare @endtime datetime
select @endtime = max(endtime) from #a
;with t1 as ( --构造扩展
    select cast(convert(varchar(10),min(begintime)-1,120)+' 07:30:00' as datetime) as begintime   --这里修改过 min(begintime)-1,120)
    ,cast(convert(varchar(10),min(begintime)-1,120)+' 19:30:00' as datetime) as endtime          --这里修改过  min(begintime)-1,120)
    from #a
    union all
    select dateadd(hour,12,begintime),dateadd(hour,12,endtime) from t1 
    where begintime< @endtime
)
 
select t2.*,
case when convert(char(10),t1.begintime,120)!=convert(char(10),t2.begintime,120) and (t2.begintime-t1.begintime>0.5) then t1.begintime  --判断是否是跨天,这里修改过,新增--》and (t2.begintime-t1.begintime>0.5)
     when t2.begintime>t1.begintime then t2.begintime  
     else t1.begintime
     end as new_begintime
,case when convert(char(10),t1.endtime,120)!=convert(char(10),t2.endtime,120) and (t2.endtime-t1.endtime>0.5) then t1.endtime  -- 这里修改过, 新增--》and (t2.endtime-t1.endtime>0.5)
     when t2.endtime>t1.endtime then t1.endtime
     else t2.endtime
     end as new_endtime
,case when convert(varchar,t1.endtime,8)='07:30:00' then '夜班' 
 else '白班' 
 end as remark
from t1
join #a t2 on (t2.begintime-t1.endtime<0.5) and (t1.endtime - t2.endtime<0.5)
where t1.endtime>t2.begintime and t1.endtime - t2.begintime<0.5 --从开始时间判断
or (t1.endtime>t2.endtime and t1.endtime-t2.endtime <0.5) --从结束时间判断
or (t1.endtime > t2.begintime and t1.endtime < t2.endtime and t2.endtime-t1.endtime>0.5) --跨天判断
or (t1.begintime > t2.begintime and t1.begintime < t2.endtime and t2.endtime-t1.begintime>0.5) --跨天判断
order by t2.begintime desc,new_begintime

--

 

  

 

【4.3】控制补全成上面没有空行的值

来源:sql server 技术群,代码参考

需求 :

  

 

 

if OBJECT_ID('tempdb..#temp') is not null
drop table #temp
select * into #temp from 
(
select '1'as  'id','44562'as  'date','1'as 's'     union all
select '2'as  'id','44563'as  'date',''as 's'     union all
select '3'as  'id','44564'as  'date',''as 's'     union all
select '4'as  'id','44565'as  'date','5'as 's'     union all
select '5'as  'id','44566'as  'date',''as 's'     union all
select '6'as  'id','44567'as  'date',''as 's'   
) as A


-- 实现
select *,
(
    select s from #temp t 
    where id=(
                select max(id) from #temp q 
                where q.id<=t1.id and q.s!='' 
            ) 
) as new_value
from #temp t1 

  

 

 update 语句

  

 

posted @ 2018-10-11 17:04  郭大侠1  阅读(845)  评论(0编辑  收藏  举报