如何将当前行与PostgreSQL中的下一行和上一行进行比较?

Ren*_*ani 22 sql postgresql window

我想知道如何在SQL查询中检索结果,与下一行或上一行进行一些逻辑比较.我正在使用PostgreSQL.

示例
假设我的数据库中有一个包含两个属性(有序位置和随机数)的表,我想检索偶数之间的奇数.我怎样才能做到这一点?

真正的用法
我想找到两个具有NAME类别的单词之间的单词(而单词不是名称).订购由句子和位置提供.

编辑 我想知道PostgreSQL的Window函数是否是解决此类问题的最佳解决方案.我听说过他们,但从未使用过.

Ren*_*ani 43

这是我使用的解决方案WINDOW functions.我使用了laglead函数.两者都从与当前行偏移的行中的列返回值.lag返回并lead在偏移中接下来.

SELECT tokcat.text
FROM (
    SELECT text, category, chartype, lag(category,1) OVER w as previousCategory, lead(category,1) OVER w as nextCategory
    FROM token t, textBlockHasToken tb
    WHERE tb.tokenId = t.id
    WINDOW w AS (
        PARTITION BY textBlockId, sentence
        ORDER BY textBlockId, sentence, position
    )
) tokcat
WHERE 'NAME' = ANY(previousCategory)
AND 'NAME' = ANY(nextCategory)
AND 'NAME' <> ANY(category)
Run Code Online (Sandbox Code Playgroud)

简化版:

SELECT text
FROM (
    SELECT text
          ,category 
          ,lag(category) OVER w as previous_cat
          ,lead(category) OVER w as next_cat
    FROM   token t
    JOIN   textblockhastoken tb ON tb.tokenid = t.id
    WINDOW w AS (PARTITION BY textblockid, sentence ORDER BY position)
    ) tokcat
WHERE  category <> 'NAME'
AND    previous_cat = 'NAME'
AND    next_cat = 'NAME';
Run Code Online (Sandbox Code Playgroud)

主要观点

  • = ANY() 不需要,window函数返回单个值
  • 子查询中的一些冗余字段
  • 无需按列排序,即PARTITION BYORDER BY 分区中应用
  • 不要在没有引用的情况下使用混合大小写标识符,这只会导致混淆.(更重要的是:不要在PostgreSQL中使用大小写混合的标识符有史以来)


mns*_*hab 6

您可以在以下地址找到最佳解决方案:

http://blog.sqlauthority.com/2013/09/25/sql-server-how-to-access-the-previous-row-and-next-row-value-in-select-statement-part-4/

SQL Server 2012及更高版本的查询1:

SELECT
LAG(p.FirstName) OVER(ORDER BY p.BusinessEntityID) PreviousValue,
    p.FirstName,
    LEAD(p.FirstName) OVER(ORDER BY p.BusinessEntityID) NextValue
FROM Person.Person p
GO
Run Code Online (Sandbox Code Playgroud)

SQL Server 2005+及更高版本的查询2:

WITH CTE AS(
    SELECT rownum = ROW_NUMBER() OVER(ORDER BY p.BusinessEntityID),
    p.FirstName FROM Person.Person p
)
SELECT
prev.FirstName PreviousValue,
    CTE.FirstName,
    nex.FirstName NextValue
FROM CTE
LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1
GO
Run Code Online (Sandbox Code Playgroud)


Erw*_*ter 5

这应该有效:

SELECT w1.word AS word_before, w.word, w2.word AS word_after
FROM   word w
JOIN   word w1 USING (sentence)
JOIN   word w2 USING (sentence)
WHERE  w.category <> 'name'
AND    w1.pos = (w.pos - 1)
AND    w1.category = 'name'
AND    w2.pos = (w.pos + 1)
AND    w2.category = 'name'
Run Code Online (Sandbox Code Playgroud)
  • 使用两个自联接
  • 所有单词必须在同一个句子中 (?) 并按顺序排列。
  • 单词之前和之后的单词必须属于“名称”类别。单词本身不是“名称”
  • 这假设类别 IS NOT NULL

回答您的附加问题:不,在这种情况下,窗口函数不是特别有用,自连接是这里的魔法词。

编辑:
我站纠正。Renato 使用窗口函数lag() 和 lead()演示了一个很酷的解决方案。
注意细微的差别:

  • 自连接对绝对值进行操作:如果pos -1缺少行,则行pos不符合条件。
  • Renatos版本lag(),并lead()在运行行的相对位置所创造ORDER BY

在许多情况下(比如可能在手头的那个?)两个版本都会导致相同的结果。id 空间的间隙会有不同的结果。