在单个 SQL SELECT 语句中区分两行
Posted
技术标签:
【中文标题】在单个 SQL SELECT 语句中区分两行【英文标题】:Difference two rows in a single SQL SELECT statement 【发布时间】:2012-05-07 21:31:37 【问题描述】:我有一个数据库表,其结构如下所示:
CREATE TABLE dated_records (
recdate DATE NOT NULL
col1 DOUBLE NOT NULL,
col2 DOUBLE NOT NULL,
col3 DOUBLE NOT NULL,
col4 DOUBLE NOT NULL,
col5 DOUBLE NOT NULL,
col6 DOUBLE NOT NULL,
col7 DOUBLE NOT NULL,
col8 DOUBLE NOT NULL
);
我想编写一个 SQL 语句,它允许我返回一条记录,其中包含指定列的两个提供日期之间的更改 - 例如col1、col2 和 col3
例如,如果我想查看 col1、col2 和 col3 中的值在两个日期之间的时间间隔内发生了多少变化。这样做的一种愚蠢方法是(分别)为每个日期选择行,然后区分数据库服务器之外的字段 -
SQL1 = "SELECT col1, col2 col3 FROM dated_records WHERE recdate='2001-01-01'";
SQL1 = "SELECT col1, col2 col3 FROM dated_records WHERE recdate='2001-02-01'";
但是,我确信有一种更智能的方法可以使用纯 SQL 执行差分。我猜测这将涉及使用自联接(可能还有嵌套子查询),但我可能过于复杂了 - 我决定最好在这里询问 SQL 专家,看看如何他们会以最有效的方式解决这个问题。
理想情况下,SQL 应该与数据库无关,但如果需要将其绑定到特定数据库,那么它必须是 PostgreSQL。
【问题讨论】:
【参考方案1】:只需选择两行,将它们合二为一,然后减去值:
select d1.recdate, d2.recdate,
(d2.col1 - d1.col1) as delta_col1,
(d2.col2 - d1.col2) as delta_col2,
...
from (select *
from dated_records
where recdate = <date1>
) d1 cross join
(select *
from dated_records
where recdate = <date2>
) d2
【讨论】:
谢谢!这就是我想要做的。【参考方案2】:我认为,如果您想要在结果集中获取不与两个选择查询相交的行,则可以使用 EXCEPT 运算符:
EXCEPT 运算符返回第一个结果集中的行 但不是在第二个。
因此,您的两个查询将成为一个查询,其中包含 except 运算符:
SELECT col1, col2 col3 FROM dated_records WHERE recdate='2001-01-01'
EXCEPT
SELECT col1, col2 col3 FROM dated_records WHERE recdate='2001-02-01'
【讨论】:
【参考方案3】:SELECT
COALESCE
(a.col1 -
(
SELECT b.col1
FROM dated_records b
WHERE b.id = a.id + 1
),
a.col1)
FROM dated_records a
WHERE recdate='2001-01-01';
【讨论】:
【参考方案4】:您可以使用窗口函数加上DISTINCT
:
SELECT DISTINCT
first_value(recdate) OVER () AS date1
,last_value(recdate) OVER () AS date2
,last_value(col1) OVER () - first_value(col1) OVER () AS delta1
,last_value(col2) OVER () - first_value(col2) OVER () AS delta2
...
FROM dated_records
WHERE recdate IN ('2001-01-01', '2001-01-03')
任意两天。使用单个索引或表扫描,所以应该很快。
我没有对窗口排序,但是所有计算都使用同一个窗口,所以值是一致的。
这个解决方案可以很容易地推广到 n 行之间的计算。在这种情况下,您可能希望使用 Postgres arsenal of window functions 中的 nth_value()
。
【讨论】:
【参考方案5】:如果您正在寻找一个简单的 delta,这似乎是一种更快的编写方式。
SELECT first(col1) - last(col1) AS delta_col1
, first(col2) - last(col2) AS delta_col2
FROM dated_records WHERE recdate IN ('2001-02-01', '2001-01-01')
你可能不知道是第一行还是第二行在前,但你总是可以将答案包裹在abs(first(col1)-last(col1))
中
【讨论】:
以上是关于在单个 SQL SELECT 语句中区分两行的主要内容,如果未能解决你的问题,请参考以下文章