ฉันจะสอบถาม sql สำหรับวันที่บันทึกล่าสุดสำหรับผู้ใช้แต่ละคนได้อย่างไร


228

ฉันมีตารางที่เป็นรายการคอลเลกชันว่าเมื่อผู้ใช้เข้าสู่ระบบ

username, date,      value
--------------------------
brad,     1/2/2010,  1.1
fred,     1/3/2010,  1.0
bob,      8/4/2009,  1.5
brad,     2/2/2010,  1.2
fred,     12/2/2009, 1.3

etc..

ฉันจะสร้างแบบสอบถามที่จะให้วันที่ล่าสุดแก่ผู้ใช้แต่ละคนได้อย่างไร

อัปเดต:ฉันลืมว่าฉันต้องมีค่าที่ตรงกับวันที่ล่าสุด


7
คุณใช้ฐานข้อมูลใด MySQL, SQL-Server, Oracle, ... ?
Peter Lang

1
คุณต้องการค่าที่ตรงกับวันที่ล่าสุดหรือค่าสูงสุดและวันที่สูงสุดหรือไม่?
Matthew Jones

คำตอบ:


381
select t.username, t.date, t.value
from MyTable t
inner join (
    select username, max(date) as MaxDate
    from MyTable
    group by username
) tm on t.username = tm.username and t.date = tm.MaxDate

3
เมื่อทำงานกับ postgresql รุ่นนี้จะเร็วกว่าการใช้ IN (แบบสอบถามย่อย) แทนที่จะเป็นการรวมภายในหรือไม่
TheOne

3
@TheOne เป็นประสบการณ์ของฉันการใช้การรวมภายในเร็วกว่าในสภาพ
dada

14
อย่างระมัดระวังด้วยวิธีนี้: มันสามารถส่งคืนมากกว่าหนึ่งแถวต่อผู้ใช้ถ้าพวกเขามีมากกว่าหนึ่งระเบียนต่อวัน ( max(date)จะกลับวันที่จะเข้าร่วมหลายระเบียน) เพื่อหลีกเลี่ยงปัญหานี้มันจะดีกว่าที่จะใช้วิธีการแก้ปัญหา @ dotjoe ของ: stackoverflow.com/a/2411763/4406793
Marco Roy

@ RedFilter สิ่งนี้ทำงานได้สมบูรณ์แบบสำหรับปัญหาของฉัน ขอบคุณมากสำหรับคำถามด้านเทคนิค ตามวิธีที่ฉันใช้วันที่และเวลาแทนวันที่เพื่อหลีกเลี่ยงการได้รับผลลัพธ์หลายรายการสำหรับวันที่ระบุ
Muhammad Khan

ทำไมคุณต้องใช้ 'และ t.date = tm.MaxDate' การจัดกลุ่มจะไม่เพียงพอ
duldi

125

การใช้ฟังก์ชั่นหน้าต่าง (ทำงานใน Oracle, Postgres 8.4, SQL Server 2005, DB2, Sybase, Firebird 3.0, MariaDB 10.3)

select * from (
    select
        username,
        date,
        value,
        row_number() over(partition by username order by date desc) as rn
    from
        yourtable
) t
where t.rn = 1

1
คุ้มค่าที่จะอธิบายถึงผลิตภัณฑ์ / เวอร์ชันของ Sybase มันใช้งานไม่ได้กับ Sybase ASE 16.
levant pied

2
ประโยชน์ที่ยิ่งใหญ่ของวิธีการนี้คือรับประกันได้ว่าจะส่งคืนแถวเดียวเท่านั้นต่อพาร์ติชัน ( usernameในกรณีนี้) เสมอและไม่จำเป็นต้องมีฟิลด์ "สั่งซื้อได้" ที่ไม่ซ้ำกัน (เช่นเข้าร่วมmax(date)ในคำตอบอื่น ๆ )
Marco Roy

