2008-11-13 7 views
6

Je cherche un moyen de sélectionner jusqu'à ce qu'une somme soit atteinte. La table "documents" contient les champs "tag_id" et "size".SQL: Sélectionnez "jusqu'à"

Je veux sélectionner tous les documents avec tag_id = 26 mais je sais que je ne peux gérer que 600 unités de taille. Donc, il ne sert à rien de sélectionner 100 documents et j'en expulserai 90 quand j'aurais pu savoir que les 10 premiers totalisaient déjà plus de 600 unités. Donc, le but est: ne ramène pas une tonne de données à analyser quand je vais en jeter la plus grande partie.

... mais je voudrais aussi éviter d'introduire le travail avec des curseurs pour cette application.

J'utilise mysql.

+0

Est-ce que MySQL a des fonctions analytiques? –

+0

Puisqu'il y aurait beaucoup de combinaisons de documents ajoutant à> 600, comment savez-vous quels documents vous voulez utiliser? Ou cela n'a pas d'importance? – Kev

+0

La préférence est donnée aux documents récemment ajoutés (il y a un champ date_created que nous allons commander) – jhalb

Répondre

7

Vous avez besoin d'un moyen de commander les enregistrements qui ont la priorité sur les autres en additionnant vos unités max. Sinon, comment savez-vous quel ensemble d'enregistrements totalisant jusqu'à 600 gardez-vous?

SELECT d.id, d.size, d.date_created 
FROM documents d 
INNER JOIN documents d2 ON d2.tag_id=d.tag_id AND d2.date_created >= d.date_created 
WHERE d.tag_id=26 
GROUP BY d.id, d.size, d.date_created 
HAVING sum(d2.size) <= 600 
ORDER BY d.date_created DESC 

Ceci est juste une question de base pour vous aider à démarrer, et il y a un certain nombre de problèmes qui restent à résoudre:

  • Il arrête à < = 600, donc dans la plupart des cas, vous ne remplissez votre limite de taille exactement. Cela signifie que vous pourriez vouloir le modifier pour permettre un enregistrement supplémentaire. Par exemple, si le premier enregistrement est> 600, la requête ne retournera rien et cela pourrait poser problème.
  • Il ne servira à rien de vérifier par la suite si des enregistrements plus petits peuvent s'ajouter au plafond.
  • Les enregistrements avec des valeurs date_created identiques pourraient être en quelque sorte 'comptés deux fois' ici et là.

modifier
Mis à jour depuis qu'il a ajouté des informations qu'il tri par date.

+0

Je commençais à poster quelque chose de très similaire, mais en utilisant une vue auxiliaire. Le vôtre est meilleur. –

+0

C'est plus intelligent que ma réponse aussi. :) – Kev

0

Cela est beaucoup moins efficace, mais il n'évite un curseur (en supposant que votre table de documents a également une colonne de numéro de série):

select a.id, (select sum(b.size) from documents b where b.id <= a.id and b.tag_id = 26) 
from documents a 
where a.tag_id = 26 
order by a.id 

En outre, cela a été fait en pgsql, donc je ne suis pas sûr si cette syntaxe exacte fonctionnerait dans mysql.

Ensuite, vous pouvez envelopper dans une autre requête qui recherche ceux qui ont une somme> 600 (vous devrez nommer la colonne sum) et prendre le premier id. Ensuite, traitez tous les identifiants ci-dessous et en incluant celui-là.

+0

Euh, s'il n'y a pas d'identifiant, alors utilisez l'horodatage créé. – Kev

0

Vous devez d'abord stocker les documents dans une variable de table, les trier dans l'ordre dans lequel vous souhaitez les extraire, puis mettre à jour chaque ligne avec une valeur cumulée afin de pouvoir la sélectionner.

declare @documents_temp table (
    tag_id int, 
    size int, 
    cumulative_size int null) 

insert into @documents_temp 
select tag_id, size, size from documents order by tag_id 

update @documents_temp d set d.cumulative_size = d.size + 
    (select top 1 cumulative_size from @documents_temp 
    where tag_id < d.tag_id order by tag_id desc) 

select tag_id, size from @documents_temp where cumulative_size <= 600 

Je ne sais pas si cela en vaut la peine.

Questions connexes