Bir sütun için Maks değerine sahip satırı getir


574

Tablo:

UserId, Value, Date.

UserId, her UserId için max (Tarih) için değer almak istiyorum. Yani, en son tarihe sahip her UserId için Değer. Bunu sadece SQL'de yapmanın bir yolu var mı? (Tercihen Oracle)

Güncelleme: Herhangi bir belirsizlik için özür dilerim: TÜM UserIds almak gerekir. Ancak her UserId için yalnızca o kullanıcının en son tarihe sahip olduğu satır.


21
Belirli bir kullanıcı kimliği için maksimum tarih değerine sahip birden çok satır varsa ne olur?
David Aldridge

Tablonun temel alanları nelerdir?
vamosrafa

aşağıda bazı çözümler karşılaştırıldı: sqlfiddle.com/#!4/6d4e81/1
Used_By_Already

1
@DavidAldridge, Bu sütun büyük olasılıkla benzersizdir.
Pacerier

Yanıtlar:


397

Bu, my_date sütun değerinin söz konusu kullanıcı kimliği için my_date değerinin maksimum değerine eşit olduğu tüm satırları alır. Bu, maksimum tarihin birden çok satırda olduğu kullanıcı kimliği için birden çok satır alabilir.

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

"Analitik işlevler kaya"

Edit: İlk yorum ile ilgili olarak ...

"analitik sorgular ve kendi kendine birleştirme kullanmak analitik sorguların amacını yener"

Bu kodda kendiliğinden katılma yoktur. Bunun yerine, satır içi görünümün sonucuna, çok farklı bir konu ve tamamen standart bir uygulama olan analitik işlevi içeren bir yüklem vardır.

"Oracle'daki varsayılan pencere, bölümdeki ilk satırdan geçerli pencereye kadardır"

Pencereleme maddesi yalnızca sipariş cümlesi varlığında uygulanabilir. Cümle sıralaması olmadan, varsayılan olarak hiçbir pencere cümlesi uygulanmaz ve hiçbiri açıkça belirtilemez.

Kod çalışır.


38
8,8 milyon satır içeren bir tabloya uygulandığında, bu sorgu diğer yüksek oy alan cevapların sorgularının yarısını aldı.
Derek Mahar

4
Herkes varsa MySQL eşdeğeri için bir bağlantı göndermek ister misiniz, varsa?
redolent

2
Bu kopyalar tekrarlanamadı mı? Örneğin. iki satır aynı user_id ve aynı tarihe sahipse (bu maks. olur).
jastr

2
@jastr Sanırım bu soruda kabul edildi
David Aldridge

3
Yerine MAX(...) OVER (...)de kullanabilirsiniz ROW_NUMBER() OVER (...)(üst-n-başına-grup için) ya da RANK() OVER (...)(büyük-n-başına-grubu için).
MT0

441

Birçok insan bunu yapmak için alt sorgular veya başka satıcıya özgü özellikleri kullanın, ancak genellikle bu şekilde sorgu alt sorgular olmadan aşağıdaki şekilde yapmak. Düz, standart SQL kullanır, bu nedenle RDBMS'nin herhangi bir markasında çalışmalıdır.

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;

Başka bir deyişle: satırı, t1başka bir satırın olmadığı yerden getirUserId ve daha büyük bir Tarih .

("Date" tanımlayıcısını ayırıcılara koydum, çünkü bu SQL ayrılmış bir kelimedir.)

Böyle bir durumda t1."Date" = t2."Date"iki katına çıkar. Genellikle tabloların auto_inc(seq)anahtarı vardır, örn id. Çifte önlemek için aşağıdaki kullanılabilir:

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;

@Farhan'dan yorum:

İşte daha ayrıntılı bir açıklama:

Bir dış katılması için girişimlerde birleştirme t1ile t2. Varsayılan olarak, tüm sonuçlar t1döndürülür ve eğer bir maç var ise t2, o da döndürülür. t2Belirli bir satır için eşleşme yoksa, t1sorgu yine de satırını döndürür ve tüm sütunları için yer tutucu olarak t1kullanır . Dış birleşimler genel olarak böyle çalışır.NULLt2

