2010-03-10 6 views
1

Vu le tableau suivant:Trouver des lignes adjacentes sans procédure stockée

someId INTEGER #PK 
ageStart TINYINT(3) 
ageEnd TINYINT(3) 
dateBegin INTEGER 
dateEnd INTEGER 

dateBegin et dateEnd sont les dates représentées comme jours depuis 1800-12-28 ...

Et considérant certaines données de l'échantillon:

someId | ageStart | ageEnd | dateStart | dateEnd 
------------------------------------------------ 
    203 |  16 |  25 |  76533 | 76539 \ 
    506 |  16 |  25 |  76540 | 76546 adjacent rows 
    384 |  16 |  25 |  76547 | 76553 /
    342 |  16 |  25 |  76563 | 76569 \ 
    545 |  16 |  25 |  76570 | 76576 adjacent rows 
    764 |  16 |  25 |  76577 | 76583 /

(Il y aurait des lignes arbitraires mélangées bien sûr, je veux juste illustrer 2 ensembles de lignes pertinents)

Est-il possible de trouver des lignes adjacentes pour une catégorie d'âge donnée (ageStart à ageEnd) sans procédure stockée? Le critère d'adjacence est: dateStart est 1 jour après dateEnd de la ligne précédente trouvée.

Par exemple, étant donné que les données ci-dessus de l'échantillon, si je devais l'interroger avec les paramètres suivants:

ageStart = 16 
ageEnd = 25 
dateStart = 76533 

Je voudrais qu'il me retourner les lignes 1, 2 et 3 des données d'échantillons, puisque leurs dates sont adjacentes (dayStart est le jour suivant du dateEnd de la ligne précédente).

ageStart = 16 
ageEnd = 25 
dateStart = 76563 

... me donner des lignes 4, 5 et 6 des données d'échantillon

Répondre

1

Probablement pas efficace si beaucoup de données dans votre table mais essayez ceci:

SELECT b.* 
FROM 
(SELECT @continue:=2) init, 
(
SELECT * 
FROM ageTable 
WHERE ageStart=16 AND 
     ageEnd=25 AND 
     dateStart=76533 
) a 
    INNER JOIN (
    SELECT * 
    FROM ageTable 
    ORDER BY dateStart 
) b ON (
    b.ageStart=a.ageStart AND 
    b.ageEnd=a.ageEnd  AND 
    b.dateStart>=a.dateStart 
) 
    LEFT JOIN ageTable c ON (
    c.dateStart=b.dateEnd+1 AND 
    c.ageStart=b.ageStart AND 
    c.ageEnd=b.ageEnd 
) 
WHERE 
CASE 
    WHEN @continue=2 THEN 
    CASE 
    WHEN c.someId IS NULL THEN 
    @continue:=1 
    ELSE 
    @continue 
    END 
    WHEN @continue=1 THEN 
    @continue:=0 
    ELSE 
    @continue 
END 
+0

Saint moly! Ce n'est pas une question de jeu d'enfant. C'est briljant sanglant! Il me faudra probablement une bonne heure pour comprendre complètement ce qui se passe exactement dans la requête, mais il semble que ce soit le retour des données dont j'ai besoin. En passant: les données de la table sont acceptables pour cette requête je crois. Il y a environ 2000 lignes maximum à un moment donné dans la table, donc cela ne devrait pas poser de problème. Requête géniale. Je vous remercie. –

+0

La première requête (init) est juste pour initialiser la variable utilisateur @continue à 2. La seconde (a) est pour s'assurer qu'il y a au moins une ligne pour votre condition, la troisième (b) pour obtenir toutes les données triées et à partir de startDate demandé, la jointure à gauche (c) s'assure que la dernière ligne ne vérifie pas la condition sera return mais avec des valeurs nulles. Le cas est là pour arrêter la requête retourne la ligne après la dernière ligne adjacente est trouvée. Vous pouvez voir ce qui se passe en supprimant le cas where, et en faisant un select a. *, B. *, C. *;) – Patrick

+0

Merci d'avoir clarifié la requête. Cette requête m'a donné beaucoup de nouvelles astuces pour jouer avec. Surtout l'initialisation de la variable. Je me suis toujours demandé comment utiliser les variables dans des requêtes non SP, mais je n'ai jamais cherché à l'étudier. Si je pouvais vous donner plus de votes, je le ferais. Merci encore Patrick. –

0

Eh bien, vous pouvez générer un résultat ensemble ordonné de manière spécifique et l'utilisation LIMIT, pour obtenir seulement le premier enregistrement à partir de cela.

Par exemple, obtenir l'enregistrement suivant par dateEnd dans la liste:

SELECT * 
FROM `table` 
WHERE `dateEnd` > '76546' 
ORDER BY `dateEnd` 
LIMIT 1 

Vous obtiendrez:

384 |  16 |  25 |  76547 | 76553 

Pour une ligne précédente:

SELECT * 
FROM `table` 
WHERE `dateEnd` < '76546' 
ORDER BY `dateEnd` DESC 
LIMIT 1 

Vous obtiendrez :

203 |  16 |  25 |  76533 | 76539 

Je doute que cela peut être fait avec une seule requête ...

1

Vous pouvez considérer vos données pour être dans une relation parent-enfant: un dossier est un enfant d'un (parent) dossier si l'enfant startDate est égal à endDate + 1 du parent. Pour les données hiérarchiques (avec des relations parent-enfant), le modèle de jeux imbriqués vous permet d'interroger les données sans procédures stockées. Vous trouverez une brève description du modèle des ensembles imbriqués ici:

http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

L'idée est de numéroter vos dossiers de façon intelligente afin que vous puissiez utiliser des requêtes simples au lieu des procédures stockées récursives.

Bien qu'il soit très facile d'interroger des données hiérarchiques stockées de cette manière, certaines précautions sont nécessaires lors de l'ajout de nouveaux enregistrements. L'ajout de nouveaux enregistrements dans un modèle de jeux imbriqués nécessite des mises à jour des enregistrements existants. Cela peut ou peut ne pas être acceptable dans votre cas d'utilisation.

+0

Bonne idée, mais je n'ai aucun privilège de modifier la structure de base de données. Si je ne me trompe, vous proposez d'ajouter des colonnes lft et rgt, n'est-ce pas? Comme note de côté, je continue de reporter la plongée dans la modélisation de jeux imbriqués, parce que cela me semble tellement contre-intuitif. Je suppose que le temps n'est pas encore là pour commencer à plonger dedans pour moi pour le moment. ;-) –

+0

Exactement, vous avez besoin de colonnes supplémentaires pour utiliser le modèle de jeu imbriqué. Si vous ne pouvez pas modifier la structure de votre table, vous ne pouvez pas utiliser cette approche. – titanoboa

+0

Très bien titanoboa, merci pour la suggestion cependant. Sonnait comme une bonne approche si j'avais les privilèges. +1 –

Questions connexes