SQL 查询:如何将整数更改为布尔值

SQL query : How to change integer to boolean

我正在使用 Firebird 2.5.8 和 Delphi 10.2.3,我想用查询填充 DBGrid:

SELECT c.ID, l.ID,
(
SELECT COUNT(pl.ID)
FROM Tbl_ProtocolLicense AS pl
WHERE (pl.ReferenceId=l.ID)
) AS ReferenceCount

FROM  Tbl_License AS l, tbl_client AS c
WHERE l.ClientId=c.Id;

如何将值 ( ReferenceCount > 0 ) 作为布尔值或 (0/1) 添加到该查询?

为什么还要使用会为每一行一次又一次重新计算的相关查询?

第一个查询实际上不起作用。太仓促了。

SELECT 
  c.ID,
  l.ID, 
  IIF( r.CNT > 0, 1, 0 )
FROM Tbl_License AS l
  JOIN tbl_client AS c ON l.ClientId=c.Id
  JOIN (
    SELECT COUNT(*) as CNT, ReferenceId as ID
    FROM Tbl_ProtocolLicense 
    GROUP BY 2
        ) as r ON r.ID = l.ID

注意:这假设 Tbl_ProtocolLicense.ID 列永远不会是 NULL


更新。我在 上讲了一些关于 COUNT 和其他聚合的演讲 - 但在这里我自己错过了。

 SELECT COUNT(*) as CNT, ReferenceId as ID
        FROM Tbl_ProtocolLicense 
        GROUP BY 2

执行运行查询并查看结果。注意到什么可疑的东西了吗?

此查询仅 returns 行存在,不查询不存在的行。

中间分组查询不会有一行,其中 count=0 ! 因此,整个基于内部连接的查询也不会有它们!

我们应该做的是使用 Outer Join,即使在另一个 table 中没有匹配的行,它也会让行存在。阅读:https://en.wikipedia.org/wiki/Join_(SQL)

SELECT 
  c.ID,
  l.ID, 
  IIF( r.CNT is not NULL, 1, 0 )
FROM Tbl_License AS l
  JOIN tbl_client AS c ON l.ClientId=c.Id
  LEFT JOIN (
    SELECT COUNT(*) as CNT, ReferenceId as ID
    FROM Tbl_ProtocolLicense 
    GROUP BY 2
        ) as r ON r.ID = l.ID

将输出与第一个查询进行比较,看看有什么不同。


UPD 2. 然而,即使那样也可能不够好。这里的问题是 "you say you want the things you do not actually want".

当您真的不关心计数时,您要求 Firebird 计算所有行。您只关心 "if there is at least one row or none at all"。如果只有一行 - 你不关心是否会有 10 或 100 或 1000。因此,当您不希望对对象进行计数时,实际上对它们进行计数 - 是一项白费力气的额外工作。

这在 Interbase/Firebird 家庭中尤其浪费,其中超过 table 会触发 garbage collection 并减慢工作速度。但即使在纯 Delphi 中也是如此 - 如果您对找到第一个合适的元素感到满意,您不想遍历所有数组。

然后我们可以回到相关的子查询。

SELECT 
  c.ID,
  l.ID,
  IIF( EXISTS ( 
         SELECT * FROM Tbl_ProtocolLicense AS pl
         WHERE pl.ReferenceId=l.ID 
       ), 1, 0 )   
FROM  Tbl_License AS l, tbl_client AS c
WHERE l.ClientId=c.Id;
  • c.s.q 的苦涩的一面。是它被 运行 一次又一次地用于每个结果行
  • 计算分组总计数的痛苦的一面是你实际上不需要那个数据,不需要精确的计数。

哪个更糟?谁知道。取决于真实数据和真实 tables/indexes - 可能存在一种或另一种方法更快的情况。人类不会注意到小数据的差异。这是关于 "scaling up" 的问题,对于成千上万的真实数据,差异将在哪里显示。


UPD 3. 我们可以同时使用这两种方法吗?我希望我们可以。诀窍是 - 确切地 询问我们需要什么,仅此而已。我们可以要求 Firebird 列出我们在 table 中拥有的所有 ID,而不实际计算它们吗?是的,有。

 SELECT DISTINCT ReferenceId FROM Tbl_ProtocolLicense  

运行查询并查看结果!

注意,它仍然不会列出不在 table 中的 ID。明显的?好吧,我在第一次尝试时错过了它,然后两个支持我的人也错过了。愚蠢的错误是最难发现的,因为你无法相信这种愚蠢。

所以,现在我们必须插入它而不是第二次尝试的 "counting" 查询。

SELECT 
  c.ID,
  l.ID, 
  IIF( r.ReferenceId is NULL, 0, 1 )
FROM Tbl_License AS l
  JOIN tbl_client AS c ON l.ClientId=c.Id
  LEFT JOIN (
    SELECT DISTINCT ReferenceId 
    FROM Tbl_ProtocolLicense 
      ) as r ON r.ReferenceId = l.ID

更新。 4 最后一招。如果我是正确的,这个查询将得到与上面完全相同的结果,而不使用 IIF/CASE。尝试并比较。如果结果相同 - 则尝试了解它为什么和如何工作以及它需要哪些关于数据的额外假设。

SELECT 
  c.ID,
  l.ID, 
  COUNT( r.ReferenceId )
FROM Tbl_License AS l
  JOIN tbl_client AS c ON l.ClientId=c.Id
  LEFT JOIN (
    SELECT DISTINCT ReferenceId 
    FROM Tbl_ProtocolLicense 
      ) as r ON r.ReferenceId = l.ID
GROUP BY c.ID, l.ID

这个查询并不比 Upd.3 好,它只是一个思考然后更好地理解 SQL 的问题。


现在做一些工作来实际检查和比较结果,因为盲目相信互联网上的陌生人是不好的。即使那个人没有恶意,他也会犯愚蠢的错误。

无论您从 Internet 论坛上看到什么,那只是 "example" 和 "idea demonstration",理解和检查该示例始终是您的责任。也许拒绝它。


阅读和理解:

此外,阅读一些关于一般知识的好书 SQL 对您来说真的很有用,比如 Martin Gruber 的