ユーザーごとの最新の日付の行を選択


125

ユーザーのチェックインとチェックアウトの時間のテーブル( "lms_attendance")は次のようになります。

id  user    time    io (enum)
1   9   1370931202  out
2   9   1370931664  out
3   6   1370932128  out
4   12  1370932128  out
5   12  1370933037  in

ユーザーIDごとに最新のレコードのみを出力するこのテーブルのビューを作成しようとしていますが、 "in"または "out"の値を提供しているため、次のようになります。

id  user    time    io
2   9   1370931664  out
3   6   1370932128  out
5   12  1370933037  in

私はこれまでかなり近いですが、ビューはサブクエリを受け付けないことに気づきました。私が得た最も近いクエリは:

select 
    `lms_attendance`.`id` AS `id`,
    `lms_attendance`.`user` AS `user`,
    max(`lms_attendance`.`time`) AS `time`,
    `lms_attendance`.`io` AS `io` 
from `lms_attendance` 
group by 
    `lms_attendance`.`user`, 
    `lms_attendance`.`io`

しかし、私が得るものは:

id  user    time    io
3   6   1370932128  out
1   9   1370931664  out
5   12  1370933037  in
4   12  1370932128  out

これは近いですが、完璧ではありません。最後のgroup byは存在すべきではないことを知っていますが、それがないと、最新の時刻が返されますが、相対IO値は返されません。

何か案は?ありがとう!



マニュアルに戻ります。(相関および非相関)サブクエリがある場合とない場合の両方で、この問題の解決策を提供していることがわかります。
Strawberry

@Barmar、技術的には、私が私の回答で指摘したように、これは700個すべての質問の複製で、グループごとの最大のタグが付いています。
TMS 2013年

@Prodikl、「io(enum)」とは何ですか?
モニカヘドネック2017年

「IO」と呼ばれる列がありました。これは「入力」または「出力」を表す列挙型でした。これは、人々がクラスにチェックインおよびチェックアウトした時間を追跡するために使用されました。
キース

回答:


199

クエリ:

SQLFIDDLEExample

SELECT t1.*
FROM lms_attendance t1
WHERE t1.time = (SELECT MAX(t2.time)
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user)

結果:

| ID | USER |       TIME |  IO |
--------------------------------
|  2 |    9 | 1370931664 | out |
|  3 |    6 | 1370932128 | out |
|  5 |   12 | 1370933037 |  in |

常に機能するソリューション:

SQLFIDDLEExample

SELECT t1.*
FROM lms_attendance t1
WHERE t1.id = (SELECT t2.id
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user            
                 ORDER BY t2.id DESC
                 LIMIT 1)

2
うわー!これは機能しただけでなく、サブクエリが含まれていても、このクエリでビューを作成することができました。以前は、サブクエリを含むビューを作成しようとしても、できませんでした。なぜこれが許可されているのに別のルールが許可されていないのかに関するルールはありますか?
キース

とても変。トンありがとう!多分それは私のサブクエリが私がFROMを選択していた疑似テーブルであったためでした。この例では、WHERE句で使用されています。
キース

4
サブクエリは必要ありません。さらに、このソリューションは、まったく同じ時刻の2つのレコードがある場合は機能しません。これは一般的な問題であるため、毎回ホイールを再発明する必要はありません。代わりに、すでにテストおよび最適化されたソリューションを探してください-@Prodiklは私の答えを参照してください。
TMS 2013年

ああ、洞察力をありがとう!明日オフィスにいるときに新しいコードを試します。
キース

3
@TMSこのソリューションは、クエリが最大のIDを持つレコードを探しているため、レコードの時刻がまったく同じ場合に機能します。これは、表の時間が挿入時間であることを意味しますが、これは適切な仮定ではない場合があります。代わりに、ソリューションはタイムスタンプを比較し、2つのタイムスタンプが同一の場合、最大のIDを持つ行も返します。したがって、ソリューションでは、このテーブルのタイムスタンプが挿入の順序に関連していることも想定しています。これは、両方のクエリの最大の欠陥です。
WebWanderer 2016年

73

これは一般的なグループあたりの最大の問題であるため、ホイールを再発明する必要はありません。非常に素晴らしい解決策が提示されます。

私は最も単純なソリューション(SQLFiddleを参照、Justinの更新を参照)をサブクエリなしで(したがって、ビューで使いやすい)好みます。

SELECT t1.*
FROM lms_attendance AS t1
LEFT OUTER JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND (t1.time < t2.time 
         OR (t1.time = t2.time AND t1.Id < t2.Id))
WHERE t2.user IS NULL

これは、とのトリックのおかげで、同じグループ内に同じ最大値を持つ2つの異なるレコードがある場合にも機能し(t1.time = t2.time AND t1.Id < t2.Id)ます。ここで私がしているのは、同じユーザーの2つのレコードが同じ時間を持っている場合に、1つだけが選択されるようにすることです。基準がId何かであるかどうかは実際には関係ありません。基本的に、一意であることが保証されている基準であれば、ここで機能します。


1
最大使用t1.time < t2.timeと最小使用t1.time > t2.timeは私の最初の直感の反対です。
なし

1
@ J.Money非表示の暗黙の否定があるため:t2からの対応するレコードがなくt1.time < t2.time条件が適用されるt1からのすべてのレコードを選択します:-)
TMS

