ฉันจะเลือกแถวที่มี MAX (ค่าคอลัมน์) DISTINCT ตามคอลัมน์อื่นใน SQL ได้อย่างไร


768

ตารางของฉันคือ:

id  home  datetime     player   resource
---|-----|------------|--------|---------
1  | 10  | 04/03/2009 | john   | 399 
2  | 11  | 04/03/2009 | juliet | 244
5  | 12  | 04/03/2009 | borat  | 555
3  | 10  | 03/03/2009 | john   | 300
4  | 11  | 03/03/2009 | juliet | 200
6  | 12  | 03/03/2009 | borat  | 500
7  | 13  | 24/12/2008 | borat  | 600
8  | 13  | 01/01/2009 | borat  | 700

ฉันจำเป็นต้องเลือกแต่ละที่แตกต่างกันการถือครองมูลค่าสูงสุดของhomedatetime

ผลลัพธ์จะเป็น:

id  home  datetime     player   resource 
---|-----|------------|--------|---------
1  | 10  | 04/03/2009 | john   | 399
2  | 11  | 04/03/2009 | juliet | 244
5  | 12  | 04/03/2009 | borat  | 555
8  | 13  | 01/01/2009 | borat  | 700

ฉันเหนื่อย:

-- 1 ..by the MySQL manual: 

SELECT DISTINCT
  home,
  id,
  datetime AS dt,
  player,
  resource
FROM topten t1
WHERE datetime = (SELECT
  MAX(t2.datetime)
FROM topten t2
GROUP BY home)
GROUP BY datetime
ORDER BY datetime DESC

ใช้งานไม่ได้ ผลชุดมี 130 แถวแม้ว่าฐานข้อมูลถือ 187 homeผลรวมถึงรายการที่ซ้ำกันของบางส่วน

-- 2 ..join

SELECT
  s1.id,
  s1.home,
  s1.datetime,
  s1.player,
  s1.resource
FROM topten s1
JOIN (SELECT
  id,
  MAX(datetime) AS dt
FROM topten
GROUP BY id) AS s2
  ON s1.id = s2.id
ORDER BY datetime 

Nope ให้ระเบียนทั้งหมด

-- 3 ..something exotic: 

ด้วยผลลัพธ์ที่หลากหลาย

คำตอบ:


938

คุณสนิทมาก! สิ่งที่คุณต้องทำคือเลือกทั้งที่บ้านและเวลาสูงสุดของมันจากนั้นเข้าร่วมกลับไปที่toptenตารางในช่องทั้งสอง:

SELECT tt.*
FROM topten tt
INNER JOIN
    (SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home) groupedtt 
ON tt.home = groupedtt.home 
AND tt.datetime = groupedtt.MaxDateTime

5
ทดสอบสำหรับการที่แตกต่างกันถ้าสอง datetime สูงสุดเท่ากันอยู่ในบ้านเดียวกัน (กับผู้เล่นที่แตกต่างกัน)
Maksym Gontar

5
ฉันคิดว่าวิธีคลาสสิกในการทำเช่นนี้คือการเข้าร่วมที่เป็นธรรมชาติ: "SELECT tt. * จาก topten tt NATURAL JOIN (SELECT home, MAX (datetime) AS datetime จาก TOpten GROUP BY home) มากที่สุด" คำถามเดียวกัน แต่อ่านได้มากขึ้น
Parker

31
ถ้ามีสองแถวที่มีค่าฟิลด์ 'home' และ 'datetime' เหมือนกันล่ะ
Kemal Duran

3
@Young ปัญหากับการค้นหาของคุณก็คือว่ามันอาจจะกลับมาid, playerและresourceของแถวที่ไม่ใช่สูงสุดสำหรับเช่นบ้านรับสำหรับบ้าน = 10 คุณอาจได้รับ: 3 | 10 | 04/03/2009 | john | 300 ในคำอื่น ๆ ก็ไม่ได้รับประกันว่าคอลัมน์ทั้งหมดของแถวใน resultset จะเป็น สูงสุด (วันที่และเวลา) สำหรับบ้านที่กำหนด
sactiw

