2012-05-25 12 views
5

Tengo una consulta de OracleServidor SQL: ¿cómo imitar oráculo mantener consulta dense_rank?

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

que por datos como estos:

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 

me va a devolver exactamente esto:

2 10 
4 20 
6 30 

Así, por cada someId que busca para el último updateDate y devuelve el id apropiado. (Y si hay varias identificaciones para las últimas fechas, se necesita la última identificación).

Pero para SQL Server, ¿esta consulta funcionará de la misma manera? Me refiero a esta construcción keep (dense_rank first order by ..)?

Respuesta

8

No creo que su consulta en particular vaya a ejecutar SQL Server. Sin embargo, se puede lograr el mismo resultado haciendo esto:

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 no lo entiendo ... ¿cómo se supone que debe funcionar sin "grupo por parte de alguien"? Mi consulta siempre me devolverá 3 entradas ya que tengo 3 ID diferentes en la tabla. Su consulta arrojará resultados que no dependen de la cantidad de elementos diferentes, ¿entonces debería estar equivocado? – javagirl

+1

@javagirl - ¿Lo has intentado primero ?, funcionará. En cuanto a cómo funciona, las funciones analíticas (el 'OVER() ...') pueden tener un 'PARTITION BY' y no necesitan agruparse en todo el nivel. Devuelve un valor por cada fila que hay sobre la mesa. – Lamak

+0

Leí mal los datos originales. El problema se soluciona colocando someid en la partición en lugar de id. –

1

SQL Server no admite el "mantener" construir, por lo que necesita usar una subconsulta:

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 

Esto encuentra la primera fila por cada m.id con el UpdateDate más reciente. Luego elige esa fila en la consulta externa. Tenga en cuenta que no necesita un grupo con este método.

+0

No lo entiendo ... cómo se supone que debe funcionar sin "grupo por algún ID" ? Mi consulta siempre me devolverá 3 entradas ya que tengo 3 ID diferentes en la tabla. Su consulta arrojará resultados que no dependen de la cantidad de elementos diferentes, ¿entonces debería estar equivocado? – javagirl

+0

Esto no funcionará, tendrá una fila por cada identificación como resultado, y eso no es lo que op quiere. – Lamak

3

Regreso y regreso a esta pregunta y a la respuesta. Lamentablemente, hay varias situaciones en las que la migración que utiliza la "función de ventana para clasificar" se vuelve muy compleja. Esas situaciones son:

  1. muchas construcciones KEEP-DENSE_RANK en la parte de selección de consulta de Oracle basadas en diferentes órdenes
  2. agrupación mediante la agrupación de sistemas/paquetes de continuación

Por lo tanto voy a añadir a la respuesta adicional información. original SQLFIDDLE datos: http://sqlfiddle.com/#!6/e5c6d/6

1. lectura función de Oracle:

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

allí seleccionamos máximo de m.id en el grupo (someId, UpdateDate), donde UpdateDate es mayor que el grupo (someId)

2. sencillo manera no funciona debido a un error: columna 'MyTable.UpdateDate' no es válido en el selecto lista porque no está contenida ni en una función de agregado ni en la cláusula GROUP BY.

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 'sencillo' es no eficaz

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. cruz se aplican:

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. Ahora deja para conseguir la tabla más compleja y consulta más compleja: ORACLE: http://sqlfiddle.com/#!4/c943c/23 SQL Server: http://sqlfiddle.com/#!6/dc7fb/1/0 (los datos se pregenerated y es el mismo en los dos cajones de arena - es fácil comparar los resultados) Tabla:

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 consulta:

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)) 

consulta SQL:

(to be continued) 

realidad no he hecho planes para poner mi agregada a medida wroted con C#. si alguien está interesado, contácteme ... el agregado personalizado es la mejor solución para tales problemas, pero no es unviersal en términos de varchar lengths. para cada longitud de varchar estaría obligado a crear la función aggreate "especializada"

0

Eso funcionará absolutamente. Pruebe primero, luego discuta. Cuando usted tiene orden múltiple por usted puede hacer esto (ejemplo realizado en Oracle):

- éste con torreón 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(c) KEEP (DENSE_RANK FIRST ORDER BY s1) s1, 
     MAX(c) KEEP (DENSE_RANK FIRST ORDER BY s2) s2 
    FROM a 
GROUP BY g 

- Éste sin mantener 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 
Cuestiones relacionadas