Bu sorgudaki hile, birleştirmenin eşleşen koşulunu aynı ve daha büyük bir değerlet2 eşleşecek şekilde tasarlamaktır . Bir satır bulunuyorsa olmak fikri bu daha büyük vardır , o satır buna karşı karşılaştırıldığında oluyor değil kutu büyük olması bunun için . Ancak eşleşme yoksa - yani , satırdan daha büyük bir satır yoksa - içerideki satırın verilen için en büyük satır olduğunu biliyoruz . userid datet2datet1dateuseridt2datet1t1dateuserid

Bu gibi durumlarda (eşleşme olmadığında), birleşim koşulunda belirtilen sütunlar bile sütunları t2olacaktır NULL. Bu yüzden kullanıyoruz WHERE t2.UserId IS NULL, çünkü dateverilenler için daha büyük bir satır bulunmayan durumları arıyoruz userid.


7
Vay canına Bill. Gördüğüm bu sorunun en yaratıcı çözümü bu. Oldukça büyük veri setimde de oldukça iyi bir performans sergiliyor. Bu, gördüğüm diğer çözümlerin çoğunu veya bu sorunu çözme girişimimi yener.
Justin Noel

36
8,8 milyon satıra sahip bir tabloya uygulandığında, bu sorgu kabul edilen yanıttaki değerin neredeyse iki katı kadar sürdü.
Derek Mahar

16
@Derek: Optimizasyonlar, RDBMS'nin markasına ve sürümüne ve ayrıca uygun dizinlerin, veri türlerinin vb. Varlığına bağlıdır.
Bill Karwin

7
MySQL üzerinde, bu tür bir sorgu aslında tablolar arasında bir Kartezyen birleşimi sonucu döngü (O ^ n) zaman sonuç neden gibi görünüyor. Bunun yerine alt sorgu yöntemini kullanmak sorgu süresini 2.0 saniyeden 0.003 saniyeye düşürdü. YMMV.
Jesse

1
Bunu, tarihin en büyük tarihin kullanıcının verdiği tarihten küçük veya ona eşit olduğu satırlarla eşleşmenin bir yolu var mı? Örneğin, kullanıcı "23-OCT-2011" tarihini verirse ve tabloda "24-OCT-2011", "22-OCT-2011", "20-OCT-2011" satırlarını içeriyorsa, "22 EKİM 2011" olsun. Başımı kaşıyor ve bu parçacığı bir süredir okuyor ...
Cory Kendall

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

3
Çok sayıda satır içeren bir tablo kullanan testlerimde, bu çözüm kabul edilen cevapta yaklaşık iki kat daha uzun sürdü.
Derek Mahar


Diğer çözümlerden çok daha hızlı olduğunu
onaylıyorum

5
sorun tam kayıt döndürmez
Used_By_Already

@ user2067753 Hayır, tüm kaydı döndürmez. Birden fazla sütunda aynı MAX () .. KEEP .. ifadesini kullanabilirsiniz, böylece ihtiyacınız olan tüm sütunları seçebilirsiniz. Ancak çok sayıda sütun istiyorsanız ve SELECT * kullanmayı tercih ederseniz sakıncalıdır.
Dave Costa

51

Tam sütun adlarınızı bilmiyorum, ama böyle bir şey olurdu:

    kullanıcı kimliği, değer seç
      kullanıcılardan u1
     nerede tarih = (maks. (tarih) seçin
                     kullanıcılardan u2
                    burada u1.userid = u2.userid)

3
Muhtemelen çok etkili değil, Steve.
David Aldridge

7
Muhtemelen Oracle sorgu optimize edicisini hafife alıyorsunuz.
Rafał Dowgird

3
Bir şey değil. Bu neredeyse kesinlikle tarihleri ​​almak için iç içe bir döngü birleştirme ile tam bir tarama olarak uygulanacaktır. Mantıksal io'lardan, tablodaki satır sayısının 4 katı sırayla bahsediyorsunuz ve önemsiz olmayan veriler için korkunç oluyorsunuz.
David Aldridge

4
Bilginize, "Verimli değil, ama çalışıyor", "Çalışıyor, ancak verimli değil" ile aynı. Verimli bir tasarım hedefi olarak ne zaman vazgeçtik?
David Aldridge

