web-dev-qa-db-fra.com

Comment comparer la ligne actuelle avec la ligne suivante et précédente dans PostgreSQL?

Je veux savoir comment récupérer les résultats dans une requête SQL faisant une comparaison logique avec les lignes suivantes ou précédentes. J'utilise PostgreSQL.

Exemple
En supposant que j'ai une table dans ma base de données avec deux attributs (position ordonnée et nombres aléatoires), je veux récupérer les nombres impairs qui sont entre les nombres pairs. Comment puis-je faire ceci?

L'utilisation réelle
Je veux trouver des mots qui sont entre deux autres mots qui ont la catégorie NOM (et le mot n'est pas un nom). L'ordre est fourni par phrase et position.

Edit Je veux savoir si la fonction Window de PostgreSQL est la meilleure solution pour ce genre de problème que de faire des requêtes. J'en ai entendu parler, mais jamais utilisé.

22
Renato Dinhani

Ceci est ma solution en utilisant WINDOW functions. J'ai utilisé les fonctions lag et lead. Les deux renvoient une valeur d'une colonne d'une ligne en décalage par rapport à la ligne actuelle. lag revient en arrière et lead va ensuite dans l'offset.

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)

Version simplifiée:

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';

Points majeurs

  • = ANY() n'est pas nécessaire, la fonction fenêtre renvoie une seule valeur
  • certains champs redondants dans la sous-requête
  • pas besoin de commander par colonnes, que vous PARTITION BY - la commande ORDER BY s'applique dans partitions
  • N'utilisez pas d'identificateurs de casse mixte sans les citer, cela ne fait que créer de la confusion. (Mieux encore: n'utilisez pas d'identificateurs de casse mixtes dans PostgreSQL ever)
43
Renato Dinhani

Vous pouvez trouver la meilleure solution à cette adresse:

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

Requête 1 pour SQL Server 2012 et versions ultérieures:

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

Requête 2 pour SQL Server 2005+ et versions ultérieures:

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
6
mnshahab

Cela devrait fonctionner:

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'
  • Utilisez deux auto-jointures
  • Tous les mots doivent être dans la même phrase (?) Et dans l'ordre.
  • Le mot avant et le mot après doivent être de la catégorie "nom". Le mot lui-même n'est pas "nom"
  • Cela suppose que la catégorie IS NOT NULL

Pour répondre à votre question supplémentaire: non, une fonction de fenêtre ne serait pas particulièrement utile dans ce cas, l'auto-jointure est le mot magique ici.

Éditer:
Je me suis trompé. Renato montre une solution intéressante avec les fonctions de fenêtre lag () et lead () .
Notez les différences subtiles:

  • les self jointures fonctionnent sur les valeurs absolues : si la ligne avec pos -1 est manquante, alors la ligne avec pos ne pas admissible.
  • La version Renatos avec lag() et lead() fonctionne sur la position relative des lignes créées par ORDER BY.

Dans de nombreux cas (comme probablement dans celui qui nous occupe?), Les deux versions conduisent à des résultats identiques. Avec des lacunes dans l'espace d'identification, il y aura des résultats différents.

3