Pergunta

Tabela:

UserId, Value, Date.

Eu quero começar o UserId, o valor para o max (Data) para cada UserId. Ou seja, o valor para cada UserId que tem a data mais recente. Existe uma maneira de fazer isso simplesmente no SQL? (De um modo preferido Oracle)

Update: Desculpas para qualquer ambiguidade: Eu preciso para obter todos os UserIds. Mas para cada UserId, só isso linha onde o usuário tem a data mais recente.

Foi útil?

Solução

Isso irá recuperar todas as linhas para as quais o valor da coluna my_date é igual ao valor máximo de my_date para esse ID de usuário. Isto pode recuperar várias linhas para o ID de usuário, onde a data máxima é em várias linhas.

select userid,
       my_date,
       ...
from
(
select userid,
       my_date,
       ...
       max(my_date) over (partition by userid) max_my_date
from   users
)
where my_date = max_my_date

"As funções analíticas rock"

Edit: No que diz respeito ao primeiro comentário ...

"usando consultas analíticas e uma auto-associação derrota o propósito de consultas analíticas"

Não há auto-aderir a este código. Existe sim um predicado colocado no resultado da visão em linha que contém a função analítica -. Um assunto muito diferente, e na prática completamente padrão

"A janela padrão no Oracle é a partir da primeira linha na partição para o atual"

A cláusula janelamento é aplicável apenas na presença da ordem pela cláusula. Com nenhuma ordem por cláusula, nenhuma cláusula de janelas é aplicado por padrão e nenhum pode ser especificado explicitamente.

Os trabalhos de código.

Outras dicas

Eu vejo muitas pessoas usam subconsultas ou recursos else específicos do fornecedor para fazer isso, mas muitas vezes eu fazer este tipo de consulta sem subconsultas da seguinte maneira. Ele usa simples, SQL padrão assim que deve funcionar em qualquer marca de RDBMS.

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;

Em outras palavras:. Buscar a linha de t1 onde não existe outra linha com o mesmo UserId e uma maior data

(I colocar o identificador "Data" em delimitadores porque é uma palavra reservada do SQL.)

No caso t1."Date" = t2."Date", dobrando aparece. Normalmente tabelas tem a chave auto_inc(seq), por exemplo, id. Para evitar a duplicação pode ser usado a seguir:

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date") 
         OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;

Re comentário de @Farhan:

Aqui está uma explicação mais detalhada:

Uma junção externa tentativas de ingressar t1 com t2. Por padrão, todos os resultados de t1 são devolvidos, e se , há uma partida em t2, também é retornado. Se não houver correspondência no t2 para uma determinada linha de t1, em seguida, a consulta ainda retorna a linha de t1, e usa NULL como um espaço reservado para todas as colunas da t2. É assim que as junções externas trabalho em geral.

O truque nesta consulta é projetar a junção da condição de correspondência tal que t2 deve corresponder ao mesma userid, e um maior date. A idéia é se uma linha existe no t2 que tem um date maior, então a linha na t1 é comparado com não pode ser o maior date para que userid. Mas se não houver correspondência - ou seja, se nenhuma linha existe no t2 com um date maior do que a linha em t1 -. Nós sabemos que a linha na t1 foi a linha com o maior date para o userid dada

Nos casos (quando não há jogo), as colunas de t2 será NULL - mesmo as colunas especificadas na condição de junção. Então é por isso que usamos WHERE t2.UserId IS NULL, porque nós estamos procurando os casos em que nenhuma linha foi encontrada com um date maior para o userid dada.

SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid

Eu não sei seus nomes exatos colunas, mas seria algo como isto:

    select userid, value
      from users u1
     where date = (select max(date)
                     from users u2
                    where u1.userid = u2.userid)

Não estar no trabalho, eu não tenho Oracle para mão, mas eu me lembro que a Oracle permite que várias colunas a serem combinados em uma cláusula IN, que deve, pelo menos, evitar as opções que usam uma subconsulta correlacionada, que é raramente uma boa idéia.

