Как найти строку с максимальным значением sql

At first glance…

All you need is a GROUP BY clause with the MAX aggregate function:

SELECT id, MAX(rev)
FROM YourTable
GROUP BY id

It’s never that simple, is it?

I just noticed you need the content column as well.

This is a very common question in SQL: find the whole data for the row with some max value in a column per some group identifier. I heard that a lot during my career. Actually, it was one the questions I answered in my current job’s technical interview.

It is, actually, so common that Stack Overflow community has created a single tag just to deal with questions like that: greatest-n-per-group.

Basically, you have two approaches to solve that problem:

Joining with simple group-identifier, max-value-in-group Sub-query

In this approach, you first find the group-identifier, max-value-in-group (already solved above) in a sub-query. Then you join your table to the sub-query with equality on both group-identifier and max-value-in-group:

SELECT a.id, a.rev, a.contents
FROM YourTable a
INNER JOIN (
    SELECT id, MAX(rev) rev
    FROM YourTable
    GROUP BY id
) b ON a.id = b.id AND a.rev = b.rev

Left Joining with self, tweaking join conditions and filters

In this approach, you left join the table with itself. Equality goes in the group-identifier. Then, 2 smart moves:

  1. The second join condition is having left side value less than right value
  2. When you do step 1, the row(s) that actually have the max value will have NULL in the right side (it’s a LEFT JOIN, remember?). Then, we filter the joined result, showing only the rows where the right side is NULL.

So you end up with:

SELECT a.*
FROM YourTable a
LEFT OUTER JOIN YourTable b
    ON a.id = b.id AND a.rev < b.rev
WHERE b.id IS NULL;

Conclusion

Both approaches bring the exact same result.

If you have two rows with max-value-in-group for group-identifier, both rows will be in the result in both approaches.

Both approaches are SQL ANSI compatible, thus, will work with your favorite RDBMS, regardless of its “flavor”.

Both approaches are also performance friendly, however your mileage may vary (RDBMS, DB Structure, Indexes, etc.). So when you pick one approach over the other, benchmark. And make sure you pick the one which make most of sense to you.

I have this SQL query:

SELECT id, COUNT(*) AS price
FROM (SELECT * FROM rt WHERE somecondition) AS st
      JOIN tt
      ON st.id = tt.id
GROUP BY id;

Now, I want to select all rows which have the maximum price of the table. I have tried this, which unfortunately returns no row at all:

SELECT id, COUNT(*) AS price
FROM (SELECT * FROM rt WHERE somecondition) AS st
      JOIN tt
      ON st.id = tt.id
GROUP BY id
HAVING price = MAX(price);

I’m somewhat lost, does anybody have any pointers?

asked Jul 8, 2012 at 8:13

ryyst's user avatar

7

This looks fairly simple to me:

select * from <table> 
where <column name> in(
   SELECT MAX(column name) FROM table
)

ckpepper02's user avatar

ckpepper02

3,2775 gold badges29 silver badges43 bronze badges

answered May 2, 2013 at 13:18

user970780's user avatar

Try this solution:

SELECT a.id, a.price
FROM
(
    SELECT aa.id, COUNT(1) AS price
    FROM rt aa
    INNER JOIN tt bb ON aa.id = bb.id
    WHERE aa.somecondition
    GROUP BY aa.id
) a
INNER JOIN
(
    SELECT MAX(aa.price) AS maxprice
    FROM
    (
        SELECT COUNT(1) AS price
        FROM rt aaa
        INNER JOIN tt bbb ON aaa.id = bbb.id
        WHERE aaa.somecondition
        GROUP BY aaa.id
    ) aa
) b ON a.price = b.maxprice

Edit: While I can’t think of any way to rewrite this so as to not have to write the base-queries redundantly, what you could perhaps do is this:

SELECT GROUP_CONCAT(a.id) AS ids, a.price
FROM
(
    SELECT aa.id, COUNT(1) AS price
    FROM rt aa
    INNER JOIN tt bb ON aa.id = bb.id
    WHERE aa.somecondition
    GROUP BY aa.id
) a
GROUP BY a.price
ORDER BY a.price DESC
LIMIT 1

This produces a comma-separated-list of the ids that share the same maximum value. This is probably not the format you are looking for though, but it is one way to avoid having to write the base-query twice. Just putting that out there.

