我有两个表格,我将用于跟踪目的,日期表和项目表。 日期表用于跟踪被跟踪ID的开始和结束日期。 项目表是在ID的特定日期提取的项目数量。 id是这两个表之间的外键。
我想要做的是具有项目ID的GROUP BY的项目的总和,但仅基于拉出项目的日期是否落在被跟踪的start_date和end_date之间来对项目求和标识。
id start_date end_date 1 2014-01-01 NULL 2 2014-01-01 2014-01-02 3 2014-01-25 NULL
id items date 1 3 2014-01-01 1 5 2014-01-02 1 5 2014-01-26 2 2 2014-01-01 2 3 2014-01-05 2 2 2014-01-26 3 2 2014-01-01 3 3 2014-01-05 3 2 2014-01-26
我到目前为止已经使用SQL了,但是我从这里添加了什么内容,我很遗憾。
SELECT
a.id,
SUM(items)
FROM
ww_test.dbo.items a
INNER JOIN ww_test.dbo.dates b ON
a.id = b.id
WHERE
a.date >= '2014-01-01' AND a.date <= '2014-01-30'
GROUP BY
a.id
ORDER BY
a.id
id items 1 13 2 2 3 2
id items 1 13 2 7 3 7
答案 0 :(得分:1)
首先,我强烈建议您停止在日期范围内使用NULL
来表示“无结束日期”,而是使用标记值,例如9999-12-31
。其原因主要是性能,其次是查询简单性 - 现在在编写查询时以及后来需要维护查询的人或其他人都会受益。在前端或中间代码中,将日期范围与Null
或9999-12-31
进行比较几乎没有区别,事实上,您可以获得与简化代码相同的一些好处。在你的SQL中。我的建议基于10年以上的全职专业SQL查询编写经验。
要按原样修复您的查询,我认为这样可行:
SELECT
a.id,
ItemsSum = SUM(items)
FROM
ww_test.dbo.items a
INNER JOIN ww_test.dbo.dates b
ON a.id = b.id
AND a.date >= Coalesce(b.start_date, 0)
AND a.date <= Coalesce(b.end_date, '99991231')
WHERE
a.date >= '20140101'
AND a.date <= '20140130'
GROUP BY
a.id
ORDER BY
a.id
;
请注意,如果您遵循我的建议,那么您的查询JOIN
条件可能如下所示:
INNER JOIN ww_test.dbo.dates b
ON a.id = b.id
AND a.date >= b.start_date
AND a.date <= b.end_date
您会发现,如果您的数据集变得很大,那么必须在其中放置Coalesce
或IsNull
会严重影响性能。使用OR
子句也无济于事:
INNER JOIN ww_test.dbo.dates b
ON a.id = b.id
AND (a.date >= b.start_date OR b.start_date IS NULL)
AND (a.date <= b.end_date OR b.end_date IS NULL)
这会产生同样的问题(例如,当有合适的索引时,可能会将搜索转换为扫描,这会非常令人难过)。
最后,我还建议您将结束日期更改为独占而不是包含。这意味着对于结束日期,您不是输入最后一天开始日期的信息,而是将第一天的日期设为不再为真。这个建议有几个原因:
a.end_date + 1 = b.start_date
上进行所有比较而不是a.end_date = b.start_date
的简单等值连接。这很痛苦,容易犯错误。date
数据类型)仍然具有隐含的时间部分,可以直接转换为具有时间部分的日期数据类型,该时间部分始终为0
或12 a.m.
。唯一的缺点是,在某些情况下,您必须仔细考虑您向用户显示的日期(转换为包含日期),然后将他们输入的日期转换为存储到数据库的独占日期。但这仅限于UI处理代码,而不是整个数据库,所以它不是一个很大的缺点。
您查询的唯一更改是:
INNER JOIN ww_test.dbo.dates b
ON a.id = b.id
AND a.date >= b.start_date
AND a.date < b.end_date -- no equal sign now
最后一件事:请注意日期格式'yyyy-mm-dd'不是文化安全的。
SET LANGUAGE FRENCH;
SELECT Convert(datetime, '2014-01-30'); -- fails with an error
SQL Server中日期时间唯一不变的文化安全格式是:
yyyymmdd
yyyy-mm-ddThh:mm:ss
答案 1 :(得分:0)
我认为您要做的是比较start_date
表的end_date
和Data
之间的日期。
将您的查询更改为以下内容并尝试
SELECT
a.id,
SUM(items)
FROM
ww_test.dbo.items a
INNER JOIN ww_test.dbo.dates b ON a.id = b.id
WHERE
a.date >= ISNULL(b.start_date, GETDATE())
AND a.date <= ISNULL(b.end_date, GETDATE())
GROUP BY a.id
ORDER BY a.id
答案 2 :(得分:0)
查询的问题是条件部分。 此外,由于您需要根据Dates表中定义的条件检索数据,因此您不必显式硬编码条件。 假设您的结束日期可以为空或具有值,您可以使用以下内容 查询:
SELECT
a.id,
SUM(items)
FROM
ww_test.dbo.items a
INNER JOIN ww_test.dbo.dates b ON
a.id = b.id
where (b.end_date is not null and a.date between b.start_date and b.end_date)
or (b.end_date is null and a.date >= b.start_date)
GROUP BY
a.id
ORDER BY
a.id