2011-08-25 8 views
5

मेरे पास निम्न लेआउट वाला एक टेबल है।प्रति माह नाम के लिए एसक्यूएल में मानों के लिए योग की गणना करें

Email Blast Table 

EmailBlastId | FrequencyId | UserId 
--------------------------------- 
1   | 5   | 1 
2   | 2   | 1 
3   | 4   | 1 


Frequency Table 

Id | Frequency 
------------ 
1 | Daily 
2 | Weekly 
3 | Monthly 
4 | Quarterly 
5 | Bi-weekly 

मुझे अपने एएसपीनेट पेज पर ग्रिड डिस्प्ले के साथ आने की आवश्यकता है।

Email blasts per month. 

UserId | Jan | Feb | Mar | Apr |..... Dec | Cumulative 
----------------------------------------------------- 
1  7  6  6  7   6  #xx 

एक ही रास्ता मैं यह कर के बारे में सोच सकते हैं के रूप में नीचे है, प्रत्येक माह के लिए एक मामला बयान किया है।

select SUM(
     CASE WHEN FrequencyId = 1 THEN 31 
     WHEN FrequencyId = 2 THEN 4 
     WHEN FrequencyId = 3 THEN 1 
     WHEN FrequencyId = 4 THEN 1 
     WHEN FrequencyId = 5 THEN 2 END) AS Jan, 
     SUM(
     CASE WHEN FrequencyId = 1 THEN 28 (29 - leap year) 
     WHEN FrequencyId = 2 THEN 4 
     WHEN FrequencyId = 3 THEN 1 
     WHEN FrequencyId = 4 THEN 0 
     WHEN FrequencyId = 5 THEN 2 END) AS Feb, etc etc 
FROM EmailBlast 
Group BY UserId 

एक ही प्राप्त करने का कोई अन्य बेहतर तरीका?

+0

क्या आप वाकई सही डेटा प्रदर्शित कर रहे हैं? सिर्फ इसलिए कि किसी निश्चित समय से बाहर जाने के लिए कुछ कॉन्फ़िगर किया गया है, इसका मतलब यह नहीं है कि यह किया गया है। मैं गलत कर सकता हूं कि आप क्या कर रहे हैं लेकिन मुझे लगता है कि प्रति माह किए गए विस्फोटों की वास्तविक संख्या को गिनना बेहतर होगा ... – NotMe

+0

आपका आखिरी मामला नहीं होना चाहिए जब फ्रीक्वेंसी आईडी = 5 फिर 2 –

+0

@ क्रिस, ये विस्फोट वास्तव में समय सारिणी हैं। डिस्प्ले दिखाता है कि वर्तमान आवृत्तियों के साथ ईमेल कितनी बार भेजा जाएगा। मेरा विश्वास करो, यह एक आवश्यकता है इसलिए यह देखने का कोई मतलब नहीं कि क्या यह सही है या गलत है। वहाँ किया गया था कि। –

उत्तर

2

क्या यह किसी भी वर्ष के लिए है? मैं मानता हूं कि आप चालू वर्ष के लिए शेड्यूल चाहते हैं। यदि आप भविष्य के वर्ष को चाहते हैं तो आप भविष्य की तारीख निर्दिष्ट करने के लिए हमेशा DECLARE @now बदल सकते हैं।

"एक बार 2 सप्ताह में" (एक गैर लीप वर्ष में फरवरी के लिए छोड़कर) मासिक बाल्टी में अच्छी तरह से फिट नहीं करता है (आमतौर पर के रूप में "द्वि-साप्ताहिक" कहा जाता है)। क्या इसे संभवतः "महीने में दो बार" में बदला जाना चाहिए?

इसके अलावा, आवृत्ति तालिका में गुणांक को स्टोर क्यों नहीं करते, "PerMonth" नामक कॉलम जोड़ते हैं? तो फिर तुम केवल दैनिक और त्रैमासिक मामलों से निपटने के लिए (और यह एक मनमाना विकल्प है कि इस जनवरी, अप्रैल में केवल क्या होगा, और इतने पर है?)।

USE tempdb; 
GO 

CREATE TABLE dbo.Frequency 
(
    Id INT PRIMARY KEY, 
    Frequency VARCHAR(32), 
    PerMonth TINYINT 
); 

