2011-08-17 26 views
8

मेरे पास एक डेटाबेस तालिका है जो शहरों में प्रत्येक उपयोगकर्ता के चेकइन रखती है। मुझे यह जानने की जरूरत है कि उपयोगकर्ता कितने दिन शहर में रहा है, और फिर, किसी उपयोगकर्ता ने कितने विज़िट किए हैं (एक यात्रा में शहर में लगातार दिन व्यतीत होते हैं)।MySQL: समूह लगातार समूह और गिनती समूह

तो, पर विचार मैं निम्न तालिका है (सरलीकृत, केवल DATETIME रों युक्त - एक ही उपयोगकर्ता और शहर):

 datetime 
------------------- 
2011-06-30 12:11:46 
2011-07-01 13:16:34 
2011-07-01 15:22:45 
2011-07-01 22:35:00 
2011-07-02 13:45:12 
2011-08-01 00:11:45 
2011-08-05 17:14:34 
2011-08-05 18:11:46 
2011-08-06 20:22:12 

इस उपयोगकर्ता इस शहर में किया गया है दिनों की संख्या होगी (30.06, 01.07, 02.07, 01.08, ०५.०८, ०६.०८)।

मैं इस SELECT COUNT(id) FROM table GROUP BY DATE(datetime)

का उपयोग कर फिर दौरा इस उपयोगकर्ता इस शहर में बना दिया है की संख्या के लिए कर रही है, के बारे में सोचा, क्वेरी लौटना चाहिए (30.06-02.07, 01.08, ०५.०८ -06.08)।

समस्या यह है कि मुझे नहीं पता कि मैं इस प्रश्न को कैसे तैयार करूं।

किसी भी मदद की अत्यधिक सराहना की जाएगी!

उत्तर

10

आप चेकइन जहां कोई एक दिन पहले चेक इन नहीं था पता लगाकर प्रत्येक यात्रा के पहले दिन मिल सकता है।

select count(distinct date(start_of_visit.datetime)) 
from checkin start_of_visit 
left join checkin previous_day 
    on start_of_visit.user = previous_day.user 
    and start_of_visit.city = previous_day.city 
    and date(start_of_visit.datetime) - interval 1 day = date(previous_day.datetime) 
where previous_day.id is null 

इस क्वेरी के कई महत्वपूर्ण भाग हैं।

सबसे पहले, प्रत्येक चेकइन पिछले दिन से किसी भी चेकइन में शामिल हो जाता है। लेकिन चूंकि यह बाहरी जुड़ाव है, अगर पिछले दिन कोई चेकइन नहीं था तो जुड़ने के दाहिने तरफ NULL परिणाम होंगे। WHERE फ़िल्टरिंग में शामिल होने के बाद होता है, इसलिए यह केवल उन चेकिन को बाईं तरफ रखता है जहां दाएं तरफ से कोई नहीं होता है। LEFT OUTER JOIN/WHERE IS NULL वास्तव में यह जानने के लिए आसान है कि नहीं हैं।

फिर यह अलग चेकइन तिथियों की गणना करता है यह सुनिश्चित करने के लिए कि यह विज़िट के पहले दिन कई बार चेक किया गया है, यह दोबारा गिनती नहीं करता है। (मैंने वास्तव में संपादन पर उस भाग को जोड़ा, जब मैंने संभावित त्रुटि देखी।)

संपादित करें: मैंने पहले प्रश्न के लिए अपनी प्रस्तावित क्वेरी को फिर से पढ़ा है। आपकी क्वेरी आपको तारीखों की गिनती के बजाय किसी दिए गए दिनांक पर चेकइन की संख्या प्राप्त करेगी। मुझे लगता है कि आप इसके बजाय कुछ चाहते हैं:

select count(distinct date(datetime)) 
from checkin 
where user='some user' and city='some city' 
+0

की Devart डेटा सेट प्रति अंतिम सही परिणाम के लिए उन्हें गिना जाता है ... मैं पूरी तरह से अपने सुझाव को समझने के लिए प्रतीत नहीं कर सकते हैं ... क्या यह संभव है कुछ और विवरण देने के लिए? धन्यवाद! दूसरे के बारे में, मेरी क्वेरी सही है, बशर्ते कि आप मेरे प्रश्न में उल्लिखित उपयोगकर्ता और शहर की गणना न करें। – linkyndy

+0

क्षमा करें, मुझे लगता है कि "उपयोगकर्ता में कितने दिन एक शहर में हैं" के परिणाम का परिणाम दिखाना चाहिए (user_id, count_of_days)। – Simon

+0

विवरण के लिए धन्यवाद। मेरी वास्तविक डेटाबेस तालिका में फिट करने के लिए कई समायोजनों के साथ, आपकी क्वेरी एक आकर्षण की तरह काम करती है। फिर से धन्यवाद! – linkyndy

0
पहली बार एक उप कार्य के लिए

:

select count(*) 
from (
select TO_DAYS(p.d) 
from p 
group by TO_DAYS(p.d) 
) t 
0