1
@ me1111 ปัญหาเกี่ยวกับการค้นหาของคุณคือมันอาจจะ / อาจไม่กลับแถวที่มี max (datetime) สำหรับบ้านที่กำหนด เหตุผลที่ GROUP BY จะดึงข้อมูลแถวสุ่มสำหรับแต่ละบ้านและ ORDER BY จะเรียงลำดับผลลัพธ์ทั้งหมดตามที่ผลิตโดย GROUP BY
sactiw

87

MySQLโซลูชันที่เร็วที่สุดโดยไม่มีข้อความค้นหาภายในและไม่มีGROUP BY:

SELECT m.*                    -- get the row that contains the max value
FROM topten m                 -- "m" from "max"
    LEFT JOIN topten b        -- "b" from "bigger"
        ON m.home = b.home    -- match "max" row with "bigger" row by `home`
        AND m.datetime < b.datetime           -- want "bigger" than "max"
WHERE b.datetime IS NULL      -- keep only if there is no bigger than max

คำอธิบาย :

เข้าร่วมตารางด้วยตัวเองโดยใช้homeคอลัมน์ การใช้LEFT JOINเพื่อให้แน่ใจว่าแถวทั้งหมดจากตารางmจะปรากฏในชุดผลลัพธ์ ผู้ที่ไม่มีการแข่งขันในตารางbจะมีNULLคอลัมน์bสำหรับ

เงื่อนไขอื่น ๆ บนJOINถามเพื่อให้ตรงกับเฉพาะแถวจากbที่มีค่าที่ใหญ่กว่าในคอลัมน์กว่าแถวจากdatetimem

การใช้ข้อมูลที่โพสต์ในคำถามLEFT JOINจะสร้างคู่นี้

+------------------------------------------+--------------------------------+
|              the row from `m`            |    the matching row from `b`   |
|------------------------------------------|--------------------------------|
| id  home  datetime     player   resource | id    home   datetime      ... |
|----|-----|------------|--------|---------|------|------|------------|-----|
| 1  | 10  | 04/03/2009 | john   | 399     | NULL | NULL | NULL       | ... | *
| 2  | 11  | 04/03/2009 | juliet | 244     | NULL | NULL | NULL       | ... | *
| 5  | 12  | 04/03/2009 | borat  | 555     | NULL | NULL | NULL       | ... | *
| 3  | 10  | 03/03/2009 | john   | 300     | 1    | 10   | 04/03/2009 | ... |
| 4  | 11  | 03/03/2009 | juliet | 200     | 2    | 11   | 04/03/2009 | ... |
| 6  | 12  | 03/03/2009 | borat  | 500     | 5    | 12   | 04/03/2009 | ... |
| 7  | 13  | 24/12/2008 | borat  | 600     | 8    | 13   | 01/01/2009 | ... |
| 8  | 13  | 01/01/2009 | borat  | 700     | NULL | NULL | NULL       | ... | *
+------------------------------------------+--------------------------------+

ในที่สุดWHEREประโยคจะเก็บเฉพาะคู่ที่มีNULLs ในคอลัมน์ของb(พวกเขาถูกทำเครื่องหมายด้วย*ในตารางด้านบน); วิธีนี้เนื่องจากเงื่อนไขที่สองจากJOINประโยคแถวที่เลือกจากมีค่าที่ใหญ่ที่สุดในคอลัมน์mdatetime

อ่านSQL Antipatterns: หลีกเลี่ยงการผิดพลาดของการเขียนโปรแกรมฐานข้อมูลสำหรับเคล็ดลับ SQL อื่น ๆ


ด้วยSQLiteสิ่งแรกจะช้ากว่าเวอร์ชั่นของ La Voie มากเมื่อไม่มีดัชนีในคอลัมน์ที่ตรงกัน (เช่น "home") (ทดสอบกับแถวที่ 24k ส่งผลให้แถว 13k)
โทมัส Tempelmann

10
นี่คือคำตอบที่ดีที่สุดถ้าคุณแสดงแผนการดำเนินการคุณจะเห็นขั้นตอนน้อยลงในแบบสอบถามนี้
TlmaK0