6
+1 çünkü veri tablolarınız milyonlarca satır uzunluğunda değilse, bu en kolay anlaşılan çözümdür. kodu değiştiren tüm beceri seviyelerinden birden fazla geliştiriciniz varsa, anlaşılabilirlik, performansta fark edilmeyen bir saniyenin bir kısmından daha önemlidir.
n00b

35

İş yerinde değilim, elimde Oracle yok, ancak Oracle'ın birden fazla sütunun bir IN yan tümcesinde eşleşmesine izin verdiğini hatırlıyorum, en azından ilişkili bir alt sorgu kullanan seçeneklerden kaçınmalıdır, fikir.

Böyle bir şey, belki (sütun listesinin parantez içinde olup olmayacağını hatırlayamıyorum):

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

EDIT: Sadece gerçek için denedim:

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

Bu yüzden işe yarıyor, ancak başka bir yerde sözü edilen bazı yeni şeyler daha performanslı olabilir.


4
Bu PostgreSQL üzerinde de çok iyi çalışıyor. Ve basitliğini ve genelliğini seviyorum - alt sorgu "İşte benim kriterlerim" diyor, dış sorgu "Ve görmek istediğim detaylar" diyor. +1.
j_random_hacker

13

Oracle'ı sorduğunuzu biliyorum, ancak SQL 2005'te şimdi bunu kullanıyoruz:


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

7

Test edecek Oracle'ım yok, ancak en etkili çözüm analitik sorguları kullanmaktır. Bunun gibi bir şeye benzemeli:

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

Dış sorgudan kurtulabileceğinden ve iç kısma ayırabileceğinden şüpheleniyorum ama emin değilim. Bu arada bunun işe yaradığını biliyorum.

Analitik sorgular hakkında bilgi edinmek istiyorsanız, http://www.orafaq.com/node/55 ve http://www.akadia.com/services/ora_analytic_functions.html adresini okumanızı öneririm. . İşte kısa özet.

Gelişmiş analitik sorgular altında tüm veri kümesini sıralayın, ardından sırayla işleyin. İşlediğinizde, veri kümesini belirli ölçütlere göre bölümlendirirsiniz ve ardından her satır için bir pencereye bakar (varsayılan olarak geçerli satıra bölümün ilk değerini getirir - bu varsayılan değer de en verimlidir) ve bir Analitik fonksiyonların sayısı (listesi toplam fonksiyonlara çok benzer).

Bu durumda iç sorgunun yaptığı şey budur. Tüm veri kümesi UserId sonra Date DESC'ye göre sıralanır. Sonra tek geçişte işler. Her satır için UserId ve o UserId için görülen ilk Tarihi döndürürsünüz (tarihler DESC olarak sıralandığından, bu maksimum tarihtir). Bu size yinelenen satırlarla cevabınızı verir. Daha sonra DISTINCT dış kopyaları eziyor.

Bu özellikle analitik sorguların muhteşem bir örneği değildir. Çok daha büyük bir kazanç için, her bir kullanıcı ve makbuz için bir mali makbuz tablosu almayı ve hesaplamayı, ödenen toplam tutarı hesaplamayı düşünün. Analitik sorgular bunu verimli bir şekilde çözer. Diğer çözümler daha az verimlidir. Bu yüzden 2003 SQL standardının bir parçasıdır. (Ne yazık ki Postgres henüz sahip değil. Grrr ...)


Soruyu tamamen cevaplamak için tarih değerini de döndürmeniz gerekir. Bu başka bir first_value deyimi anlamına gelirse, çözümün olması gerekenden daha karmaşık olduğunu ve max (tarih) 'e dayalı analitik yöntem daha iyi okuduğunu öneririm.
David Aldridge

Soru ifadesi, tarihi iade etme hakkında hiçbir şey söylemez. Bunu başka bir İLK (Tarih) ekleyerek veya yalnızca Tarihi sorgulayarak ve dış sorguyu GROUP BY olarak değiştirerek yapabilirsiniz. İlkini kullanacağım ve optimize edicinin her ikisini de tek geçişte hesaplamasını beklerdim.
user11318