मुझे लगता है कि आपको डेटाबेस संरचना को बदलने पर विचार करना चाहिए। आप अपनी चेकइन तालिका में टेबल विज़िट और visit_id जोड़ सकते हैं। प्रत्येक बार जब आप नया चेकइन पंजीकृत करना चाहते हैं तो आप जांच लें कि क्या दिन में कोई चेकइन है या नहीं। यदि हां, तो आप कल के चेकइन से visit_id के साथ एक नया चेकइन जोड़ें।यदि नहीं तो आप नए विज़िट_आईडी के साथ विज़िट और नई चेकइन पर नई विज़िट जोड़ते हैं।

तो फिर तुम तुम ऐसा ही कुछ के साथ एक क्वेरी में डेटा प्राप्त कर सकते हैं: SELECT COUNT(id) AS number_of_days, COUNT(DISTINCT visit_id) number_of_visits FROM checkin GROUP BY user, city

यह बहुत इष्टतम नहीं है, लेकिन अभी भी मौजूदा संरचना के साथ कुछ भी करने की तुलना में बेहतर है और यह काम करेंगे। इसके अलावा यदि परिणाम अलग-अलग प्रश्न हो सकते हैं तो यह बहुत तेज़ काम करेगा।

लेकिन निश्चित रूप से कमियां हैं आपको डेटाबेस संरचना को बदलने की आवश्यकता होगी, कुछ और स्क्रिप्टिंग करें और वर्तमान डेटा को नई संरचना में परिवर्तित करें (यानी आपको वर्तमान डेटा में visit_id जोड़ने की आवश्यकता होगी)।

+0

आपके उत्तर के लिए धन्यवाद, लेकिन कम से कम अभी तक, मैं अपने वर्तमान डेटाबेस संरचना से चिपकना चाहता हूं। इसके अलावा मुझे डालने के दौरान कुछ और संचालन करने की आवश्यकता होगी, क्योंकि एक दिन में कई चेक-इन हो सकते हैं, इसलिए "दिन में कोई चेकइन होने पर जांच करें" के साथ यह इतना आसान नहीं है। इस तरह के डेटा मैनिपुलेशन को PHP में प्रदत्त डेटाबेस संरचना के साथ भी बनाया जा सकता है, लेकिन मैं इस काम को करने के लिए एक प्रश्न की तलाश कर रहा था, क्योंकि यह अधिक स्वच्छ और सुविधाजनक है। – linkyndy

3

अपने कार्य करने के लिए इस कोड को लागू करने की कोशिश करो -

CREATE TABLE visits(
    user_id INT(11) NOT NULL, 
    dt DATETIME DEFAULT NULL 
); 

INSERT INTO visits VALUES 
    (1, '2011-06-30 12:11:46'), 
    (1, '2011-07-01 13:16:34'), 
    (1, '2011-07-01 15:22:45'), 
    (1, '2011-07-01 22:35:00'), 
    (1, '2011-07-02 13:45:12'), 
    (1, '2011-08-01 00:11:45'), 
    (1, '2011-08-05 17:14:34'), 
    (1, '2011-08-05 18:11:46'), 
    (1, '2011-08-06 20:22:12'), 
    (2, '2011-08-30 16:13:34'), 
    (2, '2011-08-31 16:13:41'); 


SET @i = 0; 
SET @last_dt = NULL; 
SET @last_user = NULL; 

SELECT v.user_id, 
    COUNT(DISTINCT(DATE(dt))) number_of_days, 
    MAX(days) number_of_visits 
FROM 
    (SELECT user_id, dt 
     @i := IF(@last_user IS NULL OR @last_user <> user_id, 1, IF(@last_dt IS NULL OR (DATE(dt) - INTERVAL 1 DAY) > DATE(@last_dt), @i + 1, @i)) AS days, 
     @last_dt := DATE(dt), 
     @last_user := user_id 
    FROM 
    visits 
    ORDER BY 
    user_id, dt 
) v 
GROUP BY 
    v.user_id; 

---------------- 
Output: 

+---------+----------------+------------------+ 
| user_id | number_of_days | number_of_visits | 
+---------+----------------+------------------+ 
|  1 |    6 |    3 | 
|  2 |    2 |    1 | 
+---------+----------------+------------------+ 

स्पष्टीकरण:

को समझने के लिए कि यह कैसे की सबक्वेरी जाँच करें काम करता है, ये रहा।

SET @i = 0; 
SET @last_dt = NULL; 
SET @last_user = NULL; 


SELECT user_id, dt, 
     @i := IF(@last_user IS NULL OR @last_user <> user_id, 1, IF(@last_dt IS NULL OR (DATE(dt) - INTERVAL 1 DAY) > DATE(@last_dt), @i + 1, @i)) AS 

days, 
     @last_dt := DATE(dt) lt, 
     @last_user := user_id lu 
FROM 
    visits 
ORDER BY 
    user_id, dt; 