อะไรจะเกิดขึ้นถ้ามี 2 แถวเดียวกันhomeและdatetimeและdatetimeสูงสุดสำหรับเฉพาะที่home?
Istiaque Ahmed

ทั้งสองแถวปรากฏในชุดผลลัพธ์ คำตอบนี้เป็นหลักฐานของแนวคิด ในรหัสจริงของคุณคุณอาจมีเกณฑ์อื่นในการเลือกเพียงหนึ่งในสถานการณ์นี้ (อาจเป็นคนแรกหรือคนสุดท้ายหรือคุณใช้คอลัมน์อื่นในการตัดสินใจ) เพียงเพิ่มเกณฑ์นี้เป็นเงื่อนไขใหม่ในONข้อ เฟ... ON ... AND m.id < b.idเพื่อให้รายการล่าสุด (หนึ่งที่มีที่ยิ่งใหญ่ที่สุดid) เมื่อสองแถวมีค่าเดียวกันในhomeและคอลัมน์และมันก็เป็นสูงสุดdatetime datetime
axiac

ดัชนีใดที่จะปรับให้เหมาะสมที่สุดสำหรับแบบสอบถามเช่นนี้
AjaxLeung

73

ต่อไปนี้เป็นเวอร์ชั่นT-SQL :

-- Test data
DECLARE @TestTable TABLE (id INT, home INT, date DATETIME, 
  player VARCHAR(20), resource INT)
INSERT INTO @TestTable
SELECT 1, 10, '2009-03-04', 'john', 399 UNION
SELECT 2, 11, '2009-03-04', 'juliet', 244 UNION
SELECT 5, 12, '2009-03-04', 'borat', 555 UNION
SELECT 3, 10, '2009-03-03', 'john', 300 UNION
SELECT 4, 11, '2009-03-03', 'juliet', 200 UNION
SELECT 6, 12, '2009-03-03', 'borat', 500 UNION
SELECT 7, 13, '2008-12-24', 'borat', 600 UNION
SELECT 8, 13, '2009-01-01', 'borat', 700

-- Answer
SELECT id, home, date, player, resource 
FROM (SELECT id, home, date, player, resource, 
    RANK() OVER (PARTITION BY home ORDER BY date DESC) N
    FROM @TestTable
)M WHERE N = 1

-- and if you really want only home with max date
SELECT T.id, T.home, T.date, T.player, T.resource 
    FROM @TestTable T
INNER JOIN 
(   SELECT TI.id, TI.home, TI.date, 
        RANK() OVER (PARTITION BY TI.home ORDER BY TI.date) N
    FROM @TestTable TI
    WHERE TI.date IN (SELECT MAX(TM.date) FROM @TestTable TM)
)TJ ON TJ.N = 1 AND T.id = TJ.id

แก้ไข
น่าเสียดายที่ไม่มีฟังก์ชัน RANK () OVER ใน MySQL
แต่ก็สามารถเทิดทูนดูการลอกเลียนแบบการวิเคราะห์ (AKA ตามลำดับ) ฟังก์ชั่นกับ MySQL
ดังนั้นนี่คือเวอร์ชันMySQL :

SELECT id, home, date, player, resource 
FROM TestTable AS t1 
WHERE 
    (SELECT COUNT(*) 
            FROM TestTable AS t2 
            WHERE t2.home = t1.home AND t2.date > t1.date
    ) = 0

ขออภัยเพื่อน # 1064 - คุณมีข้อผิดพลาดในไวยากรณ์ SQL ของคุณ; ตรวจสอบคู่มือที่สอดคล้องกับรุ่นเซิร์ฟเวอร์ MySQL ของคุณเพื่อหาไวยากรณ์ที่ถูกต้องที่จะใช้ใกล้ '() OVER (พาร์ทิชันโดย krd ORDER BY ตามเวลา DESC) N จาก @rapsa) M WHERE N =' ที่บรรทัด 1
Kaptah

2
อาคุณกำลังใช้ MySQL นั่นคือสิ่งที่คุณควรเริ่มต้น! ฉันจะอัปเดตคำตอบในไม่ช้า
Maksym Gontar

