使用MIN()你不能GROUP?

时间:2013-09-18 13:22:23

标签: sql tsql

我感觉很愚蠢,但是我很难接受一个非常简单的查询。我有类似的东西,每一行都是观看电影的用户:

user_id     date    duration
   1      01-01-01    62m
   1      03-01-01    95m
   2      02-01-01    58m
   2      06-01-01    25m
   2      08-01-01    95m
   3      03-01-01    96m

现在,我想要的是一张桌子,我可以看到每个用户及其duration观看的第一部电影。问题是如果我使用MIN(),那么我必须GROUP user_idduration。但是,如果我GROUPduration,那么我基本上会有相同的表格。我该如何解决这个问题呢?

9 个答案:

答案 0 :(得分:2)

您可以使用排名函数ROW_NUMBER

WITH CTE AS
(
  SELECT rn = ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date ASC),
         user_id, date, duration
  FROM dbo.TableName
)
SELECT user_id, date, duration FROM CTE WHERE rn = 1

ROW_NUMBER的优点是您可以轻松更改逻辑。例如,如果您想要反转逻辑并获得每个用户最后观看的电影的行,您只需将ORDER BY date ASC更改为ORDER BY date DESC

CTE(common-table-expression)的优点是您还可以使用它来删除或更新这些记录。通常用于删除或识别重复项。因此,您可以先选择在执行之前查看将删除/更新的内容。

答案 1 :(得分:2)

尝试此查询。我还没有测试过。

SELECT date, duration FROM tablename n
    WHERE NOT EXISTS(
        SELECT date, user_id FROM tablename g
        WHERE n.user_id = g.user_id AND g.date < n.date
        );

答案 2 :(得分:1)

假设每个用户每个日期只能有一条记录,那就是这样的:

select y.*
from table t
inner join (
  select user_id, min(date) mindate
  from table
  group by user_id
) t1
  on  t.user_id = t1.user_id
      and t.date = t1.mindate

答案 3 :(得分:1)

您可以使用ROW_NUMBER()这是一种排名功能,可根据您要排序的列为每个组生成序号。在这种情况下,如果存在平局,则只选择每个用户的一条记录,但如果要选择所有这些记录,则需要使用DENSE_RANK()而不是ROW_NUMBER()

SELECT  user_id, date, duration
FROM    
        (
            SELECT  user_id, date, duration,
                    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date) rn
              FROM tableName
        ) a
WHERE rn = 1

这也假设列date的数据类型为DATE

答案 4 :(得分:1)

如果您想要每个用户使用第一个watch_date,则此用户在此日期之前应该没有日期:

SELECT *
FROM watched_movies wm
WHERE NOT EXISTS (
  SELECT *
  FROM watched_movies nx
  WHERE nx.user_id = wm.user_id
  AND nx.watch_date < wm.watch_date
  );

注意:我将date列替换为watch_date,因为date是保留字(类型名称)。

答案 5 :(得分:1)

如果您使用的是SQL Server 2005或更高版本,则可以使用窗口函数。

SELECT *
FROM
(
SELECT user_id, date, duration, MIN(date) OVER(PARTITION BY user_id) AS MIN_DATE
FROM MY_TABLE
) AS RESULTS
WHERE date = MIN_DATE

over子句和partion将“分组”user_id并选择每个user_id的最小日期而不消除任何行。然后从表中选择日期等于最小日期,并留下每个user_id的第一个日期。一旦你了解了窗口函数,这是一个常见的技巧。

答案 6 :(得分:0)

这可以为您提供在最早的日期观看的第一部电影的持续时间:

SELECT a.user_id, b.date, a.duration
FROM table a 
INNER JOIN (SELECT user_id,min(date) date FROM table GROUP BY user_id) b ON a.user_id = b.user_id AND a.date = b.date
INNER JOIN (SELECT user_id,date,min(session_id) FROM table GROUP BY user_id, date) c ON b.user_id = c.user_id AND b.date = c.date AND a.session_id = c.session_id

答案 7 :(得分:0)

试试这个:

WITH TABLE1
    AS (SELECT
             '1' AS USER_ID,
             '01-01-01' AS DT,
             62 AS DURATION
        FROM
             DUAL
        UNION ALL
        SELECT
             '1' AS USER_ID,
             '03-01-01' AS DT,
             95 AS DURATION
        FROM
             DUAL
        UNION ALL
        SELECT
             '2' AS USER_ID,
             '02-01-01' AS DT,
             58 AS DURATION
        FROM
             DUAL
        UNION ALL
        SELECT
             '2' AS USER_ID,
             '06-01-01' AS DT,
             25 AS DURATION
        FROM
             DUAL
        UNION ALL
        SELECT
             '2' AS USER_ID,
             '08-01-01' AS DT,
             95 AS DURATION
        FROM
             DUAL
        UNION ALL
        SELECT
             '3' AS USER_ID,
             '03-01-01' AS DT,
             96 AS DURATION
        FROM
             DUAL)
SELECT
      *
FROM
      (SELECT
            USER_ID,
            DT,
            DURATION,
            RANK ( ) OVER (PARTITION BY USER_ID ORDER BY DT ASC) AS ROW_RANK
       FROM
            TABLE1)
WHERE
      ROW_RANK = 1

答案 8 :(得分:0)

使用子查询获取最小日期,然后将其连接回表格以获取所有其他相关列。

SELECT  T2.user_id
        ,T2.date
        ,T2.duration
FROM    YourTable T2
INNER JOIN
        (
        SELECT  T1.user_id
                ,MIN(T1.date) as first_date
        FROM    YourTable T1
        ) SQ
ON      T2.user_id = sq.user_id
AND     T2.date = sq.first_date