2013-03-01 77 views
0

我試圖顯示每天發生的事務日誌。我目前的方法是令人尷尬的低效率,我相信有一個更好的解決方案。這裏是我當前的查詢:更有效的按小時分組行的方法(使用時間戳)

select ReaderMACAddress, 
count(typeid) as 'Total Transactions', 
SUM(CASE WHEN CAST("Timestamp" as TIME) between '05:00:00' and '11:59:59' THEN 1 ELSE 0 END) as 'Morning(5am-12pm)', 
SUM(CASE WHEN CAST("Timestamp" as TIME) between '12:00:00' and '17:59:59' THEN 1 ELSE 0 END) as 'AfternoonActivity(12pm-6pm)', 
SUM(CASE WHEN CAST("Timestamp" as TIME) between '18:00:00' and '23:59:59' THEN 1 ELSE 0 END) as 'EveningActivity(6pm-12am)', 
SUM(CASE WHEN CAST("Timestamp" as TIME) between '00:00:00' and '04:59:59' THEN 1 ELSE 0 END) as 'OtherActivity(12am-5am)' 
from Transactions 
where ReaderMACAddress = '0014f54033f5' 
Group by ReaderMACAddress; 

返回結果:

ReaderMACAddress Total Transactions Morning(5am-12pm) AfternoonActivity(12pm-6pm) EveningActivity(6pm-12am) OtherActivity(12am-5am) 
0014f54033f5    932    269     431       232       0 

(對不起,這裏的任何對齊問題)

此刻,我只想看一個單一的閱讀器,因此我指定(通過where子句)。理想情況下,它會更容易閱讀,如果時間段是在一個單柱和結果,即計數功能均在第二列初見成果,如:

Total Transactions   932 
Morning(5am-12pm)   269 
AfternoonActivity(12pm-6pm) 431 
EveningActivity(6pm-12am) 232 
OtherActivity(12am-5am)  0 

感謝所有幫助:)

+1

[請勿在日期範圍查詢中使用BETWEEN](http://sqlblog.com/blogs/aaron_bertrand/archive/2011/10/19/what-do-between-and-the-devil-具有功能於common.aspx)。在這種特定情況下,時間戳爲11:59:59.325或17:59:59.572的行會發生什麼?可能少量的行將滿足該標準,但準確性是準確的。 – 2013-03-01 22:27:41

回答

6

我會首先考慮一個計算列,但我相信從以前的文章你沒有能力來改變模式。那麼觀點如何?現在

CREATE VIEW dbo.GroupedReaderView 
AS 
    SELECT ReaderMACAddress, 
    Slot = CASE WHEN t >= '05:00' AND t < '12:00' THEN 1 
       WHEN t >= '12:00' AND t < '18:00' THEN 2 
       WHEN t >= '18:00' THEN 3 ELSE 4 END 
    FROM 
    (
    SELECT ReaderMACAddress, t = CONVERT(TIME, [Timestamp]) 
    FROM dbo.Transactions 
) AS x; 

您的每-MAC地址的查詢是非常非常簡單:

SELECT Slot, COUNT(*) 
    FROM dbo.GroupedReaderView 
    WHERE ReaderMACAddress = '00...' 
    GROUP BY Slot; 

這將提供類似的結果:

1 269 
2 431 
3 232 
4  0 

你也可以添加WITH ROLLUP這將提供一個總計Slot列爲NULL

SELECT Slot, COUNT(*) 
    FROM dbo.GroupedReaderView 
    WHERE ReaderMACAddress = '00...' 
    GROUP BY Slot 
    WITH ROLLUP; 

應該產生:

1  269 
2  431 
3  232 
4  0 
NULL 932 

你可以轉動,如果你需要,在你的表示層添加每個插槽等標籤。

你也可以這樣做,它只是讓視圖變得更加冗長,並且在你直接查詢時會拉出很多額外的數據;按字符串分組的效率也稍低。

CREATE VIEW dbo.GroupedReaderView 
AS 
    SELECT ReaderMACAddress, 
    Slot = CASE WHEN t >= '05:00' AND t < '12:00' THEN 
        'Morning(5am-12pm)' 
       WHEN t >= '12:00' AND t < '18:00' THEN 
        'Afternoon(12pm-6pm)' 
       WHEN t >= '18:00' THEN 
        'Evening(6pm-12am)' 
       ELSE 
        'Other(12am-5am)' 
       END 
    FROM 
    (
    SELECT ReaderMACAddress, t = CONVERT(TIME, [Timestamp]) 
    FROM dbo.Transactions 
) AS x; 

這些都是不一定比你有什麼更有效,但他們少重複,更容易對眼睛。 :-)

