2010-09-22 50 views
1

我認爲自己對T-SQL非常熟練,而且我通常能夠在不丟失可讀性的情況下優化查詢。簡而言之:我喜歡我的SQL簡潔,描述性,聲明性和優雅。如何將以下代碼轉換爲SQL Server/T-SQL CTE?

雖然下面的代碼工作,我有兩個問題是:

  1. 我使用遊標,我不能動搖的感覺我在我的後腦勺,它本來可以做更多有效地使用CTE。另外遊標不能在視圖中工作,所以我不能在客戶端或從屬SQL中處理結果/範圍。
  2. 該代碼在存儲過程中實現,導致與上面相同的問題。特別是使用LInQ進行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 

由於它可能不是完全明顯,我想在這裏做的是類似於tothe下列準SQL

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

基本上得到最後獨特指(不是唯一的行) ,通常包含重複項,並按降序排列。這絕對是棘手的問題。

這些數據是非常簡單的HTTP日誌。 ID字段只是一個唯一的行標識符,Url是完整的url requesten,Referer是該請求的HTTP引用者。沒有一個值可以爲null,但引用者可以爲空(即'')。 IsSiteLocal只是一個簡單的過濾功能,用於排除源自我自己站點的引用者。

如果有人希望得到一些樣本數據,我可以上傳一個小型的數據庫備份,這樣你就可以得到一些東西。

抽樣數據可以在這裏找到: http://svada.kjonigsen.net/files/IISLogsDBBackup.zip

+0

某些樣本數據可能是? – 2010-09-22 11:42:58

+0

儘管這比我的更重要,但它仍然不完全正確,或者不符合我的要求。下面的代碼樣本使用SQL烘烤成小的測試:DECLARE @Limit INT = 20 ; WITH查閱情況AS ( \t SELECT TOP(@Limit)l.Url,l.Referer,MAX(ID)AS' ID」 \t FROM登錄升 \t其中0 = dbo.IsLocalSite(l.Referer) \t AND l.Referer <> '' \t GROUP BY l.Url,l.Referer \t ORDER BY 3遞減 ) ,JustReferers AS ( \t SELECT DISTINCT Referer \t FROM Referer (*) FROM JustReferers - != @Limit足夠大的@Limit數量。將導致不正確的分頁。 – 2010-09-22 13:04:36

+0

你可以添加一些我們不需要下載的示例數據嗎? – DForck42 2010-09-22 18:12:43

回答

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 
+0

感謝您的回覆,但它有點遺漏。我會更新這個問題以便更具描述性。 – 2010-09-22 12:00:43

0
你想每個URL的MAX(ID)

,Referer的只要dbo.IsLocalSite(@Referer) = 0? 您是否可以通過Url,Referer進行分組以獲得最大(ID)並將其應用於WHERE子句中?

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;