我的访问日志数据库将时间存储为纪元,并将年月日提取为整数。此外,数据库的分区基于提取的Y / m / d,我保留35天。
如果我运行此查询:
select *
from mydb
where year in (2017, 2018)
and month in (12, 1)
and day in (31, 1)
我也意识到我可以做这样的事情:
select *
from mydb
where (year = 2017 and month = 12 and day = 31)
or (year = 2018 and month = 1 and day = 1)
但是我真正要寻找的是:一种写查询的好方法,在该查询中,我以年月日为起始,然后输入第四个值(天数+),然后获取所有数据例如,2017年12月31日+ 5天。
SQL中是否有本机方法可以完成此任务?我有一个庞大的数据集,如果我不指定日期,而必须依靠时代来做到这一点,那么查询将永远耗费时间。我对分区配置也没有影响。
答案 0 :(得分:1)
使用Impala作为dbms和SQL方言,您将能够使用公用表表达式,但不能使用递归。此外,插入参数也可能会出现问题。
以下是未经测试的建议,需要您找到一些替代功能。首先,它生成一组行,该行的整数为0到999(在示例中)。如果需要,扩展行数非常容易。在这些行中,可以使用date_add(timestamp startdate, int days/interval expression)
将天数添加到时间戳文字中,然后使用year(timestamp date)
和month(timestamp date)
和day(timestamp date)
see Date and Time functions创建列需要与您的数据匹配。
总体而言,您应该能够构建一个通用的表表达式,该表表达式具有涵盖所需范围的年,月,日的列,并且您可以内部联接至源表,从而实现日期范围过滤器。
下面的代码是使用T-SQL(SQL Server)生成的,可以here进行测试。
-- produce a set of integers, adjust to suit needed number of these
;WITH
cteDigits AS (
SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
)
, cteTally AS (
SELECT
d1s.digit
+ d10s.digit * 10
+ d100s.digit * 100 /* add more like this as needed */
-- + d1000s.digit * 1000 /* add more like this as needed */
AS num
FROM cteDigits d1s
CROSS JOIN cteDigits d10s
CROSS JOIN cteDigits d100s /* add more like this as needed */
-- CROSS JOIN cteDigits d1000s /* add more like this as needed */
)
, DateRange AS (
select
num
, dateadd(day,num,'20181227') dt
, year(dateadd(day,num,'20181227')) yr
, month(dateadd(day,num,'20181227')) mn
, day(dateadd(day,num,'20181227')) dy
from cteTally
where num < 10
)
select
*
from DateRange
我认为这些是上面使用的函数调用的Impala等效项:
, DateRange AS (
select
num
, date_add(to_timestamp('20181227','yyyyMMdd'),num) dt
, year( date_add(to_timestamp('20181227','yyyyMMdd'),num) ) yr
, month( date_add(to_timestamp('20181227','yyyyMMdd'),num) ) mn
, day( date_add(to_timestamp('20181227','yyyyMMdd'),num) ) dy
from cteTally
where num < 10
希望您能弄清楚如何使用它们。最终目的是像这样使用生成的日期范围:
select * from mydb t
inner join DateRange on t.year = DateRange.yr and t.month = DateRange.mn and t.day = DateRange.dy
原始帖子
在不知道要为哪个数据库提出解决方案的情况下,以下是使用SQL Server的建议:
此建议涉及一个递归公用表表达式,然后可以将其用作源数据的内部联接,以将结果限制为日期范围。
--Sql Server 2014 Express Edition
--https://rextester.com/l/sql_server_online_compiler
declare @yr as integer = 2018
declare @mn as integer = 12
declare @dy as integer = 27
declare @du as integer = 10
;with CTE as (
select
datefromparts(@yr, @mn, @dy) as dt
, @yr as yr
, @mn as mn
, @dy as dy
union all
select
dateadd(dd,1,cte.dt)
, datepart(year,dateadd(dd,1,cte.dt))
, datepart(month,dateadd(dd,1,cte.dt))
, datepart(day,dateadd(dd,1,cte.dt))
from cte
where cte.dt < dateadd(dd,@du-1,datefromparts(@yr, @mn, @dy))
)
select
*
from cte
这将产生以下结果:
+----+---------------------+------+----+----+
| | dt | yr | mn | dy |
+----+---------------------+------+----+----+
| 1 | 27.12.2018 00:00:00 | 2018 | 12 | 27 |
| 2 | 28.12.2018 00:00:00 | 2018 | 12 | 28 |
| 3 | 29.12.2018 00:00:00 | 2018 | 12 | 29 |
| 4 | 30.12.2018 00:00:00 | 2018 | 12 | 30 |
| 5 | 31.12.2018 00:00:00 | 2018 | 12 | 31 |
| 6 | 01.01.2019 00:00:00 | 2019 | 1 | 1 |
| 7 | 02.01.2019 00:00:00 | 2019 | 1 | 2 |
| 8 | 03.01.2019 00:00:00 | 2019 | 1 | 3 |
| 9 | 04.01.2019 00:00:00 | 2019 | 1 | 4 |
| 10 | 05.01.2019 00:00:00 | 2019 | 1 | 5 |
+----+---------------------+------+----+----+
和:
select * from mydb t
inner join cte on t.year = cte.yr and t.month = cte.mn and t.day = cte.dy
代替递归公用表表达式,可以使用整数表(或使用一组联合选择查询生成一组整数)-通常称为计数表。一个选择的方法将取决于所使用的dbms类型和版本。
同样,根据数据库的不同,将上面显示的结果持久保存为临时表并为其添加索引可能更有效。