此外,如果你不想(或不能)創建一個視圖,你可以把它放到子查詢中,例如,

SELECT Slot, COUNT(*) 
    FROM 
    (
    SELECT ReaderMACAddress, 
     Slot = CASE WHEN t >= '05:00' AND t < '12:00' THEN 
        'Morning(5am-12pm)' 
        WHEN t >= '12:00' AND t < '18:00' THEN 
        'Afternoon(12pm-6pm)' 
        WHEN t >= '18:00' THEN 
        'Evening(6pm-12am)' 
        ELSE 
        'Other(12am-5am)' 
        END 
    FROM 
    (
     SELECT ReaderMACAddress, t = CONVERT(TIME, [Timestamp]) 
     FROM dbo.Transactions 
    ) AS x 
) AS y 
    WHERE ReaderMACAddress = '00...' 
    GROUP BY Slot 
    WITH ROLLUP; 

只是一個替代方案,還可以讓你們之間使用,可能還要少一些冗長:

SELECT Slot, COUNT(*) 
    FROM 
    (
    SELECT ReaderMACAddress, 
     Slot = CASE WHEN h BETWEEN 5 AND 11 THEN 'Morning(5am-12pm)' 
        WHEN h BETWEEN 12 AND 17 THEN 'Afternoon(12pm-6pm)' 
        WHEN h >= 18 THEN 'Evening(6pm-12am)' 
        ELSE 'Other(12am-5am)' 
        END 
    FROM 
    (
     SELECT ReaderMACAddress, h = DATEPART(HOUR, [Timestamp]) 
     FROM dbo.Transactions 
    ) AS x 
) AS y 
    WHERE ReaderMACAddress = '00...' 
    GROUP BY Slot 
    WITH ROLLUP; 

UPDATE

始終包括每個插槽即使有沒有該槽的結果:

;WITH slots(s, label, h1, h2) AS 
(
    SELECT   1, 'Morning(5am-12pm)' , 5, 11 
    UNION ALL SELECT 2, 'Afternoon(12pm-6pm)' , 12, 17 
    UNION ALL SELECT 3, 'Evening(6pm-12am)' , 18, 23 
    UNION ALL SELECT 4, 'Other(12am-5am)'  , 0, 4 
) 
SELECT s.label, c = COALESCE(COUNT(y.ReaderMACAddress), 0) 
    FROM slots AS s 
    LEFT OUTER JOIN 
    (
    SELECT ReaderMACAddress, h = DATEPART(HOUR, [Timestamp]) 
     FROM dbo.Transactions 
     WHERE ReaderMACAddress = '00...' 
) AS y 
ON y.h BETWEEN s.h1 AND s.h2 
GROUP BY s.label 
WITH ROLLUP; 

所有這些情況下的關鍵是簡化和不重複自己。即使SQL Server只執行一次,爲什麼要將時間轉換爲4次?

+0

非常感謝。我知道這是處理這個問題的一種不好的方式,你的疑問和解釋會幫助我更好地理解我的方式錯誤。 – Grant 2013-03-04 19:38:07

+0

關於最後一個選項(與之間)的一個問題:由於Case函數的性質,當查詢的ReaderMACAddress在插槽中沒有事務時,不顯示事務的插槽。有什麼方法可以在沒有任何事件發生時顯示0,類似於方法1的結果(創建視圖)? – Grant 2013-03-04 19:44:32

+0

答覆更新... – 2013-03-04 20:42:33