आप क्वेरी सभी पंक्तियों वापस आती है और यात्राओं की संख्या के लिए रैंकिंग यह देखने के रूप में। यह चर के आधार पर रैंकिंग विधि ज्ञात है, ध्यान दें कि पंक्तियों का उपयोग उपयोगकर्ता और दिनांक फ़ील्ड द्वारा किया जाता है। 'COUNT (DISTINCT (DATE (:

+---------+---------------------+------+------------+----+ 
| user_id | dt     | days | lt   | lu | 
+---------+---------------------+------+------------+----+ 
|  1 | 2011-06-30 12:11:46 | 1 | 2011-06-30 | 1 | 
|  1 | 2011-07-01 13:16:34 | 1 | 2011-07-01 | 1 | 
|  1 | 2011-07-01 15:22:45 | 1 | 2011-07-01 | 1 | 
|  1 | 2011-07-01 22:35:00 | 1 | 2011-07-01 | 1 | 
|  1 | 2011-07-02 13:45:12 | 1 | 2011-07-02 | 1 | 
|  1 | 2011-08-01 00:11:45 | 2 | 2011-08-01 | 1 | 
|  1 | 2011-08-05 17:14:34 | 3 | 2011-08-05 | 1 | 
|  1 | 2011-08-05 18:11:46 | 3 | 2011-08-05 | 1 | 
|  1 | 2011-08-06 20:22:12 | 3 | 2011-08-06 | 1 | 
|  2 | 2011-08-30 16:13:34 | 1 | 2011-08-30 | 2 | 
|  2 | 2011-08-31 16:13:41 | 1 | 2011-08-31 | 2 | 
+---------+---------------------+------+------------+----+ 

फिर हम समूह इस डेटा उपयोगकर्ता द्वारा सेट और कुल कार्यों का उपयोग - डेटा सेट अगले जहां days स्तंभ यात्राओं की संख्या के लिए रैंक प्रदान करता है यह प्रश्न उपयोगकर्ता का दौरा, और आउटपुट की गणना करता है डीटी))) '- ' MAX (days) 'की संख्या की गणना करता है - विज़िट की संख्या, यह हमारे सबक्वायरी से days फ़ील्ड का अधिकतम मान है।

सब है;)

+0

यह बहुत जटिल लगता है ... क्या आप कृपया अपने कोड पर कुछ और विवरण दे सकते हैं? सरहाना करूँगा! – linkyndy

+0

मैंने कुछ विवरण जोड़े हैं। – Devart

+0

विवरण के लिए धन्यवाद। यह काफी दुखी है कि मैं दो उत्तरों को बक्षीस नहीं दे सकता। हालांकि, मैंने दूसरा जवाब चुना क्योंकि क्वेरी थोड़ा सा सरल है। मैं वास्तव में माफी चाहता हूं और मैं आपके जवाब के लिए फिर से धन्यवाद देना चाहता हूं! – linkyndy

1

Devart द्वारा उपलब्ध कराए गए आंकड़ों नमूना के रूप में, भीतरी "PreQuery" एसक्यूएल चर के साथ काम करता है। @LUser को -1 (संभावित गैर-मौजूद उपयोगकर्ता आईडी) को डिफ़ॉल्ट रूप से डिफ़ॉल्ट करके, IF() परीक्षण अंतिम उपयोगकर्ता और वर्तमान के बीच किसी भी अंतर के लिए जांच करता है। जैसे ही एक नया उपयोगकर्ता, इसे 1 का मान मिलता है ... इसके अतिरिक्त, यदि अंतिम तिथि चेक-इन की नई तारीख से 1 दिन से अधिक है, तो उसे 1 का मान मिलता है। फिर, बाद के कॉलम रीसेट हो जाते हैं @LUser और @LDate आने वाले रिकॉर्ड के मूल्य के लिए अभी अगले चक्र के लिए परीक्षण किया गया है। फिर, बाहरी क्वेरी सिर्फ उन्हें का सार और पहली पहलू के बारे में

User ID Distinct Visits Total Days 
1   3     9 
2   1     2 

select PreQuery.User_ID, 
     sum(PreQuery.NextVisit) as DistinctVisits, 
     count(*) as TotalDays 
    from 
     ( select v.user_id, 
       if(@LUser <> v.User_ID OR @LDate < (date(v.dt) - Interval 1 day), 1, 0) as NextVisit, 
       @LUser := v.user_id, 
       @LDate := date(v.dt) 
      from 
       Visits v, 
       (select @LUser := -1, @LDate := date(now())) AtVars 
      order by 
       v.user_id, 
       v.dt ) PreQuery 
    group by 
     PreQuery.User_ID 
+0

आपके उत्तर के लिए धन्यवाद और इसे स्पष्ट करने के लिए धन्यवाद! – linkyndy

+0

मदद करने में खुशी हुई ... क्या आपको आवश्यक सटीक समाधान प्राप्त हुआ (इस प्रकार उपयोगकर्ता आईडी जानकारी भी शामिल है) मदद के लिए। – DRapp

+0

यह बहुत बुरा था, केवल एक ही जवाब स्वीकार किया जा सकता है और पुरस्कृत किया जा सकता है ... – linkyndy