【问题标题】:Query works on SQLite but fails on SQL Server查询在 SQLite 上有效,但在 SQL Server 上失败
【发布时间】:2020-01-20 21:36:27
【问题描述】:

以下查询在 SQLite 中适用于我。

SELECT Id, (SELECT Id
            FROM Vehicles v
            WHERE v.Id = t.VehicleId) AS VehId
FROM Transactions t
WHERE t.VehicleId IS NOT NULL AND VehId IS NULL;

但是在 SQL Server 中,它给了我一个错误。

列名“VehId”无效。

编辑器在最后一个VehId(但不是第一个)下面放了一条红色波浪线。

如您所料,Vehicles.Id 是主键。我试图在Transactions 中找到所有不引用Vehicles 中任何行的外键。完成此任务的最佳方法是什么?

【问题讨论】:

  • 这对我来说看起来像是一个古怪的反加入。由于表的 ID 决不能为空(否则它不会是它的 ID),所以只有当 Transactions.VehicleIdVehicles 表中没有匹配项时,您才能获得空值。但是你正在寻找VehicleId,只有那些不是空的,另一个表的ID应该总是有一个外键来保证数据的一致性。所以,你有一个损坏的数据库,想要找到无效的行,是吗?典型的方法是使用NOT EXISTSNOT IN 进行此类查找。

标签: sql sql-server sqlite tsql join


【解决方案1】:

您可以使用LEFT OUTER JOIN 查找不匹配的记录。

SELECT Id, v.Id AS VehId
FROM Transactions t
LEFT OUTER JOIN Vehicles v ON v.Id = t.VehicleId
WHERE t.VehicleId IS NOT NULL AND v.Id IS NULL

这会保留VehId 列,但由于您知道它始终是NULL,因此可以通过子查询找到一组类似的数据。这将为您提供所有相同的行,但缺少 VehId 列 - 如果您出于某种原因需要它,您始终可以对其进行硬编码。

SELECT Id
FROM Transactions t
WHERE t.VehicleId IS NOT NULL
AND t.VehicleId NOT IN (SELECT Id FROM Vehicles WHERE Id IS NOT NULL)

这将返回所有带有未知(但不是 NULL)VehicleId 的交易

【讨论】:

  • 在这种情况下不太可能发生,但NOT IN 如果子查询返回 NULL,它会产生问题。
  • 我认为这不太可能,因为t.VehicleId IS NOT NULL,但我想子查询也可以指定WHERE Id IS NOT NULL。 (加了,以防万一)
【解决方案2】:

您的查询的问题在于,在 SQL Server 中,列列表是在 WHERE 子句之后计算的。这就是为什么不能从 WHERE 子句引用列别名的原因。

您可以通过多种方式重写查询。这是其中的 3 个。

SELECT *
FROM (
    SELECT Id, (SELECT Id
                FROM Vehicles v
                WHERE v.Id = t.VehicleId) AS VehId
    FROM Transactions t) x
WHERE VehicleId IS NOT NULL AND VehId IS NULL;

SELECT Id, VehId
FROM Transactions t
OUTER APPLY (SELECT Id
            FROM Vehicles v
            WHERE v.Id = t.VehicleId) v(VehId)
WHERE t.VehicleId IS NOT NULL AND VehId IS NULL;

SELECT Id, NULL AS VehId
FROM Transactions t
WHERE t.VehicleId IS NOT NULL 
AND NOT EXISTS (SELECT Id
            FROM Vehicles v
            WHERE v.Id = t.VehicleId); 

【讨论】:

  • 这不是“SQL Server”问题。这就是 SQL 语言的定义方式。
  • @GordonLinoff:公平地说,Luis Cazares 并没有说这是 SQL Server 的问题。我认为他指的是我的问题。
  • 不,我并不是说这是 SQL Server 的问题。这是 Jonathan 的查询与 SQL Server 的设计不兼容的问题。
  • @GordonLinoff:没有人质疑这门语言是如何设计的。我发布了一个问题,因为我遇到了问题。他说我的问题是由于 SQL Server 的设计方式造成的。他从未说过这是 SQL Server 的问题。我很惊讶你在这件事上坚持己见。他在哪里说这是 SQL Server 的问题?
  • "问题是在 SQL Server 中......"相当于“您遇到的问题来自 SQL Server 中的事实......”
【解决方案3】:

您不能在 where 子句中引用列别名,这就是查询失败的原因。

看着它,我认为这可能是您正在寻找的东西,它本质上复制了一个左连接,其中右表中的列不匹配。

SELECT Id, (SELECT Id
            FROM Vehicles v
            WHERE v.Id = t.VehicleId and v.id is null) AS VehId
FROM Transactions t
WHERE t.VehicleId IS NOT NULL;

【讨论】:

  • WHERE v.Id = t.VehicleId and v.Id is null - 这是真的吗? v.Id 怎么可能等于t.VehicleId为空?
  • 这个查询不会返回正确的结果。
【解决方案4】:

我认为您的查询可以简化为LEFT JOIN 反模式:

SELECT t.Id
FROM Transactions t
LEFT JOIN Vehicles v ON v.Id = t.VehicleId
WHERE t.VehicleId IS NOT NULL AND v.Id IS NULL

另一种选择是将NOT EXISTS 条件与相关子查询一起使用:

SELECT Id
FROM Transactions t
WHERE 
    t.VehicleId IS NOT NULL
    AND NOT EXISTS (SELECT 1 FROM Vehicles v WHERE v.Id = t.VehicleId)

【讨论】:

  • 这样比较简单,但是好像做的不一样。
  • @JonathanWood:我刚刚用 2 个解决方案纠正了我的答案
【解决方案5】:

即使您的代码有效,为什么要在结果中显示第二列 VehId,因为它会被 nulls 填充?
当然,您可以使用 LEFT 加入或使用其他方法,但如果您想像代码一样保持简单,请从 SELECT 列表中删除该列并将其移至 WHERE 子句:

SELECT t.Id 
FROM Transactions t
WHERE 
  t.VehicleId IS NOT NULL 
  AND 
  (SELECT v.Id FROM Vehicles v WHERE v.Id = t.VehicleId) IS NULL;

【讨论】:

  • 这是用于故障排除的。我真的不在乎包含哪些列,只要我能弄清楚哪些行被破坏了。
  • 我不认为这是因为他想要这个专栏,我认为这是因为它是用 SQLite 编写的。
【解决方案6】:

使用NOT EXISTS:

SELECT Id
FROM Transactions t
WHERE t.VehicleId IS NOT NULL AND 
      NOT EXISTS (SELECT Id
                  FROM Vehicles v
                  WHERE v.Id = t.VehicleId
                 );

这个不仅是标准 SQL,而且在任何将 vehicles.id 声明为主键的数据库上都应该具有良好的性能。

【讨论】:

  • 我认为这对我来说是最直接的方法。我赞成您的答案,但 Luis Cazares 在您之前得到了相同的答案以及其他一些选项,因此我将他的答案标记为已接受的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-12
  • 2013-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 2016-01-26
相关资源
最近更新 更多