CREATE TABLE dbo.EmailBlast 
(
    Id INT, 
    FrequencyId INT, 
    UserId INT 
); 

और यह नमूना डेटा:

INSERT dbo.Frequency(Id, Frequency, PerMonth) 
    SELECT 1, 'Daily', NULL 
    UNION ALL SELECT 2, 'Weekly', 4 
    UNION ALL SELECT 3, 'Monthly', 1 
    UNION ALL SELECT 4, 'Quarterly', NULL 
    UNION ALL SELECT 5, 'Twice a month', 2; 

INSERT dbo.EmailBlast(Id, FrequencyId, UserId) 
    SELECT 1, 5, 1 
    UNION ALL SELECT 2, 2, 1 
    UNION ALL SELECT 3, 4, 1; 

हम कर सकते हैं

यह मानते हुए कि इस में से कुछ यहाँ लचीला है, मैं, क्या सुझाव है कि तालिका स्कीमा को यह बहुत मामूली परिवर्तन मानते हुए है इसे एक बहुत ही जटिल क्वेरी का उपयोग करके पूरा करें (लेकिन हमें उन महीनों की संख्या को हार्ड-कोड नहीं करना है):

DECLARE @now DATE = CURRENT_TIMESTAMP; 
DECLARE @Jan1 DATE = DATEADD(MONTH, 1-MONTH(@now), DATEADD(DAY, 1-DAY(@now), @now)); 

WITH n(m) AS 
(
    SELECT TOP 12 m = number 
     FROM master.dbo.spt_values 
     WHERE number > 0 GROUP BY number 
), 
months(MNum, MName, StartDate, NumDays) AS 
( SELECT m, mn = CONVERT(CHAR(3), DATENAME(MONTH, DATEADD(MONTH, m-1, @Jan1))), 
     DATEADD(MONTH, m-1, @Jan1), 
     DATEDIFF(DAY, DATEADD(MONTH, m-1, @Jan1), DATEADD(MONTH, m, @Jan1)) 
    FROM n 
), 
grp AS 
(
    SELECT UserId, MName, c = SUM (
     CASE x.Id WHEN 1 THEN NumDays 
      WHEN 4 THEN CASE WHEN MNum % 3 = 1 THEN 1 ELSE 0 END 
      ELSE x.PerMonth END) 
    FROM months CROSS JOIN (SELECT e.UserId, f.* 
     FROM EmailBlast AS e 
     INNER JOIN Frequency AS f 
     ON e.FrequencyId = f.Id) AS x 
    GROUP BY UserId, MName 
), 
cumulative(UserId, total) AS 
(
    SELECT UserId, SUM(c) 
     FROM grp GROUP BY UserID 
), 
pivoted AS 
(
    SELECT * FROM (SELECT UserId, c, MName FROM grp) AS grp 
    PIVOT(MAX(c) FOR MName IN (
     [Jan],[Feb],[Mar],[Apr],[May],[Jun],[Jul],[Aug],[Sep],[Oct],[Nov],[Dec]) 
    ) AS pvt 
) 
SELECT p.*, c.total 
    FROM pivoted AS p 
    LEFT OUTER JOIN cumulative AS c 
    ON p.UserId = c.UserId; 

परिणाम:

UserId Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec total 
1  7 6 6 7 6 6 7 6 6 7 6 6 76 

साफ अप:

DROP TABLE dbo.EmailBlast, dbo.Frequency; 
GO 

वास्तव में स्कीमा परिवर्तन मैं वास्तव में आप ज्यादा नहीं खरीदती सुझाव दिया, यह सिर्फ आप grp CTE के अंदर दो अतिरिक्त CASE शाखाओं की बचत होती है। मूंगफली, कुल मिलाकर।

+0

मेरा सिर उन सभी विचारों के साथ फट रहा है जो मुझे इसमें डालने की ज़रूरत है :) .. धन्यवाद, मैं एक ब्रेक लेने जा रहा हूं और आज बाद में इसे देख रहा हूं। –

+1

अच्छा काम हारून। आपने उसे काम करने के लिए बहुत कुछ दिया। –

+0

