2015-08-30 1 views
2

j'ai une table appelée PROD_COST avec 5 champs:valeurs négatives et positives alternatives

(ID, Duration, Cost, COST_NEXT, COST_CHANGE)

J'ai besoin champ supplémentaire appelé Groups pour l'agrégation.

  1. Durée = nombre de jours où le prix est valide (1 jour = 1 heure).
  2. Coût = prix du produit en ce jour.
  3. -Cost_next = lead (coût, 1,0).
  4. Cost_change = Cost_next - Coût.

.

+ ID + Duration + Cost + Next_Cost + Cost_change + Groups+ 
| 1 | 1  | 10 | 8.5  | -1.5  | 1  | 
| 2 | 1  | 8.5 | 12.2  | 3.7   | 2  | 
| 3 | 1  | 12.2 | 5.3  | -6.9  | 3  | 
| 4 | 1  | 5.3 | 4.2  | 1.2   | 4  | 
| 5 | 1  | 4.2 | 6.2  | 2   | 4  | 
| 6 | 1  | 6.2 | 9.2  | 3   | 4  | 
| 7 | 1  | 9.2 | 7.5  | -2.7  | 5  | 
| 8 | 1  | 7.5 | 6.2  | -1.3  | 5  | 
| 9 | 1  | 6.2 | 6.3  | 0.1   | 6  | 
| 10 | 1  | 6.3 | 7.2  | 0.9   | 6  | 
| 11 | 1  | 7.2 | 7.5  | 0.3   | 6  | 
| 12 | 1  | 7.5 | 0   | 7.5   | 6  | 
+----+----------+------+-----------+-------------+-------+ 

J'ai besoin de champ de groupe Groups par Cost_change. Cela peut être positif, négatif ou nul.

Certains gars aimables m'a conseillé d'utiliser ce code:

Select 
    id 
    , COST_CHANGE 
    , sum(Groups) over (order by id asc) +1 
from 
    (
    select 
     pc.*, 
     (case when sign(cost_change) - sign(lag(cost_change) over (order by id)) between -1 and 1 
     then 0 
     else 1 -- `NULL` intentionally goes here 
     end) Groups 
    from Prod_Cost 
    ) pc 

Mais il y a un problème: s'il y a 0 valeurs entre deux valeurs positives/négatives ou négatives/positives puis il regroupe ensemble, par exemple :

Cost_change Groups 
| -5.279 | 33 | 
| 5.279 | 34 | 
| 0.000 | 34 | 
| -5.279 | 34 | 
| 0.000 | 34 | 
| 5.279 | 34 | 
| -8.769 | 35 | 

je dois avoir:

Cost_change Groups 
| -5.279 | 33 | 
| 5.279 | 34 | 
| 0.000 | 34 | 
| -5.279 | 35 | 
| 0.000 | 35 | 
| 5.279 | 36 | 
| -8.769 | 37 | 

Deuxième exemple:

Cost_change Groups 
| 7.574 | 68 | 
| 0.000 | 68 | 
| -5.279 | 68 | 
| -3.490 | 68 | 

Mais j'ai besoin:

Cost_change Groups 
| 7.574 | 68 | 
| 0.000 | 68 | 
| -5.279 | 69 | 
| -3.490 | 69 | 

Je serais très reconnaissant pour toute aide.

+0