“Soru cümlesi tarihi iade etme hakkında hiçbir şey söylemiyor” ... evet, haklısın. Afedersiniz. Ancak daha fazla FIRST_VALUE deyimi eklemek oldukça hızlı bir şekilde dağınık hale gelecektir. Bu tek bir pencere sıralamasıdır, ancak bu satır için döndürülecek 20 sütununuz varsa, o zaman geçmek için çok fazla kod yazdınız.
David Aldridge

Ayrıca, bu çözümün, tek bir kullanıcı kimliğinin maksimum tarihe ve farklı VALUE'lere sahip birden çok satıra sahip olduğu veriler için belirleyici olmadığı anlaşılmaktadır. Soruda cevap daha fazla bir hata.
David Aldridge

1
Acı verici bir ayrıntı olduğuna katılıyorum. Ancak bu genellikle SQL'de geçerli değil mi? Ve çözümün deterministik olmadığı konusunda haklısın. Bağlarla baş etmenin birden fazla yolu vardır ve bazen her biri istediğiniz şeydir.
user11318

6

QUALIFY deyimi hem en basit hem de en iyi yöntem olmaz mı?

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

Bağlam için, Teradata'da bunun iyi bir boyut testi, bu QUALIFY sürümü ile 17'lerde ve 23'lerde 'satır içi görünüm' / Aldridge çözümü # 1 ile çalışır.


1
Bence en iyi cevap bu. Ancak, rank()bağların olduğu durumlarda işleve dikkat edin . Birden fazla ile sonuçlanabilir rank=1. row_number()Gerçekten sadece bir kayıt döndürmek istiyorsanız kullanmak daha iyidir.
cartbeforehorse

1
Ayrıca, QUALIFYmaddenin Teradata'ya özgü olduğunu unutmayın. Oracle'da (en azından) sorgunuzu iç içe geçirmeniz ve WHEREsarma seçme deyimindeki bir maddeyi kullanarak filtrelemeniz gerekir (muhtemelen performansa bir dokunuş vurur, hayal ediyorum).
cartbeforehorse

5

İçinde Oracle 12c+, bunu alt sorgular olmadan çok özlü bir şekilde başarmak için Top n sorgularını analitik işlevle birlikte kullanabilirsiniz :rank

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

Yukarıdaki, kullanıcı başına maks. Tarihim içeren tüm satırları döndürür.

Maksimum tarihe sahip yalnızca bir satır istiyorsanız, şunu rankile değiştirin 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; 

5

Her biri ROW_NUMBER()için azalanlara benzersiz bir sıralama atamak için kullanın , ardından Dateher biri UserIdiçin ilk satıra filtre uygulayın UserId(yani, 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;

5

PostgreSQL 8.4 veya üstü ile şunları kullanabilirsiniz:

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

3

shuold i şey önceki sorgusu için bu varyant yapmak:

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

3
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  

3

İş yerinde "canlı" bir örnek yazmak zorunda kaldım :)

Bu, aynı tarihte UserId için birden çok değeri destekler .

Sütunlar: UserId, Değer, Tarih

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
)

MAX yerine FIRST_VALUE kullanabilir ve açıklama planında arayabilirsiniz. Onunla oynayacak vaktim yoktu.

Tabii ki, büyük tablolarda arama yapıyorsanız, sorgunuzda TAM ipuçları kullanıyorsanız muhtemelen daha iyi olur.


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

2

Bence böyle bir şey. (Sözdizimi hataları için beni affet; Bu noktada HQL kullanmaya alışkınım!)

EDIT: Ayrıca soruyu yanlış okudum! Sorgu düzeltildi ...

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

"Her UserId için" koşulunu karşılamıyor
David Aldridge

Nerede başarısız olur? Kullanıcılar'daki her UserID için, bu UserID'yi içeren en az bir satırın döndürüleceği garanti edilecektir. Yoksa bir yerlerde özel bir vakayı kaçırıyor muyum?
jdmichal

2

(T-SQL) Öncelikle tüm kullanıcıları ve onların maxdate'lerini alın. Maksimum sayıdaki kullanıcılar için ilgili değerleri bulmak üzere tabloya katılın.

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

Sonuçlar:

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

2

Bu sorunun yanıtı yalnızca Oracle. İşte tüm SQL'de biraz daha karmaşık bir cevap:

En iyi genel ev ödevi sonucuna kim sahip (maksimum ev ödevi puanı)?

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)