मैं अभी इस जवाब को स्वीकार करने जा रहा हूं। यह निकटतम समाधान की तरह दिखता है। हालांकि मैं कोशिश करने जा रहा हूं और आवश्यकता को अनुमानित संख्या में बदल गया हूं, शायद 100% सटीक नहीं। अधिकांश चीजों के साथ अच्छा एसक्यूएल मैंने देखा नहीं है। –

3

मुझे लगता है कि आप एक बहुत अधिक जटिल तर्क के साथ समाप्त करने के लिए जा रहे हैं। निश्चित रूप से जनवरी में 31 दिन हैं .. लेकिन फरवरी नहीं है ... और फरवरी साल के आधार पर बदलती है। इसके बाद, ईमेल विस्फोट सप्ताहांत और छुट्टियों पर भी भेजे जाते हैं या कुछ कारणों से अलग-अलग दिन छोड़ दिए जाते हैं ... यदि ऐसा है तो किसी दिए गए महीने के लिए व्यावसायिक दिन की संख्या प्रत्येक वर्ष बदल जाती है।

अगला खास महीने में पूरा सप्ताह की संख्या भी वर्ष वर्ष बदल जाता है। उन अतिरिक्त 4 आधे हफ्तों के साथ क्या होता है? क्या वे वर्तमान या अगले महीने जाते हैं? यह निर्धारित करने के लिए आप किस विधि का उपयोग कर रहे हैं? उदाहरण के लिए यह कितना जटिल हो जाता है: http://en.wikipedia.org/wiki/ISO_week_date विशेष रूप से वह हिस्सा जहां यह पहले सप्ताह के बारे में बात करता है, जिसमें वास्तव में 9 अलग-अलग परिभाषाएं होती हैं।

मैं आमतौर पर एक नहीं यह कहना चाहता हूँ, लेकिन आप एक एसक्यूएल क्वेरी के बजाय नियमित कोड के साथ इस लेखन से बेहतर हो सकता है। बस ईमेलब्लैस्ट से 'select * जारी करें जहां userid = xxx' और विभिन्न प्रकार के कोड विधियों का उपयोग करके इसे बदलें।

+0

काफी ईमानदारी से, मैं भी यहां एक नुकसान में हूँ। शायद कोड में ऐसा करने के लिए बेहतर है, लेकिन कैसे, यह एक ही समस्या नहीं होगी। ऐसा नहीं लगता था कि जब मैंने इसे पहले देखा तो यह जटिल होगा। –

+0

@Alex J: हाँ, आपको वही समस्याएं हैं। इससे निपटने के लिए बस बेहतर/तेज उपकरण। – NotMe

2

आप एक 3 तालिका अनुसूची जैसा कुछ कहा जोड़ने पर विचार कर सकते हैं।

आप इस तरह यह संरचना सकता है:

MONTH_NAME 
DAILY_COUNT 
WEEKLY_COUNT 
MONTHLY_COUNT 
QUARTERLY_COUNT 
BIWEEKLY_COUNT 

जन के लिए रिकॉर्ड

JAN 
31 
4 
1 
1 
2 

होगा या आप इसे इस तरह की संरचना कर सकते हैं:

और कई रिकॉर्ड प्रत्येक महीने के लिए:

JAN 1 31 
JAN 2 4 
JAN 3 1 
JAN 4 1 
JAN 5 2 

मैं तुम्हें यह पता लगाने अगर तर्क को पुनः प्राप्त करने के लिए इस अपने मामले संरचना की तुलना में बेहतर है करते हैं।

+1

आपको फरवरी के लिए प्रत्येक वर्ष के लिए एक की आवश्यकता होगी और यह भी हर साल तकनीकी रूप से हर महीने तकनीकी रूप से गिर जाएगी। –

+1

सच है। कैलेंडर के आधार पर बदलती गणनाओं के साथ बहुत अधिक रखरखाव, लेकिन शायद कोड के बजाय तालिका में ऐसा करने के लिए बेहतर हो सकता है। –

+0

ठीक है, कोड हमेशा यह बता सकता है कि यह किस वर्ष है, या आप इसे बता सकते हैं कि कौन सा वर्ष उपयोग करना है यदि आप नहीं चाहते हैं कि यह चालू वर्ष में फरवरी की गणना करे। –

3

आप जो खोज रहे हैं पर निर्भर करता है। सुझाव 1 आपके वास्तविक ईमेल विस्फोटों को ट्रैक करना होगा (दिनांक :-) के साथ।

