简单的 SQL 检查父级是否有子行

问题描述 投票:0回答:3

我显示一个包含父数据的网格,并且如果存在相关子行,则需要显示图标。 我的数据库位于 SQL Server 2008 中。让我简化一下,我有以下两个表 -

订单(PK:ID)

文件(PK:文件ID,FK:订单ID)

Order
可以有零个或多个与其相关的文件。
File
表有一列
OrderID
,其中保存对
Order
的 FK 引用。现在,我正在显示一个列出所有
Orders
的网格,并且我想显示一个图标图像,指示该
Order
是否有任何子行(文件)。

这是我尝试过的一种棘手方法,但不确定它的效率/可扩展性如何 -

SELECT DISTINCT o.ID, o.OrderNum, ...
,(CASE f.ID/f.ID WHEN 1 THEN 1 ELSE 0 END) as FilesExist
...
FROM  Order AS o LEFT OUTER JOIN dbo.FileHeader as f ON f.OrderID = o.ID

CASE
语句似乎可以按照要求完美运行。如果存在一个或多个文件,它将返回 1,否则返回 0。如果存在多个文件行,那么它将尝试重复我添加了 DISTINCT 并且没有选择
Order
f.ID
f.ID/f.ID
为 1(存在),0 为 null(不存在)。我了解到 JOIN 比内联
SELECT COUNT(*)
语句更好。

我已经测试过并且它有效,但我需要专家的意见,并且需要确保这是最好的方法。这是一个非常简单的示例,但我的

SELECT
语句很复杂,因为有很多查找,并且获取成本高昂,因此我需要确保其可扩展。

谢谢你。

编辑#1: 总而言之 - 要么是带有 COUNT(*) 的内部 SELECT

SELECT c.ClaimNo,(SELECT COUNT(*) FROM dbo.FileHeader AS f WHERE f.ClaimID = c.ID ) AS FilesHExist
FROM  dbo.Claim AS c

Internal Select

或者我提到的LEFT OUTER JOIN方法

SELECT DISTINCT c.ClaimNo, (CASE fh.ID / fh.ID WHEN 1 THEN 1 ELSE 0 END) AS FilesHExist               
FROM  dbo.Claim AS c LEFT OUTER JOIN dbo.FileHeader AS fh ON fh.ClaimID = c.ID

My JOIN approach

附上两张查询执行计划的图片。请大家推荐一下哪个比较好。

sql sql-server-2008 parent-child sql-execution-plan join
3个回答
16
投票

对于选择几行,这应该是最快的:

SELECT o.ID
     , o.OrderNum 
     , CASE WHEN EXISTS (SELECT * FROM dbo.FileHeader f WHERE f.OrderID = o.ID)
            THEN 1
            ELSE 0
       END AS FilesHExist
FROM   "Order" o;

(我引用了保留字“订单”。)

对于包含

SELECT
大部分
dbo.FileHeader
,这应该表现得更好:

SELECT o.ID
     , o.OrderNum 
     , CASE WHEN f.OrderID IS NULL THEN 0 ELSE 1 END AS FilesHExist
FROM   "Order" o
LEFT   JOIN
      (SELECT OrderID FROM dbo.FileHeader GROUP BY OrderID) f ON f.OrderID = o.ID

先组

OrderID
然后左加入应该会更便宜。最后不需要
DISTINCT

切勿使用:

(CASE f.ID/f.ID WHEN 1 THEN 1 ELSE 0 END)

它让你能够接受除以零的异常。将其替换为检查

NULL
,如上面所示。


3
投票

如果可以配合儿童使用的数量

SELECT o.ID
      ,o.OrderNum
      ,(SELECT COUNT(*) FROM dbo.FileHeader f WHERE f.OrderID = o.ID) AS FilesCount
FROM   Order o;

否则使用

SELECT o.ID
      ,o.OrderNum
      ,CASE WHEN (SELECT COUNT(*) FROM dbo.FileHeader f WHERE f.OrderID = o.ID) > 0 THEN 1 ELSE 0 END AS FilesExist
FROM   Order o;

建议:

每当您想知道数据库中到底发生了什么时,请比较不同版本的查询的计划 - 一切充其量都是有根据的猜测...计划会向您显示您真正要做的事情(例如,它考虑了有多少它期望返回的行以及其他我们在回答您的问题时不关心的内容)。

假设您使用的是 SQL Server,这在 SSMS 中可用...对于 Oracle 也是如此 - 它在 SQL Developer 中可用...大多数数据库都有这样的选项。


0
投票

试试这个

select A.Id OrderId ,case when isnull(B.FileCounts,0)>0 then 1 else 0 end FilesExist from [Order] A left join (select OrderId, COUNT(1) FileCounts from [File] group by OrderId)B on A.Id=B.OrderId
© www.soinside.com 2019 - 2024. All rights reserved.