2010-10-18 7 views
3

J'ai besoin d'une requête pour le serveur SQl 2005 (SQL Server Management Studio Express). Les données sont stockées sous la forme d'une période de 1 minute (1 minute par ligne), pour chaque colonne de table: ID, Symbole, DateHeure, Ouvert, Haut, Bas, Fermé, Volume. J'ai besoin de convertir (compresser) à tous les temps possibles, disons 10 minutes, 13, 15, et ainsi de suite. Donnez tous les détails si quelqu'un pouvait vous aider. Merci AlbertoAgrégation de données dans SQL Server 2005

+0

N'est-ce pas une clause GROUP BY? – leppie

+0

Mes excuses, si cette question va au-delà des règles de ce groupe, je vais supprimer ma demande. –

+2

Je ne comprends pas vraiment ce que vous voulez dire en compressant "à toutes les périodes possibles, disons 10 minutes, 13, 15, et ainsi de suite". Pouvez-vous fournir des exemples de données et le résultat souhaité? –

Répondre

1
;WITH cte AS 
(SELECT *, 
     (32 * CAST([DATETIME] AS INT)) + DATEPART(HOUR,[DATETIME]) + (DATEPART(MINUTE,[DATETIME])/15)/4.0 AS Seg 
    FROM  prices 
    ) 
,cte1 AS 
(
SELECT *, 
     ROW_NUMBER() OVER (PARTITION BY Symbol,Seg ORDER BY [DATETIME])  AS RN_ASC , 
     ROW_NUMBER() OVER (PARTITION BY Symbol,Seg ORDER BY [DATETIME] DESC) AS RN_DESC 
FROM cte 
)  
SELECT 
     Symbol, 
     Seg, 
     MAX(CASE WHEN RN_ASC=1 THEN [DATETIME] END) AS OpenDateTime, 
     MAX(CASE WHEN RN_ASC=1 THEN [OPEN] END) AS [OPEN], 
     MAX(High) High, 
     MIN(Low) Low, 
     SUM(Volume) Volume, 
     MAX(CASE WHEN RN_DESC=1 THEN [CLOSE] END) AS [CLOSE], 
     MAX(CASE WHEN RN_DESC=1 THEN [DATETIME] END) AS CloseDateTime 
FROM cte1 
GROUP BY Symbol,Seg 
ORDER BY OpenDateTime 

Ou une autre approche qui pourrait valoir la peine d'être testée pour voir si elle est plus rapide.

DECLARE @D1 DATETIME 
DECLARE @D2 DATETIME 
DECLARE @Interval FLOAT 

SET @D1 = '2010-10-18 09:00:00.000' 
SET @D2 = '2010-10-19 18:00:00.000' 
SET @Interval = 15 

;WITH 
L0 AS (SELECT 1 AS c UNION ALL SELECT 1), 
L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B), 
L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B), 
L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B), 
L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B), 
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS i FROM L4), 
Ranges AS(
SELECT 
     DATEADD(MINUTE,@Interval*(i-1),@D1) AS StartRange, 
     DATEADD(MINUTE,@Interval*i,@D1) AS NextRange 
FROM Nums where i <= 1+CEILING(DATEDIFF(MINUTE,@D1,@D2)/@Interval)) 
,cte AS (
SELECT 
    * 
    ,ROW_NUMBER() OVER (PARTITION BY Symbol,r.StartRange ORDER BY [DateTime])  AS RN_ASC 
    ,ROW_NUMBER() OVER (PARTITION BY Symbol,r.StartRange ORDER BY [DateTime] DESC) AS RN_DESC 
FROM Ranges r 
JOIN prices p ON p.[DateTime] >= r.StartRange and p.[DateTime] < r.NextRange) 
SELECT 
     Symbol, 
     MAX(CASE WHEN RN_ASC=1 THEN [DateTime] END) AS OpenDateTime, 
     MAX(CASE WHEN RN_ASC=1 THEN [Open] END) AS [Open], 
     MAX(High) High, 
     MIN(Low) Low, 
     SUM(Volume) Volume, 
     MAX(CASE WHEN RN_DESC=1 THEN [Close] END) AS [Close], 
     MAX(CASE WHEN RN_DESC=1 THEN [DateTime] END) AS CloseDateTime 
