Précédent   Forum des professionnels en informatique > Bases de données > MS SQL-Server > Développement
Développement Forum d'entraide sur le Transact-SQL, le CLR, les procédures stockées, les triggers, les requêtes SQL
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 14/05/2008, 15h14   #1
Invité de passage
 
Inscription : mai 2008
Messages : 7
Détails du profil
Informations forums :
Inscription : mai 2008
Messages : 7
Points : 0
Points : 0
Par défaut GROUP BY sur lignes contiguës

Bonjour,

Est-il possible de faire "avec un ordre SQL simple", un GROUP BY uniquement sur des lignes contiguës ?

Je m'explique. Soit les données suivantes:

COL.A COL.B COL.C
--------------------
V1 V2 2
V1 V2 4
V1 V2 1
V1 V3 1
V1 V3 5
V1 V3 6
V1 V2 7
V1 V2 2
V1 V2 9

J'aimerai obtenir:

COL.A COL.B MIN(COL.C) MAX(COL.C)
--------------------------------------
V1 V2 1 4
V1 V3 1 6
V1 V2 2 9

Merci par avance
lipsum est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/05/2008, 15h25   #2
Modérateur
 
Homme
Administrateur de base de données
Inscription : août 2007
Messages : 1 162
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 28
Localisation : Belgique

Informations professionnelles :
Activité : Administrateur de base de données
Secteur : Industrie Pharmaceutique

Informations forums :
Inscription : août 2007
Messages : 1 162
Points : 1 617
Points : 1 617
Je ne comprend pas trop bien ce que tu cherches à faire.
Qu'est ce qu'une ligne contigue pour toi ?
Peux tu nous laisser la definition de ta table ainsi qu'un script nous permettant de la remplir aisement afin de tester ?

Au cas ou...
Code :
1
2
3
SELECT col.a, col.b,min(col.c), max(col.c)
FROM test_group_by
GROUP BY col.a, col.b
Ptit_Dje est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/05/2008, 16h33   #3
Invité de passage
 
Inscription : mai 2008
Messages : 7
Détails du profil
Informations forums :
Inscription : mai 2008
Messages : 7
Points : 0
Points : 0
C'est vrai que mon exemple n'est pas top.

Je prends la table PRIME suivante:

DATE | PRIME
------------
01/01/2008 ; 10,00
02/01/2008 ; 10,00
03/01/2008 ; 10,00
04/01/2008 ; 15,00
05/01/2008 ; 15,00
06/01/2008 ; 15,00
07/01/2008 ; 10,00
08/01/2008 ; 10,00

Je souhaiterai obtenir le résultat suivant:

DEBUT ; FIN ; PRIME
----------------------------
01/01/2008 ; 03/01/2008 ; 10,00
04/01/2008 ; 06/01/2008 ; 15,00
07/01/2008 ; 08/01/2008 ; 10,00

Si je passe par le SELECT suivant:

Code :
1
2
3
4
 
SELECT MIN(DATE) AS DEBUT, MAX(DATE) AS FIN, PRIME
  FROM PRIME
 GROUP BY PRIME
j'obtiendrai

DEBUT ; FIN ; PRIME
----------------------------
01/01/2008 ; 08/01/2008 ; 10,00
04/01/2008 ; 06/01/2008 ; 15,00

et c'est ce que je ne veux pas.
lipsum est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/05/2008, 16h39   #4
Modérateur
 
Homme
Administrateur de base de données
Inscription : août 2007
Messages : 1 162
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 28
Localisation : Belgique

Informations professionnelles :
Activité : Administrateur de base de données
Secteur : Industrie Pharmaceutique

Informations forums :
Inscription : août 2007
Messages : 1 162
Points : 1 617
Points : 1 617
Comment définis tu la date de début et la date de fin ?
Est ce arbitrairement fixé ?
Est ce une contrainte business ?
Est ce sur la variation de la valeur de la prime ?
Ptit_Dje est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/05/2008, 17h07   #5
Invité de passage
 
Inscription : mai 2008
Messages : 7
Détails du profil
Informations forums :
Inscription : mai 2008
Messages : 7
Points : 0
Points : 0
La date de début et de fin sont le min et la max du champ DATE pour toutes les lignes contiguës avec la même PRIME
lipsum est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 15/05/2008, 23h03   #6
Rédacteur/Modérateur

 
Avatar de SQLpro
 
Homme Frédéric BROUARD
Expert SGBDR & SQL
Inscription : mai 2002
Messages : 10 959
Détails du profil
Informations personnelles :
Nom : Homme Frédéric BROUARD
Localisation : France

Informations professionnelles :
Activité : Expert SGBDR & SQL
Secteur : Conseil

