如何在父查询 case 语句中使用子查询的结果
How to use results of subquery in a parent query case statement
我有以下返回预期结果的查询:
SELECT
locations.*,
(
SELECT
id
FROM
hauls
WHERE
haul_type_id = 1
AND location_id = locations.id
ORDER BY
created_at DESC
LIMIT 1) AS last_delivery_id,
(
SELECT
id
FROM
hauls
WHERE
haul_type_id = 2
AND location_id = locations.id
ORDER BY
created_at DESC
LIMIT 1) AS last_pickup_id
FROM
locations
我想在父查询的 case 语句中使用子查询 (last_delivery_id
、last_pickup_id
) 的结果,但出现错误:
ERROR: Unknown column 'last_delivery_id'
SELECT
locations.*,
case when last_delivery_id = 1 then 'pending' when last_delivery_id = 2 then 'active' end as status,
(
SELECT
id
FROM
hauls
WHERE
haul_type_id = 1
AND location_id = locations.id
ORDER BY
created_at DESC
LIMIT 1) AS last_delivery_id,
(
SELECT
id
FROM
hauls
WHERE
haul_type_id = 2
AND location_id = locations.id
ORDER BY
created_at DESC
LIMIT 1) AS last_pickup_id
FROM
locations
您可以使用子查询或 CTE:
SELECT lh.*, . . . -- any expressions you want
(case when last_delivery_id = 1 then 'pending'
when last_delivery_id = 2 then 'active'
end) as status
FROM (SELECT l.*,
(SELECT id
FROM hauls h
WHERE h.haul_type_id = 1 AND
h.location_id = l.id
ORDER BY h.h.created_at DESC
LIMIT 1
) AS last_delivery_id,
(SELECT id
FROM hauls h
WHERE h.haul_type_id = 2 AND
h.location_id = l.id
ORDER BY h.created_at DESC
LIMIT 1
) AS last_pickup_id
FROM locations l
) lh
请注意 table 别名的使用,这使得查询更易于编写和阅读。也就是说,如果您只想要字符串版本,我不确定您是否真的需要 delivery_id
:
SELECT l.*,
(SELECT (case when h.id = 1 then 'pending'
when h.id = 2 then 'active'
end)
FROM hauls h
WHERE h.haul_type_id = 1 AND
h.location_id = l.id
ORDER BY h.created_at DESC
LIMIT 1
) AS status
FROM locations l
考虑使用 CTE 和 window 函数重写内联子查询。这避免了使用 LIMIT
子句,如果不是 运行,则子句可以有 performance issues。这需要支持 CTE 和 window 函数的 MySQL 8.0+ 版:
WITH sub AS (
SELECT id AS last_delivery_id
, location_id
, haul_type_id
, ROW_NUMBER() OVER (PARTITION BY location_id, haul_type_id
ORDER BY created_at DESC) AS rn
FROM hauls
)
SELECT l.* -- CONSIDER EXPLICITLY SELECTING COLUMNS FOR PERFORMANCE
, CASE
WHEN h1.last_delivery_id = 1 THEN 'pending'
WHEN h2.last_delivery_id = 2 THEN 'active'
END AS `status`
FROM locations l
LEFT JOIN sub h1
ON h1.location_id = l.id
AND h1.haul_type_id = 1
AND h1.rn = 1
LEFT JOIN sub h2
ON h2.location_id = l.id
AND h2.haul_type_id = 2
AND h2.rn = 1
对于早期版本(<= MySQL 5.7),集成聚合查询:
SELECT l.* -- CONSIDER EXPLICITLY SELECTING COLUMNS FOR PERFORMANCE
, CASE
WHEN h1.id = 1 THEN 'pending'
WHEN h2.id = 2 THEN 'active'
END AS `status`
FROM locations l
LEFT JOIN
(SELECT location_id
, MAX(CASE WHEN haul_type_id = 1 THEN created_at END) AS h1_max_date
, MAX(CASE WHEN haul_type_id = 2 THEN created_at END) AS h2_max_date
FROM hauls
GROUP BY location_id
) h_agg
ON l.location_id = h_agg.location_id
LEFT JOIN hauls h1
ON h1.location_id = h_agg.location_id
AND h1.haul_type_id = 1
AND h1.created_at = h_agg.h1_max_date
LEFT JOIN hauls h2
ON h2.location_id = h_agg.location_id
AND h2.haul_type_id = 2
AND h2.created_at = h_agg.h2_max_date
如果您是 运行 MySQL 8.0.14 或更高版本,您可以为此使用横向连接:
select l.*, h1.*, h2.*
case
when last_delivery_id = 1 then 'pending'
when last_delivery_id = 2 then 'active'
end as status
from locations l
left join lateral (
select h.id as last_delivery_id
from hauls h
where h.haul_type_id = 1 and h.location_id = l.id
order by h.created_at desc limit 1
) h1 on true
left join lateral (
select h.id as last_pickup_id
from hauls h
where h.haul_type_id = 2 and h.location_id = l.id
order by h.created_at desc limit 1
) h2 on true
横向连接是一项强大的功能,MySQL 长期以来一直缺少它。例如,上面的语句可以很容易地适应每个子查询中的 return 更多列。
因为 select 部分中的所有列同时执行并且 last_delivery_id 列尚未创建并且您尝试使用它,sql 服务器 return错误并为解决它,您可以使用 CTE,查看和驱动 table.
我有以下返回预期结果的查询:
SELECT
locations.*,
(
SELECT
id
FROM
hauls
WHERE
haul_type_id = 1
AND location_id = locations.id
ORDER BY
created_at DESC
LIMIT 1) AS last_delivery_id,
(
SELECT
id
FROM
hauls
WHERE
haul_type_id = 2
AND location_id = locations.id
ORDER BY
created_at DESC
LIMIT 1) AS last_pickup_id
FROM
locations
我想在父查询的 case 语句中使用子查询 (last_delivery_id
、last_pickup_id
) 的结果,但出现错误:
ERROR: Unknown column 'last_delivery_id'
SELECT
locations.*,
case when last_delivery_id = 1 then 'pending' when last_delivery_id = 2 then 'active' end as status,
(
SELECT
id
FROM
hauls
WHERE
haul_type_id = 1
AND location_id = locations.id
ORDER BY
created_at DESC
LIMIT 1) AS last_delivery_id,
(
SELECT
id
FROM
hauls
WHERE
haul_type_id = 2
AND location_id = locations.id
ORDER BY
created_at DESC
LIMIT 1) AS last_pickup_id
FROM
locations
您可以使用子查询或 CTE:
SELECT lh.*, . . . -- any expressions you want
(case when last_delivery_id = 1 then 'pending'
when last_delivery_id = 2 then 'active'
end) as status
FROM (SELECT l.*,
(SELECT id
FROM hauls h
WHERE h.haul_type_id = 1 AND
h.location_id = l.id
ORDER BY h.h.created_at DESC
LIMIT 1
) AS last_delivery_id,
(SELECT id
FROM hauls h
WHERE h.haul_type_id = 2 AND
h.location_id = l.id
ORDER BY h.created_at DESC
LIMIT 1
) AS last_pickup_id
FROM locations l
) lh
请注意 table 别名的使用,这使得查询更易于编写和阅读。也就是说,如果您只想要字符串版本,我不确定您是否真的需要 delivery_id
:
SELECT l.*,
(SELECT (case when h.id = 1 then 'pending'
when h.id = 2 then 'active'
end)
FROM hauls h
WHERE h.haul_type_id = 1 AND
h.location_id = l.id
ORDER BY h.created_at DESC
LIMIT 1
) AS status
FROM locations l
考虑使用 CTE 和 window 函数重写内联子查询。这避免了使用 LIMIT
子句,如果不是 运行,则子句可以有 performance issues。这需要支持 CTE 和 window 函数的 MySQL 8.0+ 版:
WITH sub AS (
SELECT id AS last_delivery_id
, location_id
, haul_type_id
, ROW_NUMBER() OVER (PARTITION BY location_id, haul_type_id
ORDER BY created_at DESC) AS rn
FROM hauls
)
SELECT l.* -- CONSIDER EXPLICITLY SELECTING COLUMNS FOR PERFORMANCE
, CASE
WHEN h1.last_delivery_id = 1 THEN 'pending'
WHEN h2.last_delivery_id = 2 THEN 'active'
END AS `status`
FROM locations l
LEFT JOIN sub h1
ON h1.location_id = l.id
AND h1.haul_type_id = 1
AND h1.rn = 1
LEFT JOIN sub h2
ON h2.location_id = l.id
AND h2.haul_type_id = 2
AND h2.rn = 1
对于早期版本(<= MySQL 5.7),集成聚合查询:
SELECT l.* -- CONSIDER EXPLICITLY SELECTING COLUMNS FOR PERFORMANCE
, CASE
WHEN h1.id = 1 THEN 'pending'
WHEN h2.id = 2 THEN 'active'
END AS `status`
FROM locations l
LEFT JOIN
(SELECT location_id
, MAX(CASE WHEN haul_type_id = 1 THEN created_at END) AS h1_max_date
, MAX(CASE WHEN haul_type_id = 2 THEN created_at END) AS h2_max_date
FROM hauls
GROUP BY location_id
) h_agg
ON l.location_id = h_agg.location_id
LEFT JOIN hauls h1
ON h1.location_id = h_agg.location_id
AND h1.haul_type_id = 1
AND h1.created_at = h_agg.h1_max_date
LEFT JOIN hauls h2
ON h2.location_id = h_agg.location_id
AND h2.haul_type_id = 2
AND h2.created_at = h_agg.h2_max_date
如果您是 运行 MySQL 8.0.14 或更高版本,您可以为此使用横向连接:
select l.*, h1.*, h2.*
case
when last_delivery_id = 1 then 'pending'
when last_delivery_id = 2 then 'active'
end as status
from locations l
left join lateral (
select h.id as last_delivery_id
from hauls h
where h.haul_type_id = 1 and h.location_id = l.id
order by h.created_at desc limit 1
) h1 on true
left join lateral (
select h.id as last_pickup_id
from hauls h
where h.haul_type_id = 2 and h.location_id = l.id
order by h.created_at desc limit 1
) h2 on true
横向连接是一项强大的功能,MySQL 长期以来一直缺少它。例如,上面的语句可以很容易地适应每个子查询中的 return 更多列。
因为 select 部分中的所有列同时执行并且 last_delivery_id 列尚未创建并且您尝试使用它,sql 服务器 return错误并为解决它,您可以使用 CTE,查看和驱动 table.