Algo como isso, talvez (não me lembro se a lista de colunas deve ser parenthesised ou não):

SELECT * 
FROM MyTable
WHERE (User, Date) IN
  ( SELECT User, MAX(Date) FROM MyTable GROUP BY User)

EDIT: Só tentei de verdade:

SQL> create table MyTable (usr char(1), dt date);
SQL> insert into mytable values ('A','01-JAN-2009');
SQL> insert into mytable values ('B','01-JAN-2009');
SQL> insert into mytable values ('A', '31-DEC-2008');
SQL> insert into mytable values ('B', '31-DEC-2008');
SQL> select usr, dt from mytable
  2  where (usr, dt) in 
  3  ( select usr, max(dt) from mytable group by usr)
  4  /

U DT
- ---------
A 01-JAN-09
B 01-JAN-09

Assim, ele funciona, embora algumas das coisas recém-fangly mencionado em outros lugares pode ser mais alto desempenho.

Eu sei que você pediu para Oracle, mas no SQL 2005 que agora usar este:


-- Single Value
;WITH ByDate
AS (
SELECT UserId, Value, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) RowNum
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE RowNum = 1

-- Multiple values where dates match
;WITH ByDate
AS (
SELECT UserId, Value, RANK() OVER (PARTITION BY UserId ORDER BY Date DESC) Rnk
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE Rnk = 1

Não seria uma cláusula QUALIFICAR ser tanto mais simples e melhor?

select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

Para contexto, em Teradata aqui um teste de tamanho decente desta corridas em 17s com este QUALIFICAR versão e em 23s com a 'visão inline' / solução Aldridge # 1.

Eu não tenho Oracle para testá-lo, mas a solução mais eficiente é usar consultas analíticas. Deve ser algo como isto:

SELECT DISTINCT
    UserId
  , MaxValue
FROM (
    SELECT UserId
      , FIRST (Value) Over (
          PARTITION BY UserId
          ORDER BY Date DESC
        ) MaxValue
    FROM SomeTable
  )

Eu suspeito que você pode se livrar da consulta externa e colocar distinta no interior, mas eu não tenho certeza. Nesse meio tempo eu sei que este funciona.

Se você quiser saber mais sobre consultas analíticas, eu sugiro a leitura http: //www.orafaq .com / node / 55 e http: //www.akadia. com / serviços / ora_analytic_functions.html . Aqui está o resumo.

Sob o capô consultas analíticas espécie todo o conjunto de dados, em seguida, processá-lo sequencialmente. Como você processá-lo você particionar o conjunto de dados de acordo com determinados critérios, e, em seguida, para cada linha olhares em algum janela (padrão para o primeiro valor na partição para a linha atual - que a inadimplência é também o mais eficiente) e pode calcular valores utilizando uma número de funções analíticas (a lista de que é muito semelhante às funções de agregação).

Neste caso, aqui está o que a consulta interna faz. Todo o conjunto de dados é classificada por UserId seguida Data DESC. Em seguida, ele processa-lo em uma passagem. Para cada linha de devolver o UserId ea primeira Data visto para esse UserId (DESC desde datas são classificadas, que é a data max). Isto dá-lhe a sua resposta com as linhas duplicadas. Em seguida, os exteriores polpas DISTINTAS duplicatas.

Este não é um exemplo particularmente espetacular de consultas analíticas. Para uma vitória muito maior considerar tomar uma tabela de recebimentos financeiros e calcular para cada usuário e recebimento, uma execução total do que eles pagaram. consultas analíticas resolver isso de forma eficiente. Outras soluções são menos eficientes. É por isso que eles são parte do padrão 2003 SQL. (Infelizmente Postgres não tê-los ainda. Grrr ...)

Em Oracle 12c+ , você pode usar Top n consultas junto com rank função analítica para alcançar este muito concisa sem subconsultas:

select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;

Os retornos acima todas as linhas com my_date max por usuário.

Se você quiser apenas uma linha com data max, em seguida, substituir o rank com row_number:

select *
from your_table
order by row_number() over (partition by user_id order by my_date desc)
fetch first 1 row with ties; 

Com PostgreSQL 8.4 ou posterior, você pode usar isto:

select user_id, user_value_1, user_value_2
  from (select user_id, user_value_1, user_value_2, row_number()
          over (partition by user_id order by user_date desc) 
        from users) as r
  where r.row_number=1

Use ROW_NUMBER() para atribuir um único ranking em descendente Date para cada UserId, em seguida, o primeiro filtro de linha para cada UserId (isto é, ROW_NUMBER = 1).

SELECT UserId, Value, Date
FROM (SELECT UserId, Value, Date,
        ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) rn
      FROM users) u