answered Jul 8, 2012 at 8:37

Zane Bien's user avatar

Zane BienZane Bien

22.6k6 gold badges44 silver badges57 bronze badges

3

try this, put MAX in select, this should be the correct way

SELECT id, COUNT(*) AS price, MAX(price) AS max_price
FROM (SELECT some_table_name FROM rt WHERE somecondition LIMIT 1) AS st
      JOIN thenextTable as tt
      ON st.id = tt.id
GROUP BY id;

answered Jul 8, 2012 at 8:21

albanx's user avatar

albanxalbanx

6,1649 gold badges66 silver badges97 bronze badges

4

Assuming that @Zane’s answer is what you do want, here’s a portable version of his query that also avoids LIMIT/TOP operations. I’m not really familiar with mysql dialects, but I imagine this will work without problem.

SELECT a.id, a.price
FROM (
    SELECT aa.id, COUNT(1) AS price
    FROM rt aa
    INNER JOIN tt bb ON aa.id = bb.id
    WHERE [somecondition]
    GROUP BY aa.id
) a
WHERE
    a.price >= ALL (
        SELECT COUNT(1) AS maxprice
        FROM rt aa
        INNER JOIN tt bb ON aa.id = bb.id
        WHERE [somecondition]
        GROUP BY aa.id
    )

answered Jul 8, 2012 at 8:49

shawnt00's user avatar

shawnt00shawnt00

16k3 gold badges17 silver badges22 bronze badges

HAVING is used to check conditions after the aggregation takes place.

WHERE is used before the aggregation takes place.

SELECT id, COUNT(*) AS price
FROM (SELECT * FROM rt WHERE somecondition) AS st
  JOIN tt
  ON st.id = tt.id
WHERE price = (SELECT MAX(price) FROM ...table)
GROUP BY id

answered Jul 8, 2012 at 8:17

Nikson Kanti Paul's user avatar

6

You asked for an approach that didn’t require the redundancy of stating the inner query more than once. That’s certainly what a cte is good for. These are two other solutions rewritten to use that tactic.

WITH basequery as (
    SELECT aa.id, COUNT(1) AS price
    FROM rt aa INNER JOIN tt bb ON aa.id = bb.id
    WHERE [aa.somecondition]
    GROUP BY aa.id
)
SELECT a.id, a.price
FROM
    basequery as a INNER JOIN
    (SELECT MAX(price) AS maxprice FROM basequery) as b
        ON a.price = b.maxprice
-- or
WITH basequery as (
    SELECT aa.id, COUNT(1) AS price
    FROM rt aa INNER JOIN tt bb ON aa.id = bb.id
    WHERE [aa.somecondition]
    GROUP BY aa.id
)
SELECT a.id, a.price
FROM
    basequery as a
WHERE
    a.price >= ALL (SELECT price FROM basequery)

answered Jul 8, 2012 at 17:26

shawnt00's user avatar

shawnt00shawnt00

16k3 gold badges17 silver badges22 bronze badges

1

Here are three examples that use SQL to find and select the row with the maximum value in a given column.

The examples work in most major RDBMSs, including MySQL, MariaDB, PostgreSQL, SQLite, Oracle, and SQL Server.

Sample Data

We’ll start with the following data:

SELECT * FROM PetShow;

Result:

+---------+-----------+---------+
| PetId   | PetName   | Score   |
|---------+-----------+---------|
| 1       | Wag       | 85      |
| 2       | Scratch   | 3       |
| 3       | Tweet     | 65      |
| 4       | Bark      | 8       |
| 5       | Ruff      | 15      |
| 6       | Woof      | 20      |
| 7       | Punch     | 3       |
+---------+-----------+---------+

Option 1

Here’s an example of selecting the row with the maximum value from the Score column in the above table:

SELECT 
    PetId,
    PetName,
    Score
FROM PetShow 
WHERE Score = ( SELECT MAX(Score) FROM PetShow );

Result:

+---------+-----------+---------+
| PetId   | PetName   | Score   |
|---------+-----------+---------|
| 1       | Wag       | 85      |
+---------+-----------+---------+

We used the MAX() function within a subquery to find the maximum value, and returned the whole row with the outer query.

When there are Multiple Rows with the Max Value