वास्तविक तिथियों के बिना, जो भी आप एक महीने के लिए आते हैं, वह हर महीने के लिए समान होगा।

वैसे भी, यदि आप सामान्यीकृत करने जा रहे हैं, तो मैं चींटियों के अलावा किसी अन्य चीज़ का उपयोग करने का सुझाव दूंगा - जैसे कि फ्लोट या डेसिमल। चूंकि आपकी पोस्ट में सूचीबद्ध टेबलों के आधार पर आपका आउटपुट केवल अनुमान लगा सकता है कि वास्तव में क्या होता है (उदाहरण के लिए, जनवरी में वास्तव में 4-1/2 सप्ताह होते हैं, 4 नहीं), आपके पास महीनों की किसी भी सीमा पर एक जटिल त्रुटि-सीमा होगी - - बदतर हो रहा है, आगे आप बाहर निकालना। यदि आप पूरे 12 महीनों में आउटपुट करते हैं, उदाहरण के लिए, आपका एक्सट्रापोलेशन 4 सप्ताह से कम अनुमान लगाएगा।

यदि आप फ्लोट या दशमलव का उपयोग करते हैं, तो आप वास्तव में क्या होता है इसके करीब आते हैं। शुरुआत के लिए: माप की एक आम इकाई खोजें (मैं "दिन" का उपयोग करने का सुझाव दूंगा) उदा।, 1 महीने = 365/12 दिन; 1 तिमाही = 365/4 दिन; 1 2week = 14 दिन; आदि

यदि आप ऐसा करते हैं - तो आपके उपयोगकर्ता के पास प्रति तिमाही 1 था, वास्तव में 1 प्रति 91.25 दिन था; प्रति सप्ताह 1 प्रति 7 दिनों में बदल जाता है; 1 प्रति बायविच 1 प्रति 14 दिनों में बदल जाता है। , (1/91,25 + 1/7 + 1/14) एक आम denom (शायद 91.25 * 14) की तरह की जरूरत है, तो यह हो जाता है (14/1277,5 + 182,5/1277,5 + 91,25 -

**EDIT** -- Incidentally, you could store the per-day value in your reference table, so you didn't have to calculate it each time. For example: 
Frequency Table 

Id | Frequency   | Value 
------------------------------- 
1 | Daily   | 1.0 
2 | Weekly   | .14286 
3 | Monthly   | .03288 
4 | Quarterly  | .01096 
5 | Once in 2 weeks | .07143 

अब गणित /1277.5)।

जो 287.75/1277.5, या प्रति दिन 225 ईमेल जोड़ता है।

चूंकि प्रति माह 365/12 दिन हैं, एकाधिक 2222 * (365/12) प्रति माह 6.85 ईमेल प्राप्त करने के लिए।

आपका आउटपुट तो कुछ इस तरह दिखेगा:

Email blasts per month. 

UserId | Jan | Feb | Mar | Apr |..... Dec | Cumulative 
----------------------------------------------------- 
1  6.85 6.85 6.85 6.85  6.85  #xx 

गणित एक छोटे से कठिन लग सकता है, लेकिन एक बार आप इसे अपने कोड पर बाहर कदम, आप इसे फिर से नहीं करना पड़ेगा। आपके परिणाम अधिक सटीक होंगे (मैं 2 दशमलव स्थानों पर गोल करता हूं, लेकिन यदि आप चाहते थे तो आप आगे जा सकते हैं)। और यदि आपकी कंपनी आने वाले वर्ष के लिए बजट/संभावित आय निर्धारित करने के लिए इस डेटा का उपयोग कर रही है, तो यह इसके लायक हो सकती है।

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

+0

आप 'DECIMAL' के बजाय 'फ़्लोट' का उपयोग क्यों करेंगे? मुझे नहीं लगता कि आपको अनुमान के उस स्तर की आवश्यकता है। :-) –

+0

@Aaron - अच्छा बिंदु - मुझे लगता है कि अगर आपको कैलक्यूड वैल्यू को फ्रीक्वेंसी में कनवर्ट करने की आवश्यकता है, तो दशमलव बेहतर होगा। लेकिन किसी भी तरह से ... जब तक यह एक int नहीं है :-)। – Chains

संबंधित मुद्दे