表:
UserId, Value, Date.
UserId、各UserIdのmax(Date)の値を取得します。つまり、最新の日付を持つ各UserIdの値です。単純にSQLでこれを行う方法はありますか? (できればOracle)
更新: /あいまいさをお詫び申し上げます:私はすべてのユーザーIDを取得する必要があります。しかし、各UserIdについては、そのユーザーが最新の日付を持つ行のみです。
これにより、my_date列の値がそのユーザーIDのmy_dateの最大値に等しいすべての行が取得されます。これは、最大日付が複数の行にあるユーザーIDに対して複数の行を検索する場合があります。
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
「解析機能が揺れる」
編集:最初のコメントに関して...
「分析クエリと自己結合を使用すると、分析クエリの目的が無効になる」
このコードには自己結合はありません。代わりに、分析関数を含むインラインビューの結果に置かれた述語があります。これは非常に異なる問題で、完全に標準的な方法です。
「Oracleのデフォルトウィンドウは、パーティションの最初の行から現在の行までです。」
ウィンドウ句はorder by句がある場合にのみ適用できます。 order by句がない場合、デフォルトではwindowing句は適用されず、明示的に指定することもできません。
コードは動作します。
私は多くの人がこれを行うためにサブクエリあるいは他のベンダー特有の機能を使うのを見ますが、私はしばしば次のようにサブクエリなしでこの種のクエリをする。標準の標準SQLを使用しているため、どのブランドの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;
言い換えると、同じUserId
とそれ以上のDateを持つ行が他に存在しないt1
から行を取得します。
(SQL予約語なので、識別子 "Date"を区切り文字に入れています。)
t1."Date" = t2."Date"
の場合は、2倍になります。通常テーブルはauto_inc(seq)
キーを持ちます。 id
。倍増を避けるためには、次のようにします。
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からの再コメント:
これがより詳細な説明です。
外部結合はt1
をt2
と結合しようとします。デフォルトでは、t1
のすべての結果が返され、t2
に一致がある場合は、{ifも返されます。指定されたt2
の行とt1
が一致しない場合でも、クエリはt1
の行を返し、すべてのt2
の列のプレースホルダとしてNULL
を使用します。それがまさにアウタージョインが一般的に機能する方法です。
このクエリのトリックは、t2
がsameuserid
、およびgreaterdate
に一致する必要があるように、結合の一致条件を設計することです。より大きなdate
を持つ行がt2
に存在する場合、cannotと比較されるt1
の行がそのdate
の最大のuserid
になります。しかし、一致がない場合、つまりt2
の行よりもdate
が大きい行がt1
に存在しない場合、t1
の行は、指定されたdate
に対して最大のuserid
の行であることがわかります。
このような場合(一致しない場合)、t2
の列は、結合条件で指定された列でもNULL
になります。そのため、特定のdate
に対して、より大きいuserid
を持つ行が見つからない場合を検索しているので、WHERE t2.UserId IS NULL
を使用するのはそのためです。
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
FROM table
GROUP BY userid
正確なカラム名はわかりませんが、これは次のようになります。
useridを選択し、値 をユーザーu1 に設定します。date =(max(date) をユーザーu2から選択します。 ここで、u1.userid = u2 。ユーザーID)
仕事をしていないので、Oracleに手渡す必要はありませんが、OracleではIN句で複数の列を一致させることができるため、少なくとも相関副問い合わせを使用するオプションは避けられるべきです。アイディア。
このようなもの、おそらく(列リストを括弧でくくるべきかどうかを思い出せない):
SELECT *
FROM MyTable
WHERE (User, Date) IN
( SELECT User, MAX(Date) FROM MyTable GROUP BY User)
編集:ちょうど本当のためにそれを試してみました:
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
他の場所で言及されている新しく目立つもののいくつかはもっとパフォーマンスがあるかもしれませんが、それでうまくいきます。
私はあなたがOracleを要求したことを知っています、しかしSQL 2005では、我々は今これを使います:
-- 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
テストするためのオラクルはありませんが、最も効率的な解決策は、分析照会を使用することです。これは次のようになります。
SELECT DISTINCT
UserId
, MaxValue
FROM (
SELECT UserId
, FIRST (Value) Over (
PARTITION BY UserId
ORDER BY Date DESC
) MaxValue
FROM SomeTable
)
私はあなたが外側のクエリを取り除き、内側に明確に置くことができると思いますが、私はよくわかりません。それまでの間、私はこれがうまくいくことを知っています。
分析クエリについて知りたい場合は、 http://www.orafaq.com/node/55 を読むことをお勧めします。 http://www.akadia.com/services/ora_analytic_functions.html。これが短い要約です。
フードの下では、分析クエリはデータセット全体をソートしてから、順次処理します。それを処理するとき、あなたは特定の基準に従ってデータセットを分割し、そして各行について何らかのウィンドウを見て(デフォルトは現在の行へのパーティションの最初の値です。そのデフォルトも最も効率的です)分析関数の数(そのリストは集約関数と非常によく似ています)。
この場合、これは内部クエリの動作です。データセット全体が、UserId、Date DESCの順にソートされています。それからそれはワンパスでそれを処理します。各行について、UserIdとそのUserIdに最初に表示されたDateを返します(日付はDESCでソートされているため、これが最大日付です)。これにより、重複した行であなたの答えが得られます。それから、外側のDISTINCTは重複を潰します。
これは分析クエリの特に壮観な例ではありません。もっと大きな勝利のためには、金融の領収書の表を取って、各ユーザーと領収書、彼らが支払ったものの積算合計を計算することを検討してください。分析クエリはそれを効率的に解決します。他の解決策はそれほど効率的ではありません。これらが2003標準SQLの一部である理由です。 (残念ながらPostgresはまだそれらを持っていません。Grrr...)
QUALIFY句は最も単純で最良のものではないでしょうか。
select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1
コンテキストとしては、ここTeradataでは、このQUALIFYバージョンでは17秒、「インラインビュー」/ Aldridgeソリューションでは1秒の適切なサイズのテストが実行されます。
PostgreSQL 8.4以降では、これを使うことができます。
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
Oracle 12c+
では、分析関数rank
と一緒に Top n クエリを使用して、これを非常に簡潔に行うことができます なし サブクエリ:
select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;
上記は、ユーザーごとにmax my_dateを持つすべての行を返します。
最大日付の1行のみが必要な場合は、rank
を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;
ROW_NUMBER()
を使用して、各Date
に対して降順のUserId
に一意のランク付けを割り当て、次に各UserId
の最初の行にフィルタをかけます(つまり、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 VALUE from TABLE1 where TIME =
(select max(TIME) from TABLE1 where DATE=
(select max(DATE) from TABLE1 where CRITERIA=CRITERIA))
仕事で「生きた」例を書く必要がありました:)
これは 同じ 日付のUserIdの複数の値をサポートします。
列:UserId、Value、Date
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の代わりにFIRST_VALUEを使用して、EXPLAIN PLANで検索することができます。遊ぶ時間がありませんでした。
もちろん、巨大なテーブルを検索する場合は、クエリにFULLヒントを使用した方がおそらく良いでしょう。
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
ここでの答えはOracleだけです。これはすべてのSQLでもう少し洗練された答えです。
誰が最高の全体的な宿題の結果(宿題ポイントの最大合計)を持っていますか?
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)
そしてもう少し説明が必要な、もっと難しい例です。
2008年に最も人気のある、つまり2008年に最も頻繁に借りられている本(ISBNとタイトル)を渡します。
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);
これが(だれでも)助けになることを願っています.. :)
よろしく、フース
私はこのようなものだと思います。 (構文上の間違いはご容赦ください。この時点ではHQLを使用していました。)
編集:また質問を誤解!クエリを修正しました...
SELECT UserId, Value
FROM Users AS user
WHERE Date = (
SELECT MAX(Date)
FROM Users AS maxtest
WHERE maxtest.UserId = user.UserId
)
(T - SQL)最初にすべてのユーザーとその最大日付を取得します。テーブルと結合して、最大日付のユーザーに対応する値を見つけます。
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
結果:
userid value date
----------- ----------- --------------------------
2 3 2003-01-01 00:00:00.000
1 2 2002-01-01 00:00:00.000
私はあなたが前のクエリにこの変形を加えるべきだと思います:
SELECT UserId, Value FROM Users U1 WHERE
Date = ( SELECT MAX(Date) FROM Users where UserId = U1.UserId)
Dateが与えられたUserIDに対してユニークであると仮定すると、ここにいくつかの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
私はパーティーにはかなり遅れていますが、以下のハックは相関サブクエリと分析機能の両方よりも優れていますが、1つ制限があります。そのため、日付、数字、その他の文字列に対して機能します。コードは見栄えがよくありませんが、実行プロファイルは素晴らしいです。
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
このコードがうまく機能するのは、テーブルを一度だけスキャンすればよいからです。インデックスを必要とせず、最も重要なのは、ほとんどの分析機能が行うようにテーブルをソートする必要がないということです。ただし、単一のユーザーIDに対して結果をフィルター処理する必要がある場合は、索引が役立ちます。
Postgresを使っているのなら、array_agg
を使えます
SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid
私はOracleに慣れていません。これは私が思い付いたものです
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
どちらのクエリも、受け入れられた回答と同じ結果を返します。 SQLFiddlesを参照してください。
これは以下のように単純なはずです。
SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)
最初に答えを答えて、私が質問を誤解してみてください、これは正しい結果を伴う完全な例です:
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)
これもまた、重複の面倒を見ます(user_idごとに1行を返します)。
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
これをテストしただけで、ログテーブルで動作するようです
select ColumnNames, max(DateColumn) from log group by ColumnNames order by 1 desc
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
私見これは動作します。 HTH
私はこれでうまくいくと思いますか?
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
check このリンク あなたの質問がそのページに似ているようであれば、私はあなたにそのリンクのための解決策を与えるであろう以下の質問を提案するでしょう
select distinct sno,item_name,max(start_date) over(partition by sno),max(end_date) over(partition by sno),max(creation_date) over(partition by sno), max(last_modified_date) over(partition by sno) from uniq_select_records order by sno,item_name asc;
そのリンクに関連する正確な結果が得られます
SELECT a.userid,a.values1,b.mm
FROM table_name a,(SELECT userid,Max(date1)AS mm FROM table_name GROUP BY userid) b
WHERE a.userid=b.userid AND a.DATE1=b.mm;
(UserID、Date)が一意である場合、すなわち同じユーザに対して2回は日付が表示されない場合:
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 T.UserId,T.dt from (select UserId,max(dt)
over (partition by UserId) as dt from t_users)T where T.dt=dt;
UserIdの重複値に関係なく、これにより結果が取得されます。あなたのUserIdがユニークなら、それはもっと簡単になります。
select UserId,max(dt) from t_users group by UserId;
select UserId,max(Date) over (partition by UserId) value from users;
パーティションKEEP、DENSE_RANKの概念がないMySQL用のソリューション。
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
参照: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html