1

我认为自己对 T-SQL 相当精通,而且我通常能够很好地优化查询而不会失去可读性。简而言之:我喜欢我的 SQL 简短、描述性、声明性和优雅。

虽然以下代码有效,但我有两个问题:

  1. 我正在使用游标,我无法摆脱我脑后的感觉,即使用 CTE 可以更有效地完成它。游标在视图中也不起作用,因此我无法在客户端或依赖 SQL 中操作结果/范围。
  2. 该代码是在存储过程中实现的,这会导致与上述相同的问题。特别是使用 LInQ to SQL 和自动分页。

因此,鉴于以下 SP,是否有人看到任何明显的方法可以使用递归 CTE 将其转换为普通选择?我试过了,失败了,我想我会看看堆栈溢出社区可能会想出什么。

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROC [dbo].[usp_GetLastReferers]
(
 @Limit int = NULL
)
AS
BEGIN
 SET NOCOUNT ON

 CREATE TABLE #Referer
 (
  ID   int,
  Url   nvarchar(500),
  Referer  nvarchar(500)
 )

 DECLARE @ID   int
 DECLARE @Url  nvarchar(500)
 DECLARE @Referer nvarchar(500)
 DECLARE @Count  int
 SET @Count = 0

 DECLARE LogCursor CURSOR FORWARD_ONLY READ_ONLY FOR
 SELECT ID, Url, Referer FROM Log WHERE Referer <> '' ORDER BY ID DESC

 OPEN LogCursor

 FETCH NEXT FROM LogCursor INTO @ID, @Url, @Referer

 WHILE @@FETCH_STATUS = 0 AND (@Count < @Limit OR @Limit IS NULL)
 BEGIN
  DECLARE @Hits int
  SELECT @Hits = COUNT(*)
  FROM #Referer
  WHERE Referer = @Referer

  DECLARE @IsLocal bit
  SELECT @IsLocal = dbo.IsLocalSite(@Referer)

  IF (@Hits = 0 OR @Hits IS NULL) AND @IsLocal = 0
  BEGIN
   INSERT INTO #Referer(ID,Url,Referer) VALUES (@ID,@Url,@Referer)
   SET @Count = @Count + 1
  END

  FETCH NEXT FROM LogCursor INTO @ID, @Url, @Referer  
 END

 CLOSE LogCursor
 DEALLOCATE LogCursor

 SELECT *
 FROM #Referer

 DROP TABLE #Referer

 SET NOCOUNT OFF
END

由于它可能并不完全明显,因此我在这里尝试做的类似于以下准 SQL

SELECT DISTINCT TOP(@Limit) ID, Url, Referer
FROM Log 
ORDER BY ID DESC

基本上是为了得到最后一个唯一的引用(不是唯一的行),它通常包含重复项,并且按降序排列。这绝对是棘手的地方。

数据是非常简单的 HTTP 日志。ID 字段只是一个唯一的行标识符,Url 是完整的 url requesten,Referer 是该请求的 HTTP referer。任何值都不能为空,但referer 可以为空(即'')。IsSiteLocal 只是一个简单的过滤功能,用于排除来自我自己网站的引用者。

如果有人想要一些样本数据,我可以上传一个小的数据库备份,这样你就有一些东西可以玩弄了。

样本数据可以在这里找到: http ://svada.kjonigsen.net/files/IISLogsDBBackup.zip

4

3 回答 3

1

为什么要将其转换为递归 CTE ?它没有理由不能作为普通选择运行。

我下载了测试数据库,它缺少您的 dbo.IsLocalSite 函数,因此对于我的测试,我创建了自己的同名函数并假设它始终返回 0。

此代码在运行时产生与上面给出的存储过程完全相同的结果:

SELECT TOP (@Limit) ID, Url, Referer
FROM (
    SELECT ID, Url, Referer, RANK() OVER (PARTITION BY Referer ORDER BY ID DESC) _RANK_
    FROM LOG
    WHERE dbo.IsLocalSite(Referer) = 0
    AND Referer != ''
) TT
WHERE _RANK_ = 1
ORDER BY ID DESC;
于 2012-11-30T13:45:12.403 回答
0

尝试这个:

 ;with Referers as (
  SELECT 
  row_number() over (order by id desc) rn 
  ,ID, Url, Referer FROM Log 
  WHERE dbo.IsLocalSite(Referer) = 0 
) 
select * from Referers
where rn <= @limit
于 2010-09-22T11:47:59.017 回答
0

只要 dbo.IsLocalSite(@Referer) = 0,您想要每个 Url,Referer 的 max(ID)?您可以按 Url,Referer 分组以获取 max(ID) 并在 WHERE 子句中应用您的函数吗?

于 2010-09-22T21:41:31.180 回答