2013-01-19 42 views
0

我有一個SQL查詢頭疼,希望有人在互聯網世界可以幫助我!爲日期範圍的登錄用戶選擇MySql行,每天返回小時計數

我有一個mysql表格,其中包含人員登錄和註銷時的值,我需要報告日期範圍內的數據,以提供一天中每個小時登錄的人數。

實例所需的輸出:

2013-1-10 00:00 5 
2013-1-10 01:00 13 
... for every hour of 2013-1-10 ending at 23:00... 
2013-1-10 23:00 23 
...and then the same for every date in the range... 
2013-1-11 00:00 4 
2013-1-11 01:00 6 

我可以按照日期編寫查詢到組的一切,並獲得基於時間登錄每個小時的計數,問題是,如果人們都登錄或長於小時,因爲我按照DATE_FORMAT(time_login,'%H')進行分組,因此它們的計數在第二個小時內不會顯示。

例子查詢是在這裏:

SELECT mac, 
DATE_FORMAT(time_login, '%H') AS hour_logged_in, 
DATE_FORMAT(time_logout, '%H') AS hour_logged_out, 
DATE_FORMAT(time_login, '%Y-%m-%d') as date, 
count(*) as dealcounts 
FROM sessions 
WHERE (
    time_logout IS NOT NULL 
    ) 
AND (
    DATE_FORMAT(time_login, '%Y-%m-%d') BETWEEN 
    DATE_FORMAT('2013-1-1', '%Y-%m-%d') AND DATE_FORMAT('2013-1-18', '%Y-%m-%d')) 
GROUP BY hour_logged_in 

有誰知道我可以做到讓中記錄的一天的每一個小時行的總數?

我有一個sqlfiddle在這裏設置:http://www.sqlfiddle.com/#!2/e13a0/7上述查詢

附加題:空值小時,沒有人登錄:-)

快樂到PayPal一些啤酒&咖啡的人誰可以幫助!!! 謝謝!

+0

如果可能,請嘗試在用戶登錄後每小時記錄一次數據。我認爲它應該解決問題? – haudoing

+0

並且此方法應該對用戶狀態更可靠。 – haudoing

回答

1

我認爲下面的mysql代碼接近你要找的東西?

注意

  1. 這不是優雅的,除非它是在一個重大的黑客可以認爲優雅的方式優雅。首先看可能會導致中度到嚴重的腸道/腦部痙攣,而不是純粹的醜陋。

  2. 這將需要增加的指數和這樣的實際使用,是我的猜測。

  3. 請注意,我將會話表的名稱更改爲sessions_example。如果您執行此操作,我不想破壞會話表。

  4. 最後,在一個名爲hours_expanded的新表中查找結果。

  5. 它需要更多的測試。我現在很疲憊,所以我提交了潛在的 解決方案。我認爲一般的方法是正確的,代碼似乎是正確的,但驗證!

  6. 我通過將登錄次數延長至當前小時(NOW())來處理主動登錄的記錄。如果有人可以在沒有註銷的情況下注銷,並且總是將其註銷到會話表中,則可能需要做一些不同的事情。即爲標記爲活動的記錄提供最大截止時間。

一般方法

基本上,當我讀到這個問題,我知道我的首選方法是將「擴大」到涉及實際的錶行的所有時間。在這種情況下,我發現這樣做對於測試和理解來說更可取,儘管它並不一定是一個好表演者。當我正在弄清楚如何做到這一點時,我發現了一篇博客文章http://datacharmer.blogspot.com/2007/12/data-from-nothing-solution-to-pop-quiz.html(參見Roland Bouman和Kai Voigt),這真的幫助我獲得了我想要的擴展。我習慣在MSSQL中用CTE來做這種事情 - 所以我需要這種幫助來找到一種方法來在MySQL中生成任意數量的行。

這裏所說:

/* Following test data from provided sql fiddle */ 

/*!40101 SET @[email protected]@CHARACTER_SET_CLIENT */; 
/*!40101 SET @[email protected]@CHARACTER_SET_RESULTS */; 
/*!40101 SET @[email protected]@COLLATION_CONNECTION */; 
/*!40101 SET NAMES utf8 */; 