1
เพียงเพิ่มบางสิ่งในสิ่งที่ @MarcoRoy พูดหากคุณมีมากกว่าหนึ่งระเบียนที่มีวันที่สูงสุดเหมือนกันถ้าคุณเปลี่ยนแบบสอบถามเช่นเมื่อคุณทำการแก้ไขข้อบกพร่องนั้นระเบียนที่แตกต่างกันอาจได้รับหมายเลขแถว 1 ดังนั้น ผลลัพธ์อาจไม่สอดคล้องกัน แต่ตราบใดที่คุณไม่แคร์จริงๆนี่ก็ไม่เป็นปัญหา สิ่งนี้สามารถแก้ไขได้ถ้าคุณเพิ่ม PK หลังจากวันที่ ตัวอย่างเช่นorder by date desc, id desc).
แอนดรู

40

ฉันเห็นนักพัฒนาซอฟต์แวร์ส่วนใหญ่ใช้การสืบค้นแบบอินไลน์โดยไม่คำนึงถึงผลกระทบต่อข้อมูลจำนวนมาก

เพียงคุณสามารถบรรลุสิ่งนี้โดย:

SELECT a.username, a.date, a.value
FROM myTable a
LEFT OUTER JOIN myTable b
ON a.username = b.username 
AND a.date < b.date
WHERE b.username IS NULL
ORDER BY a.date desc;

3
จริง ๆ แล้วมันใช้งานได้เฉพาะกับรายการที่ซ้ำกันหากคุณมีค่ามากกว่า 2 เงื่อนไขเงื่อนไข a.date <b.date ไม่ทำงานหมายถึงไม่ใช่วิธีการแก้ปัญหาทั่วไปแม้ว่าแนวคิดในการทำงานกับ LEU OUTER JOIN เป็นสิ่งสำคัญ สิ่งในคำตอบนี้
iversoncru

น่าสนใจพอ Sybase ASE 16 ทำงานได้ดีสำหรับตารางขนาดเล็ก (<10k แถว) แต่มีขนาดที่ใหญ่กว่า (> แถว 100k) มันแขวน ... ฉันคิดว่านี่เป็นตัวอย่างที่สมบูรณ์แบบที่สัมพันธ์กันควรจะดีกว่า ...
levant pied

1
@levantpied ... ใช่แล้วการเข้าร่วมเป็นค่าใช้จ่ายสำหรับชุดข้อมูลขนาดใหญ่ คุณสามารถปรับแต่งประสิทธิภาพได้โดยใส่เงื่อนไขตัวกรองเพื่อรวมเข้าด้วยกันเพื่อจัดการกับวิธีการถ้าเป็นไปได้
sujeet

21

ในการรับทั้งแถวที่มีวันที่สูงสุดสำหรับผู้ใช้:

select username, date, value
from tablename where (username, date) in (
    select username, max(date) as date
    from tablename
    group by username
)

1
ทำงานกับ MySQL
School Boy

1
ระวังว่าสิ่งนี้จะให้คุณทำซ้ำหากมีมากกว่าหนึ่งระเบียนที่มีวันเดียวกันสำหรับผู้ใช้ที่เฉพาะเจาะจง คุณอาจต้องการหรือไม่ต้องการ
แอนดรู

sql นี้ทำงานช้าใน Oracle พร้อมด้วย clause จะไม่ใช้ดัชนี
meadlai

9
SELECT *     
FROM MyTable T1    
WHERE date = (
   SELECT max(date)
   FROM MyTable T2
   WHERE T1.username=T2.username
)

4
แม้ว่านี่จะเป็นวิธีแก้ปัญหาอื่นที่เป็นไปได้ แต่โดยปกติจะไม่เป็นวิธีที่ดีในการแก้ปัญหานี้ การทำเช่นนี้จะทำให้คิวรีภายในทำงานหนึ่งครั้งสำหรับแต่ละชื่อในตารางทำให้เกิดการชะลอตัวครั้งใหญ่สำหรับตารางที่มีขนาดใหญ่ การทำแบบสอบถามแยกต่างหากที่ไม่ได้มีองค์ประกอบจากการสอบถามครั้งแรกในข้อที่แล้วมีสองตารางเข้าร่วมจะมักจะได้เร็วขึ้น
Scott Chamberlain

