我想基于案例条件的状态字段,但我得到null。
Declare @Status NVarchar(20);
Set @Status = Case When Exists ( Select GunSerialNo
From dbo.ArmouryIssueGun
Where ModifiedOn != Null
And CreatedOn != Null ) Then 'In Field'
When Exists ( Select GunSerialNo
From dbo.ArmouryIssueGun
Where ModifiedOn = Null
And CreatedOn != Null ) Then 'In Armory'
End;
-- Insert statements for procedure here
Select (Select BranchName
From Branch
Where BranchId = gun.BranchId
) As BranchName
, gun.SerialNo As GunSerialNo
, gun.GunType
, gun.ModelNo
, gun.GunId
, Convert(Varchar(12), aig.CreatedOn, 103) IssueDate
, Substring(Convert(Varchar(20), aig.CreatedOn, 9), 13, 5) + ' ' + Substring(Convert(Varchar(30), aig.CreatedOn, 9), 25, 2) As IssueTime
, cl.LicenceHolderName As CarriedBy
, (Select TypeName
From dbo.CommonValues
Where ID = aig.Purpose
) As Purpose
, @Status As status
, Convert(Varchar(12), aig.ModifiedOn, 103) As CollectedDate
, Substring(Convert(Varchar(20), aig.ModifiedOn, 9), 13, 5) + ' ' + Substring(Convert(Varchar(30), aig.ModifiedOn, 9), 25, 2) As TimeIn
From dbo.CarryAndUseLicence cl
Join dbo.Branch b
On b.BranchId = cl.BranchId
Join dbo.Gun gun
On cl.GunSerialNo = gun.SerialNo
Join dbo.ArmouryIssueGun aig
On aig.StaffId = cl.StaffId;
答案 0 :(得分:4)
你的问题在这里:
Set @Status = Case When Exists ( Select GunSerialNo
From dbo.ArmouryIssueGun
Where ModifiedOn != Null
And CreatedOn != Null ) Then 'In Field'
When Exists ( Select GunSerialNo
From dbo.ArmouryIssueGun
Where ModifiedOn = Null
And CreatedOn != Null ) Then 'In Armory'
End;
特别是这些部分:
Where ModifiedOn != Null
And CreatedOn != Null
并且
Where ModifiedOn = Null
And CreatedOn != Null
NULL
不能等于任何东西。甚至不是另一个NULL
。因此,您无法使用=
和!=
来比较它们。您需要使用IS NULL
和IS NOT NULL
代替。
尝试将它们更改为以下内容:
Where ModifiedOn Is Not Null
And CreatedOn Is Not Null
并且
Where ModifiedOn Is Null
And CreatedOn Is Not Null
答案 1 :(得分:4)
您的子查询没有相关条款!他们所做的只是查询查询表中的任何一行是否为空。
如果您对特定GunSerialNo
的状态感兴趣,则必须对其进行查询,例如:
SELECT
FROM dbo.ArmouryIssueGun
WHERE
ModifiedOn IS NOT NULL
AND CreatedOn IS NOT Null
AND GunSerialNo = @GunSerialNo -- A specific GunSerialNo
;
但是,我怀疑您对单个项目的状态不感兴趣。相反,您想知道一堆行的状态。在这种情况下,在@Status
变量中存储单个值对您没有好处。您需要将其合并到一个查询中,并将子查询与外部查询关联起来(参见Status =
表达式):
Select (Select BranchName
From Branch
Where BranchId = gun.BranchId
) As BranchName
, gun.SerialNo As GunSerialNo
, gun.GunType
, gun.ModelNo
, gun.GunId
, Convert(Varchar(12), aig.CreatedOn, 103) IssueDate
, Substring(Convert(Varchar(20), aig.CreatedOn, 9), 13, 5) + ' ' + Substring(Convert(Varchar(30), aig.CreatedOn, 9), 25, 2) As IssueTime
, cl.LicenceHolderName As CarriedBy
, (Select TypeName
From dbo.CommonValues
Where ID = aig.Purpose
) As Purpose
, Status =
Case When Exists (
Select *
From dbo.ArmouryIssueGun aig
Where
gun.SerialNo = aig.GunSerialNo // correlate to outer query
AND aig.ModifiedOn != Null
AND aig.CreatedOn != Null
) Then 'In Field'
When Exists (
Select *
From dbo.ArmouryIssueGun aig
Where
gun.SerialNo = aig.GunSerialNo // correlate to outer query
AND aig.ModifiedOn = Null
AND aig.CreatedOn != Null
) Then 'In Armory'
End
, Convert(Varchar(12), aig.ModifiedOn, 103) As CollectedDate
, Substring(Convert(Varchar(20), aig.ModifiedOn, 9), 13, 5) + ' ' + Substring(Convert(Varchar(30), aig.ModifiedOn, 9), 25, 2) As TimeIn
From dbo.CarryAndUseLicence cl
Join dbo.Branch b
On b.BranchId = cl.BranchId
Join dbo.Gun gun
On cl.GunSerialNo = gun.SerialNo
Join dbo.ArmouryIssueGun aig
On aig.StaffId = cl.StaffId;
另外,请注意这些可以帮助您的其他评论:
当您与NULL
进行比较时,将无效以使用=
或!=
或<>
。答案永远是NULL
,这既不是假的,也不是真的。你无能为力将会发生任何其他事情。 x.Column != NULL
将被视为假,NOT (x.Column != NULL)
。您必须x.Column IS NULL
或x.Column IS NOT NULL
或NOT (x.Column IS NULL)
。
似乎数据库设计可能存在严重的数据一致性错误。使用ModifiedOn
作为检查枪是否已检出的代理完全不安全。该设计假设一条“快乐的道路”,在检查之前不能修改枪(很可能被违反),并且一旦修改后它就不能仍然在军械库中(例如,重新检入)。如果您没有立即使用某些实际状态列或其他机制来指示枪的当前状态,您的系统可能会出现严重的问题。
如果ArmouryIssueGun
表与每支枪都有一对一的关系,那么就会出现问题,除非在每种情况下,每支枪只能100%可靠地完全正确一次性发出。这似乎不太可能。 可以是ArmouryIssueGun
真的不是历史表,而是代表Gun
表的一部分(就像C#中的部分类),因此是正确的1- to - (0 or)1。在这种情况下,我对使用ModifiedOn
的评论肯定是正确的,因为这是一种完全不可靠的方式来判断枪是否已经检查到现场。
如果关系是一对多的,因为我怀疑(并且应该如此),使用相当于历史日志表来检测某事物的当前状态通常不是最好的设计。可以犯错误。日志条目可能无法插入。应该有一个规范的地方,每个GunSerialNo
在一行中。使用历史表可以让后来不知道您在此查询中使用的奇怪代理依赖关系的人认为简单地插入另一个ArmoryIssueGun
行将解决问题,但您的查询假定只有ModifiedOn
值证明该字段中当前是,而不仅仅是在过去某个时间点的字段。
每个表中的列名应该相同。您不应该有一个名为SerialNo
的列,另一个列的名称为GunSerialNo
。
我可以看到枪的序列号如何为您的数据库创建一个相当不错的自然/业务键。但是,我怀疑是否存在对所有表格来说都不是一个好键的情况。一个序列号长多少个字符? int只使用4个字节,但如果您的序列号是20个字符,那么每行将占用序列号的5倍空间。请记住,聚集索引列在每个非聚集索引中都会重复。我不知道你的数据库的大小,但是如果你正在创建一个服务于整个国家军队的数据库,理论上可能有数百万条目!此时,性能确实开始变得重要,并且列使用的空间变得非常重要。
两次加入同一张桌子是不理想的。您可以像这样更改上面的查询,使连接只发生一次,另外还允许SELECT子句甚至包括找到的最新行中的列:
OUTER APPLY (
SELECT TOP 1
aig.*
FROM
dbo.ArmoryIssueGun aig
WHERE
gun.SerialNo = aig.GunSerialNo
ORDER BY
aig.ModifiedOn DESC
) aig
也没有理由进行子查询,例如您为BranchName
所做的子查询。它使查询混乱并且不一致。相反,一致地使用JOIN:
LEFT JOIN Branch b
ON gun.BranchId = b.BranchId
然后,您可以在b.BranchName
子句中使用SELECT
,还可以从Branch
表中提取其他列。根据我的经验,使用这样的子查询会导致某些思维习惯导致次优组织且难以理解的查询,有时甚至会产生负面的性能影响(例如,如果您想要Branch
表中的另一列,那么只是投入了另一个子查询,你不必要地将获取数据的成本增加了一倍。)