WHERE rn = 1;
Select  
   UserID,  
   Value,  
   Date  
From  
   Table,  
   (  
      Select  
          UserID,  
          Max(Date) as MDate  
      From  
          Table  
      Group by  
          UserID  
    ) as subQuery  
Where  
   Table.UserID = subQuery.UserID and  
   Table.Date = subQuery.mDate  

Apenas teve de escrever um exemplo "ao vivo" no trabalho:)

este suporta múltiplos valores para UserID no mesma data.

Colunas: UserId, valor, data

SELECT
   DISTINCT UserId,
   MAX(Date) OVER (PARTITION BY UserId ORDER BY Date DESC),
   MAX(Values) OVER (PARTITION BY UserId ORDER BY Date DESC)
FROM
(
   SELECT UserId, Date, SUM(Value) As Values
   FROM <<table_name>>
   GROUP BY UserId, Date
)

Você pode usar FIRST_VALUE vez de MAX e procurá-lo no plano de explicar. Eu não tenho tempo para brincar com ele.

Claro que, se pesquisar através de enormes mesas, provavelmente é melhor se você usar as dicas completa em sua consulta.

select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))

Eu acho que algo como isto. (Perdoe-me por quaisquer erros de sintaxe;! Estou acostumado a usar HQL neste ponto)

EDIT: também descaracterizou a pergunta! Corrigida a consulta ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)

coisa que você deve fazer esta variante a consulta anterior:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

(T-SQL) Primeiro obter todos os usuários e seus MaxDate. Junte-se a tabela para encontrar os valores correspondentes para os usuários sobre os maxdates.

create table users (userid int , value int , date datetime)
insert into users values (1, 1, '20010101')
insert into users values (1, 2, '20020101')
insert into users values (2, 1, '20010101')
insert into users values (2, 3, '20030101')

select T1.userid, T1.value, T1.date 
    from users T1,
    (select max(date) as maxdate, userid from users group by userid) T2    
    where T1.userid= T2.userid and T1.date = T2.maxdate

resultados:

userid      value       date                                    
----------- ----------- -------------------------- 
2           3           2003-01-01 00:00:00.000
1           2           2002-01-01 00:00:00.000

A resposta aqui é apenas Oracle. Aqui é a resposta um pouco mais sofisticado em todos SQL:

Quem tem o melhor resultado lição de casa geral (soma máxima de pontos de trabalhos de casa)?

SELECT FIRST, LAST, SUM(POINTS) AS TOTAL
FROM STUDENTS S, RESULTS R
WHERE S.SID = R.SID AND R.CAT = 'H'
GROUP BY S.SID, FIRST, LAST
HAVING SUM(POINTS) >= ALL (SELECT SUM (POINTS)
FROM RESULTS
WHERE CAT = 'H'
GROUP BY SID)

E um exemplo mais difícil, que precisa de alguma explicação, para o qual eu não tenho tempo atm:

Dê o livro (ISBN e título) que é mais popular em 2008, ou seja, que é emprestado na maioria das vezes em 2008.

SELECT X.ISBN, X.title, X.loans
FROM (SELECT Book.ISBN, Book.title, count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title) X
HAVING loans >= ALL (SELECT count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title);

Espero que isso ajude (qualquer um) ..:)

Saudações, Guus

Assumindo Data é exclusivo para um determinado UserID, aqui estão algumas TSQL:

