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é.
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';
= ANY()
n'est pas nécessaire, la fonction fenêtre renvoie une seule valeurPARTITION BY
- la commande ORDER BY s'applique dans partitionsVous pouvez trouver la meilleure solution à cette adresse:
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
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'
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:
pos -1
est manquante, alors la ligne avec pos
ne pas admissible.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.