テーブル:
UserId, Value, Date.
UserId、各 UserId の max(Date) の値を取得したい。つまり、最新の日付を持つ各 UserId の値です。これを単純に SQL で行う方法はありますか? (できればオラクル)
更新:あいまいさをお詫びします: すべての UserId を取得する必要があります。ただし、UserId ごとに、そのユーザーが最新の日付を持つ行のみです。
テーブル:
UserId, Value, Date.
UserId、各 UserId の max(Date) の値を取得したい。つまり、最新の日付を持つ各 UserId の値です。これを単純に SQL で行う方法はありますか? (できればオラクル)
更新:あいまいさをお詫びします: すべての UserId を取得する必要があります。ただし、UserId ごとに、そのユーザーが最新の日付を持つ行のみです。
多くの人がこれを行うためにサブクエリやウィンドウ関数を使用していますが、私はしばしば次のようにサブクエリを使用せずにこの種のクエリを実行します。プレーンな標準 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;
言い換えると、同じ日付以上の日付をt1持つ行が他に存在しない場所から行をフェッチします。UserId
(SQL 予約語なので、識別子 "Date" を区切り文字に入れます。)
の場合t1."Date" = t2."Date"、倍増が表示されます。通常、テーブルには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はそれも返されます。t2の特定の行に一致するものがない場合t1でも、クエリは の行を返し、 のすべての列のプレースホルダとしてt1を使用します。それが、外部結合が一般的にどのように機能するかです。NULLt2
このクエリの秘訣は、同じと大きいt2に一致する必要があるように、結合の一致条件を設計することです。より大きな を持つ行が存在する場合、比較対象の行はそのために最大になることはできません。しかし、一致するものがない場合、つまり in の行よりも大きい の行が存在しない場合、 inの行が指定された の最大の行であることがわかります。 userid datet2datet1dateuseridt2datet1t1dateuserid
そのような場合 (一致がない場合)、の列はt2--NULL結合条件で指定された列であっても。を使用するのはそのためです。これは、指定WHERE t2.UserId IS NULLされた より大きい行が見つからない場合を検索するためです。dateuserid
これにより、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 句がない場合、デフォルトではウィンドウ句は適用されず、明示的に指定することもできません。
コードは機能します。
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
FROM table
GROUP BY userid
正確な列名はわかりませんが、次のようになります。
ユーザーID、値を選択
ユーザー u1 から
ここで、日付 = (最大 (日付) を選択)
ユーザーu2から
ここで、u1.userid = u2.userid)
仕事をしていないので、手元に 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
テストする Oracle はありませんが、最も効率的なソリューションは分析クエリを使用することです。次のようになります。
SELECT DISTINCT
UserId
, MaxValue
FROM (
SELECT UserId
, FIRST (Value) Over (
PARTITION BY UserId
ORDER BY Date DESC
) MaxValue
FROM SomeTable
)
外側のクエリを削除して、内側にdistinctを配置できると思いますが、よくわかりません。その間、私はこれが機能することを知っています。
分析クエリについて知りたい場合は、http://www.orafaq.com/node/55とhttp://www.akadia.com/services/ora_analytic_functions.htmlを読むことをお勧めします。ここに短い要約があります。
内部では、分析クエリによってデータセット全体が並べ替えられ、順番に処理されます。それを処理するとき、特定の基準に従ってデータセットを分割し、次に各行についていくつかのウィンドウを調べ (デフォルトは現在の行への分割の最初の値です - そのデフォルトも最も効率的です)、分析関数の数 (そのリストは集計関数と非常によく似ています)。
この場合、内部クエリが行うことは次のとおりです。データセット全体は、UserId、次に Date DESC でソートされます。次に、それを 1 回のパスで処理します。行ごとに、UserId とその UserId に表示される最初の日付を返します (日付は DESC で並べ替えられているため、これが最大の日付です)。これにより、重複した行で答えが得られます。次に、外側の DISTINCT が重複を押しつぶします。
これは、分析クエリの特に目を見張る例ではありません。はるかに大きな利益を得るには、金銭的な領収書の表を作成し、ユーザーと領収書ごとに計算して、支払った金額の累計を計算することを検討してください。分析クエリはそれを効率的に解決します。他のソリューションは効率的ではありません。これが、2003 SQL 標準の一部である理由です。(残念ながら、Postgres にはまだありません。うーん...)
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
QUALIFY 句が最も単純であり、最適ではないでしょうか。
select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1
コンテキストとして、Teradata では、この QUALIFY バージョンでは 17 秒、「インライン ビュー」/Aldridge ソリューション #1 では 23 秒で、適切なサイズのテストが実行されます。
仕事で「ライブ」の例を書く必要がありました:)
これは、同じ日付の 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 を使用して、説明計画で調べることができます。私はそれで遊ぶ時間がありませんでした。
もちろん、巨大なテーブルを検索する場合は、クエリで FULL ヒントを使用した方がよいでしょう。
私はパーティーにかなり遅れていますが、次のハックは相関サブクエリと分析関数の両方よりも優れていますが、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
このコードがうまく機能する理由は、テーブルを 1 回スキャンするだけで済むからです。インデックスは必要ありません。最も重要なことは、ほとんどの分析関数が行うテーブルの並べ替えを行う必要がないことです。ただし、単一のユーザー ID の結果をフィルタリングする必要がある場合は、インデックスが役立ちます。
前のクエリにこのバリアントを作成する必要があります。
SELECT UserId, Value FROM Users U1 WHERE
Date = ( SELECT MAX(Date) FROM Users where UserId = U1.UserId)
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
みたいなことを思います。(構文の間違いはご容赦ください。この時点では HQL の使用に慣れています!)
編集:また、質問を読み間違えました!クエリを修正しました...
SELECT UserId, Value
FROM Users AS user
WHERE Date = (
SELECT MAX(Date)
FROM Users AS maxtest
WHERE maxtest.UserId = user.UserId
)
select VALUE from TABLE1 where TIME =
(select max(TIME) from TABLE1 where DATE=
(select max(DATE) from TABLE1 where CRITERIA=CRITERIA))
ここでの答えは 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)
そして、説明が必要な、より難しい例です。時間のatmがありません。
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);
これが役立つことを願っています(誰でも).. :)
よろしく、グース
これをテストしたところ、ロギングテーブルで動作するようです
select ColumnNames, max(DateColumn) from log group by ColumnNames order by 1 desc
(T-SQL) まず、すべてのユーザーとその maxdate を取得します。テーブルと結合して、maxdates のユーザーに対応する値を見つけます。
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
特定の UserID に対して Date が一意であると仮定すると、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
これは次のように単純である必要があります。
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)
私はこれがうまくいくと思いますか?
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
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
これにより、重複も処理されます (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 UserId,max(Date) over (partition by UserId) value from users;
あなたの質問がそのページに似ていると思われる場合は、このリンクを確認してください。そのリンクの解決策を提供する次のクエリをお勧めします
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;
そのリンクに関連する正確な結果が得られます
(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;