【问题标题】:Simple SQL to check if parent has any child rows or not检查父级是否有任何子行的简单 SQL
【发布时间】:2011-12-22 13:21:09
【问题描述】:

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

订单(PK : ID)

文件(PK:FileID,FK:OrderID)

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。如果存在多个文件行,那么它将尝试重复 Order 行,我已为其添加了 DISTINCT 并且我没有选择 @987654332 @ 但 f.ID/f.ID 将是 1(它存在)和 0 代表 null(不存在)。我了解到 JOIN 比内联 SELECT COUNT(*) 语句更好。

我已经测试过并且它有效,但我需要专家意见并且需要确保这是最好的方法。这是一个非常简单的例子,但我的 SELECT 语句很复杂,因为有很多查找,而且它的获取成本很高,所以我需要确保它的可扩展性。

谢谢。

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

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

或我提到的 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

附上两张查询执行计划的图片。请建议哪个更好。

【问题讨论】:

  • 您使用的是什么类型的 SQL?此外,File 的左外连接似乎会产生NULL 值,这是否由dbo.FileHeaderISNULL() 处理?你的意思是f.ID/o.ID 吗?显然f.ID/f.ID总是 == 1?
  • 我的数据库在 SQL Server 2008 中。我的想法是在 JOIN 产生 NULL 值时设置为 0。因此,如果 f.ID/f.ID 存在,则始终为 1,如果为 NULL,则始终为 0。这就是 CASE 语句所暗示的 - 它无需包含任何中间 SELECT * 或 COUNT 语句即可达到目的。
  • 如果您得到答案,请标记为是
  • 附加查询执行计划。请让我知道您的最终裁决。

标签: sql sql-server-2008 parent-child sql-execution-plan join


【解决方案1】:

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

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;

对于包含 大部分 dbo.FileHeader 的 SELECT,这应该会更好:

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,如上所示。

【讨论】:

  • 附加查询执行计划。请让我知道您的最终裁决。
  • 谢谢。我理解但在我的情况下 f.ID 是自动增量 int 所以它不能为零。是的,我将来会有大数据库,所以我得出结论,这是最好的 - “当 f.OrderID 为 NULL THEN 0 ELSE 1 END AS FilesHExist 时的情况”
【解决方案2】:

如果您可以使用孩子的数量

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;

建议:

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

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

【讨论】:

  • 感谢您的建议,我会这样做,但目前我的数据库记录太少。无论如何,前一段时间我从 SO 那里了解到,JOIN 比中间 SELECT 语句更便宜。我正在尝试通过简单的 JOIN 并避免 GROUP BY 来完成它。最后,我使用 DISTINCT 来获取唯一记录。请建议。
  • @HemantTank DISTINCT 通常在数据库内部实现为GROUP BY...GROUP BY 本身并没有什么坏处,而且“中间选择”有点难看,但尽管如此不应该放弃的东西......刚刚看到你在 SQL 2008 上 - 检查不同查询的执行计划,即使有一小部分记录,计划也会概述 Db 在运行时内部使用的不同路径turn 提供了有用的提示。
  • 附加查询执行计划。请让我知道您的最终裁决。
【解决方案3】:

试试这个

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-09
    • 1970-01-01
    • 1970-01-01
    • 2022-12-16
    • 1970-01-01
    相关资源
    最近更新 更多