Pouvez-vous avoir plusieurs zéros consécutifs (plus d'un)? La première rangée peut-elle avoir zéro? Quel résultat final devrait être dans ces cas? –

+0

Vous devez fournir des zéros dans vos données d'échantillon pour clarifier ce que vous voulez vraiment faire. Que se passe-t-il lorsque plusieurs zéros sont dans une rangée? Que se passe-t-il lorsque les signes sont les mêmes de chaque côté? –

+0

@GordonLinoff, salut Gordon, Voici un fragment de mes données http://s000.tinyupload.com/index.php?file_id=59699169718899539984 (votre requête). Mais j'ai besoin http://s000.tinyupload.com/?file_id=99257550972254124566 Vous me comprenez correctement à propos de 0 valeurs, ils doivent être inclus à +/- valeurs, Quand mes données comme -8,2; 0; 0; -8.2, alors votre requête le groupe comme: 1; 1 ; 1 ; 1 et c'est correct parce que la séquence est: moins; 0; 0; "Mais quand mes données: -8,2; 0; 0; +7,2, alors il doit être 1; 1; 1; 2 parce que la phrase est:" moins; 0; 0; plus "Je ne suis pas très bon en Egnlish, mais j'espère vous me comprenez – RussianBear7

Répondre

1

Voici SQL Fiddle avec la solution. Je vais me concentrer sur votre problème et ne laisser que les colonnes pertinentes dans le tableau: ID et Cost_change.

Créons des exemples de données qui couvrent tous les cas possibles. J'ai ajouté quelques zéros dans divers endroits:

DECLARE @T TABLE (ID int IDENTITY(1,1), Cost_change decimal(10,3)); 

INSERT INTO @T (Cost_change) VALUES 
(0.000), 
(0.000), 
(-1.5), 
( 3.7), 
(-6.9), 
( 1.2), 
(0.000), 
(0.000), 
( 2.0), 
( 3.0), 
(-2.7), 
(0.000), 
(-1.3), 
( 0.1), 
( 0.9), 
( 0.3), 
( 7.5), 
(-5.279), 
(5.279), 
(0.000), 
(-5.279), 
(0.000), 
(5.279), 
(-8.769), 
(7.574), 
(0.000), 
(-5.279), 
(-3.490), 
(-5.279), 
(5.279), 
(0.000), 
(0.000), 
(0.000), 
(-5.279), 
(0.000), 
(0.000), 
(5.279), 
(-8.769), 
(7.574), 
(0.000), 
(0.000), 
(0.000), 
(0.000), 
(-5.279), 
(-3.490); 

J'épelle chaque étape explicitement, il est plus facile à comprendre.

Il y aura deux parties principales dans le traitement des données. Au début, nous allons traiter toutes les valeurs non nulles et générer des numéros de groupe pour les ignorer. Ensuite, pour chaque valeur zéro, nous trouverons le numéro de groupe correspondant parmi les générés.

CTE_Signs calcule sign de Cost_change pour la ligne en cours et pour la ligne précédente en utilisant la fonction LAG. Notez que nous filtrons les valeurs nulles ici.

CTE_Changes compare les signes des lignes actuelles et précédentes et définit Change sur 1 s'ils diffèrent. La toute première rangée a NULL comme un signe de la rangée précédente, ISNULL prend soin d'elle.

CTE_Groups calcule la somme cumulée de Change, ce qui génère des numéros de groupe qui augmentent avec chaque Change.

Cela nous donnera des numéros de groupe corrects pour toutes les valeurs non nulles.

La deuxième partie importante consiste à obtenir toutes les valeurs zéro et à trouver le numéro de groupe correct pour eux en utilisant OUTER APPLY. Enfin UNION ALL les deux parties.

WITH 
CTE_Signs 
AS 
(
    SELECT * 
     ,SIGN(Cost_change) AS SignCurr 
     ,SIGN(LAG(Cost_change) OVER (ORDER BY ID)) AS SignPrev 
    FROM @T 
    WHERE Cost_change <> 0 
) 
,CTE_Changes 
AS 
(
    SELECT * 
     , CASE WHEN SignCurr <> ISNULL(SignPrev, SignCurr) THEN 1 ELSE 0 END AS Change 
    FROM CTE_Signs 
) 
,CTE_Groups 
AS 
(
    SELECT * 
     , SUM(Change) OVER (ORDER BY ID) AS Groups 
    FROM CTE_Changes 
) 
SELECT TT.ID, TT.Cost_change, ISNULL(CA.Groups, 0) AS Groups 
FROM 
    @T AS TT 
    OUTER APPLY 
    (
     SELECT TOP(1) CTE_Groups.Groups 
     FROM CTE_Groups 
     WHERE CTE_Groups.ID < TT.ID 
     ORDER BY CTE_Groups.ID DESC 
    ) AS CA 
WHERE 
    TT.Cost_change = 0 

UNION ALL 

SELECT ID, Cost_change, Groups 
FROM CTE_Groups 

ORDER BY ID; 

Résultat

ID Cost_change Groups 
1  0.000   0 
2  0.000   0 
3 -1.500   0 
4  3.700   1 
5 -6.900   2 
6  1.200   3 
7  0.000   3 
8  0.000   3 
9  2.000   3 
10 3.000   3 
11 -2.700   4 
12 0.000   4 
13 -1.300   4 
14 0.100   5 
15 0.900   5 
16 0.300   5 
17 7.500   5 
18 -5.279   6 
19 5.279   7 
20 0.000   7 
21 -5.279   8 
22 0.000   8 
23 5.279   9 
24 -8.769   10 
25 7.574   11 
26 0.000   11 
27 -5.279   12 
28 -3.490   12 
29 -5.279   12 
30 5.279   13 
31 0.000   13 
32 0.000   13 
33 0.000   13 
34 -5.279   14 
35 0.000   14 
36 0.000   14 
37 5.279   15 
38 -8.769   16 
39 7.574   17 
40 0.000   17 
41 0.000   17 
42 0.000   17 
43 0.000   17 
44 -5.279   18 
45 -3.490   18 
+0

Cela ne fonctionne pas, mec Si 0 valeurs entre deux valeurs positives ou deux valeurs négatives qu'il les sépare ... Par exemple: 2.1; 0; 0; 1.8 Groupes: 1; 1; 1; 2, mais ça devrait être Groupes: 1; 1; 1; 1; – RussianBear7

+0

Hmm ... Ok. Je n'ai pas réfléchi à ça ... Voyons voir quoi faire .... –

+0

http://s000.tinyupload.com/? file_id = 86112541269641548360 Vous voyez ici la langue russe? – RussianBear7

0

Si cette table est pas trop grand, vous pouvez essayer cte récursive:

create table t(id int, v int) 

insert into t values 
(1, -4), 
(2, -3), 
(3, 0), 
(4, 0), 
(5, 1), 
(6, 2), 
(7, -7), 
(8, 9), 
(9, 0), 
(10, -5) 

;with cte as 
(select *, 1 as gr, case when v <> 0 then v end pr 
from t where id = 1 
union all 
select t.*, c.gr + case when t.v = 0 or(t.v*c.v > 0) then 0 else 1 end, 
case when t.v <> 0 then t.v else c.pr end 
from cte c 
join t on c.id + 1 = t.id 
) 
select * from cte 
order by id 

http://sqlfiddle.com/#!3/92b11/1

Ou si le nombre de groupe ne peuvent pas être positif ou consécutif:

;with cte as(select *, (select case when t.v <> 0 then t.v 
            else(select top 1 ti.v from t ti 
              where ti.id < t.id and ti.v <> 0 
              order by ti.id desc) 
           end) nv 
      from t) 
select *, 
     row_number() over(order by case when nv < 0 then 1 else 2 end), 
     id - row_number() over(order by case when nv < 0 then 1 else 2 end, id) 
from cte 
order by id 

http://sqlfiddle.com/#!3/92b11/2