Ve biraz açıklamaya ihtiyaç duyan daha zor bir örnek, bunun için zaman atmım yok:

2008'de en popüler olan kitabı (ISBN ve başlık), yani 2008'de en sık ödünç verilen kitabı verin.

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

Umarım bu yardımcı olur (herkes) .. :)

Saygılarımızla, Guus


Kabul edilen cevap "yalnızca Oracle" değildir - standart
SQL'dir

2

Tarihin belirli bir UserID için benzersiz olduğunu varsayarsak, işte bazı 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 

2

Partiye oldukça geç kaldım, ancak aşağıdaki hack hem ilişkili alt sorgulardan hem de herhangi bir analitik işlevden daha iyi performans gösterecek, ancak bir kısıtlama var: değerler dizelere dönüştürülmelidir. Böylece tarihler, sayılar ve diğer dizgiler için çalışır. Kod iyi görünmüyor ancak yürütme profili harika.

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

Bu kodun bu kadar iyi çalışmasının nedeni, tabloyu yalnızca bir kez taraması gerektiğidir. Herhangi bir indeks gerektirmez ve en önemlisi, çoğu analitik fonksiyonun yaptığı tabloyu sıralaması gerekmez. Tek bir kullanıcı kimliği için sonucu filtrelemeniz gerekiyorsa dizinler yardımcı olacaktır.


Bu, çoğuna kıyasla iyi bir uygulama planıdır, ancak tüm bu hileleri birkaç alandan daha fazlasına uygulamak sıkıcı olacaktır ve buna karşı çalışabilir. Ama çok ilginç - teşekkürler. bkz. sqlfiddle.com/#!4/2749b5/23
Used_By_Already

Bu sıkıcı olabilir haklı, bu yüzden bu sadece sorgu performansı gerektirdiğinde yapılmalıdır. ETL komut dosyalarında genellikle durum böyledir.
aLevelOfIndirection

bu çok güzel. LISTAGG kullanarak benzer bir şey yaptı ama çirkin görünüyor. postgres, array_agg kullanarak daha iyi bir alt etkiye sahiptir. cevabımı gör :)
Bruno Calza

1
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 bu işe yarıyor. HTH


1

Bence bu işe yarıyor mu?

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

1

İlk önce soruyu yanlış okudum, üst cevabı takip ederek, doğru sonuçlarla tam bir örnek:

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)

1

Bu aynı zamanda kopyaları da dikkate alır (her kullanıcı_kimliği için bir satır döndürür):

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

1

Sadece bunu test ettim ve bir günlük tablosunda çalışıyor gibi görünüyor

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

1

Bu kadar basit olmalıdır:

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

1

KEEP, DENSE_RANK bölümleme kavramlarına sahip olmayan MySQL çözümü.

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

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


Bu, " diğer DB'lerde de " çalışmaz . Benzer değişken kavramlarına sahip olduğu için bu sadece MySQL ve muhtemelen SQL Server'da çalışır. Kesinlikle Oracle, Postgres, DB2, Derby, H2, HSQLDB, Vertica, Greenplum üzerinde çalışmaz. Ek olarak kabul edilen cevap standart ANSI
SQL'dir

at, sanırım haklısın. Diğer DB'ler veya ANSI hakkında bilgim yok. Çözümüm, sorunu ANS SQL'de standart şekilde çözmesi için uygun desteğe sahip olmayan MySQL'de çözebiliyor.
Ben Lin

1

Eğer Postgres ediyorsanız kullanabilirsiniz array_agggibi

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

Oracle'a aşina değilim. Ben de bunu buldum

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 

Her iki sorgu da kabul edilen yanıtla aynı sonuçları döndürür. Bkz. SQLFiddles:

  1. Kabul edilen cevap
  2. Postgres ile çözümüm
  3. Oracle ile çözümüm

0

(KullanıcıKimliği, Tarih) benzersizse, yani aynı kullanıcı için iki kez hiçbir tarih görünmezse:

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;

Ben de UserID katılmak gerekir inanıyorum
Tom H

0
select   UserId,max(Date) over (partition by UserId) value from users;

2
Bu, kullanıcı başına yalnızca bir satır değil tüm satırları döndürür.
Jon Heller
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.