4
WHERE t2.user IS NULL少し奇妙です。このラインはどのような役割を果たしますか?
tumultous_rooster

1
ジャスティンによって投稿された受け入れられた答えは、より最適かもしれません。受け入れられた回答は、テーブルの主キーの後方インデックススキャンを使用し、その後に制限が続き、その後にテーブルのシーケンススキャンが続きます。したがって、受け入れられた回答は、追加のインデックスで大幅に最適化できます。このクエリは、2つのシーケンススキャンを実行するだけでなく、シーケンススキャンの結果のハッシュと「ハッシュアンチ結合」と他のシーケンススキャンのハッシュを含むため、インデックスによっても最適化できます。どのアプローチが本当に最適であるかについての説明に興味があります。
WebWanderer 2016年

@TMS OR (t1.time = t2.time AND t1.Id < t2.Id))セクションを明確にしていただけませんか?
Oleg Kuts

6

@TMSの回答に基づいていますが、サブクエリが必要ないので気に入っていますが、この'OR'部分を省略しても十分理解でき、理解も簡単です。

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL

null時間のある行に関心がない場合は、WHERE句でそれらをフィルタリングできます。

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL and t1.time IS NOT NULL

OR2つのレコードが同じになる可能性がある場合、部分を省略するのは非常に悪い考えですtime
TMS

パフォーマンス上の理由から、このソリューションは避けます。@OlegKutsが述べたように、これは中規模から大規模のデータセットでは非常に遅くなります。
Peter Meadley 2018年

4

もう解決しましたが、念のため、2つのビューを作成する方法もあります...

CREATE TABLE lms_attendance
(id int, user int, time int, io varchar(3));

CREATE VIEW latest_all AS
SELECT la.user, max(la.time) time
FROM lms_attendance la 
GROUP BY la.user;

CREATE VIEW latest_io AS
SELECT la.* 
FROM lms_attendance la
JOIN latest_all lall 
    ON lall.user = la.user
    AND lall.time = la.time;

INSERT INTO lms_attendance 
VALUES
(1, 9, 1370931202, 'out'),
(2, 9, 1370931664, 'out'),
(3, 6, 1370932128, 'out'),
(4, 12, 1370932128, 'out'),
(5, 12, 1370933037, 'in');

SELECT * FROM latest_io;

ここをクリックして、SQL Fiddleでの動作を確認してください。


1
フォローアップありがとうございます!ええ、もっと簡単な方法がなければ、複数のビューを作成するつもりでした。再度ありがとう
キース

0
select b.* from 

    (select 
        `lms_attendance`.`user` AS `user`,
        max(`lms_attendance`.`time`) AS `time`
    from `lms_attendance` 
    group by 
        `lms_attendance`.`user`) a

join

    (select * 
    from `lms_attendance` ) b

on a.user = b.user
and a.time = b.time

ありがとう。私はサブクエリを使用してそれを行うことができることを知っていますが、これをビューに変えたいと思っていました、そしてビューのサブクエリを許可していません。各サブクエリをビューなどに変換する必要がありますか?
キース

join (select * from lms_attendance ) b= join lms_attendance b
azerafati 2016年


0

MySQL 8.0以降の場合、ウィンドウ関数を使用できます。

クエリ:

DBFiddleExample

SELECT DISTINCT
FIRST_VALUE(ID) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS ID,
FIRST_VALUE(USER) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS USER,
FIRST_VALUE(TIME) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS TIME,
FIRST_VALUE(IO) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS IO
FROM lms_attendance;

結果:

| ID | USER |       TIME |  IO |
--------------------------------
|  2 |    9 | 1370931664 | out |
|  3 |    6 | 1370932128 | out |
|  5 |   12 | 1370933037 |  in |

ジャスティンによって提案されソリューションを使用する上で私が見る利点は、中間ビューやテーブルを必要とせずに、サブクエリからでも、ユーザーごと(またはIDごと、または何でも)の最新データを含む行を選択できることです。

そして、あなたがHANAを実行している場合、それはまた〜7倍速くなります:D


-1

わかりました、これはハックまたはエラーが発生しやすいかもしれませんが、どういうわけかこれも機能しています-

SELECT id, MAX(user) as user, MAX(time) as time, MAX(io) as io FROM lms_attendance GROUP BY id;

-2

このクエリを試してください:

  select id,user, max(time), io 
  FROM lms_attendance group by user;

このSQLFiddleを作ってみてください。あなたはおそらくそれidio非集計列であることに気付くでしょうgroup by。これはで使用できません。
Dewi Morgan

1
idがmax(time)のIDになる保証はなく、グループ内の任意のIDである可能性があります。これは私が解決のためにここに来て、まだ見ている問題です
robisrob

-3

おそらく、ユーザーごとにグループ化して、時間順に並べ替えることができます。以下のようなもの

  SELECT * FROM lms_attendance group by user order by time desc;

-3

これは私のために働きました:

SELECT user, time FROM 
(
    SELECT user, time FROM lms_attendance --where clause
) AS T 
WHERE (SELECT COUNT(0) FROM table WHERE user = T.user AND time > T.time) = 0
ORDER BY user ASC, time DESC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.