Using this method, if there’s more than one row with the max value, all of them are returned.

Suppose we insert another row into our table with the same score as the existing max score:

INSERT INTO PetShow VALUES (8, 'Purr', 85);
SELECT * FROM PetShow;

Our table now looks like this:

+---------+-----------+---------+
| PetId   | PetName   | Score   |
|---------+-----------+---------|
| 1       | Wag       | 85      |
| 2       | Scratch   | 3       |
| 3       | Tweet     | 65      |
| 4       | Bark      | 8       |
| 5       | Ruff      | 15      |
| 6       | Woof      | 20      |
| 7       | Punch     | 3       |
| 8       | Purr      | 85      |
+---------+-----------+---------+

We can see that both Wag and Purr have got the highest score of 85.

Let’s run the previous query again to return the maximum value from that column:

SELECT 
    PetId,
    PetName,
    Score
FROM PetShow 
WHERE Score = ( SELECT MAX(Score) FROM PetShow );

Result:

+---------+-----------+---------+
| PetId   | PetName   | Score   |
|---------+-----------+---------|
| 1       | Wag       | 85      |
| 8       | Purr      | 85      |
+---------+-----------+---------+

Both rows with the max values are returned as expected.

We can limit the result set to just one row if required. The exact code will depend on the RDBMS being used.

The LIMIT clause can be used with RDBSs such as PostgreSQL, MariaDB, MySQL, and SQLite:

SELECT 
    PetId,
    PetName,
    Score
FROM PetShow 
WHERE Score = ( SELECT MAX(Score) FROM PetShow )
ORDER BY PetId ASC
LIMIT 1;

Result:

+-------+---------+-------+
| PetId | PetName | Score |
+-------+---------+-------+
|     1 | Wag     |    85 |
+-------+---------+-------+

In SQL Server, we can use the TOP clause:

SELECT TOP 1
    PetId,
    PetName,
    Score
FROM PetShow 
WHERE Score = ( SELECT MAX(Score) FROM PetShow )
ORDER BY PetId ASC;

Result:

+-------+---------+-------+
| PetId | PetName | Score |
+-------+---------+-------+
|     1 | Wag     |    85 |
+-------+---------+-------+

And in Oracle Database:

SELECT
    PetId,
    PetName,
    Score
FROM PetShow 
WHERE Score = ( SELECT MAX(Score) FROM PetShow )
ORDER BY PetId ASC
FETCH FIRST 1 ROW ONLY;

Result:

+-------+---------+-------+
| PetId | PetName | Score |
+-------+---------+-------+
|     1 | Wag     |    85 |
+-------+---------+-------+

Option 2

If we only want one row returned, we can actually do away with most of the other code and just get the first row out of the ordered results:

SELECT 
    PetId,
    PetName,
    Score
FROM PetShow 
ORDER BY Score DESC
LIMIT 1;

Result:

+---------+-----------+---------+
| PetId   | PetName   | Score   |
|---------+-----------+---------|
| 1       | Wag       | 85      |
+---------+-----------+---------+

In SQL Server:

SELECT TOP 1
    PetId,
    PetName,
    Score
FROM PetShow 
ORDER BY Score DESC;

Result:

+---------+-----------+---------+
| PetId   | PetName   | Score   |
|---------+-----------+---------|
| 1       | Wag       | 85      |
+---------+-----------+---------+

And in Oracle Database:

SELECT
    PetId,
    PetName,
    Score
FROM PetShow 
ORDER BY Score DESC
FETCH FIRST 1 ROW ONLY;

Result:

+---------+-----------+---------+
| PetId   | PetName   | Score   |
|---------+-----------+---------|
| 1       | Wag       | 85      |
+---------+-----------+---------+

Option 3

Another way to select the row with the maximum value is to join the table on itself, like this:

SELECT 
    p1.PetId, 
    p1.PetName, 
    p1.Score
FROM PetShow p1
LEFT JOIN PetShow p2 ON p1.Score < p2.Score
WHERE p2.PetId IS NULL;

Result:

+---------+-----------+---------+
| PetId   | PetName   | Score   |
|---------+-----------+---------|
| 1       | Wag       | 85      |
| 8       | Purr      | 85      |
+---------+-----------+---------+

As with the earlier example, we can limit the results to one row (or some other number) if required.