@ MaxGontar โซลูชัน mysql ของคุณเริ่มต้นแล้วขอบคุณ เกิดอะไรขึ้นถ้าใน @_TestTable คุณลบแถว # 1>: SELECT 1, 10, '2009-03-04', 'john', 399 นี่คืออะไรถ้าคุณมีแถวเดี่ยวสำหรับค่าบ้านที่กำหนด? ขอบคุณ.
egidiocs

2
BUG: แทนที่ "RANK ()" ด้วย "ROW_NUMBER ()" หากคุณมีเน็คไท (เกิดจากค่าวันที่ซ้ำกัน) คุณจะมีสองระเบียนที่มี "1" สำหรับ N
MikeTeeVee

29

สิ่งนี้จะใช้ได้แม้ว่าคุณจะมีสองแถวขึ้นไปสำหรับแต่ละแถวที่homeมีค่าเท่ากันDATETIME:

SELECT id, home, datetime, player, resource
FROM   (
       SELECT (
              SELECT  id
              FROM    topten ti
              WHERE   ti.home = t1.home
              ORDER BY
                      ti.datetime DESC
              LIMIT 1
              ) lid
       FROM   (
              SELECT  DISTINCT home
              FROM    topten
              ) t1
       ) ro, topten t2
WHERE  t2.id = ro.lid

ฟิลด์ฝาเพิ่มเข้ามาในตารางไม่ดี
Kaptah

1
อันนี้ไม่ได้รันบน PHPMyAdmin รีเฟรชหน้า แต่ไม่มีผลลัพธ์หรือข้อผิดพลาด .. ?
Kaptah

WHERE ti.home = t1.home- คุณช่วยอธิบายไวยากรณ์ได้ไหม?
Istiaque Ahmed

@IstiaqueAhmed: อะไรคือสิ่งที่คุณไม่เข้าใจที่นี่? มันเป็นคิวรีที่สัมพันธ์กันและนิพจน์ที่คุณพูดถึงนั้นเป็นเงื่อนไขที่สัมพันธ์กัน
Quassnoi

@Quassnoi, The selectแบบสอบถามที่มีสายWHERE ti.home = t1.home ไม่จำเป็นต้องเป็นไปตามข้อที่กำหนดFROM t1ดังนั้นมันจะใช้อย่างไร?
Istiaque Ahmed

26

ฉันคิดว่านี่จะให้ผลลัพธ์ที่คุณต้องการ:

SELECT   home, MAX(datetime)
FROM     my_table
GROUP BY home

แต่ถ้าคุณต้องการคอลัมน์อื่นเช่นกันเพียงเข้าร่วมกับตารางเดิม (ตรวจสอบMichael La Voieคำตอบ)

ขอแสดงความนับถืออย่างสูง.


8
เขาต้องการคอลัมน์อื่นด้วย
Quassnoi

4
id, บ้าน, วันที่และเวลา, ผู้เล่น, ทรัพยากร
Quassnoi

17

เนื่องจากคนดูเหมือนจะยังคงทำงานอยู่ในหัวข้อนี้ (ช่วงวันที่ความคิดเห็นจาก 1.5 ปี) ไม่ได้ง่ายกว่านี้มาก

SELECT * FROM (SELECT * FROM topten ORDER BY datetime DESC) tmp GROUP BY home

ไม่จำเป็นต้องมีฟังก์ชั่นการรวม ...

ไชโย


6
ดูเหมือนจะใช้งานไม่ได้ ข้อความแสดงข้อผิดพลาด: คอลัมน์ 'x' ไม่ถูกต้องในรายการที่เลือกเนื่องจากมันไม่ได้อยู่ในฟังก์ชั่นรวมหรือประโยคตามหมวดหมู่
ไก่

สิ่งนี้จะไม่ทำงานใน SQL Server หรือ Oracle แม้ว่ามันจะดูเหมือนว่ามันจะทำงานใน MySQL
ErikE

นี่มันช่างสวยงามจริงๆ! มันทำงานอย่างไร โดยใช้ DESC และคอลัมน์ส่งคืนกลุ่มเริ่มต้น ดังนั้นถ้าฉันเปลี่ยนเป็น datetime ASC จะส่งคืนแถวแรกสุดสำหรับแต่ละบ้านหรือไม่
wayofthefuture

