2012-05-25 5 views
5

Ich habe eine Abfrage OracleSQL Server: Wie Orakel zu imitieren behalte Dichte_Rank Abfrage?

select max(m.id), 
     m.someId keep (DENSE_RANK FIRST ORDER BY m.UpdateDate desc) 
from MyTable m 
groupBy m.someId 

, welche diese für Daten wie:

id UpdateDate someId 
1 20-01-2012 10 
2 20-01-2012 10 
3 01-01-2012 10 
4 10-02-2012 20 
5 01-02-2012 20 
6 01-04-2012 30 

wird mir zurückkehren genau dieses:

2 10 
4 20 
6 30 

Also, für jeden es für die neuesten updateDate someId sucht und gibt das entsprechende id zurück. (Und wenn es mehrere IDs für die letzten Daten gibt, dauert es die letzte ID).

Aber für SQL-Server wird diese Abfrage auf die gleiche Weise funktionieren? Ich meine diese Konstruktion keep (dense_rank first order by ..)?

Antwort

8

Ich glaube nicht, dass Ihre bestimmte Abfrage SQL Server ausführen wird. Aber man kann das gleiche Ergebnis erzielen dies zu tun:

SELECT id, SomeId 
FROM ( SELECT *, ROW_NUMBER() OVER(PARTITION BY someId ORDER BY UpdateDate DESC, id DESC) Corr 
     FROM MyTable) A 
WHERE Corr = 1 
+0

I versteh es nicht .. wie soll es funktionieren ohne 'group by someId'? Meine Abfrage würde immer 3 Einträge zurückgeben, da ich 3 verschiedene someIds in der Tabelle habe. Ihre Abfrage wird Ergebnisse liefern, die nicht von der Anzahl der verschiedenen someIds abhängen, also sollte es dann falsch sein? – javagirl

+1

@javagirl - Hast du es zuerst versucht ?, es wird funktionieren. Wie es funktioniert, können analytische Funktionen (die "OVER() ...") eine "PARTITION BY" haben und müssen nicht auf der gesamten Ebene gruppieren. Es gibt einen Wert für jede Zeile in der Tabelle zurück. – Lamak

+0

Ich habe die Originaldaten falsch gelesen. Das Problem wird behoben, indem man etwas in die Partition und nicht in die ID setzt. –

1

SQL Server nicht die „halten“ Konstrukt nicht unterstützt, so benötigen Sie eine Unterabfrage verwenden:

select m.* 
from (select *, row_number() over (partition by m.someid ORDER BY m.UpdateDate desc) as seqnum 
     from MyTable m 
    ) m 
where seqnum = 1 

Dies ist für jede der ersten Zeile findet m.id mit dem neuesten UpdateDate. Es wählt dann diese Zeile in der äußeren Abfrage aus. Beachten Sie, dass Sie mit dieser Methode keine Gruppe benötigen.

+0

ich bekomme es nicht .. wie es ohne "Gruppe durch someId" arbeiten sollte ? Meine Abfrage würde immer 3 Einträge zurückgeben, da ich 3 verschiedene someIds in der Tabelle habe. Ihre Abfrage wird Ergebnisse liefern, die nicht von der Anzahl der verschiedenen someIds abhängen, also sollte es dann falsch sein? – javagirl

+0

Dies wird nicht funktionieren, Sie werden eine Zeile pro ID als Ergebnis haben, und das ist nicht was OP will. – Lamak

3

Ich zurück und zurück zu dieser Frage und die Antwort. Leider gibt es mehrere Situationen, in denen die Migration mit der "Fensterfunktion für das Ranking" sehr komplex wird. Diese Situationen sind:

  1. viele KEEP-DENSE_RANK Konstruktionen in der Auswahl Teil Oracle Abfrage basierend auf verschiedenen Ordnungen
  2. Gruppierung durch Sätze/Rollups Gruppierung

Deshalb werde ich auf die Antwort hinzufügen, um zusätzliche Information. Originaldaten SQLFIDDLE: http://sqlfiddle.com/#!6/e5c6d/6

1. Lesen Orakel Funktion:

select max(m.id), m.someId keep (DENSE_RANK FIRST ORDER BY m.UpdateDate desc) 
from MyTable m 
groupBy m.someId 

dort wählen wir max von m.id in der Gruppe (someId, UpdateDate), wo UpdateDate ist größte es die Gruppe (someId

)

2. straight forward Weg nicht wegen Fehler funktioniert: Spalte 'MyTable.UpdateDate' ist ungültig in der Auswahl Liste, weil sie weder in einer Aggregatfunktion noch in der GROUP BY-Klausel enthalten ist.

SELECT FIRST_VALUE(id) OVER(PARTITION BY someId ORDER BY UpdateDate DESC, id DESC) first_in_orderedset , someId 
FROM MyTable 
GROUP BY someId 