สิ่งนี้มีคุณสมบัติที่ดีในการเป็นหนึ่งในโซลูชันที่เข้าใจได้มากกว่าซึ่งไม่ได้ใช้เฉพาะ
Michael Szczepaniak

7

จากประสบการณ์ของฉันวิธีที่เร็วที่สุดคือการใช้แต่ละแถวที่ไม่มีแถวที่ใหม่กว่าในตาราง

ข้อดีอีกอย่างคือไวยากรณ์ที่ใช้นั้นง่ายมากและความหมายของแบบสอบถามค่อนข้างง่ายต่อการเข้าใจ (รับทุกแถวโดยไม่มีแถวที่ใหม่กว่าสำหรับชื่อผู้ใช้ที่กำลังพิจารณา)

ไม่มีอยู่

SELECT username, value
FROM t
WHERE NOT EXISTS (
  SELECT *
  FROM t AS witness
  WHERE witness.username = t.username AND witness.date > t.date
);

ROW_NUMBER

SELECT username, value
FROM (
  SELECT username, value, row_number() OVER (PARTITION BY username ORDER BY date DESC) AS rn
  FROM t
) t2
WHERE rn = 1

เข้าร่วมภายใน

SELECT t.username, t.value
FROM t
INNER JOIN (
  SELECT username, MAX(date) AS date
  FROM t
  GROUP BY username
) tm ON t.username = tm.username AND t.date = tm.date;

ซ้ายเข้าร่วม

SELECT username, value
FROM t
LEFT OUTER JOIN t AS w ON t.username = w.username AND t.date < w.date
WHERE w.username IS NULL

ฉันมีปัญหาในการทำความเข้าใจกับเวอร์ชันที่ไม่มีอยู่ คุณไม่ได้รวมการค้นหาในส่วนย่อยหรือไม่ ถ้าฉันเรียกใช้สิ่งนี้บนโต๊ะของฉันฉันจะได้รับเพียง 3 ระเบียนพนักงานจาก 40 พนักงานที่ฉันมีในตาราง ฉันควรได้รับบันทึกอย่างน้อย 40 รายการ ในข้อความค้นหาภายในเราไม่ควรจับคู่ชื่อผู้ใช้ด้วยหรือ
Narshe

มันใช้งานได้สำหรับฉันโดยใช้สิ่งต่อไปนี้:SELECT username, value FROM t WHERE NOT EXISTS ( SELECT * FROM t AS witness WHERE witness.date > t.date AND witness.username = t.username );
Narshe

ฉันได้ดู NOT EXISTS แล้วและดูเหมือนว่าจะส่งคืนรายการที่สูงกว่าสำหรับผู้ใช้ทั้งหมดซึ่งตรงข้ามกับ: "แบบสอบถามที่จะให้วันที่ล่าสุดแก่ฉันสำหรับผู้ใช้แต่ละคน"
Tasos Zervos

คุณพูดถูกจริงๆฉันอัพเดตคำถามของฉัน ขอบคุณสำหรับคำพูดของคุณ! @Narshe ขอโทษฉันพลาดความเห็นของคุณด้วยเหตุผลบางอย่าง: / แต่คุณพูดถูก
Fabian Pijcke

2

อันนี้ควรให้ผลลัพธ์ที่ถูกต้องสำหรับคำถามที่คุณแก้ไข

แบบสอบถามย่อยทำให้แน่ใจว่าจะพบเฉพาะแถวของวันที่ล่าสุดและด้านนอกGROUP BYจะดูแลความสัมพันธ์ valueเมื่อมีสองรายการสำหรับวันที่เดียวกันสำหรับผู้ใช้เดียวกันก็จะกลับเป็นหนึ่งเดียวกับที่สูงที่สุด

SELECT t.username, t.date, MAX( t.value ) value
FROM your_table t
JOIN (
       SELECT username, MAX( date ) date
       FROM your_table
       GROUP BY username
) x ON ( x.username = t.username AND x.date = t.date )
GROUP BY t.username, t.date