มันยอดเยี่ยมมาก!
Dog Lover

การจัดเรียงตรงนี้ใช้ไม่ได้หากคุณมีคอลัมน์ที่ไม่ได้รวบรวมไว้ (ใน MySQL)
user3562927

11

คุณยังสามารถลองอันนี้และประสิทธิภาพของการสืบค้นตารางขนาดใหญ่จะดีขึ้น มันทำงานเมื่อมีไม่เกินสองบันทึกสำหรับแต่ละบ้านและวันที่ของพวกเขาจะแตกต่างกัน แบบสอบถาม MySQL ทั่วไปที่ดีกว่านั้นมาจาก Michael La Voie ด้านบน

SELECT t1.id, t1.home, t1.date, t1.player, t1.resource
FROM   t_scores_1 t1 
INNER JOIN t_scores_1 t2
   ON t1.home = t2.home
WHERE t1.date > t2.date

หรือในกรณีของ Postgres หรือ dbs ที่ให้ฟังก์ชันการวิเคราะห์ลอง

SELECT t.* FROM 
(SELECT t1.id, t1.home, t1.date, t1.player, t1.resource
  , row_number() over (partition by t1.home order by t1.date desc) rw
 FROM   topten t1 
 INNER JOIN topten t2
   ON t1.home = t2.home
 WHERE t1.date > t2.date 
) t
WHERE t.rw = 1

คำตอบนี้ถูกต้องหรือไม่ ฉันพยายามที่จะใช้มัน แต่มันตะเข็บไม่ให้เลือกบันทึกที่มีวันที่ใหม่ล่าสุดสำหรับ 'บ้าน' แต่เพียงลบบันทึกด้วยวันที่เก่าที่สุด นี่คือตัวอย่าง: SQLfiddle
marcin93w

1
@kidOfDeath - อัปเดตข้อความตอบกลับของฉันพร้อมบริบทและ Postgres
Shiva

ด้วยSQLiteสิ่งแรกจะช้ากว่าเวอร์ชั่นของ La Voie มากเมื่อไม่มีดัชนีในคอลัมน์ที่ตรงกัน (เช่น "home")
Thomas Tempelmann

8

สิ่งนี้ใช้ได้กับ Oracle:

with table_max as(
  select id
       , home
       , datetime
       , player
       , resource
       , max(home) over (partition by home) maxhome
    from table  
)
select id
     , home
     , datetime
     , player
     , resource
  from table_max
 where home = maxhome

1
วิธีนี้จะเลือกวันที่และเวลาสูงสุด เขาขอให้จัดกลุ่มตามบ้านและเลือกวันที่และเวลาสูงสุด ฉันไม่เห็นว่ามันจะทำอย่างไร
n00b

8
SELECT  tt.*
FROM    TestTable tt 
INNER JOIN 
        (
        SELECT  coord, MAX(datetime) AS MaxDateTime 
        FROM    rapsa 
        GROUP BY
                krd 
        ) groupedtt
ON      tt.coord = groupedtt.coord
        AND tt.datetime = groupedtt.MaxDateTime

8

ลองสิ่งนี้สำหรับ SQL Server:

WITH cte AS (
   SELECT home, MAX(year) AS year FROM Table1 GROUP BY home
)
SELECT * FROM Table1 a INNER JOIN cte ON a.home = cte.home AND a.year = cte.year


5

นี่คือเวอร์ชั่น MySQL ที่พิมพ์เพียงหนึ่งรายการที่มีการซ้ำซ้อนสูงสุด (datetime) ในกลุ่ม

คุณสามารถทดสอบได้ที่นี่http://www.sqlfiddle.com/#!2/0a4ae/1

ตัวอย่างข้อมูล

