为什么 SQL count(*) 与 SQL count(numeric) 存在行为差异
Why there is a behavior difference of SQL count(*) vs SQL count(numeric)
我知道
count(*) - 将 returns 包括空值在内的所有行的总数。
count(colName) - 将 returns colName 不为空的所有行的总数。
今天我的一所大学在 SQL 中遇到了 count() 的问题。他试图在应用一些日期过滤器后从视图中获取行数。
查看Return数据结构
[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'
这 returns 约 94 条记录。
查询 2
SELECT Count(*) from vw_ActualAllocation_New
where EID = '442105' and
Period >= '01-Jan-2017' AND Period < '01-Jan-2018'
这returns一个错误
Msg 241, Level 16, State 1, Line 1 Conversion failed when converting
date and/or time from character string.
查询 3
SELECT Count(EMargin) from vw_ActualAllocation_New
where EID = '442105' and
Period >= '01-Jan-2017' AND Period < '01-Jan-2018'
这个returns我算94。
Please note that EMargin is a NUMERIC datatype and all other types
such as int and varchar returns the same error.
请分享您对这两种行为之间差异的看法。
SQL 服务器环境:Microsoft SQL Server 2012 (Build 7601: Service Pack 1) (Hypervisor)
更新 - 查看代码
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的建议得出了结论,如果您觉得自己可能有其他想法,请post在这里
我还尝试将视图中的数据转换为新的 table 并且它工作正常。直接从视图访问时发生问题。视图生成发生在大量数据上,由于其巨大的规模和隐私协议,无法在此处 post 生成视图。感谢您理解我的局限并帮助我
问题最可能的原因是这行代码:
CONVERT(DATETIME, D.Month + '-01-' + D.Year) AS Period
在 SQL 服务器中,你不应该在没有指定格式或使用标准格式的情况下使用 CONVERT()
从字符串到日期(SQL 服务器首选 YYYYMMDD,但我认为YYYY-MM-DD 也可以接受。
在旧版本的 SQL 服务器中,您可以:
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(*) - 将 returns 包括空值在内的所有行的总数。 count(colName) - 将 returns colName 不为空的所有行的总数。
今天我的一所大学在 SQL 中遇到了 count() 的问题。他试图在应用一些日期过滤器后从视图中获取行数。
查看Return数据结构
[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'
这 returns 约 94 条记录。
查询 2
SELECT Count(*) from vw_ActualAllocation_New
where EID = '442105' and
Period >= '01-Jan-2017' AND Period < '01-Jan-2018'
这returns一个错误
Msg 241, Level 16, State 1, Line 1 Conversion failed when converting date and/or time from character string.
查询 3
SELECT Count(EMargin) from vw_ActualAllocation_New
where EID = '442105' and
Period >= '01-Jan-2017' AND Period < '01-Jan-2018'
这个returns我算94。
Please note that EMargin is a NUMERIC datatype and all other types such as int and varchar returns the same error.
请分享您对这两种行为之间差异的看法。
SQL 服务器环境:Microsoft SQL Server 2012 (Build 7601: Service Pack 1) (Hypervisor)
更新 - 查看代码
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的建议得出了结论,如果您觉得自己可能有其他想法,请post在这里 我还尝试将视图中的数据转换为新的 table 并且它工作正常。直接从视图访问时发生问题。视图生成发生在大量数据上,由于其巨大的规模和隐私协议,无法在此处 post 生成视图。感谢您理解我的局限并帮助我
问题最可能的原因是这行代码:
CONVERT(DATETIME, D.Month + '-01-' + D.Year) AS Period
在 SQL 服务器中,你不应该在没有指定格式或使用标准格式的情况下使用 CONVERT()
从字符串到日期(SQL 服务器首选 YYYYMMDD,但我认为YYYY-MM-DD 也可以接受。
在旧版本的 SQL 服务器中,您可以:
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 指出的那样,问题在于在执行引擎中评估值的位置。