Извлечь строку, которая имеет максимальное значение для столбца



стол:

UserId, Value, Date.

Я хочу получить идентификатор пользователя, значение для max (дата) для каждого идентификатора пользователя. То есть значение для каждого идентификатора пользователя, имеющего самую последнюю дату. Есть ли способ сделать это просто в SQL? (Желательно Oracle)

обновление: извиняюсь за любую двусмысленность: мне нужно получить все идентификаторы пользователей. Но для каждого идентификатора пользователя, только та строка, где этот пользователь имеет последнюю дату.


598  


30  

30 ответов:

Это позволит получить все строки, для которых значение столбца my_date равно максимальному значению my_date для этого идентификатора пользователя. Это может получить несколько строк для каждого пользователя, где максимальная дата на несколько строк.

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 находится от первой строки раздела до текущей”

предложение windowing применимо только при наличии предложения order by. Без предложения order by предложение windowing не применяется по умолчанию и не может быть явно указано.

код завод.

я вижу, что многие люди используют подзапросы или другие специфические для поставщика функции для этого, но я часто делаю этот вид запроса без подзапросов следующим образом. Он использует простой, стандартный SQL, поэтому он должен работать в любой марке СУБД.

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.)

In случай если t1."Date" = t2."Date", удвоение появляется. Обычно таблицы имеет

SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid

Я не знаю ваши точные имена столбцов, но это будет что-то вроде этого:

    select userid, value
      from users u1
     where date = (select max(date)
                     from users u2
                    where u1.userid = u2.userid)

не будучи на работе, у меня нет Oracle, но я, кажется, помню, что Oracle позволяет сопоставлять несколько столбцов в предложении IN, что должно по крайней мере избегать параметров, которые используют коррелированный подзапрос, что редко бывает хорошей идеей.

что-то вроде этого, возможно (не могу вспомнить, должен ли список столбцов быть заключен в скобки или нет):

SELECT * 
FROM MyTable
WHERE (User, Date) IN
  ( SELECT User, MAX(Date) FROM MyTable GROUP BY User)

EDIT: просто попробовал это по-настоящему:

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

разве предложение QUALIFY не было бы самым простым и лучшим?

select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

для контекста, на Teradata здесь тест приличного размера этого выполняется в 17s с этой квалифицированной версией и в 23s с “встроенным представлением” /Aldridge solution #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. Вот краткий итог описания.

под капотом аналитические запросы сортируют весь набор данных, а затем обрабатывают его последовательно. По мере его обработки вы разбиваете набор данных по определенным критериям, а затем для каждой строки просматриваете некоторое окно (по умолчанию первое значение в разделе для текущей строки – это значение по умолчанию также является наиболее эффективным) и можете вычислить значения, используя ряд аналитические функции (список которых очень похож на агрегатные функции).

в этом случае вот что делает внутренний запрос. Весь набор данных сортируется по идентификатору пользователя, а затем по дате DESC. Затем он обрабатывает за один проход. Для каждой строки вы возвращаете идентификатор пользователя и первую дату, увиденную для этого идентификатора пользователя (поскольку даты сортируются DESC, это максимальная дата). Это дает вам ответ с дублированными строками. Затем внешние четкие раздавливания дублируются.

Это не a особенно эффектный пример аналитических запросов. Для гораздо большего выигрыша рассмотрите возможность взять таблицу финансовых поступлений и рассчитать для каждого пользователя и квитанции, общую сумму того, что они заплатили. Аналитические запросы решают это эффективно. Другие решения менее эффективны. Именно поэтому они являются частью стандарта SQL 2003. (К сожалению, у 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

на Oracle 12c+, вы можете использовать Top n запросы вместе с аналитической функцией rank чтобы достичь этого очень кратко без подзапросы:

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 на пользователя.

если вы хотите только одну строку с максимальной датой, затем заменить 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; 
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  

просто было написать “живой” пример на работе 🙂

Это один поддерживает несколько значений для идентификатора пользователя на то же самое дата.

столбцы:
Идентификатор Пользователя, Значение, Дата

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
)

вы можете использовать FIRST_VALUE вместо MAX и посмотреть его в плане объяснения. У меня не было времени играть с ним.