mysql> SELECT * from topten;
+------+------+---------------------+--------+----------+
| id   | home | datetime            | player | resource |
+------+------+---------------------+--------+----------+
|    1 |   10 | 2009-04-03 00:00:00 | john   |      399 |
|    2 |   11 | 2009-04-03 00:00:00 | juliet |      244 |
|    3 |   10 | 2009-03-03 00:00:00 | john   |      300 |
|    4 |   11 | 2009-03-03 00:00:00 | juliet |      200 |
|    5 |   12 | 2009-04-03 00:00:00 | borat  |      555 |
|    6 |   12 | 2009-03-03 00:00:00 | borat  |      500 |
|    7 |   13 | 2008-12-24 00:00:00 | borat  |      600 |
|    8 |   13 | 2009-01-01 00:00:00 | borat  |      700 |
|    9 |   10 | 2009-04-03 00:00:00 | borat  |      700 |
|   10 |   11 | 2009-04-03 00:00:00 | borat  |      700 |
|   12 |   12 | 2009-04-03 00:00:00 | borat  |      700 |
+------+------+---------------------+--------+----------+

เวอร์ชั่น MySQL พร้อมตัวแปรผู้ใช้

SELECT *
FROM (
    SELECT ord.*,
        IF (@prev_home = ord.home, 0, 1) AS is_first_appear,
        @prev_home := ord.home
    FROM (
        SELECT t1.id, t1.home, t1.player, t1.resource
        FROM topten t1
        INNER JOIN (
            SELECT home, MAX(datetime) AS mx_dt
            FROM topten
            GROUP BY home
          ) x ON t1.home = x.home AND t1.datetime = x.mx_dt
        ORDER BY home
    ) ord, (SELECT @prev_home := 0, @seq := 0) init
) y
WHERE is_first_appear = 1;
+------+------+--------+----------+-----------------+------------------------+
| id   | home | player | resource | is_first_appear | @prev_home := ord.home |
+------+------+--------+----------+-----------------+------------------------+
|    9 |   10 | borat  |      700 |               1 |                     10 |
|   10 |   11 | borat  |      700 |               1 |                     11 |
|   12 |   12 | borat  |      700 |               1 |                     12 |
|    8 |   13 | borat  |      700 |               1 |                     13 |
+------+------+--------+----------+-----------------+------------------------+
4 rows in set (0.00 sec)

Outout คำตอบที่ยอมรับแล้ว

SELECT tt.*
FROM topten tt
INNER JOIN
    (
    SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home
) groupedtt ON tt.home = groupedtt.home AND tt.datetime = groupedtt.MaxDateTime
+------+------+---------------------+--------+----------+
| id   | home | datetime            | player | resource |
+------+------+---------------------+--------+----------+
|    1 |   10 | 2009-04-03 00:00:00 | john   |      399 |
|    2 |   11 | 2009-04-03 00:00:00 | juliet |      244 |
|    5 |   12 | 2009-04-03 00:00:00 | borat  |      555 |
|    8 |   13 | 2009-01-01 00:00:00 | borat  |      700 |
|    9 |   10 | 2009-04-03 00:00:00 | borat  |      700 |
|   10 |   11 | 2009-04-03 00:00:00 | borat  |      700 |
|   12 |   12 | 2009-04-03 00:00:00 | borat  |      700 |
+------+------+---------------------+--------+----------+
7 rows in set (0.00 sec)

ถึงแม้ว่าฉันรักคำตอบนี้เนื่องจากมันช่วยฉันได้มากฉันจึงต้องชี้ไปที่ข้อบกพร่องสำคัญอย่างหนึ่งซึ่งขึ้นอยู่กับระบบ mysql ที่ใช้ โดยพื้นฐานแล้ววิธีนี้จะใช้คำสั่งย่อย ORDER BY ในการเลือกย่อย อาจนี้หรืออาจไม่ทำงานในสภาพแวดล้อม mysql ต่างๆ ฉันไม่ได้พยายามมันใน MySQL บริสุทธิ์ แต่สำหรับแน่ใจว่านี้ไม่ได้ทำงานบน MariaDB 10.1 ตามที่อธิบายไว้ที่นี่ stackoverflow.com/questions/26372511/... แต่รหัสเดียวกันมากไม่ตกลงทำงานใน Percona เซิร์ฟเวอร์ เพื่อความแม่นยำคุณอาจหรืออาจไม่ได้ผลลัพธ์เดียวกันขึ้นอยู่กับจำนวนคอลัมน์ t1
Radek