3. improoved 'straight forward' ist noneffective

SELECT someId, MIN(first_in_orderedset) 
FROM 
(SELECT FIRST_VALUE(id) OVER(PARTITION BY someId ORDER BY UpdateDate DESC, id DESC) first_in_orderedset , someId 
    FROM MyTable) t 
GROUP BY someId; 

4. Quer gelten:

SELECT grouped.someId, orderedSet.FirstUpdateDate, maxInSet.first_in_orderedset FROM 
(
    SELECT mt.someId 
    FROM MyTable mt 
    GROUP BY mt.someId 
) grouped CROSS APPLY 
( 
    SELECT top 1 mt2.UpdateDate as FirstUpdateDate 
    FROM MyTable mt2 
    WHERE mt2.someId=grouped.someId 
    ORDER BY UpdateDate desc 
) orderedSet CROSS APPLY 
( 
    SELECT max(mt3.id) as first_in_orderedset 
    FROM MyTable mt3 
    WHERE mt3.someId=grouped.someId and mt3.UpdateDate=orderedSet.FirstUpdateDate 
) maxInSet; 

5. lässt nun den komplexeren Tisch bekommen und komplexere Abfrage: ORACLE: http://sqlfiddle.com/#!4/c943c/23 SQL SERVER: http://sqlfiddle.com/#!6/dc7fb/1/0 (Daten vorab generiert wird, und es ist das gleiche in beiden Sandkästen - es ist leicht, Ergebnisse) Tabelle zum Vergleich:

CREATE TABLE AlarmReports (
    id int PRIMARY KEY, 
    clientId int, businessAreaId int , projectId int, taskId int, 
    process1Spent int, process1Lag int, process1AlarmRate varchar2(1) null, 
    process2Spent int, process2Lag int, process2AlarmRate varchar2(1) null, 
    process3Spent int, process3Lag int, process3AlarmRate varchar2(1) null 
) 

Oracle Abfrage:

SELECT clientId, businessAreaId, projectId, 
    sum(process1Spent), 
    sum(process2Spent), 
    sum(process3Spent), 
    MIN(process1AlarmRate) KEEP (DENSE_RANK FIRST ORDER BY process1Lag DESC), 
    MIN(process2AlarmRate) KEEP (DENSE_RANK FIRST ORDER BY process2Lag DESC), 
    MIN(process3AlarmRate) KEEP (DENSE_RANK FIRST ORDER BY process3Lag DESC) 
FROM AlarmReports 
GROUP BY GROUPING SETS ((),(clientId),(clientId, projectId),(businessAreaId),(clientId,businessAreaId)) 

SQL-Abfrage:

(to be continued) 

tatsächlich dort habe ich geplant, mein benutzerdefiniertes Aggregat setzen mit C# wroted. Wenn jemand interessiert ist, bitte kontaktieren Sie mich ... benutzerdefinierte Aggregat ist die beste Lösung für solche Probleme, aber es ist nicht ungewöhnlich in Bezug auf Varchar Längen. Für jede Varchar-Länge müssten Sie eine "spezialisierte" Aggregatfunktion erstellen

0

Das wird absolut funktionieren. Probieren Sie zuerst, dann streiten Sie. Wenn Sie mehrere haben, um die von Ihnen diese (zB gemacht auf Oracle) tun können:

- dieses mit keep

WITH a AS (SELECT 1 s1, 4 s2, 'a' c, 10 g FROM dual UNION all 
      SELECT 2 s1, 2 s2, 'b' c, 10 g FROM dual UNION ALL 
      SELECT 3 s1, 1 s2, 'c' c, 20 g FROM dual UNION ALL 
      SELECT 4 s1, 3 s2, 'd' c, 20 g FROM dual) 
SELECT g, 
     MAX(c) KEEP (DENSE_RANK FIRST ORDER BY s1) s1, 
     MAX(c) KEEP (DENSE_RANK FIRST ORDER BY s2) s2 
    FROM a 
GROUP BY g 

DENSE_RANK - halten auch dieses ohne DENSE_RANK

WITH a AS (SELECT 1 s1, 4 s2, 'a' c, 10 g FROM dual UNION all 
       SELECT 2 s1, 2 s2, 'b' c, 10 g FROM dual UNION ALL 
       SELECT 3 s1, 1 s2, 'c' c, 20 g FROM dual UNION ALL 
       SELECT 4 s1, 3 s2, 'd' c, 20 g FROM dual) 
SELECT g, 
     MAX(DECODE(s1, 1, c)) s1, 
     MAX(DECODE(s2, 1, c)) s2 
    FROM (SELECT g,c, 
       ROW_NUMBER() OVER (PARTITION BY g ORDER BY s1) s1, 
       ROW_NUMBER() OVER (PARTITION BY g ORDER BY s2) s2 
      FROM a) b 
GROUP BY g