1

คุณสามารถใช้ฟังก์ชันอันดับวิเคราะห์

    with temp as 
(
select username, date, RANK() over (partition by username order by date desc) as rnk from t
)
select username, rnk from t where rnk = 1

0
SELECT Username, date, value
 from MyTable mt
 inner join (select username, max(date) date
              from MyTable
              group by username) sub
  on sub.username = mt.username
   and sub.date = mt.date

จะแก้ไขปัญหาที่ปรับปรุงแล้ว มันอาจทำงานได้ไม่ดีนักในตารางขนาดใหญ่แม้จะมีการทำดัชนีที่ดี



0

สำหรับ Oracle จะเรียงลำดับชุดผลลัพธ์ตามลำดับจากมากไปน้อยและรับระเบียนแรกดังนั้นคุณจะได้รับระเบียนล่าสุด:

select * from mytable
where rownum = 1
order by date desc

0
SELECT DISTINCT Username, Dates,value 
FROM TableName
WHERE  Dates IN (SELECT  MAX(Dates) FROM TableName GROUP BY Username)


Username    Dates       value
bob         2010-02-02  1.2       
brad        2010-01-02  1.1       
fred        2010-01-03  1.0       

สิ่งนี้อาจไม่ทำงานหากผู้ใช้หลายคนมีคำสั่งซื้อในวันเดียวกัน เกิดอะไรขึ้นถ้าทั้งแบรดและบ๊อบมีคำสั่งในวันที่ 2 มกราคม
AHiggins

ฉันกำลังจัดกลุ่มโดยชื่อผู้ใช้ดังนั้นมันจะทำงานและผลลัพธ์จะเป็นดังนี้: ชื่อผู้ใช้วันที่มูลค่าบ๊อบ 2010-02-02 1.2 brad 2010-02-02 1.4 fred 2010-01-03 1.0
wara

0
SELECT t1.username, t1.date, value
FROM MyTable as t1
INNER JOIN (SELECT username, MAX(date)
            FROM MyTable
            GROUP BY username) as t2 ON  t2.username = t1.username AND t2.date = t1.date

4
ประโยคหนึ่งหรือสองประโยคเกี่ยวกับการนำไปใช้หรือคำอธิบายเป็นวิธีการสร้างคำตอบคุณภาพ

0

Select * from table1 where lastest_date=(select Max(latest_date) from table1 where user=yourUserName)

Inner Query จะส่งคืนวันที่ล่าสุดสำหรับผู้ใช้ปัจจุบันการสืบค้นภายนอกจะดึงข้อมูลทั้งหมดตามผลการสืบค้นภายใน


0

ฉันใช้วิธีนี้ในการบันทึกล่าสุดสำหรับผู้ใช้แต่ละคนที่ฉันมีบนโต๊ะของฉัน มันเป็นคำถามที่จะได้รับตำแหน่งสุดท้ายสำหรับพนักงานขายตามเวลาที่ตรวจพบในอุปกรณ์ PDA

CREATE FUNCTION dbo.UsersLocation()
RETURNS TABLE
AS
RETURN
Select GS.UserID, MAX(GS.UTCDateTime) 'LastDate'
From USERGPS GS
where year(GS.UTCDateTime) = YEAR(GETDATE()) 
Group By GS.UserID
GO
select  gs.UserID, sl.LastDate, gs.Latitude , gs.Longitude
        from USERGPS gs
        inner join USER s on gs.SalesManNo = s.SalesmanNo 
        inner join dbo.UsersLocation() sl on gs.UserID= sl.UserID and gs.UTCDateTime = sl.LastDate 
        order by LastDate desc

0
SELECT * FROM TABEL1 WHERE DATE= (SELECT MAX(CREATED_DATE) FROM TABEL1)

