在论坛中出现的比较难的sql问题:2
最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了。
所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路。
1、时间间隔计算.
http://bbs.csdn.net/topics/390608930
这个问题非常复杂。
start_time end_time
2013-09-11 17:26:02.382 2013-09-24 10:38:01.41
2013-09-18 17:02:40.444 2013-09-22 15:27:58.984
2013-09-18 08:21:32.036 2013-09-22 15:31:52.499
2013-09-13 16:28:29.832 2013-09-16 09:41:47.988
2013-09-09 10:59:59.835 2013-09-10 14:06:21.223
要求计算这两个列的时间差 但是要去除9月份的正常休假并且只计算正常工作时间(上午8:30--12:00 下午14:00--18:00)
计算结果如下:
start_time end_time diff_time(小时)
2013-09-11 17:26:02.382 2013-09-24 10:38:01.41 55.1
2013-09-18 17:02:40.444 2013-09-22 15:27:58.984 5.9
2013-09-18 08:21:32.036 2013-09-22 15:31:52.499 12.5
2013-09-13 16:28:29.832 2013-09-16 09:41:47.988 2.7
2013-09-09 10:59:59.835 2013-09-10 14:06:21.223 1.1
请各位大大帮忙看看这个时间差应该怎么计算 谢谢了
我的解法:
if object_id('tab') is not null drop table tabif object_id('holiday') is not nulldrop table holidaygocreate table tab(start_time datetime,end_time datetime)insert into tabselect '2013-09-11 17:26:02.382','2013-09-24 10:38:01.41' unionselect '2013-09-18 17:02:40.444','2013-09-22 15:27:58.984' unionselect '2013-09-18 08:21:32.036','2013-09-22 15:31:52.499' unionselect '2013-09-13 16:28:29.832','2013-09-16 09:41:47.988' unionselect '2013-09-09 10:59:59.835','2013-09-09 14:06:21.223' create table holiday(h_date datetime)insert into holidayselect '2013-09-01' union select '2013-09-07' union select '2013-09-08' union select '2013-09-14'union select '2013-09-15'union select '2013-09-19'union select '2013-09-20'union select '2013-09-21'union select '2013-09-29'goWITH calendar --产生日历AS(SELECT CAST('2013-09-01' as varchar(10)) AS r --月份的开始日期UNION ALLSELECT convert(VARCHAR(10),dateadd(day,1,r),120)FROM calendarWHERE r < '2013-09-30' --月份的结束日期),tt --计算时间间隔,单位为秒as(SELECT t.start_time, t.end_time, c.r, h.h_date, /* 通过tab表和calendar表的关联,就能把开始时间到结束时间,多对应的多天, 都给关联出来, 比如开始时间 2013-09-18 08:21:32.037结束时间 2013-09-22 15:31:52.500, 其实就是,18、19、20、21、22这一共5天,会由原来的1条记录,现在变为5条记录。 如果h_date为null,说明这一天不是假日, 就需要计算时间间隔,有几种可能性: 1.开始时间和结束时间,在同一天的 2.当前日期和开始日期相同 3.当前日期和结束日期相同 4.当前日期是在开始日期和结束日期之间的某天 如果h_date是null,那么返回0,说明是节假日,就不用计算时间间隔了 */ case when h_date IS null and convert(varchar(10),t.start_time_temp,120) = c.r and CONVERT(varchar(10),t.end_time_temp,120) = c.r then case when convert(varchar(5),t.start_time_temp,114) between '08:30' and '12:00' and not (convert(varchar(5),t.end_time_temp,114) between '08:30' and '12:00') then DATEDIFF(second,t.start_time_temp,c.r +' 12:00:00') else 0 end + case when convert(varchar(5),t.end_time_temp,114) between '14:00' and '18:00' and not (convert(varchar(5),t.start_time_temp,114) between '14:00' and '18:00') then DATEDIFF(second,c.r+' 14:00:00',t.end_time_temp) else 0 end + case when (convert(varchar(5),t.start_time_temp,114) between '08:30' and '12:00' and convert(varchar(5),t.end_time_temp,114) between '08:30' and '12:00') or (convert(varchar(5),t.end_time_temp,114) between '14:00' and '18:00' and convert(varchar(5),t.start_time_temp,114) between '14:00' and '18:00') then DATEDIFF(SECOND,t.start_time_temp,t.end_time_temp) else 0 end /* 注意下面的计算逻辑是,如果这天不是假日,同时与开始日期相同 那么就要计算时间间隔,如果时间是在上午的工作时间范围内, 那么用当前日期的12点,减去开始日期,就是时间间隔,但还必须要加上下午的工作时间, 也就是4个小时,转化为秒数,就是4*3600 */ when h_date IS null and convert(varchar(10),t.start_time_temp,120) = c.r then case when convert(varchar(5),t.start_time_temp,114) between '08:30' and '12:00' then DATEDIFF(second,t.start_time_temp,c.r +' 12:00:00') + 4 * 3600 else 0 end + case when convert(varchar(5),t.start_time_temp,114) between '14:00' and '18:00' then DATEDIFF(second,t.start_time_temp,c.r +' 18:00:00') else 0 end when h_date IS null and CONVERT(varchar(10),t.end_time_temp,120) = c.r then case when convert(varchar(5),t.end_time_temp,114) between '08:30' and '12:00' then DATEDIFF(second,c.r +' 08:30:00',t.end_time_temp) else 0 end + case when convert(varchar(5),t.end_time_temp,114) between '14:00' and '18:00' then DATEDIFF(second,c.r +' 14:00:00',t.end_time_temp) + 3.5 * 3600 else 0 end when h_date is null and convert(varchar(10),t.start_time_temp,120) < c.r and CONVERT(varchar(10),t.end_time_temp,120) > c.r then 7.5 * 3600 when h_date IS null then 0 end as seconds FROM (/*这里之所以要转换,是由于有些时间比如 start_time为2013-09-18 08:21:32.037,不在正常工作时间(上午8:30--12:00 下午14:00--18:00)内,所以要先转化为正常工作时间,否则后面的case when的逻辑判断就太复杂了。*/SELECT start_time, end_time, case when CONVERT(varchar(5),start_time,114) < '08:30' then cast(CONVERT(varchar(10),start_time,120) + ' 08:30:00' AS datetime) when CONVERT(varchar(5),start_time,114) between '12:00' and '14:00' then cast(CONVERT(varchar(10),start_time,120) + ' 12:00:00' AS datetime) else start_time end as start_time_temp, case when CONVERT(varchar(5),end_time,114) between '12:00' and '14:00' then cast(CONVERT(varchar(10),end_time,120) + ' 12:00:00' AS datetime) when CONVERT(varchar(5),end_time,114) > '18:00' then cast(CONVERT(varchar(10),end_time,120) + ' 18:00:00' AS datetime) else end_time end as end_time_temp FROM tab) tinner join calendar c on convert(varchar(10),t.start_time,120) <= c.r and convert(varchar(10),t.end_time,120) >= c.r left join holiday h on c.r = h.h_date--OPTION(MAXRECURSION 1000) --限制最大递归次数)--select * from ttselect start_time, end_time, --汇总秒数,同时转化为小时 cast(round(SUM(seconds) / 3600 ,1,1) as numeric(10,1)) as diff_timefrom ttgroup by start_time, end_time /*start_timeend_timediff_time2013-09-09 10:59:59.8372013-09-09 14:06:21.2231.12013-09-13 16:28:29.8332013-09-16 09:41:47.9872.72013-09-18 17:02:40.4432013-09-22 15:27:58.9835.92013-09-18 08:21:32.0372013-09-22 15:31:52.50012.52013-09-11 17:26:02.3832013-09-24 10:38:01.41055.1*/
2、统一改换查询出的字段。。这是不是想多了?
http://bbs.csdn.net/topics/390610092
能不能这样
select A.* as A_*
from QAQuestion Q
inner join QAAnswer A ON A.QuestionID = Q.ID
简单地说,不想一个个地去给每个字段as别名
我如上去写只是打个比方。。实际运行不了的,想得到的查询结果是
A_字段1,A_字段2,A_字段3,A_字段4
有没有办法呢?
我的回复:
本质上来说,只有在sql server端,能把select a.* as a_*,也就是自动进行转换,才能支持。
因为在sql server端,你写的sql语句是各式各样的,要想实现你的A.* as A_*,实际上就是要改写查询,改为:
select a.字段1 as a_字段1,
a.字段2 as a_字段2,
a.字段3 as a_字段3,
from a
下面是通过动态语句来实现的:
--先建个表select * into wc_tablefrom sys.objects/*要实现select a.* as a_*from wc_table的效果*/--动态生成语句为:declare @sql varchar(max);set @sql = '';select @sql = @sql + ',' + c.name + ' as A_' + c.name from sys.tables tinner join sys.columns c on t.object_id = c.object_idwhere t.name = 'wc_table'order by c.column_idset @sql = 'select ' + STUFF(@sql,1,1,'') + ' from wc_table A'select @sql /*我把结果格式化了一下就是这样:SELECT name AS A_name, object_id AS A_object_id, principal_id AS A_principal_id, schema_id AS A_schema_id, parent_object_id AS A_parent_object_id, type AS A_type, type_desc AS A_type_desc, create_date AS A_create_date, modify_date AS A_modify_date, is_ms_shipped AS A_is_ms_shipped, is_published AS A_is_published, is_schema_published AS A_is_schema_published FROM wc_table A */exec(@sql)
3、求一SQL语句。
http://bbs.csdn.net/topics/390496661
create table #tab (col1 char(10),col2 char(10),item char(10),num int,[Date] varchar(10))insert #tab values('AAA','BBB','A',50,'2013-06-10')insert #tab values('ABB','BGG','B',30,'2013-06-10')insert #tab values('AAA','BBB','C',80,'2013-06-13')
我的解法:
create table tab (col1 char(10),col2 char(10),item char(10),num int,[Date] varchar(10))insert tab values('AAA','BBB','A',50,'2013-06-10')insert tab values('ABB','BGG','B',30,'2013-06-10')insert tab values('AAA','BBB','C',80,'2013-06-13')--动态生成sql语句declare @start_date varchar(10) = '2013-06-01', @end_date varchar(10) = '2013-06-30';declare @date varchar(10), @sql varchar(max) = '', @sql1 varchar(8000), @sql2 varchar(8000);set @date = @start_date;set @sql1 = 'select case when rownum = 1 then col1 else '''' end as col1, case when rownum = 1 then col2 else '''' end as col2, item'set @sql2 = 'select col1,col2,item,row_number() over(partition by col1,col2 order by item) as rownum' while @date <= @end_datebegin set @sql1 = @sql1 + ',v_' + REPLACE( right(@date,5),'-','') + ' as ''' + CAST(DATEPART(month,@date) as varchar) + '/' + CAST(DATEPART(day,@date) as varchar) +''''; set @sql2 = @sql2 + ',SUM(case when date =''' + @date + ''' then num else 0 end) as v_' + REPLACE( right(@date,5),'-','')set @date = CONVERT(varchar(10),dateadd(day,1,@date),120)endset @sql = @sql1 + ' from (' + @sql2 + ' from tab group by col1,col2,item' + ') v' --生产的动态sql语句 select @sqlexec(@sql)
上面由于是动态生成语句,所以不能用局部的临时表,所以建了一个表。
下面是动态生成的sql语句,经过了格式化:
select case when rownum = 1 then col1 else '' end as col1, case when rownum = 1 then col2 else '' end as col2, item, v_0601 as '6/1',v_0602 as '6/2',v_0603 as '6/3', v_0604 as '6/4',v_0605 as '6/5', v_0606 as '6/6',v_0607 as '6/7', v_0608 as '6/8',v_0609 as '6/9', v_0610 as '6/10',v_0611 as '6/11', v_0612 as '6/12',v_0613 as '6/13', v_0614 as '6/14',v_0615 as '6/15', v_0616 as '6/16',v_0617 as '6/17', v_0618 as '6/18',v_0619 as '6/19', v_0620 as '6/20',v_0621 as '6/21', v_0622 as '6/22',v_0623 as '6/23', v_0624 as '6/24',v_0625 as '6/25', v_0626 as '6/26',v_0627 as '6/27', v_0628 as '6/28',v_0629 as '6/29', v_0630 as '6/30' from (select col1,col2,item, row_number() over(partition by col1,col2 order by item) as rownum, SUM(case when date ='2013-06-01' then num else 0 end) as v_0601, SUM(case when date ='2013-06-02' then num else 0 end) as v_0602, SUM(case when date ='2013-06-03' then num else 0 end) as v_0603, SUM(case when date ='2013-06-04' then num else 0 end) as v_0604, SUM(case when date ='2013-06-05' then num else 0 end) as v_0605, SUM(case when date ='2013-06-06' then num else 0 end) as v_0606, SUM(case when date ='2013-06-07' then num else 0 end) as v_0607, SUM(case when date ='2013-06-08' then num else 0 end) as v_0608, SUM(case when date ='2013-06-09' then num else 0 end) as v_0609, SUM(case when date ='2013-06-10' then num else 0 end) as v_0610, SUM(case when date ='2013-06-11' then num else 0 end) as v_0611, SUM(case when date ='2013-06-12' then num else 0 end) as v_0612, SUM(case when date ='2013-06-13' then num else 0 end) as v_0613, SUM(case when date ='2013-06-14' then num else 0 end) as v_0614, SUM(case when date ='2013-06-15' then num else 0 end) as v_0615, SUM(case when date ='2013-06-16' then num else 0 end) as v_0616, SUM(case when date ='2013-06-17' then num else 0 end) as v_0617, SUM(case when date ='2013-06-18' then num else 0 end) as v_0618, SUM(case when date ='2013-06-19' then num else 0 end) as v_0619, SUM(case when date ='2013-06-20' then num else 0 end) as v_0620, SUM(case when date ='2013-06-21' then num else 0 end) as v_0621, SUM(case when date ='2013-06-22' then num else 0 end) as v_0622, SUM(case when date ='2013-06-23' then num else 0 end) as v_0623, SUM(case when date ='2013-06-24' then num else 0 end) as v_0624, SUM(case when date ='2013-06-25' then num else 0 end) as v_0625, SUM(case when date ='2013-06-26' then num else 0 end) as v_0626, SUM(case when date ='2013-06-27' then num else 0 end) as v_0627, SUM(case when date ='2013-06-28' then num else 0 end) as v_0628, SUM(case when date ='2013-06-29' then num else 0 end) as v_0629, SUM(case when date ='2013-06-30' then num else 0 end) as v_0630 from tab group by col1,col2,item) v
4、这个语句怎么写?
http://bbs.csdn.net/topics/390490832?page=1
我有一张表:CarRule
有下面这些列和数据
ID Keywords
1 时速50%、 不到100%
2 违反禁令标志
3 违反规定停放、拒绝立即驶离、妨碍其他车辆
我要查询这个CarRule表,根据关键字获取ID
例如:机动车行驶超过规定时速50%以上不到100%的 就能获取到 ID=1
机动车违反禁令标志的 就能获取到 ID=2
违反规定停放、临时停车且驾驶人不在现场或驾驶人虽在现场拒绝立即驶离,妨碍其他车辆、行人通行的
就能获取到 ID=3
这个查询我怎么写。
我的解法:
--1.先建立一个函数,通过分隔符来拆分keywords成多个关键字create function dbo.fn_splitSTR(@s varchar(8000), --要分拆的字符串@split varchar(10) --分隔字符) returns @re table( --要返回的临时表 col varchar(1000) --临时表中的列 )asbegin declare @len int set @len = LEN(@split) --分隔符不一定就是一个字符,可能是2个字符 while CHARINDEX(@split,@s) >0 begininsert into @re values(left(@s,charindex(@split,@s) - 1))set @s = STUFF(@s,1,charindex(@split,@s) - 1 + @len ,'') --覆盖:字符串以及分隔符 end insert into @re values(@s) return --返回临时表endgo --2.建表DECLARE @CarRule TABLE(id INT,Keywords VARCHAR(100))INSERT INTO @carruleVALUES(1,'时速50%、不到100%'), (2,'违反禁令标志'), (3,'违反规定停放、拒绝立即驶离、妨碍其他车辆');WITH split --拆分关键字as(SELECT c.id, c.keywords, f.col FROM @carrule cCROSS apply dbo.fn_splitSTR(c.keywords,'、') f)--3.第1个查询SELECT s.id, s.keywordsFROM split sINNER JOIN (SELECT s.id, s.keywords, count(col) AS split_str_count --拆分成了几个关键字FROM split sGROUP BY s.id, s.keywords) ss ON s.id = ss.idWHERE charindex(s.col,'机动车行驶超过规定时速50%以上不到100%的') > 0GROUP BY s.id, s.keywordsHAVING count(*) = max(ss.split_str_count) --比如第一条记录拆分成了2个关键词,那么在匹配时要2个都匹配上了,才算为匹配
第2个查询:
DECLARE @CarRule TABLE(id INT,Keywords VARCHAR(100))INSERT INTO @carruleVALUES(1,'时速50%、不到100%'), (2,'违反禁令标志'), (3,'违反规定停放、拒绝立即驶离、妨碍其他车辆');WITH split --拆分关键字as(SELECT c.id, c.keywords, f.col FROM @carrule cCROSS apply dbo.fn_splitSTR(c.keywords,'、') f)--3.SELECT s.id, s.keywordsFROM split sINNER JOIN (SELECT s.id, s.keywords, count(col) AS split_str_count --拆分成了几个关键字FROM split sGROUP BY s.id, s.keywords) ss ON s.id = ss.idWHERE charindex(s.col,'机动车违反禁令标志的') > 0GROUP BY s.id, s.keywordsHAVING count(*) = max(ss.split_str_count) --比如第一条记录拆分成了2个关键词,那么在匹配时要2个都匹配上了,才算为匹配
第3个查询:
DECLARE @CarRule TABLE(id INT,Keywords VARCHAR(100))INSERT INTO @carruleVALUES(1,'时速50%、不到100%'), (2,'违反禁令标志'), (3,'违反规定停放、拒绝立即驶离、妨碍其他车辆');WITH split --拆分关键字as(SELECT c.id, c.keywords, f.col FROM @carrule cCROSS apply dbo.fn_splitSTR(c.keywords,'、') f)--3.SELECT s.id, s.keywordsFROM split sINNER JOIN (SELECT s.id, s.keywords, count(col) AS split_str_count --拆分成了几个关键字FROM split sGROUP BY s.id, s.keywords) ss ON s.id = ss.idWHERE charindex(s.col,'违反规定停放、临时停车且驾驶人不在现场或驾驶人虽在现场拒绝立即驶离,妨碍其他车辆、行人通行的就能获取到') > 0GROUP BY s.id, s.keywordsHAVING count(*) = max(ss.split_str_count) --比如第一条记录拆分成了2个关键词,那么在匹配时要2个都匹配上了,才算为匹配
5、数据统计的问题。
http://bbs.csdn.net/topics/390618778
有2个字段,Profit, profitSum。默认profitSum的值为0。如下图
现在要做下统计,规则第一条profitSum的值就为Profit
第二条profitSum的值为第一条的profitSum+第二条的Profit
第三条profitSum的值为第二条的profitSum+第三条的Profit,
结果集如下图
我的解法:
--drop table tbcreate table tb(Profit decimal(10,2),profitSum decimal(10,2))insert into tbselect 20000.0,0.00 union allselect 5.00,0.00 union allselect 0.00,0.00 union allselect 0.00,0.00 union allselect -383.40,0.00 union allselect 379.80,0.00 union allselect 3.50,0.00;with t as(select *, row_number() over(order by @@servername) as rownumfrom tb)select profit, (select sum(profit) from t t2 where t2.rownum <= t1.rownum) as profitSumfrom t t1/*profit profitSum20000.0020000.005.00 20005.000.00 20005.000.00 20005.00-383.40 19621.60379.80 20001.403.50 20004.90*/