SQLite:将不存在的列视为 NULL
SQLite: treat non-existent column as NULL
我有一个这样的查询(简化和匿名):
SELECT
Department.id,
Department.name,
Department.manager_id,
Employee.name AS manager_name
FROM
Department
LEFT OUTER JOIN Employee
ON Department.manager_id = Employee.id;
字段 Department.manager_id
可能是 NULL
。如果它是非 NULL
那么它保证在 Employee
table 中的恰好一行是有效的 id
,所以 OUTER JOIN
就在那里对于 Department
table 中的行 NULL
.
这是问题所在:数据库的旧实例根本没有此 Department.manager_id
列。在那些情况下,我希望查询表现得好像该字段确实存在但总是 NULL
,所以例如manager_name
字段返回为 NULL
。如果查询只使用 Department
table 那么我可以只使用 SELECT *
并检查我的应用程序中的列,但是 JOIN
似乎使这不可能。我宁愿不修改数据库,部分原因是我可以以只读模式加载数据库。是否可以仅通过巧妙调整查询来完成?
这似乎有效:
SELECT
Dept.id,
Dept.name,
Dept.manager_id,
Employee.name AS manager_name
FROM
(SELECT *, NULL AS manager_id FROM Department) AS Dept
LEFT OUTER JOIN Employee
ON Dept.manager_id = Employee.id;
如果 manager_id
列存在于 Department
中,则它用于连接,而如果不存在,则 Dept.manager_id
和 Employee.name
均为 NULL。
如果我交换子查询中的列顺序:
(SELECT NULL AS manager_id, * FROM Department) AS Dept
那么 Dept.manager_id
和 Employee.name
都是 NULL 即使 Department.manager_id
列存在,所以似乎 Dept.manager_id
首先引用 具有该名称的 Dept
子查询中的 列。最好在 SQLite 文档中找到一个参考,说这种行为是有保证的(或者明确地说不是),但我找不到任何东西(例如在 SELECT
or expression 页)。
我没有在其他数据库系统上尝试过,所以我不知道它是否适用于 SQLite 以外的任何系统。
为了完整起见,这里有一个不需要将两种可能的模式合并到一个查询中的答案(但仍然不需要您实际进行模式迁移):
检查模式版本,并使用它来确定发出哪个 SELECT
查询(即有或没有 manager_id
列和 JOIN
)作为一个单独的步骤。以下是确定架构版本的几种可能性:
- 理想情况是您已经通过为模式分配版本号并将它们记录在数据库中来跟踪模式。通常这是通过以下任一方式完成的:
-
user_version
pragma.
- 一个名为 "Schema" 或类似的 table,其中一行包含模式版本号。
- 您可以直接确定该列是否存在于 table 中。两种可能性:
- 使用
table_info
pragma确定table中的列列表。
- 使用简单的
SELECT * FROM Table LIMIT 1
并查看返回的列(这可能更好,因为它独立于数据库引擎)。
我有一个这样的查询(简化和匿名):
SELECT
Department.id,
Department.name,
Department.manager_id,
Employee.name AS manager_name
FROM
Department
LEFT OUTER JOIN Employee
ON Department.manager_id = Employee.id;
字段 Department.manager_id
可能是 NULL
。如果它是非 NULL
那么它保证在 Employee
table 中的恰好一行是有效的 id
,所以 OUTER JOIN
就在那里对于 Department
table 中的行 NULL
.
这是问题所在:数据库的旧实例根本没有此 Department.manager_id
列。在那些情况下,我希望查询表现得好像该字段确实存在但总是 NULL
,所以例如manager_name
字段返回为 NULL
。如果查询只使用 Department
table 那么我可以只使用 SELECT *
并检查我的应用程序中的列,但是 JOIN
似乎使这不可能。我宁愿不修改数据库,部分原因是我可以以只读模式加载数据库。是否可以仅通过巧妙调整查询来完成?
这似乎有效:
SELECT
Dept.id,
Dept.name,
Dept.manager_id,
Employee.name AS manager_name
FROM
(SELECT *, NULL AS manager_id FROM Department) AS Dept
LEFT OUTER JOIN Employee
ON Dept.manager_id = Employee.id;
如果 manager_id
列存在于 Department
中,则它用于连接,而如果不存在,则 Dept.manager_id
和 Employee.name
均为 NULL。
如果我交换子查询中的列顺序:
(SELECT NULL AS manager_id, * FROM Department) AS Dept
那么 Dept.manager_id
和 Employee.name
都是 NULL 即使 Department.manager_id
列存在,所以似乎 Dept.manager_id
首先引用 具有该名称的 Dept
子查询中的 列。最好在 SQLite 文档中找到一个参考,说这种行为是有保证的(或者明确地说不是),但我找不到任何东西(例如在 SELECT
or expression 页)。
我没有在其他数据库系统上尝试过,所以我不知道它是否适用于 SQLite 以外的任何系统。
为了完整起见,这里有一个不需要将两种可能的模式合并到一个查询中的答案(但仍然不需要您实际进行模式迁移):
检查模式版本,并使用它来确定发出哪个 SELECT
查询(即有或没有 manager_id
列和 JOIN
)作为一个单独的步骤。以下是确定架构版本的几种可能性:
- 理想情况是您已经通过为模式分配版本号并将它们记录在数据库中来跟踪模式。通常这是通过以下任一方式完成的:
-
user_version
pragma. - 一个名为 "Schema" 或类似的 table,其中一行包含模式版本号。
-
- 您可以直接确定该列是否存在于 table 中。两种可能性:
- 使用
table_info
pragma确定table中的列列表。 - 使用简单的
SELECT * FROM Table LIMIT 1
并查看返回的列(这可能更好,因为它独立于数据库引擎)。
- 使用