合并两个 SQL 查询
Coalesce two SQL queries
我需要一个 SQL 查询,它模仿以下形式的 if-then-else 语句:
if (query1 != null)
return query1
else
return query2
由于 COALESCE 不能处理结果集,我创建了一个联合查询来完成这项工作:
SELECT * FROM obs WHERE cond1 --query1
UNION
SELECT * FROM obs WHERE (NOT EXISTS(query1)) AND cond2
在SQL中:
( SELECT * FROM obs WHERE src = @id AND tstart <= @instant AND tend >= @instant )
UNION
( SELECT * FROM obs WHERE (NOT EXISTS (SELECT 1 FROM obs WHERE src = @id AND tstart <= @instant AND tend >= @instant )) AND src = @id AND tstart <= @instant ORDER BY tend DESC LIMIT 1);
Table obs 具有字段 ( src | tstart | tend | ... )。我想 select 那些与@instant 重叠的行。如果没有找到重叠的行,则应返回@instant 之前最近的行。
SQL UNION 语句有效,但它非常笨拙,我正在寻找更短更清晰的语句。本着 COALESCE ( query1, query2 ) 精神的东西会很好。我的数据库是 Postgresql。
首先,在这种情况下,union all
可能比 union
更合适。
其次,您可以使用 with
来表达这一点以简化查询:
WITH t1 as (
SELECT *
FROM obs
WHERE src = @id AND tstart <= @instant AND tend >= @instant
)
SELECT t1.*
FROM t1
UNION ALL
(SELECT *
FROM obs
WHERE NOT EXISTS (SELECT 1 FROM t1) AND
src = @id AND tstart <= @instant
ORDER BY tend DESC
LIMIT 1
);
但是,如果您要查找单行,这会更简单:
SELECT *
FROM obs
WHERE src = @id
ORDER BY (CASE WHEN tstart <= @instant AND tend >= @instant THEN 1
ELSE 2
END),
tend DESC
LIMIT 1;
而且,如果不是单行,那么也可以使用window函数:
SELECT o.*
FROM (SELECT o.*,
DENSE_RANK() OVER (PARTITION BY src
ORDER BY (CASE WHEN tstart <= @instant AND tend >= @instant THEN 1
ELSE 2
END),
(CASE WHEN tstart <= @instant AND tend >= @instant THEN NULL
ELSE tend
END) DESC
) as seqnum
FROM obs o
WHERE src = @id
) o
WHERE seqnum = 1;
这对我有用。我结合了这两个查询,然后对记录进行了排名。它会在table2之前从table1中获取记录。
with prev_tables as (
select name, id, address, 1 as ranking
from table1
union
select name, id, address, 2
from table2
),
prev_dedupe as (
select p.*, rank() over (partition by id order by ranking) as prev_table_rk
from prev_tables p
)
select * from prev_dedupe where prev_table_rk = 1
我需要一个 SQL 查询,它模仿以下形式的 if-then-else 语句:
if (query1 != null)
return query1
else
return query2
由于 COALESCE 不能处理结果集,我创建了一个联合查询来完成这项工作:
SELECT * FROM obs WHERE cond1 --query1
UNION
SELECT * FROM obs WHERE (NOT EXISTS(query1)) AND cond2
在SQL中:
( SELECT * FROM obs WHERE src = @id AND tstart <= @instant AND tend >= @instant )
UNION
( SELECT * FROM obs WHERE (NOT EXISTS (SELECT 1 FROM obs WHERE src = @id AND tstart <= @instant AND tend >= @instant )) AND src = @id AND tstart <= @instant ORDER BY tend DESC LIMIT 1);
Table obs 具有字段 ( src | tstart | tend | ... )。我想 select 那些与@instant 重叠的行。如果没有找到重叠的行,则应返回@instant 之前最近的行。
SQL UNION 语句有效,但它非常笨拙,我正在寻找更短更清晰的语句。本着 COALESCE ( query1, query2 ) 精神的东西会很好。我的数据库是 Postgresql。
首先,在这种情况下,union all
可能比 union
更合适。
其次,您可以使用 with
来表达这一点以简化查询:
WITH t1 as (
SELECT *
FROM obs
WHERE src = @id AND tstart <= @instant AND tend >= @instant
)
SELECT t1.*
FROM t1
UNION ALL
(SELECT *
FROM obs
WHERE NOT EXISTS (SELECT 1 FROM t1) AND
src = @id AND tstart <= @instant
ORDER BY tend DESC
LIMIT 1
);
但是,如果您要查找单行,这会更简单:
SELECT *
FROM obs
WHERE src = @id
ORDER BY (CASE WHEN tstart <= @instant AND tend >= @instant THEN 1
ELSE 2
END),
tend DESC
LIMIT 1;
而且,如果不是单行,那么也可以使用window函数:
SELECT o.*
FROM (SELECT o.*,
DENSE_RANK() OVER (PARTITION BY src
ORDER BY (CASE WHEN tstart <= @instant AND tend >= @instant THEN 1
ELSE 2
END),
(CASE WHEN tstart <= @instant AND tend >= @instant THEN NULL
ELSE tend
END) DESC
) as seqnum
FROM obs o
WHERE src = @id
) o
WHERE seqnum = 1;
这对我有用。我结合了这两个查询,然后对记录进行了排名。它会在table2之前从table1中获取记录。
with prev_tables as (
select name, id, address, 1 as ranking
from table1
union
select name, id, address, 2
from table2
),
prev_dedupe as (
select p.*, rank() over (partition by id order by ranking) as prev_table_rk
from prev_tables p
)
select * from prev_dedupe where prev_table_rk = 1