ยินดีต้อนรับสู่ StackOverflow และขอขอบคุณที่พยายามช่วย คำตอบแบบรหัสเท่านั้นเช่นคุณจะไม่ค่อยชื่นชมเมื่อเปรียบเทียบกับคำตอบที่อธิบายวิธีแก้ปัญหา
Yunnosch

โปรดอ่านวิธีการตอบสำหรับการให้คำตอบที่มีคุณภาพนี้
ทุก ๆ

และ. มันไม่ได้กลับไปที่ MAX สำหรับแต่ละชื่อผู้ใช้เพียงแค่ไปยังแถวเดียวล่าสุด
IrvineCAGuy

0

รวบรวมเล็กของฉัน

  • ตนเองjoinดีกว่าซ้อนกันselect
  • แต่group byไม่ให้สิ่งprimary keyที่ดีกว่าjoin
  • สามารถใช้รหัสนี้partition byร่วมกับfirst_value( เอกสาร )

ดังนั้นนี่คือแบบสอบถาม:

เลือก
 t. *
จาก 
 ตารางที่ t เข้าร่วมภายใน (
  เลือกที่แตกต่างกัน first_value (ID) มากกว่า (พาร์ทิชันตาม GroupColumn ลำดับโดย DateColumn desc) เป็น ID
  จากตาราง
  โดยที่ FilterColumn = 'value'
 ) j ใน t.ID = j.ID

ข้อดี:

  • กรองข้อมูลด้วยwhereคำสั่งโดยใช้คอลัมน์ใด ๆ
  • select คอลัมน์ใด ๆ จากแถวที่ถูกกรอง

จุดด้อย:

  • ต้องการ MS SQL Server ที่เริ่มต้นด้วย 2012

0

ฉันทำเพื่อการสมัครของฉันค่อนข้าง:

ด้านล่างคือแบบสอบถาม:

select distinct i.userId,i.statusCheck, l.userName from internetstatus 
as i inner join login as l on i.userID=l.userID 
where nowtime in((select max(nowtime) from InternetStatus group by userID));    

0

นี่คล้ายกับหนึ่งในคำตอบข้างต้น แต่ในความคิดของฉันมันง่ายกว่าและเป็นระเบียบมากขึ้น นอกจากนี้ยังแสดงให้เห็นถึงการใช้งานที่ดีสำหรับคำสั่ง cross Apply สำหรับ SQL Server 2005 และสูงกว่า ...

select
    a.username,
    a.date,
    a.value,
from yourtable a
cross apply (select max(date) 'maxdate' from yourtable a1 where a.username=a1.username) b
where a.date=b.maxdate

0
SELECT MAX(DATE) AS dates 
FROM assignment  
JOIN paper_submission_detail ON  assignment.PAPER_SUB_ID = 
     paper_submission_detail.PAPER_SUB_ID 

1
แม้ว่ารหัสนี้อาจแก้ปัญหาได้รวมถึงคำอธิบายว่าทำไมและวิธีแก้ปัญหานี้จะช่วยปรับปรุงคุณภาพการโพสต์ของคุณได้อย่างไรและอาจส่งผลให้คะแนนมากขึ้น จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่เพียงแค่คนที่ถามตอนนี้ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้ จากรีวิว
ส่งเสียงบี๊บ

-2

สิ่งนี้ควรใช้เพื่อรับรายการล่าสุดทั้งหมดสำหรับผู้ใช้

SELECT username, MAX(date) as Date, value
FROM MyTable
GROUP BY username, value

1
สวัสดีคอลัมน์ค่าจำเป็นต้องอยู่ในกลุ่มตามข้อ
Juan Ruiz de Castilla

-4

คุณจะใช้ฟังก์ชันการรวม MAX และ GROUP BY

SELECT username, MAX(date), value FROM tablename GROUP BY username, value

7
การแก้ไขของคุณจะเลือกแบบสุ่มvalueไม่ใช่แบบที่เกี่ยวข้องกับMAX(date)แถว
Alison R.

มันจะให้วันที่สูงสุด แต่ชื่อผู้ใช้และค่าอาจไม่เหมือนกัน
SKR
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.