数据库:选择最后一个非空条目
Posted
技术标签:
【中文标题】数据库:选择最后一个非空条目【英文标题】:Database: Select last non-null entries 【发布时间】:2012-02-17 03:25:38 【问题描述】:这是我一直在思考的一个问题。假设我有一个表,其中包含一系列时间戳和一个零件号作为主键。该表存储增量更改,这意味着对于每个时间戳,如果字段更改,则记录该更改。如果该字段没有改变,那么对于新的时间戳,它是 NULL。 这是基本思想。
part | timestamp | x-pos | y-pos | status
------+-----------+-------+-------+--------
a5 | 151 | 5 | 15 | g
a5 | 153 | NULL | 17 | NULL
(part, timestamp)
是主键。第二条记录中的NULL
s 表示自第一条记录以来未更改的值。
我希望能够为按部件分组的每个字段选择最新值。例如,给定上述条目,a5 部分的结果将为 153,5,17,g。
到目前为止,我已经完成了这个组合查询。
((SELECT x-pos FROM part_changes WHERE x-pos IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1)
UNION
(SELECT y-pos FROM part_changesWHERE y-pos IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1)
UNION
(SELECT status FROM part_changes WHERE status IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1))
但这会返回一个单列,这意味着我可以使用 group-by 进行组织。
必须有一种更优雅的做事方式,例如以创造性的方式使用 COALESCE 或 IS NULL。但我被卡住了,无法弄清楚。有人有想法吗?
不,我无法更改数据库结构。
编辑:ruakh 的想法是正确的。现在唯一的问题是按部分分组。我似乎无法绕过LIMIT 1
按多个部分进行分组。有任何想法吗?
mdahlman,我对 postgresql 中的分析函数不太熟悉。因此,如果该解决方案比复杂查询更容易,那么请务必发表您的想法。
编辑 2:谢谢大家的帮助。我想我已经足够了解我需要做什么了。
【问题讨论】:
您应该指定是否允许分析函数。有了他们,答案应该很简单。没有他们......这会很艰难。 有多少空值可以出现的限制吗?如果有限制,这将是一个带有一些左连接的解决方案.. 不好但可以做到;) 我认为没有限制。事实上,其中一个字段的绝大多数(大约 99%)记录为 NULL。 【参考方案1】:相关时间戳列表:
select max timestamp from part_changes where x_POS is not null group by part
你可以把它变成一个视图:让我们称之为 view1
SELECT part_changes.part, part_changes.x-pos
FROM part_changes left join view1 on part_changes.part = view1.part
WHERE x-pos IS NOT NULL
AND part_changes.timestamp = view1.timestamp
GROUP BY part_changes.part
看到我要去哪里了吗?这应该会为您提供 x-pos 的完整列表。
【讨论】:
有趣的想法。我考虑过使用某些连接的消除空值的特性来发挥我的优势,但我不知道从哪里开始。我将对此进行一些实验。我唯一担心的是,就我而言,实际上还有更多的列。帖子中的示例是简化版本。这么多的连接会不会有内存问题? 如果你有几百个它可能...我也刚刚编辑了我的帖子以包含分组依据。现在我想起来了,我认为这会为您提供您想要的 x_pos。未经测试! 我相信只有大约 15 个字段,而且每天有数千条记录。我会尝试一下。这可能正是我所需要的。【参考方案2】:而不是使用UNION
,听起来你真的想要字段列表中的子查询。也就是说,您需要SELECT (SELECT ...), (SELECT ...), (SELECT ...)
,而不是(SELECT ...) UNION (SELECT ...) UNION (SELECT ...)
。
例如:
SELECT part,
( SELECT x_pos
FROM part_changes
WHERE part = pc.part
AND x_pos IS NOT NULL
ORDER
BY timestamp DESC
LIMIT 1
) AS x_pos,
( SELECT y_pos
FROM part_changes
WHERE part = pc.part
AND y_pos IS NOT NULL
ORDER
BY timestamp DESC
LIMIT 1
) AS y_pos,
( SELECT status
FROM part_changes
WHERE part = pc.part
AND status IS NOT NULL
ORDER
BY timestamp DESC
LIMIT 1
) AS status
FROM ( SELECT DISTINCT
part
FROM part_changes
) AS pc
;
但在这一点上,我真的会考虑编写一个存储过程。
或者:
SELECT DISTINCT
part,
FIRST_VALUE(x_pos) OVER
( PARTITION BY part
ORDER BY CASE WHEN x_pos IS NULL
THEN NULL
ELSE TIMESTAMP
END DESC NULLS LAST
) AS x_pos,
FIRST_VALUE(y_pos) OVER
( PARTITION BY part
ORDER BY CASE WHEN y_pos IS NULL
THEN NULL
ELSE TIMESTAMP
END DESC NULLS LAST
) AS y_pos,
FIRST_VALUE(status) OVER
( PARTITION BY part
ORDER BY CASE WHEN status IS NULL
THEN NULL
ELSE TIMESTAMP
END DESC NULLS LAST
) AS status
FROM part_changes
;
【讨论】:
这似乎解决了一个问题。谢谢。但是,我不确定“分组依据”会去哪里以便按零件号分组。有什么想法吗? @BatMasterson:那种细节取决于 SQL 的方言。从您对ORDER BY ... LIMIT 1
的使用来看,我猜您的目标是 mysql;那是对的吗? (如果是——或者如果不是——你应该编辑你的问题的标签。)
除非他避免他被卡住的限制1,所以我认为这种方法是一种混淆。
@BatMasterson:我已经扩展了我的答案。
啊,不同。我什至没有想到如何使用它。因此,您的替代答案非常有效。 ORDER BY CASE... 非常聪明。我永远不会想到这一点。谢谢,这是我需要的答案。【参考方案3】:
对于一个 part 这应该会给你一个答案.. 感谢 ruakh
但我不喜欢这个版本..
SELECT
(SELECT timestamp FROM part_changes WHERE part = $part
ORDER BY timestamp DESC
LIMIT 1) as timestamp,
(SELECT x-pos FROM part_changes WHERE part = $part and x-pos IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1) as xpos,
(SELECT y-pos FROM part_changes WHERE part = $part and y-pos IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1) as ypos,
(SELECT status FROM part_changes WHERE part = $part and status IS NOT NULL
ORDER BY timestamp DESC
LIMIT 1)) as status
【讨论】:
我同意,对每个部分进行递归处理有点粗糙。 我想我可以简单地编写一个函数,将零件号作为参数,然后使用它。谢谢。【参考方案4】:ruak 是对的。备选方案:使用 SQL-CLR 编写用户定义的聚合。此聚合可以在您的行上自下而上运行,并记住每列的第一个非空值。
您也可以在应用程序中执行此操作。使您的程序请求行按批量排序,例如 10 行。如上所述聚合这些批次。如果在当前批次完成后有一个空列,则获取下一批。
【讨论】:
SQL-CLR 不会帮助 OP 处理 PostgreSQL。但是,它可以使用内置的东西来完成,并且有一个您所描述的示例 (FIRST
) on the PostgreSQL wiki。以上是关于数据库:选择最后一个非空条目的主要内容,如果未能解决你的问题,请参考以下文章