конечно, при поиске по огромным таблицам, вероятно, лучше, если вы используете полные подсказки в своем запросе.

select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))

Я думаю что-то вроде этого. (Простите меня за любые синтаксические ошибки; я привык использовать HQL в этот момент!)

EDIT: также неправильно понял вопрос! Исправлен запрос…

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)

Я вещь, которую вы shuold сделать этот вариант к предыдущему запросу:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

(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

ответ здесь только для 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:

дайте книгу (ISBN и название), которая наиболее популярна в 2008 году, т. е. которая чаще всего заимствована в 2008 году.

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);

надеюсь, что это поможет (кто-нибудь).. 🙂

С уважением,
Гус

предполагая, что дата уникальна для данного идентификатора пользователя, вот некоторые 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,
    to_number(substr(max(to_char(date,'yyyymmdd') || to_char(value)), 9)) as value,
    max(date) as date
from 
    users
group by
    userid

причина, по которой этот код работает так хорошо, заключается в том, что ему нужно только сканировать таблицу один раз. Он не требует каких-либо индексов и, самое главное, ему не нужно сортировать таблица, которую делает большинство аналитических функций. Индексы помогут, хотя, если вам нужно отфильтровать результат для одного идентификатора пользователя.

использовать 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 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

ИМХО это работает. ХТ

Я думаю, что это должно работать?

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

первая попытка я неправильно понял вопрос, после верхнего ответа, вот полный пример с правильными результатами:

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):

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
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)

Если вы используете 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:

  1. принято отвечать
  2. мое решение с Postgres
  3. мое решение с Oracle

Если (UserID, Date) является уникальным, т. е. ни одна дата не появляется дважды для одного и того же пользователя, то:

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   UserId,max(Date) over (partition by UserId) value from users;

This SQL tutorial explains how to use the SQL MAX function with syntax and examples.

Description

The SQL MAX function is used to return the maximum value of an expression in a SELECT statement.

Syntax

The syntax for the MAX function in SQL is:

SELECT MAX(aggregate_expression)
FROM tables
[WHERE conditions];

OR the syntax for the MAX function when grouping the results by one or more columns is:

SELECT expression1, expression2, ... expression_n,
       MAX(aggregate_expression)
FROM tables
[WHERE conditions]
GROUP BY expression1, expression2, ... expression_n;

Parameters or Arguments

expression1, expression2, … expression_n
Expressions that are not encapsulated within the MAX function and must be included in the GROUP BY clause at the end of the SQL statement.
aggregate_expression
This is the column or expression from which the maximum value will be returned.
tables
The tables that you wish to retrieve records from. There must be at least one table listed in the FROM clause.
WHERE conditions
Optional. These are conditions that must be met for the records to be selected.

Example – With Single Expression

The simplest way to use the SQL MAX function would be to return a single field that calculates the MAX value.

For example, you might wish to know the maximum salary of all employees.

SELECT MAX(salary) AS "Highest salary"
FROM employees;

In this SQL MAX function example, we’ve aliased the MAX(salary) field as “Highest salary”. As a result, “Highest salary” will display as the field name when the result set is returned.

Example – Using SQL GROUP BY Clause

In some cases, you will be required to use the SQL GROUP BY clause with the SQL MAX function.

For example, you could also use the SQL MAX function to return the name of each department and the maximum salary in the department.

SELECT department, MAX(salary) AS "Highest salary"
FROM employees
GROUP BY department;

Because you have listed one column in your SQL SELECT statement that is not encapsulated in the MAX function, you must use the SQL GROUP BY clause. The department field must, therefore, be listed in the GROUP BY section.

Frequently Asked Questions

Question: I’m trying to pull some info out of a table. To simplify, let’s say the table (report_history) has 4 columns: user_name, report_job_id, report_name, and report_run_date.

Each time a report is run in Oracle, a record is written to this table noting the above info. What I am trying to do is pull from this table when the last time each distinct report was run and who ran it last.

My initial query:

SELECT report_name, MAX(report_run_date)
FROM report_history
GROUP BY report_name

runs fine. However, it does not provide the name of the user who ran the report.

Adding user_name to both the select list and to the group by clause returns multiple lines for each report; the results show the last time each person ran each report in question. (i.e. User1 ran Report 1 on 01-JUL-03, User2 ran Report1 on 01-AUG-03). I don’t want that….I just want to know who ran a particular report the last time it was run.

