2008-12-29 5 views
8

Il est tout à fait possible qu'une question comme celle-ci ait déjà été posée, mais je ne peux pas penser aux termes à rechercher. Je travaille sur une application de galerie de photos, et je veux afficher 9 vignettes montrant le contexte de la photo actuelle affichée (dans une grille 3x3 avec la photo actuelle au centre, sauf si la photo actuelle est dans la première 4 photos étant affichées, dans ce cas si par exemple si la photo actuelle est la 2ème je veux sélectionner les photos 1 à 9). Par exemple, étant donné un album contenant la liste des photos avec ids:SQL Sélection de "Fenêtre" autour d'une ligne particulière

1, 5, 9, 12, 13, 18, 19, 20, 21, 22, 23, 25, 26

Si le courant photo est 19, je veux voir aussi:

9, 12, 13, 18, 19, 20, 21, 22, 23

Si la photo actuelle est 5, je veux voir aussi:

1, 5, 9, 12, 13, 18, 19, 20, 21

J'ai pensé à quelque chose le long des lignes de:

SELECT * 
FROM photos 
WHERE ABS(id - currentphoto) < 5 
ORDER BY id ASC 
LIMIT 25 

mais cela ne fonctionne pas dans le cas où les ids sont non-séquentielle (comme dans l'exemple ci-dessus), ou pour le cas où il Les photos sont insuffisantes avant l'instantané.

Des pensées?

Merci,

Dom

P.S. S'il vous plaît laissez un commentaire si quelque chose n'est pas clair, et je vais clarifier la question. Si quelqu'un peut penser à un titre plus utile pour aider d'autres personnes à trouver cette question à l'avenir, alors s'il vous plaît commentez aussi.

Répondre

5

Probablement pourrait simplement utiliser une UNION, puis couper les résultats supplémentaires dans le code de procédure qui affiche les résultats (comme cela renvoie 20 lignes dans les cas de non-bord):

(SELECT 
    * 
FROM photos 
    WHERE ID < #current_id# 
    ORDER BY ID DESC LIMIT 10) 
UNION 
    (SELECT * 
    FROM photos 
    WHERE ID >= #current_id# 
    ORDER BY ID ASC LIMIT 10) 
ORDER BY ID ASC 

EDIT: limite augmentée à 10 sur les deux côtés de l'Union, comme suggéré par le dorfier.

EDIT 2: Modifié pour mieux refléter l'implémentation finale, comme suggéré par Dominic.

+1

C'est une bonne technique utile. Je l'ai utilisé avec MySQL dans le passé. Pour gérer les extrémités de la liste, où vous n'auriez pas assez de lignes: utilisez LIMIT 10 et développez la sélection dans le code de procédure. – dkretz

+0

Merci le dorfier, qui résout le problème avec les limites, je vais modifier comme suggéré. – Turnkey

+0

J'ai fini par faire quelque chose de très similaire à ça. J'ai ajouté un "ORDER BY id ASC" sur le résultat de l'UNION, donc les lignes sont revenues comme prévu. Vous devrez changer la première ORDRE pour être de DESC, sinon les premières lignes reviendront toujours. –

0

Il s'agit d'un problème de "classement par lignes" standard ... Si votre base de données a une capacité rowId, vous pouvez utiliser une sous-requête qui compte le nombre de lignes avec Ids inférieur à l'ID de la ligne courante ... comme ceci:

- asssuming @Id est la valeur d'identité dans le « milieu »

Select * From Photos P 
Where (Select Count(*) From Photos 
     Where id <= P.Id) 
    Between (Select Count(*) From Photos 
       Where id < @Id) - 4 
     And (Select Count(*) From Photos 
       Where id < @Id) + 4 

comme un commentaire a soulevé la question de l'album que vous voulez ajouter album principale à chaque sous-requête

Select * From Photos P 
    Where (Select Count(*) From Photos 
      Where album = @album 
      And id <= P.Id) 
    Between (Select Case When Count(*) < 4 
         Then 4 Else Count(*) End 
       From Photos 
       Where album = @album 
       And id < @Id) - 4 
     And (Select Case When Count(*) < 4 
         Then 4 Else Count(*) End 
       From Photos 
       Where album = @album 
        And id < @Id) + 4 
+0

Il devrait y avoir un filtre supplémentaire par album, sinon il ne résout pas le problème - c'est-à-dire qu'il ne fonctionnera que sur un identifiant séquentiel. –

1

Si vous utilisez SQL Server, vous pouvez utiliser la fonction row_number() pour vous donner l'index de commande de ligne et faire quelque chose comme ceci:

declare @selected_photo integer; 
set @selected_photo = 5; 

declare @buffer_size integer; 
set @buffer_size = 2; 

select 
    ph.rownum, 
    ph.id 
from 
    (select row_number() over (order by Id) as rownum, * from Photos) as ph 
where 
    ph.rownum between case 
         when @selected_photo - @buffer_size < 1 then 1 
         else @selected_photo - @buffer_size 
         end 
         and @selected_photo + @buffer_size 

Edit: Voici un article sur la simulation de la fonction row_number() en MySQL, en combinant cela avec cela pourrait vous donner ce dont vous avez besoin - je l'essayerais mais je n'ai pas de db MySQL à portée de main au travail.:-)

http://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/

+0

Merci Ron - mais j'utilise MySQL, donc je ne pense pas pouvoir l'utiliser - puis-je? –

+0

Je ne vois rien de similaire dans les docs MySQL ... mais je vais continuer à fouiner. :-) –

+0

@RonSavage: ne vous embêtez pas. MySQL est l'un des rares SGBD qui ne supportent pas les fonctions de fenêtrage. –

Questions connexes