FROM cte 
GROUP BY Symbol,StartRange 
ORDER BY OpenDateTime 
+0

Merci Martin, mais j'ai une erreur: peut-être que je peux vous fournir des exemples de données xls à partir de ma base de données SQL, je peux imaginer qu'il est très difficile de coder correctement cette requête sans données. Puis-je joindre un fichier ici? Sinon, acepsut est mon pseudo Skype ainsi que mon compte gmail.com –

+0

Quelle erreur obtenez-vous? (Si vous voulez mettre des données quelque part peut-être Google feuilles de calcul serait un bon endroit?) –

+0

@Alberto - Ceci est mis à jour suite à des commentaires sous la réponse de smirkingman. –

3

Alberto, il semble que vous ayez besoin d'une clause "Group By" dans les instructions SQL (comme Leppie l'a indiqué). Donc, vous devriez mieux le chercher.

Vous devez d'abord filtrer les lignes soumises à l'agrégation en utilisant les dates et heures de début et de fin, puis les regrouper selon la clause mentionnée.

Voici la première link lorsque je recherche des mots-clés "sql group by" via Google.

1

Pas simple "Regrouper par" - Les valeurs Open et Close doivent être prises pour la première et la dernière ligne du groupe. Ou du moins est-ce aussi pour les données Forex :)

+0

Oui, c'est correct, ce n'est pas un groupe simple . –

0

Est-ce plus joli avec un proc stocké pour extraire MIN (datetime) d'abord, mais voici un croquis:

WITH quarters(q) AS (
    SELECT DISTINCT 
     15*CAST(DATEDIFF("n",'2000/01/01',dataora)/15 as Int) AS primo 
    FROM 
     Prezzi 
) 
SELECT 
    simbolo, DATEADD("n",q,'2000/01/01') AS tick, 
     MIN(minimo) AS minimo, MAX(massimo) AS massimo, 
     (SELECT 
      TOP 1 apertura FROM Prezzi P 
     WHERE 
      P.simbolo = simbolo AND 
      P.dataora >= DATEADD("n",q,'2000/01/01') 
     ORDER BY 
      P.dataora ASC 
     ) as primaapertura, 
     (SELECT 
      TOP 1 chiusura FROM Prezzi P 
     WHERE 
      P.simbolo = simbolo AND 
      P.dataora < DATEADD("s",14*60+59,DATEADD("n",q,'2000/01/01')) 
     ORDER BY 
      P.dataora DESC 
     ) as ultimachiusara, 
     SUM(volume)/COUNT(*) AS volumemedio 
FROM 
    quarters INNER JOIN Prezzi 
    ON dataora BETWEEN DATEADD("n",q,'2000/01/01') 
     AND DATEADD("s",14*60+59,DATEADD("n",q,'2000/01/01')) 
GROUP BY 
    simbolo, DATEADD("n",q,'2000/01/01') 
ORDER BY 
    1, 2 

La clause obtient une liste des intervalles de 15 minutes, arrondi vers le bas, dans votre ensemble de données (supposons rien avant 2000). Ensuite, utilisez ces intervalles pour grouper par intervalle de 14:59. Pour le volume, vous devrez décider si vous voulez la moyenne ou le total.

La syntaxe peut être un peu large, mais vous devriez avoir l'idée.

EDIT: MIN ajusté (ouvert), MIN (proche) pour sélectionner PREMIER et DERNIER. En réalité, cela ne changera pas beaucoup, car le concept d'ouverture et de fermeture dépend de la différence de temps entre l'échange d'où provient la citation et l'horloge de l'ordinateur qui collecte les données.

De plus, à moins que l'OP n'ait le privilège d'un flux en temps réel de tous les centraux, toutes les citations sont quand même retardées de 20 minutes.

EDIT (2): Tout à fait prenom et sont reports de mes jours IBM> ;-)

Solution sélectionne maintenant première et dernière citations au cours de l'intervalle à l'aide TOP avec ASC/DESC.

+0

Pourquoi avez-vous' MIN (ouvert) 'et' MIN (fermer) ' ? Le PO a besoin du prix «ouvert» lié au premier enregistrement par segment et du prix «proche» du dernier enregistrement par segment. –

+0