ตัวอย่างสำหรับคำสั่งนี้คือว่าบน MariaDB 10.1 มันทำงานได้เมื่อฉันใช้ 5 คอลัมน์จากตาราง t1 ทันทีที่ฉันเพิ่มคอลัมน์ที่หกชัดว่ายุ่งกับการเรียงลำดับข้อมูล "ธรรมชาติ" ในตารางดั้งเดิมมันหยุดทำงาน เหตุผลก็คือข้อมูลในการเลือกย่อยกลายเป็นไม่ได้รับคำสั่งและทำให้ฉันมีเงื่อนไข "is_first_appear = 1" พบหลายครั้ง รหัสเดียวกันกับข้อมูลเดียวกันทำงานบน Percona ok
Radek

5

อีกวิธีในการ gt แถวล่าสุดต่อกลุ่มโดยใช้คิวรีย่อยซึ่งโดยทั่วไปจะคำนวณอันดับสำหรับแต่ละแถวต่อกลุ่มแล้วกรองแถวล่าสุดของคุณเช่นเดียวกับอันดับ = 1

select a.*
from topten a
where (
  select count(*)
  from topten b
  where a.home = b.home
  and a.`datetime` < b.`datetime`
) +1 = 1

การสาธิต

นี่คือตัวอย่างของ Visualสำหรับอันดับที่ไม่มีในแต่ละแถวเพื่อความเข้าใจที่ดีขึ้น

โดยการอ่านความคิดเห็นจะเกิดอะไรขึ้นถ้ามีสองแถวที่มีค่าฟิลด์ 'home' และ 'datetime' เหมือนกัน?

แบบสอบถามด้านบนจะล้มเหลวและจะส่งกลับมากกว่า 1 แถวสำหรับสถานการณ์ด้านบน เพื่อปกปิดสถานการณ์นี้จะต้องมีเกณฑ์ / พารามิเตอร์ / คอลัมน์อื่นในการตัดสินใจว่าควรใช้แถวใดที่อยู่ในสถานการณ์ด้านบน โดยการดูชุดข้อมูลตัวอย่างฉันถือว่ามีคอลัมน์คีย์หลักidซึ่งควรตั้งค่าให้เพิ่มขึ้นอัตโนมัติ ดังนั้นเราสามารถใช้คอลัมน์นี้เพื่อเลือกแถวล่าสุดโดยการปรับแต่งข้อความค้นหาเดียวกันด้วยความช่วยเหลือของCASEข้อความสั่งเช่น

select a.*
from topten a
where (
  select count(*)
  from topten b
  where a.home = b.home
  and  case 
       when a.`datetime` = b.`datetime`
       then a.id < b.id
       else a.`datetime` < b.`datetime`
       end
) + 1 = 1

การสาธิต

แบบสอบถามด้านบนจะเลือกแถวที่มีรหัสสูงสุดในdatetimeค่าเดียวกัน

การสาธิตด้วยภาพสำหรับอันดับที่ไม่มีสำหรับแต่ละแถว


2

ทำไมไม่ใช้: SELECT home, MAX (datetime) AS MaxDateTime, ผู้เล่น, ทรัพยากรจาก topten GROUP BY home ฉันพลาดอะไรไปรึเปล่า?


4
นั่นจะใช้ได้กับ MySQL เท่านั้นและเฉพาะรุ่นก่อน 5.7 (?) หรือหลัง 5.7 เมื่อ ONLY_FULL_GROUP_BY ถูกปิดใช้งานเนื่องจากเป็นคอลัมน์การเลือกที่ยังไม่ได้รวม / GROUPed (ผู้เล่นทรัพยากร) ซึ่งหมายความว่า MySQL จะให้ค่าที่เลือกแบบสุ่มสำหรับสิ่งเหล่านั้น สองช่องผลลัพธ์ มันจะไม่เป็นปัญหาสำหรับคอลัมน์ผู้เล่นตั้งแต่นั้นมีความสัมพันธ์กับคอลัมน์บ้าน แต่คอลัมน์ทรัพยากรจะไม่สัมพันธ์กับคอลัมน์บ้านหรือวันที่และเวลาและคุณไม่สามารถรับประกันได้ว่าคุณจะได้รับมูลค่าทรัพยากรใด
simpleuser