Informations forums :
Inscription : mai 2002
Messages : 10 959
Points : 17 793
Points : 17 793
Ce que vous voulez faire c'est de l'agrégation de période. Les requêtes pour faire cela sont EXTREMEMENT compliquées. Soit le nombre de tranche est peu élevé et je vous conseille de faire cela dans un curseur, soit le nombre de ligne est élevé et dans ce cas il y a du travail.

Ayant déjà, fait cela pour un de mes clients, je peut vous dire que c'est le genre de requête qui m'a demandé 2 jours de boulots facturé 1600 euros...

A +
__________________
Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
Site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
Blog SQL, SQL Server, modélisation données : http://blog.developpez.com/sqlpro
http://www.sqlspot.com : modélisation, conseils, audit, optimisation, formation
* * * * * Enseignant CNAM PACA - ISEN Toulon - CESI Aix en Provence * * * * *
SQLpro est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/07/2011, 04h37   #7
Membre du Club
 
Inscription : juin 2007
Messages : 115
Détails du profil
Informations forums :
Inscription : juin 2007
Messages : 115
Points : 41
Points : 41
Initialisation des tables :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
CREATE TABLE Primes (id int identity, jour date, Montant money)
--Jeu d'essai sans trous ni doublons:
INSERT INTO Primes (jour, Montant)
VALUES 
('20080101','10'),
('20080102','10'),
('20080103','10'),
('20080104','15'),
('20080105','15'),
('20080106','15'),
('20080107','10'),
('20080108','10'),
('20080109','10'),
('20080110','10')
--Jeu d'essai avec trous et doublons:
INSERT INTO Primes (jour, Montant)
VALUES 
('20080102','10'),
('20080102','10'),
('20080103','10'),
('20080104','15'),
('20080105','15'),
('20080107','15'),
('20080117','10'),
('20080118','10'),
('20080118','10'),
('20080118','10')
Solution avec CTE :
Code :
1
2
3
4
5
6
7
8
9
10
11
 
WITH PrimesTrie AS (
    SELECT *, tri = ROW_NUMBER() over (ORDER  BY Jour)  FROM Primes
),
cte AS (
    SELECT ID, ID AS Debut, tri, Jour,Montant FROM PrimesTrie AS P1 WHERE NOT EXISTS(SELECT * FROM PrimesTrie AS P3 WHERE P3.Montant  = P1.Montant AND ( P3.tri = P1.tri - 1))
    union ALL
    SELECT P2.Id, cte.Debut,P2.tri, p2.Jour, P2.Montant  FROM PrimesTrie AS P2
    INNER JOIN cte ON P2.Montant = cte.Montant AND P2.tri = cte.tri + 1      
    )
SELECT  du=MIN(Jour), au=MAX(Jour),Montant, nb = COUNT(*)  FROM cte GROUP BY Debut, Montant ORDER BY debut OPTION (MAXRECURSION 0)
L'ennui avec CTE c'est que chaque écart de période consomme un niveau de récursivité.
On est obligé de mettre MAXRECURSION 0 pour fonctionner avec des zones contigües de plus de 100 jours.
Je ne sait pas ce que ça vaut niveau perfs.

Solution sans CTE :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
WITH PrimesTrie AS (
    SELECT *, tri = ROW_NUMBER() over (ORDER  BY Jour) FROM Primes
),
Debuts AS (
    SELECT NumPeriode= row_number()  over (ORDER  BY tri), ID AS Debut, tri, Jour,Montant FROM PrimesTrie AS P1 WHERE NOT EXISTS(SELECT * FROM PrimesTrie AS P3 WHERE P3.Montant  = P1.Montant AND P3.tri = P1.tri - 1)
    ),
Fins AS (
    SELECT  NumPeriode= row_number()  over (ORDER  BY tri) , ID AS Fin, tri, Jour,Montant FROM PrimesTrie AS P1 WHERE NOT EXISTS(SELECT * FROM PrimesTrie AS P3 WHERE P3.Montant  = P1.Montant AND P3.tri = P1.tri + 1)
    ),
periodes AS (
    SELECT Debuts.NumPeriode,Debut, fin FROM Debuts INNER JOIN Fins ON debuts.NumPeriode = fins.NumPeriode
    ),
Groupes AS (
    SELECT NumPeriode, Id, tri, Jour, Montant  FROM PrimesTrie AS P1
    INNER JOIN periodes ON P1.tri BETWEEN Debut AND Fin
    )
SELECT NumPeriode,  du=MIN(Jour), au=MAX(Jour), Montant  FROM Groupes GROUP BY NumPeriode,Montant ORDER BY NumPeriode
La rupture se fait uniquement sur le Montant, avec tri primaire sur la date. Les jours en double et les jours espacés sont considérés comme contigus.

On peut un peu modifier les critères pour ne pas considérer comme contigus les jours espacés ou les jours en double.

