5

我有一个使用两个标识列的表,我们称它们为 id 和 userid。ID 在每条记录中都是唯一的,而 userid 对用户来说是唯一的,但在许多记录中都是唯一的。

我需要做的是通过用户 ID 获取用户的记录,然后将该记录加入我们为用户拥有的第一条记录。查询逻辑如下:

SELECT v1.id, MIN(v2.id) AS entryid, v1.userid
FROM views v1
INNER JOIN views v2
  ON v1.userid = v2.userid

我希望我不必将表加入处理 min() 代码片段的子查询,因为这似乎很慢。

4

3 回答 3

13

我猜(尚不完全清楚)您想为每个用户找到具有 minimum 的表的行id,因此每个用户一行。

在这种情况下,您可以使用子查询(派生表)并将其连接到表中:

SELECT v.*
FROM views AS v
  JOIN
    ( SELECT userid, MIN(id) AS entryid
      FROM views
      GROUP BY userid
    ) AS vm
    ON  vm.userid = v.userid 
    AND vm.entryid = v.id ;

如果您喜欢,也可以使用Common Table Expression (CTE)编写上述内容:

; WITH vm AS
    ( SELECT userid, MIN(id) AS entryid
      FROM views
      GROUP BY userid
    )
  SELECT v.*
  FROM views AS v
    JOIN vm
      ON  vm.userid = v.userid 
      AND vm.entryid = v.id ;

两者都将在索引上非常有效(userid, id)

使用 SQL-Server,您可以使用ROW_NUMBER()窗口函数编写此代码:

; WITH viewsRN AS
    ( SELECT *
           , ROW_NUMBER() OVER (PARTITION BY userid ORDER BY id) AS rn
      FROM views
    ) 
  SELECT *                      --- skipping the "rn" column
  FROM viewsRN
  WHERE rn = 1 ;
于 2012-12-03T17:39:08.100 回答
1

好吧,要将MIN函数与非聚合列一起使用,您必须对语句进行分组。这可以通过您的查询来实现......(根据附加信息进行编辑)

SELECT MIN(v2.id) AS entryid, v1.id, v1.userid
FROM views v1
INNER JOIN views v2
  ON v1.userid = v2.userid      
GROUP BY v1.id, v1.userid

...但是,如果这只是一个简单的示例,并且您希望通过此查询提取更多数据,那么它很快就会成为一个不可行的解决方案。

您似乎想要的是此视图中所有用户数据的列表,每行上都有一个链接,指向同一用户存在的“第一条”记录。上面的查询会得到你想要的,但是有更简单的方法来确定每个用户的第一条记录:

SELECT v1.id, v1.userid
FROM views v1
ORDER BY v1.userid, v1.id

每个唯一用户的第一条记录是您的“入口点”。我想我理解您为什么要按照您指定的方式进行操作,并且我给出的第一个查询将具有合理的性能,但是您必须考虑不必使用 order by 子句来获得正确答案是否值得.

于 2012-12-03T17:32:22.200 回答
-2

edit-1:正如评论中所指出的,该解决方案还使用子查询。但是,它不使用聚合函数,这(取决于数据库)可能会对性能产生巨大影响。


无需子查询即可实现(见下文)。显然,一个索引对views.userid性能具有巨大的价值。

SELECT      v1.*
FROM        views v1
WHERE       v1.id = (
        SELECT  TOP 1 v2.id
        FROM    views v2
        WHERE   v2.userid = v1.userid
        ORDER BY v2.id ASC
    )
于 2012-12-03T17:39:37.140 回答