SELECT 
    UserTest.UserID, UserTest.Value
FROM UserTest
INNER JOIN
(
    SELECT UserID, MAX(Date) MaxDate
    FROM UserTest
    GROUP BY UserID
) Dates
ON UserTest.UserID = Dates.UserID
AND UserTest.Date = Dates.MaxDate 

Estou muito atrasado para a festa, mas o seguinte truque irá superar ambas as subconsultas correlacionados e quaisquer análises funcionar, mas tem uma restrição: valores devem converter para strings. Então, ele funciona para datas, números e outras cordas. O código não parece bom, mas o perfil de execução é grande.

select
    userid,
    to_number(substr(max(to_char(date,'yyyymmdd') || to_char(value)), 9)) as value,
    max(date) as date
from 
    users
group by
    userid

A razão pela qual esse código funciona tão bem é que ele só precisa examinar a tabela uma vez. Ele não requer nenhum índice e, mais importante ele não precisa para classificar a tabela, que a maioria das funções analíticas fazer. Índices vai ajudar embora se você precisa filtrar o resultado para um único ID de usuário.

select userid, value, date
  from thetable t1 ,
       ( select t2.userid, max(t2.date) date2 
           from thetable t2 
          group by t2.userid ) t3
 where t3.userid t1.userid and
       t3.date2 = t1.date

IMHO isso funciona. HTH

Eu acho que isso deve funcionar?

Select
T1.UserId,
(Select Top 1 T2.Value From Table T2 Where T2.UserId = T1.UserId Order By Date Desc) As 'Value'
From
Table T1
Group By
T1.UserId
Order By
T1.UserId

Primeiro tente eu descaracterizou a pergunta, seguindo a resposta superior, aqui está um exemplo completo com resultados corretos:

CREATE TABLE table_name (id int, the_value varchar(2), the_date datetime);

INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'a','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'b','2/2/2002');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'c','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'d','3/3/2003');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'e','3/3/2003');

-

  select id, the_value
      from table_name u1
      where the_date = (select max(the_date)
                     from table_name u2
                     where u1.id = u2.id)

-

id          the_value
----------- ---------
2           d
2           e
1           b

(3 row(s) affected)

Isso também vai cuidar de duplicatas (linha um retorno para cada user_id):

SELECT *
FROM (
  SELECT u.*, FIRST_VALUE(u.rowid) OVER(PARTITION BY u.user_id ORDER BY u.date DESC) AS last_rowid
  FROM users u
) u2
WHERE u2.rowid = u2.last_rowid

Apenas testei isso e parece trabalhar em uma tabela de log

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc

Isto deve ser tão simples como:

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)

Se você estiver usando PostgreSQL, você pode usar array_agg como

SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid

Eu não estou familiarizado com a Oracle. Isto é o que eu vim com

SELECT 
  userid,
  MAX(adate),
  SUBSTR(
    (LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)),
    0,
    INSTR((LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)), ',')-1
  ) as value 
FROM YOURTABLE
GROUP BY userid 

Ambas as consultas retornam os mesmos resultados que a resposta aceita. Veja SQLFiddles:

  1. aceitado
  2. Minha solução com Postgres
  3. Minha solução com a Oracle

Se (UserID, Data) é única, ou seja, sem data aparece duas vezes para o mesmo usuário, em seguida:

select TheTable.UserID, TheTable.Value
from TheTable inner join (select UserID, max([Date]) MaxDate
                          from TheTable
                          group by UserID) UserMaxDate
     on TheTable.UserID = UserMaxDate.UserID
        TheTable.[Date] = UserMaxDate.MaxDate;
select   UserId,max(Date) over (partition by UserId) value from users;

Solução para MySQL que não tem conceitos de MANTER partição, DENSE_RANK.

select userid,
       my_date,
       ...
from
(
select @sno:= case when @pid<>userid then 0
                    else @sno+1
    end as serialnumber, 
    @pid:=userid,
       my_Date,
       ...
from   users order by userid, my_date
) a
where a.serialnumber=0

Referência: http: // benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top