Si les tarifs n'ont pas bougés depuis 2008, je voudrais bien monnayer mes CTE à 1600 euros
azur668 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/07/2011, 10h47   #8
Candidat au titre de Membre du Club
 
Inscription : janvier 2009
Messages : 14
Détails du profil
Informations forums :
Inscription : janvier 2009
Messages : 14
Points : 14
Points : 14
Une question, est ce pour une utilisation unique? ou pour un lancement régulier d'une requête?

Pour une utilisation unique, avec un cursor, cela est le plus simple. On parcourt la table et on la met à jour.

Pour une utilisation régulière à mon avis il manque fonctionnellement des datas.

Se baser sur le changement d'une valeur pour calculer des périodes, c'est pas top.
michaud_fr est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/07/2011, 11h45   #9
Membre du Club
 
Inscription : juin 2007
Messages : 115
Détails du profil
Informations forums :
Inscription : juin 2007
Messages : 115
Points : 41
Points : 41
Citation:
Envoyé par michaud_fr Voir le message
On parcourt la table et on la met à jour.
Qui parle de mise à jour ici ?
Citation:
Envoyé par michaud_fr Voir le message
Pour une utilisation régulière à mon avis il manque fonctionnellement des datas..
Quelles data manque-t-il ?
Quel rapport avec l'utilisation régulière ou unique ?
Citation:
Envoyé par michaud_fr Voir le message
Se baser sur le changement d'une valeur pour calculer des périodes, c'est pas top.
Pourquoi pas ?
si ce besoin est exprimé, c'est qu'il correspond à quelque chose, non ?
azur668 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/07/2011, 09h36   #10
Rédacteur/Modérateur

 
Avatar de SQLpro
 
Homme Frédéric BROUARD
Expert SGBDR & SQL
Inscription : mai 2002
Messages : 10 959
Détails du profil
Informations personnelles :
Nom : Homme Frédéric BROUARD
Localisation : France

Informations professionnelles :
Activité : Expert SGBDR & SQL
Secteur : Conseil

Informations forums :
Inscription : mai 2002
Messages : 10 959
Points : 17 793
Points : 17 793
J'ai publié différentes solution sur mon blog, il y a un certain temps :
http://blog.developpez.com/sqlpro/p7...valles-en-sql/
http://blog.developpez.com/sqlpro/p9...lles-en-sql-1/

A +
__________________
Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
Site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
Blog SQL, SQL Server, modélisation données : http://blog.developpez.com/sqlpro
http://www.sqlspot.com : modélisation, conseils, audit, optimisation, formation
* * * * * Enseignant CNAM PACA - ISEN Toulon - CESI Aix en Provence * * * * *
SQLpro est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/07/2011, 17h00   #11
Membre confirmé
 
Inscription : novembre 2007
Messages : 134
Détails du profil
Informations forums :
Inscription : novembre 2007
Messages : 134
Points : 213
Points : 213
Par défaut pattern

Bonjour SqlPro,

Existe t il des sortes de "patterns" en sql comme il en existe en développement ?
Bien souvent on peut trouver différentes solutions pour résoudre un problème, hors à moins de prendre beaucoup de temps pour les chercher toutes, puis les tester pour en désigner la ou les meilleures, on prend la première idée qui fonctionne.
Des livres intéressants peut être ?

Merci
patic est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/08/2011, 17h11   #12
Membre Expert
 
Inscription : janvier 2010
Messages : 1 084
Détails du profil
Informations personnelles :
Localisation : France, Rhône (Rhône Alpes)

Informations forums :
Inscription : janvier 2010
Messages : 1 084
Points : 1 573
Points : 1 573
Bonjour,

Voici une autre solution sans curseur ni récursivité. Il lui faudra cependant un index sur (jour, montant)

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
 
SELECT 
	MIN(p1.jour) AS Debut, 
	MAX(p1.jour) AS Fin, 
	MAX(montant) AS Montant
FROM primes p1
OUTER APPLY (
	SELECT TOP(1) jour
	FROM primes p2
	WHERE p2.montant <> p1.montant
		AND p2.jour > p1.jour
	ORDER BY Jour
	) T
GROUP BY T.jour
ORDER BY Debut
aieeeuuuuu est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 03/08/2011, 14h35   #13
Membre du Club
 
Inscription : juin 2007
Messages : 115
Détails du profil
Informations forums :
Inscription : juin 2007
Messages : 115
Points : 41
Points : 41
Très belle solution !
Surement bien plus efficace que la mienne avec CTE, sous-requêtes et fonctions de fenêtrage !
Citation:
Envoyé par aieeeuuuuu Voir le message
Il lui faudra cependant un index sur (jour, montant)
Pas plus que avec les autres solutions.
Et puis c'est juste pour les perfs, si il y a que 1000 lignes l'index est inutile
azur668 est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 16h41.


 
 
 
 
Partenaires

Hébergement Web