Paresse. Ses données proviennent d'un flux de prix. Ouvrir et fermer ne peut pas changer pendant la journée, ils sont le cours de l'action première chose ce matin et la dernière chose la nuit dernière. – smirkingman

+0

Cela a effectivement du sens, mais ce n'est pas ainsi que l'OP le définit dans les commentaires. "Fermer (dernier prix dans cet intervalle de temps)" @Alberto - Pouvez-vous clarifier? –

0
Declare @tbl1MinENI Table 
    (ID int identity, 
    Simbolo char(3), 
    DataOra datetime, 
    Apertura numeric(15,4), 
    Massimo numeric(15,4), 
    Minimo numeric(15,4), 
    Chiusura numeric(15,4), 
    Volume int) 

    Insert Into @tbl1MinENI ( Simbolo, DataOra, Apertura, Massimo, Minimo, Chiusura, Volume) 
    Values 
    ('ENI', '2010/10/18 09:00:00', 16.1100, 16.1800, 16.1100, 16.1400, 244015), 
    ('ENI', '2010/10/18 09:01:00', 16.1400, 16.1400, 16.1300, 16.1400, 15692), 
    ('ENI', '2010/10/18 09:02:00', 16.1400, 16.1500, 16.1400, 16.1500, 147035), 
    ('ENI', '2010/10/18 09:03:00', 16.1500, 16.1600, 16.1500, 16.1600, 5181 ), 
    ('ENI', '2010/10/18 09:04:00', 16.1600, 16.2000, 16.1600, 16.1900, 5134 ), 
    ('ENI', '2010/10/18 09:05:00', 16.1900, 16.1900, 16.1800, 16.1800, 15040), 
    ('ENI', '2010/10/18 09:06:00', 16.1900, 16.1900, 16.1600, 16.1600, 68867), 
    ('ENI', '2010/10/18 09:07:00', 16.1600, 16.1600, 16.1600, 16.1600, 7606 ), 
    ('ENI', '2010/10/18 09:08:00', 16.1500, 16.1500, 16.1500, 16.1500, 725 ), 
    ('ENI', '2010/10/18 09:09:00', 16.1600, 16.1600, 16.1600, 16.1600, 81 ), 
    ('ENI', '2010/10/18 09:10:00', 16.1700, 16.1800, 16.1700, 16.1700, 68594), 
    ('ENI', '2010/10/18 09:11:00', 16.1800, 16.1800, 16.1800, 16.1800, 6619 ) 

    Declare @nRowsPerGroup int = 3 

;With Prepare as 
(
Select datediff(minute, '2010/10/18 09:00:00', DataOra)/@nRowsPerGroup as Grp, 
     Row_Number() over (partition by datediff(minute, '2010/10/18 09:00:00', DataOra)/@nRowsPerGroup order by dataora) as rn, 
     * 
    From tbl1MinENI  
), b as 
(
Select a.Grp, 
     Min(a.DataOra)   as GroupDataOra, 
     Min(ID) AperturaID, 
     max(a.Massimo)   as Massimo, 
     Min(a.Minimo)   as Minimo, 
     max(id) ChiusuraID, 
     sum(a.Volume)   as Volume 
    From Prepare a 
    Group by Grp 
) 
Select b.grp, 
     b.GroupDataOra, 
     ta.Apertura, 
     b.Massimo, 
     b.Minimo, 
     tc.Chiusura, 
     b.Volume 
From b 
Inner Join tbl1MinENI ta on ta.ID=b.AperturaID 
Inner Join tbl1MinENI tc on tc.ID=b.ChiusuraID 
; 
+0

Merci Nikola, j'ai quelques erreurs 1) Message 102 niveau 15 rang 13 Sintaxe incorrecte près de ',' 2) Message 139 niveau 15 row impossibile pour allouer une valeur prédéfinie à une variable locale 3) Message 137 level 15 row 30 Déclarer la valeur scalaire "@nRowsPerGroup". –

+0

Cela ne fonctionnera pas si l'enregistrement pour 09:00:00 est manquant (voir les commentaires précédents) – smirkingman

+0

Cela fonctionne. Retirez simplement l'insertion pour le 09:00:00 et essayez. En fait, vous pouvez supprimer n'importe quel nombre de lignes que vous voulez. – Niikola