Any suggestions?

Answer: This is where things get a bit complicated. The SQL SELECT statement below will return the results that you want:

SELECT rh.user_name, rh.report_name, rh.report_run_date
FROM report_history rh,
  (SELECT MAX(report_run_date) AS maxdate, report_name
   FROM report_history
   GROUP BY report_name) maxresults
WHERE rh.report_name = maxresults.report_name
AND rh.report_run_date= maxresults.maxdate;

Let’s take a few moments to explain what we’ve done.

First, we’ve aliased the first instance of the report_history table as rh.

Second, we’ve included two components in our FROM clause. The first is the table called report_history (aliased as rh). The second is a select statement:

(SELECT MAX(report_run_date) AS maxdate, report_name
 FROM report_history
 GROUP BY report_name) maxresults

We’ve aliased the max(report_run_date) as maxdate and we’ve aliased the entire result set as maxresults.

Now, that we’ve created this select statement within our FROM clause, Oracle will let us join these results against our original report_history table. So we’ve joined the report_name and report_run_date fields between the tables called rh and maxresults. This allows us to retrieve the report_name, max(report_run_date) as well as the user_name.


Question: I need help with a SQL query. I have a table in Oracle called orders which has the following fields: order_no, customer, and amount.

I need a query that will return the customer who has ordered the highest total amount.

Answer: The following SQL should return the customer with the highest total amount in the orders table.

SELECT query1.*
FROM (SELECT customer, SUM(orders.amount) AS total_amt
      FROM orders
      GROUP BY orders.customer) query1,

     (SELECT MAX(query2.total_amt) AS highest_amt
      FROM (SELECT customer, SUM(orders.amount) AS total_amt
            FROM orders
            GROUP BY orders.customer) query2) query3
WHERE query1.total_amt = query3.highest_amt;

This SQL SELECT statement will summarize the total orders for each customer and then return the customer with the highest total orders. This syntax is optimized for Oracle and may not work for other database technologies.


Question: I’m trying to retrieve some info from an Oracle database. I’ve got a table named Scoring with two fields – Name and Score. What I want to get is the highest score from the table and the name of the player.

Answer: The following SQL SELECT statement should work:

SELECT Name, Score
FROM Scoring
WHERE Score = (SELECT MAX(Score) FROM Scoring);

Question: I need help in a SQL query. I have a table in Oracle called cust_order which has the following fields: OrderNo, Customer_id, Order_Date, and Amount.

I would like to find the customer_id, who has Highest order count.

I tried with following query.

SELECT MAX(COUNT(*))
FROM CUST_ORDER
GROUP BY CUSTOMER_ID;

This gives me the max Count, But, I can’t get the CUSTOMER_ID. Can you help me please?

Answer: The following SQL SELECT statement should return the customer with the highest order count in the cust_order table.

SELECT query1.*
FROM (SELECT Customer_id, Count(*) AS order_count
      FROM cust_order
      GROUP BY cust_order.Customer_id) query1,

     (SELECT max(query2.order_count) AS highest_count
      FROM (SELECT Customer_id, Count(*) AS order_count
            FROM cust_order
            GROUP BY cust_order.Customer_id) query2) query3
WHERE query1.order_count = query3.highest_count;

This SQL SELECT statement will summarize the total orders for each customer and then return the customer with the highest order count. This syntax is optimized for Oracle and may not work for other database technologies.


Question: I’m trying to get the employee with the maximum salary from department 30, but I need to display the employee’s full information. I’ve tried the following query, but it returns the result from both department 30 and 80:

SELECT *
FROM employees
WHERE salary = (SELECT MAX(salary)
                FROM employees
                WHERE department_id=30);

Answer: The SQL SELECT statement that you have written will first determine the maximum salary for department 30, but then you select all employees that have this salary. In your case, you must have 2 employees (one in department 30 and another in department 80) that have this same salary. You need to make sure that you are refining your query results to only return employees from department 30.

Try using this SQL SELECT statement:

SELECT *
FROM employees
WHERE department_id=30
AND salary = (SELECT MAX(salary)
              FROM employees
              WHERE department_id=30);

This will return the employee information for only the employee in department 30 that has the highest salary.

Добавить комментарий