+1 สำหรับคำอธิบาย แต่ WRT คำถามที่ถามแบบสอบถามนี้จะไม่กลับมาexpectedส่งออกใน MySQL รุ่น 5.6 และbeforeและผมขอสงสัยมันจะประพฤติอย่างอื่นใน MySQL รุ่น 5.7 afterและ
sactiw

@simpleuser, `มันจะไม่เป็นปัญหาสำหรับคอลัมน์ผู้เล่นตั้งแต่ที่มีความสัมพันธ์กับคอลัมน์บ้าน '- คุณสามารถอธิบายเพิ่มเติมหรือไม่
Istiaque Ahmed

@IstiaqueAhmed เมื่อฉันดูอีกครั้งข้อความนั้นไม่ถูกต้อง ฉันคิดว่าผู้เล่นแต่ละคนมีค่าบ้านเหมือนกันเสมอ แต่ฉันเห็นตอนนี้ว่าพวกเขาทำไม่ได้ดังนั้นปัญหาการเลือกแบบสุ่มเดียวกันจะเกิดขึ้นสำหรับคอลัมน์นั้นเช่นกัน
simpleuser

1

ลองสิ่งนี้

select * from mytable a join
(select home, max(datetime) datetime
from mytable
group by home) b
 on a.home = b.home and a.datetime = b.datetime

ขอแสดงความนับถือ K


5
ทดสอบสำหรับการที่แตกต่างกันถ้าสอง datetime สูงสุดเท่ากันอยู่ในบ้านเดียวกัน (กับผู้เล่นที่แตกต่างกัน)
Maksym Gontar

นามแฝงสำหรับมีmax(datetime) datetimeมันจะไม่ทำให้เกิดปัญหาใด ๆ ?
Istiaque Ahmed

datetimeเลือกมากที่สุดได้อย่างไร?
Istiaque Ahmed

1

นี่คือแบบสอบถามที่คุณต้องการ:

 SELECT b.id, a.home,b.[datetime],b.player,a.resource FROM
 (SELECT home,MAX(resource) AS resource FROM tbl_1 GROUP BY home) AS a

 LEFT JOIN

 (SELECT id,home,[datetime],player,resource FROM tbl_1) AS b
 ON  a.resource = b.resource WHERE a.home =b.home;

คุณอธิบายคำตอบของคุณได้ไหม
Istiaque Ahmed

1

@Michae คำตอบที่ได้รับการยอมรับจะทำงานได้ดีในกรณีส่วนใหญ่ แต่ล้มเหลวสำหรับหนึ่งสำหรับด้านล่าง

ในกรณีที่มี 2 HomeID และ Datetime เดียวกันแบบสอบถามจะส่งกลับทั้งสองแถวไม่แตกต่าง HomeID ตามที่จำเป็นสำหรับที่เพิ่ม Distinct ในแบบสอบถามดังต่อไปนี้

SELECT DISTINCT tt.home  , tt.MaxDateTime
FROM topten tt
INNER JOIN
    (SELECT home, MAX(datetime) AS MaxDateTime
    FROM topten
    GROUP BY home) groupedtt 
ON tt.home = groupedtt.home 
AND tt.datetime = groupedtt.MaxDateTime

แสดงให้เห็น - "# 1054 - คอลัมน์ที่ไม่รู้จัก 'tt.MaxDateTime' ใน 'รายการฟิลด์'"
Istiaque Ahmed

@IstiaqueAhmed คุณมี MaxDatetime หรือไม่เช่นชื่อคอลัมน์ใด ๆ เช่นนั้น ..
Manoj Kargeti

ไม่ตารางใน OP ไม่มีคอลัมน์ดังกล่าว
Istiaque Ahmed

ข้อผิดพลาดยังพูดเหมือนกันได้โปรดว่าคุณต้องการทำอะไร? คุณสามารถส่งโครงสร้างตารางและคิวรีของคุณได้ไหม
Manoj Kargeti

1

หวังว่าแบบสอบถามด้านล่างจะให้ผลลัพธ์ที่ต้องการ:

Select id, home,datetime,player,resource, row_number() over (Partition by home ORDER by datetime desc) as rownum from tablename where rownum=1
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.