/*changed name of sessions table to sessions_example, so as not to interfere with any prior sessions table*/ 
DROP TABLE IF EXISTS sessions_example; 
CREATE TABLE `sessions_example` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `user_id` int(11) NOT NULL, 
    `network_id` int(11) NOT NULL, 
    `mac` varchar(17) NOT NULL, 
    `time_login` datetime NOT NULL, 
    `time_logout` datetime DEFAULT NULL, 
    `data_in` bigint(20) DEFAULT NULL, 
    `data_out` bigint(20) DEFAULT NULL, 
    `sessionid` varchar(20) NOT NULL, 
    `user_ip` varchar(20) DEFAULT NULL, 
    `external_ip` varchar(20) DEFAULT NULL, 
    `opfield1` varchar(255) DEFAULT NULL, 
    `opfield2` varchar(255) DEFAULT NULL, 
    `opfield3` varchar(255) DEFAULT NULL, 
    `txtfield4` text, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `id` (`id`), 
    KEY `sessionid` (`sessionid`), 
    KEY `time_login` (`time_login`), 
    KEY `time_logout` (`time_logout`), 
    KEY `ap_id` (`network_id`), 
    KEY `user_id` (`user_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1009253406 ; 

INSERT INTO `sessions_example` (`id`, `user_id`, `network_id`, `mac`, `time_login`, `time_logout`, `data_in`, `data_out`, `sessionid`, `user_ip`, `external_ip`, `opfield1`, `opfield2`, `opfield3`, `txtfield4`) VALUES 
(1009250911, 0, 254, 'F8-7B-7A-AC-EA-D6', '2013-01-07 15:45:05', '2013-01-07 16:55:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250912, 0, 254, '00-88-65-D9-B1-8E', '2013-01-07 15:50:04', '2013-01-07 19:11:58', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250914, 0, 254, 'D0-23-DB-17-DC-FA', '2013-01-07 16:20:04', '2013-01-07 17:35:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250915, 0, 254, 'F8-7B-7A-AC-EA-D6', '2013-01-07 16:25:01', '2013-01-07 16:50:04', NULL, NULL, 'active', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250917, 0, 254, 'E0-B9-BA-F0-45-83', '2013-01-07 16:45:05', '2013-01-07 19:11:58', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250919, 0, 254, 'F8-7B-7A-AC-EA-D6', '2013-01-07 16:55:05', '2013-01-07 17:12:30', NULL, NULL, 'active', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250920, 0, 254, 'F8-7B-7A-AC-EA-D6', '2013-01-07 17:12:30', '2013-01-07 17:15:06', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250921, 0, 254, 'F8-7B-7A-AC-EA-D6', '2013-01-07 17:15:06', '2013-01-07 19:11:59', NULL, NULL, 'active', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250926, 0, 254, 'F8-7B-7A-AC-EA-D6', '2013-01-07 19:11:59', '2013-01-07 20:30:04', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250927, 0, 254, '00-1B-9E-75-3E-DA', '2013-01-07 19:11:59', '2013-01-07 20:45:04', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250929, 0, 254, '00-88-65-D9-B1-8E', '2013-01-07 20:35:04', '2013-01-07 22:15:04', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250930, 0, 254, 'F8-7B-7A-AC-EA-D6', '2013-01-07 20:50:04', '2013-01-07 22:25:04', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250931, 0, 254, '00-26-C7-CD-10-9C', '2013-01-07 21:10:04', NULL, NULL, NULL, 'active', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250933, 0, 254, '00-1B-9E-75-3E-DA', '2013-01-07 21:30:04', '2013-01-08 00:20:04', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250934, 0, 254, '00-88-65-D9-B1-8E', '2013-01-07 23:30:04', '2013-01-08 00:05:04', NULL, NULL, 'active', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250935, 0, 254, 'F8-7B-7A-AC-EA-D6', '2013-01-07 23:50:04', '2013-01-08 00:50:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250936, 0, 254, '00-88-65-D9-B1-8E', '2013-01-08 00:05:04', '2013-01-08 02:20:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250940, 0, 254, '00-26-C7-CD-10-9C', '2013-01-08 01:10:05', '2013-01-08 02:10:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250941, 0, 254, '5C-0A-5B-5E-5D-C3', '2013-01-08 01:25:05', '2013-01-08 02:25:06', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250942, 0, 254, '00-88-65-D9-B1-8E', '2013-01-08 03:00:06', '2013-01-08 05:45:07', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009250943, 0, 254, '10-40-F3-6E-D0-FA', '2013-01-08 03:05:07', '2013-01-08 04:45:06', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251100, 0, 254, '28-CF-DA-D9-30-66', '2013-01-08 03:20:06', '2013-01-08 05:10:06', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251101, 0, 253, '00-26-AB-00-E0-1F', '2013-01-08 03:45:03', '2013-01-11 05:15:12', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251102, 0, 253, '8C-A9-82-85-EF-72', '2013-01-08 03:45:03', '2013-01-11 05:25:15', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251103, 0, 253, 'F0-CB-A1-43-A8-4D', '2013-01-08 04:05:03', '2013-01-11 05:15:09', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251104, 0, 254, '00-1B-9E-75-3E-DA', '2013-01-08 04:10:07', '2013-01-08 05:10:07', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251105, 0, 254, 'F8-7B-7A-AC-EA-D6', '2013-01-08 04:20:06', '2013-01-08 06:35:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251106, 0, 253, '90-18-7C-A9-0B-2B', '2013-01-08 04:30:03', '2013-01-11 05:15:14', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251107, 0, 253, '30-F7-C5-C5-9F-61', '2013-01-08 04:30:03', '2013-01-11 05:15:15', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251108, 0, 253, '70-DE-E2-32-F8-66', '2013-01-08 04:30:03', '2013-01-11 09:55:15', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251109, 0, 254, '00-26-C7-CD-10-9C', '2013-01-08 04:55:07', '2013-01-08 06:00:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251110, 0, 254, '5C-0A-5B-A7-54-20', '2013-01-08 05:15:07', '2013-01-08 06:15:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251111, 0, 253, '78-A3-E4-E7-0F-1C', '2013-01-08 05:20:03', '2013-01-11 05:15:06', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251112, 0, 254, '00-1B-9E-75-3E-DA', '2013-01-08 05:35:08', '2013-01-08 06:45:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251113, 0, 253, '00-1F-F3-FA-01-43', '2013-01-08 06:35:04', '2013-01-11 05:15:14', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251114, 0, 253, 'D8-B3-77-7A-C3-F2', '2013-01-08 06:40:03', '2013-01-11 05:15:13', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL), 
(1009251128, 0, 254, '00-88-65-D9-B1-8E', '2013-01-08 16:25:08', '2013-01-08 17:40:05', NULL, NULL, 'passive', NULL, NULL, NULL, NULL, NULL, NULL); 

/*!40101 SET [email protected]_CHARACTER_SET_CLIENT */; 
/*!40101 SET [email protected]_CHARACTER_SET_RESULTS */; 
/*!40101 SET [email protected]_COLLATION_CONNECTION */; 

/* end of provided sql fiddle data */ 

#Here is where we set up a table to hold expansion of all hours in rage as unix timestamp values. 
DROP TABLE IF EXISTS hours_expanded; 
CREATE TABLE hours_expanded 
(
    id int auto_increment, 
    hour_as_unix_ts int, 
    full_datetime datetime NULL, 
    hour_as_time time NULL, 
    count_logged_in int DEFAULT 0, 
    PRIMARY KEY (id) 
); 

#get min/max hours 
SELECT @min_in := floor(unix_timestamp(min(time_login))/3600), 
    @max_out := floor(unix_timestamp(NOW())/3600) 
FROM sessions_example; 

#total number of rows that we will need in the `hours_expanded` table 

SELECT @hours_in_sessions:= @max_out - @min_in + 1; 

#dial up packet length for the string based row-generation solution (see tip of the hat) 
SET @old_max_packet = @@max_allowed_packet; 
SET GLOBAL max_allowed_packet = 1073741824; 

#Next, we will create a long UNION ALL string that will create one row per hour needed. 
#For the following concat based approach major tip of the hat to 
#http://datacharmer.blogspot.com/2007/12/data-from-nothing-solution-to-pop-quiz.html 
#and Roland Bouman who is mentioned there as proposing this solution to generating rows. 
SET @one_row_per_hour = (SELECT concat("INSERT INTO hours_expanded (hour_as_unix_ts) ",(repeat(concat(@s:="select 1 ","union all "),@hours_in_sessions)),"select 1;")); 

#dial packet length back down 
SET GLOBAL max_allowed_packet = @old_max_packet; 

#make that a prepared statement via the string and execute 
prepare q from @one_row_per_hour; 
execute q; 

#set hour_as_unix_ts column to actual UNIX TIMESTAMP hour value and full_datetime to show datetime for that hour 
UPDATE hours_expanded SET hour_as_unix_ts = (SELECT @min_in * 3600 + 3600 *(id - 1)), 
    full_datetime = FROM_UNIXTIME(hour_as_unix_ts), 
    hour_as_time = time(full_datetime); 

#get the counts and update the hours table 
UPDATE hours_expanded h 
INNER JOIN 
(
    SELECT h.hour_as_unix_ts, COUNT(*) as the_count 
    FROM hours_expanded h 
    INNER JOIN sessions_example 
    WHERE h.hour_as_unix_ts >= floor(unix_timestamp(time_login)/3600)*3600 and h.hour_as_unix_ts <= floor(unix_timestamp(ifnull(time_logout,now()))/3600)* 3600 
    GROUP by h.hour_as_unix_ts 
) t on h.hour_as_unix_ts = t.hour_as_unix_ts SET h.count_logged_in = t.the_count; 

#null out any hours that have a count of 0 
UPDATE hours_expanded SET full_datetime = NULL, hour_as_time = NULL WHERE count_logged_in = 0; 

#Final Select with desired output (reformat full_datetime and hour_as_time as desired) 
SELECT full_datetime, hour_as_time, count_logged_in FROM hours_expanded WHERE full_datetime IS NOT NULL 

的字符串連接最後說明/重複

百萬行的時間,它涵蓋了一段大於110年裏,需要的13.5M字符串「SELECT 1 UNION ALL SELECT 1。」等

因此,儘管方法涉及使用大字符串來獲得行擴展,字符串的大小對涉及的問題的規模來說並不壞 - 除非你有幾千年的會話日誌處理: - }。

我確實包含了max_packet_size臨時增加到最大值(1G),以確保不會出現任何問題 - 過度殺傷,具有大的安全裕度。只要我不再需要,我會將max_packet_size減少回系統設置。

+0

這有什麼好運氣? – DWright