为啥 SQL 计数(*)与 SQL 计数(数字)存在行为差异
Posted
技术标签:
【中文标题】为啥 SQL 计数(*)与 SQL 计数(数字)存在行为差异【英文标题】:Why there is a behavior difference of SQL count(*) vs SQL count(numeric)为什么 SQL 计数(*)与 SQL 计数(数字)存在行为差异 【发布时间】:2017-05-03 13:17:54 【问题描述】:我知道 count(*) - 将返回包括空值在内的所有行的总数。 count(colName) - 将返回 colName 不为空的所有行的总数。
今天我的一所大学遇到了 SQL 中的 count() 问题。在应用了一些日期过滤器后,他试图从视图中获取行数。
查看返回数据结构
[Year] VARCHAR(4)
,[Month] VARCHAR(4)
,AType VARCHAR(20)
,PActualsID INT
,EID VARCHAR(12)
,CID INT
,CGId INT
,EMargin NUMERIC(17,3)
,Period DATETIME
,PLID INT
,PCID INT
,PSID INT
,VPID INT
,VSID INT
,STID INT
查询 1
SELECT * from vw_ActualAllocation_New
where EntityId = '442105' and
Period >= '01-Jan-2017' AND Period < '01-Jan-2018'
这会返回大约 94 条记录。
查询 2
SELECT Count(*) from vw_ActualAllocation_New
where EID = '442105' and
Period >= '01-Jan-2017' AND Period < '01-Jan-2018'
这会返回一个错误
Msg 241, Level 16, State 1, Line 1 转换时转换失败 字符串中的日期和/或时间。
查询 3
SELECT Count(EMargin) from vw_ActualAllocation_New
where EID = '442105' and
Period >= '01-Jan-2017' AND Period < '01-Jan-2018'
这将返回我的计数为 94。
请注意 EMargin 是 NUMERIC 数据类型和所有其他类型 例如 int 和 varchar 返回相同的错误。
请分享您对这两种行为之间差异的看法。
SQL Server 环境:Microsoft SQL Server 2012(Build 7601:Service Pack 1)(管理程序)
更新 - 查看代码
CREATE VIEW [dbo].[vw_ActualAllocation_New]
SELECT D.Year, D.Month, A.AType, A.PAID, D.EntityID, D.CustomerID
,B.CGId, SUM(A.EBITRMargin) AS EBITRMargin
,CONVERT(DATETIME,D.Month + '-01-' + D.Year) AS Period, D.PLID, D.PCID
,D.PSID, D.VPID, D.VSID,D.STID
FROM dbo.AAllocations AS A
INNER JOIN dbo.PActuals AS D ON D.PActualsID = A.PActualsID AND D.Active = 1
INNER JOIN dbo.Customer AS B ON D.CustomerID = B.CustomerID AND D.EntityID =
B.EntityID
INNER JOIN dbo.AStatus AS C ON A.ASID = C.ASID
WHERE (A.Active = 1) AND (C.Active = 1) AND (C.Reference = 'Actuals') AND
(C.Status = 1)
GROUP BY D.Year, D.Month, A.AType, A.PAID, D.EntityID, D.CustomerID, B.CGId,
D.PLID, D.PCID, D.PSID, D.VPID, D.VSID, D.STID
结论更新
根据Gordon的建议得出结论,如果您觉得自己可能有其他想法,请在此处发布 我还尝试将视图中的数据放入一个新表中,并且工作正常。直接从视图访问时会发生问题。视图生成发生在大量数据中,由于其庞大的规模和隐私协议,无法在此处发布。感谢您了解我的局限性并帮助我
【问题讨论】:
Period 的数据类型是什么?你真的应该使用符合 ANSI 的日期字符串。 该错误与您在count
中包含的内容无关。如错误消息所示,日期比较失败。
我有点同意 vkp 但如果它与下面的查询相同,它应该不会失败。老实说很困惑。
答案总是一样的。这两个查询生成不同的执行计划(因为一个只需要测试是否存在行,一个需要找到非空的EMargin
值)。反过来,在其中一个计划中,谓词被推到比另一个“更深”的地方,因此日期时间和字符串值之间的比较发生在 早于的某个“守卫”谓词之前防止发生不适当的比较(当字符串不包含类似日期的内容时)
可重现的例子,否则它没有发生。
【参考方案1】:
问题的最可能原因是这行代码:
CONVERT(DATETIME, D.Month + '-01-' + D.Year) AS Period
在 SQL Server 中,如果没有指定格式或使用标准格式(SQL Server 首选 YYYYMMDD,但我认为 YYYY-MM-DD 也是可以接受的),则永远不应在字符串到日期中使用 CONVERT()
。
在旧版本的 SQL Server 中,您可以:
CONVERT(DATE, d.Year + RIGHT('00' + D.Month, 2) + '01') as period
此转换将始终有效。在较新的版本中,使用datefromparts()
:
DATEFROMPARTS(d.Year, d.Month, 1) as Period
为什么会这样?我推测日期格式被解释为 DD-MM-YYYY 而不是 MM-DD-YYYY。换句话说,你认为的 2 月 1 日实际上是 1 月 2 日。
此外,实体“442105”的期间值都转换为合理的日期。 WHERE
子句过滤掉错误值。问题在于其他实体和问题,正如 Damien 指出的那样,是在执行引擎中评估值的位置。
【讨论】:
我在从视图中选择 * 时获取值,但在应用 count(*)Cannot construct data type date, some of the arguments have values which are not valid.
时出现错误@
之前的时间段是日期时间,现在是日期。
我正在尝试这样SELECT count(*) from vw_ActualAllocation_New_Edit WHERE EntityId = '442105' and Period >= '2017-01-01' AND Period < '2018-01-01'
我也以你的方法结束以上是关于为啥 SQL 计数(*)与 SQL 计数(数字)存在行为差异的主要内容,如果未能解决你的问题,请参考以下文章